简体   繁体   中英

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

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.

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.

those are the steps I made to do it:

  1. Define the custom intent filter in your manifest
  2. Inside your activity (that you want to launch) override onCreate and onNewIntent
  3. Test it by creating another app that start the custom intent

Manifest example:

<activity
   android:name="com.test.MainActivity">
   <intent-filter>
       <action android:name="test_action_that_should_be_unique"></action>
       <category android:name="android.intent.category.DEFAULT"></category>
   </intent-filter>
</activity>

Overriding the activity life cycle:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        if (intent == null)
            return;
        String action = intent.getAction();
        if (action == null || !action.equals("test_action_that_should_be_unique"))
            return;

        Toast.makeText(this, "onCreate: test_action_that_should_be_unique", Toast.LENGTH_SHORT).show();
    }


 @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (intent == null)
            return;
        String action = intent.getAction();
        if (action == null || !action.equals("test_action_that_should_be_unique"))
            return;

        Toast.makeText(this, "onNewIntent: test_action_that_should_be_unique", Toast.LENGTH_SHORT).show();
    }

Test the new custom intent from other application:

  Intent intent = new Intent("test_action_that_should_be_unique");
  startActivity(intent);
  • Off course check the above intent for null:

    PackageManager packageManager = getActivity().getPackageManager(); if (intent.resolveActivity(packageManager) != null) { startActivity(intent); }

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); }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM