How to get the sender of an Intent? - android

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.

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.

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

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,

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.

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.

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

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.

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

Related

How can I handle a push notification intent with changing activities after creation?

I have a two-activity android app (the activities are both single-top) and I am handling push notifications. Every push notification scenario is handled perfectly for me except for one, which is because of how the push notification intents are constructed.
The scenario that does not perform as desired is when the user is in one activity when the push notification comes in, and then they navigate to a different activity, at which point they then decide to select the push notification from their phone's dropdown bar. My problem is that the app then attempts to go back to the activity that was active when the notification was created, which isn't what I want. I want it to still do everything that it would do with the notification and its data the same way, but instead do it on the current activity and not switch back.
I know why it is happening, because the notification creation code is designed like this:
// create notification
val builder = NotificationCompat.Builder(context,
channelId)
// more things are done to set up the builder...
// here is why the problem is happening
val notificationIntent = if (we_are_in_activity_one)
Intent(context, ActivityOne::class.java)
else
Intent(context, ActivityTwo::class.java)
notificationIntent.putExtras(data_from_notification)
builder.setContentIntent(PendingIntent.getActivity(context,
notificationId,
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT))
//Auto cancel
builder.setAutoCancel(true)
builder.priority = NotificationCompat.PRIORITY_HIGH
//Return the generated notification
return builder
What I am wondering is what can be done or changed so that when the user selects the notification, it doesn't automatically start up the activity that it was originally bundled with if circumstances have changed. Is there maybe an obvious flag I am missing that I can use?
Another piece of info that may be helpful is that it is only a problem when they are in activity 2, as activity 1 is the launcher activity. So if the notification is created while in 2, but they navigate back to 1, Activity 2 won't be active anymore. When they tap the notification, it restarts activity 2, which isn't what I want. I would only want the notification to actually go back to Activity 2 if it is still active (aka the user is still on it).
Thanks in advance and let me know if I can provide any more helpful information
If I understand correctly, do you want to receive notification on the user's current activity?
You can do it as follows.
You need to verify that the second activity is running. If it is not, your notification will open on the main screen. If it is, the notification will be opened on it in onNewIntent().
An easy way to achieve this would be:
public static boolean isActivityActive(Activity activity) {
return !activity.isFinishing() && !activity.isDestroyed();
}
and then
Intent intent = new Intent(context, !isActivityActive(new ActivityTwo()) ? ActivityOne.class : ActivityTwo.class);
This method will return true if the activity is open.
Alternatively you can use:
activity.getWindow().getDecorView().isShown();
in isActivityActive(Activity) The value returned by this expression changes in onStart() / onStop()
If the method above does not provide the result you expect, you can create a class for reuse and save the activity state to shared keys that you can use in any activity.
Only a few lines of code are needed.
First do the following:
public class MyPreference {
private Context c;
private SharedPreferences.Editor editor;
private SharedPreferences preferences;
public MyPreference (Context c) {
this.c = c;
preferences = c.getSharedPreferences("shared_preferences", Context.MODE_PRIVATE);
}
public void save (String preference, boolean value) {
editor = preferences.edit();
editor.putBoolean(preference, value);
editor.apply();
}
public boolean boo (String preference) {
return preferences.getBoolean(preference, false);
}
public String is_second_activity_active = "is_second_activity_active";
}
You can now save the life cycles of your second activity by starting MyPreference as follows:
public class ActivityTwo extends AppCompatActivity {
MyPreference p;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
p = new MyPreference(context);
// some source...
}
#Override
protected void onStart() {
super.onStart();
p.save(p.is_second_activity_active, true);
// your activity is active
}
#Override
protected void onStop() {
super.onStop();
p.save(p.is_second_activity_active, false);
// your activity is not active
}
}
You can understand the life cycle of the Activity here
Finally, in your notification class, retrieve the status of your second activity:
// create notification
// ...
MyPreference p = new MyPreference(context);
Intent intent = new Intent
(context, p.boo(p.is_second_activity_active) ? ActivityTwo.class : ActivityOne.class);
// continue...
Sorry my English, i am not fluent. I hope you understand.
I don't write in kotlin, so you can convert the code.
Let me know if it helped you.
Thank you. > <
 
CHANGE
It is not possible to change the behavior of a notification after it has been created in one way. You can update the notification when circumstances change.
You need to define setOnlyAlertOnce (true)in your NotificationCompat.Builder, and then send a new notification with the same id as the old notification.
If the id is the same, and setOnlyAlertOnce (true), your notification will be updated without a pop-up, sound or vibration warning. You can use the same Builder for each update.
Although this works well, the documentation itself warns that if the notification is updated several times in a short period of time, some notifications will not be packaged.
Caution: Android applies a rate limit when updating a notification. If you post updates to a notification too frequently (many in less than one second), the system might drop some updates.
Documentation
In your case, the notification would need to be updated whenever the user signs in and out of ActivityTwo.class, and that would not be a good thing.
Alternatively, I recommend that you try this.
Instead of opening the activity directly by notification, open it from a BroadcastReceiver,
and then decide which activity to open..
To broadcast your notification, your Intent needs to look like this
// create notification
// ...
Intent intent = new Intent();
intent.setAction(MyNotification.ACTION_OPEN);
intent.putExtra("get", "something");
// use getBroadCast instead of getActivity
PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pIntent);
// configure your builder normally
Now in your BroadcastReceiver, make sure the user opened their notification, and what activity they were in when they did it.
public class MyNotification extends BroadcastReceiver {
public static String ACTION_OPEN = "com.example.intent.action.NOTIFICATION_OPEN";
#Override
public void onReceive(Context context, Intent intent) {
MyPreference m = new MyPreference(context);
boolean open_notification = ACTION_OPEN.equals(intent.getAction());
if (open_notification ) {
boolean is_active = m.boo(m.is_second_activity_active);
String log = is_active ? "Open in ActivityTwo" : "Open in ActivityOne";
Intent new_intent = new Intent(context, is_active ? two.class : one.class);
new_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
new_intent.putExtra("get", intent.getExtras());
context.startActivity(new_intent);
Log.e("log", log);
}
}
}
If you prefer, use your method to find out if the user is in the second activity, or use the examples I mentioned earlier.
NOTE: The onStart () and onStop () methods are actually better than onResume () and onDestroy () to check if the user is still in the activity.
onDestroy () is never called when the user closes the application, from recent applications.
Finally, declare your broadcast in your AndroidManifest.xml
<receiver android:name=".MyNotification"
android:exported="false">
<intent-filter>
<action android:name="com.example.intent.action.NOTIFICATION_OPEN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Do not forget add android:launchMode="singleTop" to your ActivityOne and ActivityTwo
The notification will open according to the user's activity, no matter where it was created.
Thus, if the activity is the only thing that changes, there is no need to update notifications whenever circumstances change.
It should work now. x-x

Xamarin Android: get one app's state information from another

I have two Xamarin Android apps -- let's call them "Archy" and "Mehitabel".
Archy has some persistent state information (which, let's say for the sake of argument, is in a SQLite DB).
If a certain thing happens to Mehitabel, she needs to know a piece of that state information.
To accomplish this feat, I have Mehitabel send an intent to Archy. Archy has a broadcast receiver that hears it, gathers the necessary state, and fires a different intent back to Mehitabel.
Here's the code from Archy:
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new [] { "com.example.Archy.SendStateToMehitabel"})]
public class StateQueryReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
var msg = new Intent("com.example.Mehitabel.StateFromArchy");
msg.PutExtra("ImportantStateInfo", GetSomeState());
context.SendBroadcast(msg);
}
}
And here's the code from Mehitabel:
private async Task AskArchyForState()
{
var filter = new IntentFilter("com.example.Mehitabel.StateFromArchy");
var csrc = new TaskCompletionSource<bool>();
var rcvr = new ActionBroadcastReceiver((context, intent) =>
{
State = intent.GetStringExtra("ImportantStateInfo");
csrc.TrySetResult(State != null);
});
RegisterReceiver(rcvr, filter);
var msg = new Intent("com.example.Archy.SendStateToMehitabel");
SendBroadcast(msg);
var task = await Task.WhenAny(csrc.Task, Task.Delay(Timeout));
UnregisterReceiver(rcvr);
if (task != csrc.Task)
bomb("Archy has not answered state query after {0}ms", Timeout);
if (!csrc.Task.IsCompletedSuccessfully || csrc.Task.Result == false)
bomb("failed to get all necessary state from Archy");
}
That all works great, provided Archy is actually running (i.e., shown in the "recent" list). If Archy isn't running, Archy's receiver code is never executed and Mehitabel times out.
My hope is I'm missing something simple (like a flag in one of the receiver attributes, or some secret sauce in the com.example.Archy.SendStateToMehitabel intent).
Can you tell me what I'm missing here?
Do I need to be using a completely different approach (like having Mehitabel StartActivityForResult() an activity within Archy, or using a service that starts on boot and runs all the time)?
Based on my research, I think you could open Archy before you need the data in Mehitabel. This is demo about opening an app in code.
Intent launchIntent = PackageManager.GetLaunchIntentForPackage("NewTestApp.NewTestApp");
if (launchIntent != null)
{
StartActivity(launchIntent);
}
Note:NewTestApp.NewTestApp is package name of Archy.

Android - How to receive shortcut create result

Looking at the code sample here - I find the following comment puzzling:
// ... We assume here that the
// app has implemented a method called createShortcutResultIntent() that
// returns a broadcast intent.
what does it mean the app has implemented ... where is this implementation done?
is it a broadcast receiver? registered to which intent filter?
is this an abstract method? of which class?
and then I see this code sample - which handles a completely different flow (I think) and I'm lost again
You can obtain feedback via catching the broadcast event which you setup while use requestPinShortcut function.
At first you need a usual broadcast receiver (in the code below it has name ShortcutReceiver). You can even use existing broadcast receiver and simple add new action which it should catch.
Lets the action will be "general.intent.action.SHORTCUT_ADDED" and it will be stored in ShortcutReceiver.kInstalledAction constant. In this case in the manifest you should have:
<receiver android:name=".ShortcutReceiver" >
<intent-filter>
<action android:name="general.intent.action.SHORTCUT_ADDED"/>
</intent-filter>
</receiver>
After this you can use following code in the activity for create a pinned shortcut (in other places change this on object of Context class):
ShortcutManager manager = this.getSystemService(ShortcutManager.class);
Intent targetIntent = new Intent(ShortcutReceiver.kInstalledAction);
targetIntent.setPackage(this.getPackageName());
PendingIntent intent = PendingIntent.getBroadcast(this, 0, targetIntent, 0);
manager.requestPinShortcut(info, intent.getIntentSender());
In this code info is correct object of ShortcutInfo class.
You can handle the event while catch the broadcast:
public class ShortcutReceiver extends BroadcastReceiver {
public static final String kInstalledAction = "general.intent.action.SHORTCUT_ADDED";
#Override
public void onReceive(Context context, Intent intent) {
if (kInstalledAction.equals(intent.getAction())) {
// Handle the event after the shortcut has been added
Toast.makeText(context, "The shortcut has been added", Toast.LENGTH_LONG).show();
}
}
}
Please take into account that from my experience the broadcast event happens after the shortcut has been added but sometimes there can be some delays (at about some minutes). But may be there is some dependency on the launcher.
Update
As described in other answers on Android 8 catching of implicit intent via broadcast in general doesn't work.
So I simple changed the intent to explicit via set package name of the current app. So only our broadcast receiver can catch the intent.
First things first. Implicit intents on Android 8.0 Oreo:
Because Android 8.0 (API level 26) introduces new limitations for broadcast receivers, you should remove any broadcast receivers that are registered for implicit broadcast intents. Leaving them in place does not break your app at build-time or runtime, but they have no effect when your app runs on Android 8.0.
Explicit broadcast intents—those that only your app can respond to—continue to work the same on Android 8.0.
There are exceptions to this new restriction. For a list of implicit broadcasts that still work in apps targeting Android 8.0, see Implicit Broadcast Exceptions.
https://developer.android.com/about/versions/oreo/android-8.0-changes
Note: there are some exceptions: https://developer.android.com/guide/components/broadcast-exceptions (very few)
Instead, we will use the so-called context-registered receiver, it will last as long as our app lives, or until we unregister it.
Also, ShortcutManager requires API 25 that's why we will use it's compat version in order not to duplicate the code for old and new versions. (ShortcutManagerCompat was added in version 26.1.0)
Code to create a pinned shortcut on the Home screen:
public static void addShortcut(Context context, String id) {
if(context == null || note == null)
return;
//there may be various Home screen apps, better check it
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)){
Intent shortcutIntent = new Intent(context, MainActivity.class);
shortcutIntent.setAction(Constants.ACTION_SHORTCUT); // !!! intent's action must be set on oreo
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, note.get_id().toString())
.setIntent(shortcutIntent)
.setShortLabel("MyShortcut") //recommend max 10 chars
.setLongLabel("Long shortcut name")//recommend max 25 chars
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut))
.build();
//callback if user allowed to place the shortcut
Intent pinnedShortcutCallbackIntent = new Intent(ACTION_SHORTCUT_ADDED_CALLBACK);
PendingIntent successCallback = PendingIntent.getBroadcast(context, REQ_CODE_SHORTCUT_ADDED_CALLBACK,
pinnedShortcutCallbackIntent, 0);
ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, successCallback.getIntentSender());
}
And here is the code to receive the broadcast in your Activity, for example. Note that this "callback" will be called only if your app is running, receiver is registered and the user allowed the shortcut:
private ShortcutAddedReceiver shortcutAddedReceiver;
private void registerShortcutAddedReceiver(){
if(shortcutAddedReceiver == null){
shortcutAddedReceiver = new ShortcutAddedReceiver();
}
IntentFilter shortcutAddedFilter = new IntentFilter(ShortcutHelper.ACTION_SHORTCUT_ADDED_CALLBACK);
registerReceiver(shortcutAddedReceiver, shortcutAddedFilter);
}
private void unregisterShortcutAddedReceiver(){
if(shortcutAddedReceiver != null){
unregisterReceiver(shortcutAddedReceiver);
shortcutAddedReceiver = null;
}
}
#Override
public void onStart() {
super.onStart();
registerShortcutAddedReceiver();
}
#Override
public void onStop() {
super.onStop();
unregisterShortcutAddedReceiver();
}
private class ShortcutAddedReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Snackbar.make(view, "Shortcut added", Snackbar.LENGTH_LONG).show();
}
}
Hope this helps!

How to get app uses time in android

I am creating a custom launcher in android application and , I want to track actual time which application is opened.
suppose i open Facebook for 10 minute and I went ideal for 5 minute how to calculate 5 minute which is used by user.
Thanks
You can define two buttons to start and stop your service. Fire an intent on Start button something like this:
Intent startServiceIntent = new Intent();
startServiceIntent.setClass(this, MainService.class);
startServiceIntent.setComponent(new ComponentName(this, MainService.class));
bindService(startServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
In the service, you can define broadcast receivers to detect screen on-off events.
private BroadcastReceiver screenWakeUp = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("android.intent.action.SCREEN_ON")) {
isRunningForegroundAppsThread = true;
isScreenOn = true;
startThread();
}
}
};
From here, you can start a thread which constantly runs inside a loop and checks whether the application being used by the user has changed meanwhile. If it has, you may update time for that app in the map else you can just continue. At the SCREEN_OFF event, you may stop the thread as user is no more using the phone.
You can retrieve the application name using this:
ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
PackageManager pm = mContext.getPackageManager();
ApplicationInfo af = null;
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
// String activity = taskInfo.get(0).topActivity.getClassName();
ComponentName c = taskInfo.get(0).topActivity;
String packageName = c.getPackageName();
try {
af = pm.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String app_Name_current = (String) pm.getApplicationLabel(af);
It solely depends on your requirement, and if needed the time can be stored in a DB. Also, this records just the time for apps which require screen to be ON, so you'll have to separately maintain time for background apps and store that in a separate map maybe. At the end, when you press the stop button, you can display the results in a listview or whatever UI element you consider suitable for this.
I know this is not complete code, but I guess it gives you an idea of how to go about it. Also, I'd say that though this might solve the problem, but it's not a good way of achieving the same, as PackageManager objects are heavyweight and dealing with them continuously is expensive operation.
The above approach only works on pre-L devices as ActivityManager has been deprecated in API level 21. So you have to use UsageStats class for Lollypop devices.

How to get the class name that fired an Activity? [duplicate]

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.
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.
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
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,
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.
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.
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");
}
}
}
}
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.
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);

Categories

Resources