{"id":19860816,"url":"https://github.com/rikkaapps/hiddenapirefineplugin","last_synced_at":"2025-04-05T15:07:51.124Z","repository":{"id":37700225,"uuid":"362680249","full_name":"RikkaApps/HiddenApiRefinePlugin","owner":"RikkaApps","description":"A Gradle plugin that improves the experience when developing Android apps, especially system tools, that use hidden APIs.","archived":false,"fork":false,"pushed_at":"2023-10-29T06:21:05.000Z","size":233,"stargazers_count":289,"open_issues_count":7,"forks_count":17,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-29T14:11:15.061Z","etag":null,"topics":["android"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RikkaApps.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-04-29T03:38:40.000Z","updated_at":"2025-03-28T02:36:10.000Z","dependencies_parsed_at":"2023-10-16T16:18:59.387Z","dependency_job_id":"23223ef7-9401-4d31-b3f6-2d63efedf5e1","html_url":"https://github.com/RikkaApps/HiddenApiRefinePlugin","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RikkaApps%2FHiddenApiRefinePlugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RikkaApps%2FHiddenApiRefinePlugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RikkaApps%2FHiddenApiRefinePlugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RikkaApps%2FHiddenApiRefinePlugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RikkaApps","download_url":"https://codeload.github.com/RikkaApps/HiddenApiRefinePlugin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247353746,"owners_count":20925329,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android"],"created_at":"2024-11-12T15:07:17.663Z","updated_at":"2025-04-05T15:07:51.104Z","avatar_url":"https://github.com/RikkaApps.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HiddenApiRefinePlugin\n\nA Gradle plugin that improves the experience when developing Android apps, especially system tools, that use hidden APIs.\n\n## Background\n\nWhen developing system tools, it's inevitable to use hidden APIs. There are two ways to use hidden APIs, the reflection way and the linking way. For some types of system tools, a large amount of hidden APIs is required, using the reflection way is too inconvenient. So the linking way is commonly used in those projects.\n\nIn short, the linking way is to create Java-only modules with stub classes and then `compileOnly` them in the main Android modules. This is the same as what Android SDK's `android.jar` does.\n\nHowever, the linking way, or \"the stub way\", have some problems:\n\n1. \"Bridge classes\" is required if only some members of the class are hidden.\n2. Kotlin will try to link public classes from the stub module, `implementation` a second Java-only which `compileOnly` the stub module can workaround the problem.\n3. Interface implementation will be removed by R8 if the stub module is not `compileOnly` directly from the main module, however doing this will bring back problem 2.\n\nThis plugin is to solve these problems.\n\n## What do this plugin do\n\nThis plugin uses the Transform API of the Android Gradle Plugin to create a transform that modifies class names, the names are user specified by annotation `@RefineAs`. So that all the problems will be gone and the stub module does not need to be a Java-only module.\n\nThe idea and the implementation is from [@Kr328](https://github.com/Kr328).\n\n## Usage\n\n![gradle-plugin](https://img.shields.io/maven-central/v/dev.rikka.tools.refine/gradle-plugin?label=gradle-plugin)\n![annotation-processor](https://img.shields.io/maven-central/v/dev.rikka.tools.refine/annotation?label=annotation-processor)\n![annotation](https://img.shields.io/maven-central/v/dev.rikka.tools.refine/annotation?label=annotation)\n![runtime](https://img.shields.io/maven-central/v/dev.rikka.tools.refine/runtime?label=runtime)\n\nReplace all the `\u003cversion\u003e` below with the version shows here.\n\n### Create \"hidden-api\" module\n\n1. Create a new Android library module\n\n2. Add annotation dependency\n\n   ```gradle\n   dependencies {\n       annotationProcessor 'dev.rikka.tools.refine:annotation-processor:\u003cversion\u003e'\n       compileOnly 'dev.rikka.tools.refine:annotation:\u003cversion\u003e'\n   }\n   ```\n\n3. Add the hidden classes\n\n   Here, we use a hidden `PackageManager` API as the example.\n\n   ```java\n   package android.content.pm;\n\n   import dev.rikka.tools.refine.RefineAs;\n\n   @RefineAs(PackageManager.class)\n   public class PackageManagerHidden {\n       public interface OnPermissionsChangedListener {\n           void onPermissionsChanged(int uid);\n       }\n\n       public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {\n           throw new RuntimeException(\"Stub!\");\n       }\n       public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) {\n           throw new RuntimeException(\"Stub!\");\n       }\n   }\n   ```\n\n   This line `@RefineAs(PackageManager.class)` will let the plugin renames `android.app.PackageManagerHidden`/`android.app.PackageManagerHidden.OnPermissionsChangedListener` to `android.app.PackageManager`/`android.app.PackageManager.OnPermissionsChangedListener`.\n\n### Use hidden API\n\n1. Apply this plugin in the final Android application module\n\n   ```groovy\n   plugins {\n       id('dev.rikka.tools.refine') version '\u003cversion\u003e'\n   }\n   ```\n   \n   If your project includes mulitple modules, you need to apply this plugin to all the module that uses hidden APIs.\n   \n   There is a convenient way to do this, add this to your root `build.gradle`:\n\n   ```groovy\n   subprojects {\n       plugins.withId(\"com.android.base\") {\n           plugins.apply('dev.rikka.tools.refine')\n       }\n   }\n   ```\n\n\n2. Import \"hidden-api\" module with `compileOnly`\n\n   ```groovy\n   dependencies {\n       compileOnly project(':hidden-api')\n   }\n   ```\n\n3. Import the \"Refine\" class (Optional, if you don't need the \"unsafeCast\" method)\n\n   ```gradle\n   dependencies {\n       implementation(\"dev.rikka.tools.refine:runtime:\u003cversion\u003e\")\n   }\n   ```\n\n4. Use the hidden API\n\n   ```java\n   Refine.\u003cPackageManagerHidden\u003eunsafeCast(context.getPackageManager())\n                .addOnPermissionsChangeListener(new PackageManagerHidden.OnPermissionsChangedListener() {\n                    @Override\n                    public void onPermissionsChanged(int uid) {\n                        // do staff\n                    }\n                });\n   ```\n\n   After R8, this \"cast\" will be \"removed\" or \"inlined\". This line will be identical to a normal method call.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frikkaapps%2Fhiddenapirefineplugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frikkaapps%2Fhiddenapirefineplugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frikkaapps%2Fhiddenapirefineplugin/lists"}