Use custom intent action to launch activity only in specific application context

Viewed 5280

I need to specify my login activity with intent filter:

    <activity
        android:name=".view.LoginActivity"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="com.example.sdk.LOGIN" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

The reason why I want to do it that way is that I am developing an SDK that I want to use in few projects and this is the best way I could figure out of how to notify the SDK which is the login activity, while it is defined only in the app module.

After I do such declaration I start the login activity like so (both in library and app):

    Intent intent = new Intent();
    intent.setAction("com.example.sdk.LOGIN");
    context.startActivity(intent);

There is the possibility that two or more applications relying on the same SDK will be installed simultaneously on the same device. If I get it correctly, in such case in each of these applications the Activity start will launch a random login activity (with the possibility of the call in application A launching the login of application B).

  1. Am I correct in my understanding of how custom intent filters work?
  2. Is there a way to tell the call in application A to use only intent filter declarations in application A

Suggestions of alternative approaches to reusing the login-related logic, not relying on intent filters are also welcome.

4 Answers

I figured out a way to achieve what I wanted.

Basically I do not hard code the package in my library, rather I start the login activity from the library using:

private static final String INTENT_ACTION_PATTERN = "%s.LOGIN";
public void startLoginActivity() {
    Intent intent = new Intent();
    String intentAction = String.format(INTENT_ACTION_PATTERN, mContext.getPackageName());
    intent.setAction(intentAction);
    mContext.startActivity(intent);
}

And I will just use a convention that I will always define the custom intent filter to match the package of my application:

<activity
    android:name=".view.LoginActivity"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="com.example.myapp.LOGIN" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

In such case in each of these applications the Activity start will launch a random login activity.

That's not quite true. From docs:

If there's only one app that can handle it, that app opens immediately and is given the intent. If multiple activities accept the intent, the system displays a dialog, so the user can pick which app to use.

As to your second question:

Is there a way to tell the call in application A to use only intent filter declarations in application A?

I assume you are unable to launch the activity using explicit intents, because you cannot know the activity name of your client within the library module.

There is no API, that would give you activity class, filtered by its intent-filter property. PackageManager API cannot come in help. That means, you cannot get the class instance of the activity in order to explicitly start the activity. That in turn means, that the only way for you is to start the activity implicitly, which, as already said, will prompt with dialog.

I think better solution is use of <meta-data> attribute in <application> section in AndroidManifest. Your client can specify any value here and you can then read it in your library. So your client will specify class name of his LoginActivity and you will use that for starting it via explicit Intent.

AndroidManifest in client app

<application ...>
    ...
    <meta-data android:name="com.example.sdk.LOGIN_ACTIVITY" android:value=".view.LoginActivity" />
</application>

Usage in your library code

(don't forget to handle special situations and add exceptions handling)
public void startLoginActivity(Context context) {
    String packageName = context.getPackageName();

    PackageManager pm = context.getPackageManager();
    ApplicationInfo appInfo = pm.getApplicationInfo(packageName, PackgeManager.GET_META_DATA);
    Bundle data = appInfo.metaData;

    String activityName = data.getString("com.example.sdk.LOGIN_ACTIVITY");
    if (activityName == null || activityName.isEmpty()) {
        return;
    }
    ComponentName component = ComponentName.createRelative(packageName, activityName);
    Intent intent = new Intent().setComponent(component);

    context.startActivity(intent);
}
Related