Guide Area
Guidearea banner

Find classes in Android project or application path

During the internship in 5th semester of university’s Computer Science program, I had to program an extendable Android application. What I mean by this is that I did not program the application the usual way. There was one main activity that scanned the application for other Activity classes in a package called ‘plugins’.

Each activity had its own resource file, layout file, icon and name. The main activity loaded these activities and added them into the toolbar menu. This way, the only thing to add a new functionality to the application was to create a new plugin activity together with its resource files and an icon, and the application took care of the rest.

In order to do so, I needed to create an algorithm that would scan the project files at the run-time. I had a superclass called Plugin that defined all the Plugin classes. These plugin classes included the plugin’s activity and resources paths. These classes had a name that started with ‘Plugin’, so for example ‘PluginUsers’, and were all located in package called ‘com.xxx.xxx.plugins’. There is an attribute ‘defaultPlugin’ in each plugin which tells us that the plugin is default and should be shown as the activity after MainActivity. There can be more plugins set to default; the scanner algorithm will consider the last default scanned plugin to be really default. Although, there has to be at least one plugin which is set to defaultPlugin = true;

Plugins plugins = Plugins.getInstance();
String packageCodePath = context.getPackageCodePath();
DexFile df = new DexFile(packageCodePath);
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {

    String className = iter.nextElement();
    if (className.contains("plugins.Plugin")) {
        //creates short name of the class, later to be saved as key value in map of plugins
        String pluginNameShort = className.substring(className.lastIndexOf(".") + 1, className.length());
        Class<?> clazz;
        clazz = Class.forName(className);
        //checks is the clazz is subclass of Plugin class
        if (Plugin.class.isAssignableFrom(clazz)) {
            Plugin p = (Plugin) clazz.newInstance();
            activityClass = Class.forName(className.substring(0,className.lastIndexOf(".") + 1)+p.getActivityFileName());
            //check if activity exists in plugin
            if (activityClass!=null){

                int layout =  context.getResources().getIdentifier(p.getXmlLayoutName(), "layout", context.getPackageName());
                int icon =  context.getResources().getIdentifier(p.getIconName(), "drawable", context.getPackageName());
                //checks if fields from plugin exist, if yes plugin is considered to be valid
                if (layout>0&&icon>0){
                    // Check if the class claimed to be Activity really is Activity
                    if (activityClass.newInstance() instanceof Activity){
                        plugins.addPlugin(pluginNameShort, p);
                        lastActivityClass = activityClass;
                        pluginCounter++;
                        if (p.isDefaultPlugin()){
                            isDefaultPluginSet =true;
                            defaultPluginClass = activityClass;
                        }
                    }
                }
            }
        }
    }
}

The singleton Plugins only took care of storing of plugins in a collection. The superclass Plugin that each plugin had to extend looks like this:

public abstract class Plugin {
    //return the name of plugin
    public  abstract String getName();

    //return the filename of plugins activity
    public abstract  String getActivityFileName();

    //return icon name from drawable
    public  abstract  String getIconName();

    // returns name of layout xml file
    public abstract String getXmlLayoutName();

    // returns name of resource xml file
    public abstract String getXmlResourceName();

    // returns true if plugin in the default one
    public  abstract boolean isDefaultPlugin();
}

So let’s see an example. My plugin PluginUsers has all the necessary attributes and it extends the plugin class, that is why it will be loaded as an activity. It’s also specified as default, so unless there are other plugins that are set to default too, this activity will be loaded after the plugin scan is complete. It is really up to you to make sure that all the resources are programmed properly, but if you do, you will have a fully functional plugin that gets inserted into the application. If you’d like to deactivate the plugin, you can simply remove the subset ‘Plugin’ from the name of the plugin, or replace it with other letters or words.

public class PluginUsers extends Plugin {
    private static final String name = "Users";
    private static final String activityFileName = "users.UsersActivity";
    private static final String iconName = "users";
    private static final String xmlLayoutFileName = "activity_users";
    private static final String xmlResourceFileName = "plugin_users";
    private boolean isDefault = true;

    public PluginUsers() {
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getActivityFileName() {
        return activityFileName;
    }

    @Override
    public String getIconName() {
        return iconName;
    }

    @Override
    public String getXmlLayoutName() {
        return xmlLayoutFileName;
    }

    @Override
    public String getXmlResourceName() {
        return xmlResourceFileName;
    }

    @Override
    public boolean isDefaultPlugin() {
        return isDefault;
    }
}

You will probably not come across a requirement to design an application this way. I was asked to find a way to make this work as it was a requirement from my internship company. It is not the best way to program and it does not find many applications in real life, althoughย it is interesting to see that things can be done this way too.

Vladimir Marton

DevOps Engineer focused on cloud infrastructure, automation, CI/CD and programming in Javascript, Python, PHP and SQL. Guidearea is my oldest project where I write articles about programming, marketing, SEO and others.