📜 ⬆️ ⬇️

We write plugins for Android

On duty, I’ve been writing for one and a half years to write for the Android platform, and my knowledge in this area seems to be extensive, but the volume of topics already covered is not small in Habré either. In general, after much deliberation, I decided to tell the habra people about this topic.

Introduction

So, for example, you want to make downloadable game levels or individual themes for your application. This can be done in three ways :

With downloading additional files from the Internet, everything seems to be clear, the main thing is to provide a good channel for a popular application. A separate application requires less material costs, but a little more intellectual.

Implementation

In the main application, all the necessary logic is implemented, in the plugin application there are additional resources, and in addition to them you can slightly alter the main Activity, which will inform the user that this is just a plugin and will send it to the main application. You can do it like this:
Intent intent = new Intent(); intent.setClassName("package.name", "package.name.LauncherActivityName"); startActivityForResult(intent,REQUEST_CODE); 

The easiest way to name packages of plug-ins is by adding a single domain to the parent package. Then in order to get a list of plug-ins from the main application, we make a request for all installed applications containing the name of the current package.
 ArrayList<String> packs = new ArrayList<String>(); PackageManager mngr = context.getPackageManager(); List<PackageInfo> list = mngr.getInstalledPackages(0); for (PackageInfo packageInfo : list) { if (packageInfo.packageName.indexOf(context.getPackageName()) != -1 && !packageInfo.packageName.equals(DONATE_PACK) && !packageInfo.packageName.equals(FREE_PACK)) { packs.add(packageInfo.packageName); 

Perhaps a bit redundant piece of code. In addition to a simple check for the compliance of the package name with our name, 2 applications are indicated here: the FREE & DONATE version of my application. Knowing the package names, we can access application resources:
 PackageManager mngr = getPackageManager(); res = mngr.getResourcesForApplication(pack); if (res != null) {// use it!} 

Reef number one times

When I implemented this for the first time, I tried to simply access the necessary resource through R.string, R.id, etc. But naturally (now - naturally) the numbering in the R file of other apk is different, and first we need to get the id of the resource we need with the help of:
 int id = res.getIdentifier("app_name", "string", pack); String name = res.getString(id); 

In the example above, we got an id, and then read the resulting resource from an object of the Resources type that we already have. I use reading strings if I need to provide the user with a listing of available plugins. In this case, we must provide him with localized names, adding them to R.strings (see any e-sample “Writing the first application ..”).
At the same time, if we want to read layouts, then we can also call them fixed names, and then get them from resources in a similar way, but here we stumble upon ...

Reef number two

After we make an inflate to our layout, wherever in xml links to resources are used, resources from our main application will be used. Those. if in the layout'e from the plugin on the button there was a certain background = "@ drawable / best_bg", then after reading the layout'a, the element will not go into the background of the plugin’s resources, but something from the main project with the same id, if it is found at all, otherwise it’s crash. Avoid this by:
 drawID = res.getIdentifier(layoutName + "_btn", "drawable", plugins[i]); Bitmap bmp = BitmapFactory.decodeResource(res, drawID); NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null); NinePatchDrawable drawable = new NinePatchDrawable(patch); btn.setBackground(drawable); 

Total: we get the resources by handles from the resources of the plugin and assign them to the received (as well as handles) views. In this case, in order to get instances of elements lying in this layout, I use the construction:
 view.findViewWithTag(tag); 

All for the same reason - we cannot access it through R.id.
And finally.
')
Reef number three

As you saw above - I read the NinePatch resources, but if you just add these ninepatch.9.png to the plugin project, then you will be surprised - the images will be stretched like ordinary images. The bottom line is that adb compiles ninepatch'i - only after that we can use them. And apparently when we refer to the drawable in the plugin, we are given the original non-compiled resources. You can fix it, the recipe is:
  1. Compile plugin project
  2. Unarchive it
  3. We get ninepatch'i and copy with replacement in the project
  4. Compile again, profit

Conclusion

I ask you not to judge strictly - this is my first post on Habré and I didn’t figure out everything, for example, how to indent code and why, despite the closed code tag, the text of the article starting from a certain line has the style of the code. I hope the above will be useful to someone. I apply this method successfully on all platforms 1.6+, nothing is buggy and does not fall.
The only disadvantage of this approach is plug-ins that lie as separate applications. But even in the android market there is a separate category for libraries (Miscellaneous / Libraries and demos).
UPD: Thanks to user andycaramba for help with formatting.

Source: https://habr.com/ru/post/139379/


All Articles