Multiple Apps use same content provider

Solution 1:

It's an old question, but I was looking at doing something similar recently. With the Build flavours, its really straight forward now.

Specify the BuildConfigField in the gradle file:

    productFlavors {
    free {
        applicationId "com.example.free"
        buildConfigField 'String', 'AUTHORITY', '"com.example.free.contentprovider"'
    }

    paid {
        applicationId "com.example.paid"
        buildConfigField 'String', 'AUTHORITY', '"com.example.paid.contentprovider"'
    }

Specify the provider authority in the manifest:

    <provider
        android:name=".ContentProvider"
        android:authorities="${applicationId}.contentprovider" />

Set the authority in the provider using the BuildConfigField Variable:

    public static final String AUTHORITY = BuildConfig.AUTHORITY

Solution 2:

ContentProviders are identified by the authority, so it needs to be unique. I don't think there are any tricks around that.

Additionally, there's a bug in the Android platform that also prevents using the same classname for two different ContentProviders, even if they have different authority and are contained in separate APKs. See the bug here.

The solution I advise for you is to create the abstract provider class in your library project, and then extend it with a unique name in each of the individual applications. To make this practical you will probably need to create a script to generate/modify the individual manifests and contentprovider classes.

Hope this helps.

Solution 3:

YOU CAN!

As said in this post (wich explains how Firebase initializes its library without giving it a context from your Application#onCreate() method), you can use a placeholder in your manifest, like this:

    <provider
         android:authorities="${applicationId}.yourcontentprovider"
         android:name=".YourContentProvider" />

Solution 4:

Lets say your library package is com.android.app.library free package is com.android.app.free paid package is com.android.app.paid

In your free project and paid project, make an identical file in a package which can be anything, but must be the same.

Example:

  1. Create a new package in your free version with com.android.app.data

  2. Create a file called Authority.java and inside (Authority.java) put:

    public class Authority {

    `public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";`
    

    }

  3. Repeat this for the paid version, remember to keep the package name the same and class name.

Now, in your contract file, in your library use the following:

public static String AUTHORITY = initAuthority();

    private static String initAuthority() {
        String authority = "something.went.wrong.if.this.is.used";

        try {

            ClassLoader loader = Contract.class.getClassLoader();

            Class<?> clz = loader.loadClass("com.android.app.data.Authority");
            Field declaredField = clz.getDeclaredField("CONTENT_AUTHORITY");

            authority = declaredField.get(null).toString();
        } catch (ClassNotFoundException e) {} 
        catch (NoSuchFieldException e) {} 
        catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }

        return authority;
    }

    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);

Now you should be able to use two authorities.

Credit: Ian Warick (code write up) Android - Having Provider authority in the app project Disclaimer: I posted it here as well: Android duplicate provider authority problem - Not sure if allowed to answer the same type of question with the same answer.

Solution 5:

The following way can be used to package a ContentProvider within a library and set the ContentProvider's authority at runtime, so that it can be included into multiple projects without ContentProvider Authority conflict. This works because the real 'authority' comes from the AndroidManifest...not the ContentProvider class.

Start with the basic ContentProvider implementation..AUTHORITY, CONTENT_URI and UriMatcher are static, but not 'final'....

public class MyContentProvider extends ContentProvider {
    public static String  AUTHORITY = "com.foo.bar.content";
    public static Uri     CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    protected static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

Then, override the 'attachInfo' method, so that when the ContentProvider is first initialized, your ContentProvider will be called with the ProviderInfo that's gleaned from the AndroidManifest. This will occur BEFORE any possible queries are made, most likely during the initial Application class setup. Use this opportunity to reset the AUTHORITY, CONTENT_URI and UriMatcher to their 'real' values, as provided by the Application that's using the ContentProvider library.

    @Override
public void attachInfo(Context context, ProviderInfo info) {
    super.attachInfo(context, info);
    AUTHORITY = info.authority;
    CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI(AUTHORITY, AlarmTable.TABLENAME, ALARMS);
    uriMatcher.addURI(AUTHORITY, AttributeTable.TABLENAME, ATTRIBUTES);
    uriMatcher.addURI(AUTHORITY, DeepLinkTable.TABLENAME, DEEPLINKS);
    uriMatcher.addURI(AUTHORITY, NotificationTable.TABLENAME, NOTIFICATIONS);
    uriMatcher.addURI(AUTHORITY, MetaDataTable.TABLENAME, RESOURCE_METADATA);
    uriMatcher.addURI(AUTHORITY, ResourceTable.TABLENAME, RESOURCES);
    uriMatcher.addURI(AUTHORITY, ResourceAttributeTable.TABLENAME, RESOURCES_ATTRIBUTES);
    uriMatcher.addURI(AUTHORITY, ResourceTagTable.TABLENAME, RESOURCES_TAGS);
    uriMatcher.addURI(AUTHORITY, TagTable.TABLENAME, TAGS);
    uriMatcher.addURI(AUTHORITY, UserTagTable.TABLENAME, USER_TAGS);
    uriMatcher.addURI(AUTHORITY, UserTable.TABLENAME, USERS);
    uriMatcher.addURI(AUTHORITY, CUSTOM, RAW);
}

When the Application is started, the ContentProvider is actually instantiated along with the Application class, so it will have access to all the required package info. the ProviderInfo object will contain the information provided in the AndroidManifest... The listing that's included in the final Application.

        <provider android:authorities="com.foo.barapp.content"
              android:name="com.foo.bar.MyContentProvider"/>

The Authority will now be rewritten with "com.foo.barapp.content" instead of the default value, and the UriMatcher will be updated to the application's value instead of the default. Classes that rely on the "AUTHORITY" will now access the updated value, and the UriMatcher will properly distinguish the incoming queries for the 'com.foo.barapp.content'.

I've tested this with both both a sample application and an androidTest package simultaneously and found it to work correctly.