zer0stimulus
zer0stimulus

Reputation: 23606

How to get the sender of an Intent?

Is there a way for an Activity to find out who (i.e. class name) has sent an Intent? I'm looking for a generic way for my Activity to respond to a received intent by sending one back to the sender, whoever that may be.

Upvotes: 47

Views: 50234

Answers (9)

Muntashir Akon
Muntashir Akon

Reputation: 9431

A better solution is combining both solutions as follows (Apache-2.0 licensed):

    @Nullable
    public static String getReferrer(@NonNull Activity activity) {
        String callingPackage = activity.getCallingPackage();
        if (callingPackage != null) {
            // Activity started via startActivityForResult
            return callingPackage;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
            Intent intent = activity.getIntent();
            intent.removeExtra(Intent.EXTRA_REFERRER_NAME);
            intent.removeExtra(Intent.EXTRA_REFERRER);
            // Now that the custom referrers are removed, it should return the real referrer.
            // android-app:authority
            Uri referrer = activity.getReferrer();
            return referrer != null ? referrer.getAuthority() : null;
        }
        return null;
    }

While this works for a regular activity, if you're fetching it inside Activity#onNewIntent(intent) make sure to set the new intent via Activity#setIntent(intent) before doing so.

Upvotes: 1

KnIfER
KnIfER

Reputation: 750

Use UsageStatsManager and the old RecentTaskInfo to get the intent sender for OnCreate or onNewIntent:

    public static String getTopMostThirdPartyPackage(Context context) {
        String thisPak = null, tmp, top = null;
        try {
            thisPak = context.getPackageName();
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
                UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
                long now = System.currentTimeMillis();
                UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
                UsageEvents.Event e = new UsageEvents.Event();
                while (uEvts.getNextEvent(e)){
                    tmp = e.getPackageName();
                    if (!thisPak.equals(tmp)) {
                        top = tmp;
                        break;
                    }
                }
            } else {
                ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
                for(ActivityManager.RecentTaskInfo info:tasks) {
                    tmp = info.baseIntent.getComponent().getPackageName();
                    if (!thisPak.equals(tmp)) {
                        top = tmp;
                        break;
                    }
                }
            }
        } catch (Exception e) {
        }
        return top;
    }

permissions :

    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
    <uses-permission android:name="android.permission.GET_TASKS" />
        intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
        startActivity(intent);

Upvotes: -1

ZhouX
ZhouX

Reputation: 2156

In my case, neither the accepted here and another most voted answer works perfectly.

Activity.getCallerActivity() works only for the sender which starts your activity by startActivityForResult, meaning that if the sender is also in your app and you have full control, it works, but not every external app starts others in that way.

Another most voted answer provides the solution for external app, but it too has issue. First I would prefer getAuthority() instead of getHost(), secondly, if the sender is a browser kind of app, like Chrome, both host and authority will give you the browsing web page's address host, such as www.google.com, instead of the app itself. So it depends on how you define 'sender', if you need to find out which web page starts you, the authority/host is good enough, but if you need to find out which app starts you, I am afraid authority/host can be trusted only when getScheme() gives you android-app instead of http.

Upvotes: 0

donfuxx
donfuxx

Reputation: 11323

Is it an external app you receive the intent from? You could use the getReferrer() method of the activity class

A simple example: I opened google map app to share some location with my app by using the share option of google maps. Then my app opens and this method call in the Activity:

 this.getReferrer().getHost()

will return:

 com.google.android.apps.maps

see documentation here: https://developer.android.com/reference/android/app/Activity.html#getReferrer()

Note that this requires API 22. For older Android versions see answer from ajwillliams

Upvotes: 29

Ali Kazi
Ali Kazi

Reputation: 1636

Based on your question, since you want to send an intent back to the sender startActivityForResult is a better choice than what I am going to suggest. But I needed to start activity B when a notification is clicked by the user and execute some code in activity B only if the sender activity is activity A. This is how I did it quite simply.

Inside Activity A:

String senderName = this.getClass().getSimpleName();        
Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
clickIntent.putExtra("SENDER_CLASS_NAME", senderName);

//I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);

Inside Activity B:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState == null) {
        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            if(bundle.containsKey("SENDER_CLASS_NAME")){
                String senderName = bundle.getString("SENDER_CLASS_NAME");                  
                //Execute some code
                Log.d("GCM", "Notifications clicked");
                }
            }
        }
    }

Upvotes: 0

Mauricio Andrada
Mauricio Andrada

Reputation: 141

A technique I use is to require the application sending the relevant Intent to add a PendingIntent as a Parcelable extra; the PendingIntent can be of any type (service, broadcast, etc.). The only thing my service does is call PendingIntent.getCreatorUid() and getCreatorPackage(); this information is populated when the PendingIntent is created and cannot be forged by the app so I can get the info about an Intent's sender. Only caveat is that solution only works from Jellybean and later which is my case. Hope this helps,

Upvotes: 14

ajwillliams
ajwillliams

Reputation: 111

This isn't incredibly direct but you can get a list of the recent tasks from ActivityManager. So the caller would essentially be the task before yours and you can fetch info on that task.

Example usage:

ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);

The above will return a list of all the tasks from most recent (yours) to the limit specified. See docs here for the type of info you can get from a RecentTaskInfo object.

Upvotes: 11

adamp
adamp

Reputation: 28932

Generally you don't need to know this. If the calling activity uses startActivityForResult(Intent, int), the callee can use setResult(int, Intent) to specify an Intent to send back to the caller. The caller will receive this Intent in its onActivityResult(int, int, Intent) method.

Upvotes: 6

Andy Zhang
Andy Zhang

Reputation: 8315

There may be another way, but the only solution I know of is having Activity A invoke Activity B via startActivityForResult(). Then Activity B can use getCallingActivity() to retrieve Activity A's identity.

Upvotes: 35

Related Questions