I have some troubles with the management of recommendations (notifications) on an Android TV with the Leanback Launcher.
I use the google sample here to implement my own : https://github.com/googlesamples/androidtv-Leanback
Expected result :
I'm on my activity "MainActivity" (with a webview)
I press HOME, so I'm on the Leanback launcher with recommendation include mines.
I press on one of them
Resume activity "MainActivity" without recreate it with a new Intent with a new extra.
Actually, The resume of the activity without reload the activity works fine, below the creation of the PendingIntent :
private PendingIntent buildPendingIntent(Parcelable media, int id) {
Intent detailsIntent = new Intent(this, MainActivity.class);
detailsIntent.putExtra("media", media);
detailsIntent.putExtra("id", id);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(detailsIntent);
return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
Current result :
I'm on my activity "MainActivity" (with a webview)
I press HOME, so I'm on the Leanback launcher with recommendation include mines.
I press on one of them
My activity receive "onNewIntent" event by in the extrat intent values I have always the same media.
This solution doesn't work, because Google said in a comment of the sample code :
// Ensure a unique PendingIntents, otherwise all recommendations end up with the same
// PendingIntent
detailsIntent.setAction(movie.getId());
So to differentiate all recommandations, I have to set Action with an ID, else, it will be always the last pendingintent sent to my activity. It's my current behavior, I receive in "onNewIntent" always the same "media" and "id" in the intent (the last one), whatever on which recommendation I click, I always get the same Intent.
But if I set the action with an "id", the activity "MainActivity" is recreated, so the resume failed, and the context of my webview is cleared my webview is reloaded :( but I get the good intent with the good media in the extra intent values.
Have you a solution to help me to have the behavior I want ?
To simplify my question, how can I resume my Activity B from a recommendation with the good pendingintent without reload my activity ?
Thank you in advance for your help.
I found a solution. I don't think it's the good one but it works for my case.
PendingIntent pIntent = PendingIntent.getActivity(this, id, detailsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return pIntent;
So, If I set the "resquetCode" with the id, so Android think it's a different Activity and resume it without recreate a new one.
Related
I have issue in intent of my launcher activity.Scenerio is:
1. Send intents form notification service to my launcher activity
PendingIntent contentIntent = PendingIntent.getActivity(this, TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID, new Intent(this, MainActivity.class).putExtra("is_log", true), Intent.FLAG_ACTIVITY_CLEAR_TOP);
2. In my MainActivity i getting this intent. code is:
if(this.getIntent().getExtras()!=null){
boolean isLogNewTripScreen = (boolean)this.getIntent().getExtras().getBoolean("is_log");
}
}
3. this work fine but when i come from notification service,but when i launch from not notification service ,that data in intentis still there.How can i remove that data from intent.
EDIT: I've created a sample application to test this problem and possible solutions. Here are my findings:
If you launch your app from a notification with extras and then later return to your app by selecting it from the list of recent tasks, Android will launch the app again the same way it was launched from the notification (ie: with the extras). This is either a bug or a feature, depending on who you ask.
You'll need to add additional code to deal with this situation. I can offer 2 suggestions:
1. Use FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
When you create your notification, set the flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS in the Intent. Then, when the user selects the notification and launches the app from the notification, this will not create an entry for this task in the list of recent tasks. Also, if there was an entry in the list of recent tasks for this application, that entry will also be removed. In this case, it will not be possible for the user to return to this task from the list of recent tasks. This solves your problem by removing the possibility that the user launches the app from the list of recent tasks (but only when the app has been launched from the notification).
2. Detect FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
When the user launches your app from the list of recent tasks, Android sets the flag Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY in the Intent that is passed to onCreate() of your launch activity. You can detect the presence of this flag in onCreate() and then you know that the app has been launched from the recent tasks list and not from the notification. In this case, you can just ignore the fact that the extras in the Intent still contain data.
Choose the solution that best suits the workflow for your application. And thanks for the question, this was an interesting challenge to solve :-)
Additional information:
You are creating the PendingIntent incorrectly. You are calling
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true),
Intent.FLAG_ACTIVITY_CLEAR_TOP);
You are passing Intent.FLAG_ACTIVITY_CLEAR_TOP as the 4th parameter to getActivity(). However, that parameter should be PendingIntent flags. If you want to set FLAG_ACTIVITY_CLEAR_TOP on the Intent, you need to do it this way:
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
I noticed that using fragments. I read a QR Code in Activity A that opens fragment 1, send its content to a webservice and if goes right, replace it with fragment 2. When user press back, the onBackPressed in Activity A call finish. If user select the app again in the list, it was opening fragment 1 instead of fragment 2.
I solved that checking in onBackPressed if extra contains a field indicating that fragment 2 was already opened. If true, moveTaskToBack(true) is called instead of finish()
Activity A
#Override
public void onBackPressed() {
Bundle extras = getIntent().getExtras();
if(extras.containsKey(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN)){
moveTaskToBack(true);
}else {
finish();
}
}
Fragment 2
Intent mainIntent = getActivity().getIntent();
mainIntent.putExtra(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN, true);
getActivity().setIntent(mainIntent);
I've tested all the answers of stackoverflow with no luck, what worked for me was this. Create a helper class to check the activity flags. Or a function, it does not matter.
object FlagHelper {
fun notLaunchedFromNotification(activity: AppCompatActivity): Boolean {
return activity.intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
}
}
Then use as the following code. It returns a boolean so you can check the intent extras when it's false
val notLaunchedFromNotification = FlagHelper.notLaunchedFromNotification(this)
Add android:launchMode="singleInstance" to your launcher activity
and then Use flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS while starting your activity
This is the code I use to create the PendingIntent for my notification.
Intent notificationIntent = new Intent(context, Activity1.class);
PendingIntent myIntent = PendingIntent.getActivity(context, 0,notificationIntent, 0);
This PendingIntent launches Activity1 when the notification is click.
How can I simply reopen the app and go to the most recent Activity (as though clicking on the launcher icon) instead of launching a new Activity when the notification is clicked?
Activity1 is just an example. I have multiple Activity in the app. I just want to reopen the app and go to the most recent Activity
NOTE: this looks like wrong design for me, because notification should allow user to enter activity that is in context with the notification.
Technically, you can create redirecting activity and your notification intent should launch it when tapped. In its onCreate() you check what activity you want user to be redirected (you can keep this info in SharedPreferences, and each activity would write this info in onCreate() (or make that in your base class if you have it). Then in redirector you call regular startActivity() to go last activity and call finish() to conclude your redirector. Moreover, your redirector activity does not need any layout so add
android:theme="#android:style/Theme.NoDisplay"
to its Manifest entry (of course you also need no call to setContentView())
create Activity1 as a singletask activity , by changing the launchMode of the activity to singleTask...
set your activity to launchMode="singleTop" in your Manifest.xml then use this code instead of what you are using above to reopen the active one:
Title = "YourAppName";
Text = "open";
notificationIntent = new Intent(this, Activity1.class);
cIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), Title, Text, cIntent);
You can change the android:launchMode in the manifest file for the activity targeted by the pending intent.
Typically, you can use singleTop, which will reuse the same instance when the targeted activity is already on top of the task stack (i.e.: Activity is shown before you left your app).
You can also consider SingleTask and SingleInstance, if you want to keep only a single instance of the activity.
In my app, i created a custom notification that contain a button that show recent apps(instead of home button long press)
when user open main activity and press home button to go to home screen then drag the notification drawer and click on the button:
Desired result is that the recent apps are shown and the main activity is one of the recent apps.
Actual result is that the recent apps are shown but the main activity is not in them and the main activity resumes.
My code to start "RecentApps (is a dummy class that show recent apps)" class with pending intent
Intent recentAppIntent = new Intent(getBaseContext(), RecentApps.class);
PendingIntent pendingrecentAppIntent = PendingIntent.getActivity(getBaseContext(), 1, recentAppIntent, 0);
notificationView.setOnClickPendingIntent(R.id.recentAppButt, pendingrecentAppIntent);
I think that the problem is getBaseContext, so when the main activity is alive and not finished the recent apps are shown but with context is the main activity.
I tried getApplication and getApplicationContext but not working.
I also tried to use flags for "recentAppIntent" but it is not working, It is a half solution to use
recentAppIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
but it not what i need.
So, my question is "How to finish the main activity in which the pending intent starts".
Thanks in advance, Mostafa
You need to ensure that your MainActivity and your RecentApps don't belong to the same task. The "Recent apps" doesn't actually show recent apps. It shows recent tasks.
To make sure that your RecentApps isn't in the same task as your MainActivity, you can add the following to the <activity> definition in the manifest for RecentApps:
aandroid:taskAffinity=""
Also, when creating the notification, add FLAG_ACTIVITY_NEW_TASK to the Intent, like this:
Intent recentAppIntent = new Intent(getBaseContext(), RecentApps.class);
recentAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingrecentAppIntent = PendingIntent.getActivity(getBaseContext(), 1, recentAppIntent, 0);
notificationView.setOnClickPendingIntent(R.id.recentAppButt, pendingrecentAppIntent);
I think FLAG_ACTIVITY_CLEAR_TOP will help as a flag.
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Explanation for Intent flags are available here: link
I have two (or more) widgets, e.g. A and B, which should launch the same activity passing their appWidgetId in the Intent extras to the activity. This works fine after application install on the first launch from any of the widgets (the activity receives the correct appWidgetId). It also works fine if I press the back button after activity launch and launch the activity from a different widget. However, if I launch the activity from widget A, hit the home button and then launch the activity from widget B (or C or D...) it launches with A's appWidgetId. I'm baffled as to how to fix this. Here is how I am declaring my PendingIntent:
Intent intent = new Intent(context, WidgetActivity.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setAction(this.getClass().getName() + System.currentTimeMillis());
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
If I understand correctly, I am successfully setting up unique PendingIntents for each widget. Any help most appreciated!
EDIT:
The accepted answer to this question suggests that:
I believe the problem is that you have a PendingIntent that only differs by extra. PendingIntents are cached, so if you use two with the same action and data, they'll overwrite each other.
However, my code creates randomized actions to perform and so I think my PendingIntents differ by more than just extras
Looks like I need to brush up on my activity lifecycle events. The answer was the following code put into my Activity:
#Override
protected void onNewIntent(Intent intent) {
setIntent(intent);
super.onNewIntent(intent);
}
The problem was that, on Home button press, the Activity was holding onto the original Intent which launched it. Then, when the other widget launched the Activity, the original intent was being reused. The above code simply replaces the original intent with the new one and carries on with the remaining lifecycle events.
I've got an Android App Widget that when clicked uses a PendingIntent to start an Activity, which the main Application associated with the App Widget also uses. After the user is done with the Activity started from the App Widget, and presses the back button, the user is taken to the Activity on the top of the MAIN application Activity stack. I'd like the back button to take the user back to the Android desktop/home screen.
In short, I'd like separate Activity stacks for my App Widget and Application. Not sure why Android wants to combine these.
This is what I've got now in the App Widget and it's not working. Neither Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK seems to have any impact on the stack.
Intent intent = buildWidgetIntent(context, info);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context,
mRequestCode++, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Thanks for any help with this one.
I haven't faced this exact situation, but Intent.FLAG_ACTIVITY_NO_HISTORY is what I needed to make my widget-launched Activity always go Back to the Home screen.