How to add a view to an app from another app

My app is called MyNiceApp . MyNiceApp is just a kernel that loads a view called MainActivity . populated with views from other plugins that the user uploads at will. I define various areas of the main view that can be populated with plugins via interfaces in MyNiceApp. How to load and transfer Views from plugins to ? coreView

onCreate

coreView

coreView

I was told that RemoteViews is a good option, but I don't know how to implement it. What other options are there?

Are RemoteViews the best way to go? I'm willing to try anything that will work, even if it's not the best approach. Hack will be. For now, anything that can serve this functionality will be sufficient. Improvements can be made later.

Thanks everyone in advance.

UPDATE

I think they are hosted on my personal server. They will be downloaded to a dedicated folder called /data/app/com.myniceapp.plugins

I think it would be better organized if I had a folder created at /data/app/com.myniceapp./plugins then DexClassLoader has crawl / data / app / com.myniceapp / plugins for loaded plugins then I could call the implementation of the class and dynamically load the plugin views into the main view at runtime.

TEMPORARY UPDATE

Hi Leleoman and everyone else. I tried to make your decision, but so far I have not been successful.

I created a new project called Test View . It has an XML layout that I am trying to inflate and send to Core View like this:

package rev.ca.testview;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;

import ca.rev.libs.core.MyViewCreator;

public class TestView implements MyViewCreator {
    @Override
    public View createMyView(Context context) {
        LayoutInflater revInfl = LayoutInflater.from(context);
        View toolBarItemsLL = revInfl.inflate(R.layout.layout, null, false);

        Button button = (Button) toolBarItemsLL.findViewById(R.id.testButton);
        return button;
    }
}  

      

This does not work, however. Here's the rest:

In the MainActivity view, which should receive views from plugins:

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);

String dexPath = "/data/app/rev.ca.testview";
String optimizedDirectory = this.getCacheDir().getAbsolutePath();
String libraryPath = null;

DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, null, ClassLoader.getSystemClassLoader());
DexFile dexFile = null;
try {
    dexFile = DexFile.loadDex(dexPath, File.createTempFile("opt", "dex", this.getCacheDir()).getPath(), 0);

    for (Enumeration<String> classNames = dexFile.entries(); classNames.hasMoreElements(); ) {
        String className = classNames.nextElement();
        Class myClass = dexClassLoader.loadClass(className);
        if (myClass.isAssignableFrom(MyViewCreator.class)) {
            MyViewCreator creator = (MyViewCreator) myClass.getConstructor().newInstance();
            View myView = creator.createMyView(this);
                    // add myView wherever you want
            navigationView.addView(myView);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}  

      


FEEDBACK SOFTWARE UPDATE

Hello leleoman again. I was trying to implement my solution in my main project with Drawer layout. It breaks down when final View toolBarItemsLL = revInfl.inflate(R.layout.activity_main, null, false);

.

Why won't it work with Layer Layout. If you add Navigation Drawer Activity ( android-plugins/MyNiceApps/app/src/main/

: New -> Activity -> Navigation Drawer Activity), that is when it all falls apart. Hope you can help.

Here is StackTrace:

08-14 21:44:27.564 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f0b005e, entry index(94) is beyond type entryCount(9)
08-14 21:44:27.564 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f0b005e (t=10 e=94) (error -75)
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f0a002c, entry index(44) is beyond type entryCount(5)
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f0a002c (t=9 e=44) (error -75)
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f060022, entry index(34) is beyond type entryCount(1)
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f060022 (t=5 e=34) (error -75)
08-14 21:44:27.565 13390-13390/rev.ca.revcore D/AndroidRuntime: Shutting down VM
08-14 21:44:27.566 13390-13390/rev.ca.revcore E/AndroidRuntime: FATAL EXCEPTION: main
                                                                Process: rev.ca.revcore, PID: 13390
                                                                android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class TextView
                                                                Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class TextView
                                                                Caused by: java.lang.UnsupportedOperationException: Can't convert to ComplexColor: type=0x1
                                                                    at android.content.res.ResourcesImpl.loadComplexColorForCookie(ResourcesImpl.java:879)
                                                                    at android.content.res.ResourcesImpl.loadComplexColorFromName(ResourcesImpl.java:756)
                                                                    at android.content.res.ResourcesImpl.loadColorStateList(ResourcesImpl.java:835)
                                                                    at android.content.res.Resources.loadColorStateList(Resources.java:1002)
                                                                    at android.content.res.TypedArray.getColorStateList(TypedArray.java:531)
                                                                    at android.widget.TextView.<init>(TextView.java:1076)
                                                                    at android.widget.TextView.<init>(TextView.java:704)
                                                                    at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:62)
                                                                    at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:58)
                                                                    at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:103)
                                                                    at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1029)
                                                                    at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1087)
                                                                    at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:47)
                                                                    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:769)
                                                                    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
                                                                    at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
                                                                    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
                                                                    at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
                                                                    at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
                                                                    at rev.ca.revbags.MyViewCreator.createView(MyViewCreator.java:24)
                                                                    at rev.ca.revcore.rev_plugin_loader.RevPluginLoader.revLoadView(RevPluginLoader.java:36)
                                                                    at rev.ca.revcore.RevCoreMainActivity$1.handleMessage(RevCoreMainActivity.java:21)
                                                                    at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                    at android.os.Looper.loop(Looper.java:154)
                                                                    at android.app.ActivityThread.main(ActivityThread.java:6119)
                                                                    at java.lang.reflect.Method.invoke(Native Method)
                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

      

+3


source to share


1 answer


First, it seems you don't need a RemoteView at all, you can create a view in your process and attach them to an Activity.

Then you need to clarify one thing with you: do you need to load classes at runtime? This would be the case if you want to use the new classes in your application without updating it. This is not trivial, you will be forced to use some of the already defined interfaces in your application, or scan your way through it with reflection. It would hurt.

Another, much simpler option would be to load an xml layout for each of your views and an associated config file that could describe some of the behaviors. If you choose to load classes at runtime from an external plugin, you can go like this:

  • define the ViewCreator class inside the library
  • when you want to create a plugin you need to make an apk that contains one (or more?) of this ViewCreator class
  • in your application you then download the apk using DexClassLoader find the ViewCreator class and create it
  • the ViewCreator instance can then generate your View

This approach gives you tremendous power, far beyond simple representation, but it also comes with a lot of complexity. If you are doing this for fun, I think you are on the right track, but I would not recommend doing this for a commercial project. I created a sample repository with a minimal working example here .

The main part of this approach is that you create a "plugin" library that contains common interfaces for your application and each plugin. In my example, the library contains only one class:

public abstract class AbstractViewCreator {

    private final Context context;

    public AbstractViewCreator(Context context) {
        this.context = context;
    }

    public abstract View createView();

    protected Context getContext() {
        return context;
    }
}

      

Then you need to create a "plugin" application, the sample includes PluginA. This application must contain one implementation AbstractViewCreator

. Then NiceApp needs to download the apk of the plugin, in the sample apk is copied to the folder with resources. After that, you need to download apk:



DexClassLoader dexClassLoader = new DexClassLoader(apkPath, codeCachePath, librariesPath, parentClassLoader);

      

then you need to load the class, you can get the name of the class you want to load, for example:

String className = "com.lelloman." + assetsFileName.replace(".apk", "").toLowerCase() + ".MyViewCreator");
Class fooClass = dexClassLoader.loadClass(className);

      

Then get the constructor and instantiate the ViewCreator using reflection

Constructor constructor = myClass.getConstructor(Context.class);
AbstractViewCreator creator = (AbstractViewCreator) constructor.newInstance(context);

      

then you can create your View

View viewFromPlugin = creator.createView();

      

+1


source







All Articles