Android TV - Recommendation (Notification) will not auto-cancel - android

When creating a recommendation (or Notification) in Lollipop on Android TV, I cannot get it to Auto-cancel.
I am using the "NotificationCompat.BigPictureStyle" as recommended in the Android TV developer pages. The notification works as designed, triggering the PendingIntent as expected but does not auto-cancel and dissappear from recommendations bar. A second selection of the recommendation brings up a blank screen, so I guess the PendingIntent is null at that point. (ADB shows android.content.IntentSender$SendIntentException on 2nd invocation.)
Tested on Nexus Player and Android TV Emulator.
private void buildAndroidTVRecommendation(String name, PendingIntent pIntent,
Context context2, Bundle extras) {
NotificationManager mNotificationManager = (NotificationManager)
context2.getSystemService(Context.NOTIFICATION_SERVICE);
Bitmap smallBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.air_share);
Notification notification = new NotificationCompat.BigPictureStyle(
( new NotificationCompat.Builder(context)
.setContentTitle("Air-Share - incoming share")
.setContentText("From: "+name)
.setContentInfo("Air-Share"))
.setGroup("Air-Share")
.setColor(0xFFFF2020)
.setCategory(Notification.CATEGORY_RECOMMENDATION)
.setLargeIcon(smallBitmap)
.setSmallIcon(R.drawable.air_share)
.setContentIntent(pIntent)
.setExtras(extras)
.setAutoCancel(true)
)
.build();
mNotificationManager.notify(pendingCounter, notification);
mNotificationManager = null;
}

For what it's worth I have created a work-around to get by this issue.
I created a new Activity whose sole purpose is to receive a Pending intent from the Recommendation.
I alter the original Pending Intent of the recommendation to invoke this new activity instead of my desired activity. (The desired activity is outside my app.) In the Extras, I bundle everything I need to know for my original desired intent as well as the notification ID.
When the new activity is launched (after user clicks on recommendation), I extract the ID and cancel the recommendation. I then extract the information for the desired intent, create the intent and finally finish the activity.
public class TVRecommendationActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent in = (Intent) getIntent();
String jsonString = in.getStringExtra("jsonString");
String fileUri = in.getStringExtra("fileUri");
int id =in.getIntExtra("notificationID", -1);
NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
if (id >= 0 ) nm.cancel(id);
JSONIntent newIntent = new JSONIntent(getApplicationContext());
newIntent.setIncomingLocalFileURI(fileUri);
Intent out = newIntent.buildIntentFromJSON(jsonString, fileUri);
if (out != null) startActivity(out);
finish();
}
}

Related

Why is putExtra is not passing the correct values from a service to an activity?

My app has a service that tracks the user's location and compares it to a list of places, then when it finds a latitude/longitude match, it displays a custom notification. Tapping the notification then starts an activity, using a full screen intent, and passes a place name (string) from the service to the activity using putExtra/getExtras. This can happen multiple times as new places are found.
When I first install the app, this works perfectly. If the first place found is, say, London, the name London is passed to the activity correctly. However, if the next place is Bristol, the service has the correct name Bristol at the point where the notification is created (log.v below, labelled "show input"), but the activity still shows the name London (log.v below, labelled "show output") - the new name string is not getting passed across.
However many new places are found, the activity still shows the original name (London in this example) as the place. Also, if I stop and re-start the app, the activity still always shows the place as London, even if the first place found is somewhere else. The only way to get a new place name string passed across correctly is to uninstall and re-install the app.
The relevant code from the service is:
String placeName;
void createPopUpNotification(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.v("sTag","Create notification: placeNumber = "+ placeNumber+" placeName = "+placeName);//show input
String NOTIFICATION_CHANNEL_ID = "notifychannel";
Intent goShow = new Intent(this, DisplayActivity.class);
goShow.putExtra("plname",placeName);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, goShow, 0);
RemoteViews expandedView=new RemoteViews(getApplicationContext().getPackageName(),R.layout.expandednotify);
expandedView.setTextViewText(R.id.placename,placeName);
NotificationManager notificationManager = (NotificationManager) getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
builder.setSmallIcon(R.mipmap.amjnotify);
builder.setCustomContentView(expandedView);
builder.setAutoCancel(true);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setFullScreenIntent(pendingIntent,true);
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "mychannel", NotificationManager.IMPORTANCE_HIGH);
builder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
channel.setSound(null, null);
notificationManager.createNotificationChannel(channel);
notificationManager.notify(123, builder.build());
}
}
and the code from the activity DisplayActivity is:
int placeNumber;
String placeName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
Intent goShow = getIntent();
Bundle params = goShow.getExtras();
placeName=params.getString("plname");
Log.v("sTag","getParams: placeNumber = "+placeNumber+" placeName="+placeName);//show output
goShow.removeExtra("plnumber");
goShow.removeExtra("plname");
}
Can anyone help me to understand what is going on, and how to pass data (string in this case) correctly from the service to the activity?
copy this code and check
placeName=getIntent().getStringExtra("plname"));
Log.v("sTag","getParams: placeNumber = "+placeNumber+"
placeName="+placeName);//show output
goShow.removeExtra("plnumber");
goShow.removeExtra("plname");

Dispatching notification in correct activity

I have a problem with notification on Android (Xamarin).
My scenario is this:
I have a class handling (inheriting GcmServiceBase) the message and creating the Notification object.
In this class I override OnMessage method in this way:
protected override void OnMessage(Context context, Intent intent)
{
if (intent != null || intent.Extras != null)
{
string messageText = intent.Extras.GetString("message");
string messageTitle = intent.Extras.GetString("title");
Intent app_launch_intent = new Intent(context, typeof(Project.WaitForm));
if (App.Instance == null)
{
Console.WriteLine("GCM: Notification received while application not running...");
app_launch_intent.AddFlags(ActivityFlags.ClearTop);
app_launch_intent.AddFlags(ActivityFlags.SingleTop);
}
else if ((App.Instance != null) && (App.Instance.mainActivity.IsInBackground))
{
App.Instance.Logger.Write("GCM: Notification received while application in background...", LogType.Default, LogLevel.Info);
app_launch_intent = new Intent(context, App.Instance.mainActivity.GetType());
}
else
{
App.Instance.Logger.Write("GCM: Notification received while application in foreground...", LogType.Default, LogLevel.Info);
app_launch_intent = new Intent(context, App.Instance.mainActivity.GetType());
app_launch_intent.AddFlags(ActivityFlags.SingleTop);
}
app_launch_intent.PutExtras(intent.Extras);
app_launch_intent.PutExtra("isNotify", true);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, PushService.notificationId, app_launch_intent, PendingIntentFlags.OneShot);
createNotification(context, app_launch_intent, pendingIntent, messageTitle, messageText);
if (App.Instance == null)
{
Console.WriteLine("GCM: Notification object correctly created.");
}
else
{
App.Instance.Logger.Write("GCM: Notification object correctly created.", LogType.Default, LogLevel.Info);
}
}
}
public void createNotification(Context context, Intent result_intent, PendingIntent pendingIntent, string title, string desc)
{
NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
Notification.Builder builder = new Notification.Builder(this)
.SetContentIntent(pendingIntent)
.SetAutoCancel(true)
.SetContentTitle(title)
.SetContentText(desc)
.SetSmallIcon(Resource.Drawable.notify_icon_transparent)
.SetLargeIcon(PushService.IconAgenda)
.SetTicker(title);
Notification notification = builder.Build();
PushService.notificationId += 1;
notificationManager.Notify(PushService.notificationId, notification);
}
as you can see, when message is received I test if application object exists then I create Intent and notification object (App.Instance is a singleton holding informations about the app itself. mainActivity is the current activity shown on screen).
In this way when notification is clicked, last activity on the top of the stack (current on-screen activity if app is in foreground or last on-screen activity before app went in background) will come
up and something in it will take care about notification itself, called by OnNewIntent.
Everything is working fine except for a thing:
Let's say I have 3 Activity called A, B, C.
I start the app and then I receive 2 different notifications when current activity on screen is A.
Both will be displayed in the top bar without problems.
I click on the first of them and it asks me to open another activity (catched in OnNewEvent of current activity and due to notification type), so I say "yes" and I will have a transition to activity C.
Once reached activity C and after done some work, I click on the other notification.
Clicking on it I will be carried to activity A and then notification will be handled.
I know this "problem" is because at notify creation I use the App.Instance.mainActivity.GetType() to create intent that will be use to create PendingIntent and that at that time was activity A for both.
So my question is:
How can I handle second notify click in last activity on screen (so in activity C) instead of activity A?
(I add Xamarin tag just because code above is in C# and not in Java so it may sound strange to a native Android developer)

Resume singleTask activity

I am trying to "resume" a single task activity so it appears in the foreground when a user clicks my notification. (Same behavior as if the user tapped on the app icon from the applications menu.)
My notification creates a PendingIntent which broadcasts an action that is received by my broadcast receiver. If the app is in not in the foreground, I try to resume the app. Additionally, I'm trying to pass a message to my onResume function through the intent. However, I'm hitting an error:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Despite this error, my app is being resumed...don't understand why. However, my extras are not being passed to my onResume function.
So first I create a notification.
public static class MyNotificationCreator {
private static final int MY_NOTIFICATION_ID = 987;
public static void createNotification(Context context) {
Intent openAppIntent = new Intent(context, MyReceiver.class);
openAppIntent.setAction("PleaseOpenApp");
PendingIntent pi = PendingIntent.getBroadcast(context, /*requestCode*/0, openAppIntent, /*flags*/0);
Notification notification = ne Notification.Builder(context)
.setContentTitle("")
.setContentText("Open app")
.setSmallIcon(context.getApplicationInfo().icon)
.setContentIntent(pi)
.build();
NotificationManager notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, notification); }
}
Which broadcasts "PleaseOpenApp" for MyReceiver.
public class MyReceiver extends BroadcastReceiver {
#Override
public void onRecieve(Context context, Intent intent) {
if (intent.action() == "PleaseOpenApp" && !MyPlugin.isForeground) {
PackageManager pm = context.getPackageManager();
//Perhaps I'm not supposed to use a "launch" intent?
Intent launchIntent = pm.getLaunchIntentForPackage(context.getPackageName());
//I'm adding the FLAG_ACTIVITY_NEW_TASK, but I'm still hitting an error saying my intent does not have the FLAG_ACTIVITY_NEW_TASK...
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.putExtra("foo", "bar");
context.startActivity(launchActivity);
} else {
//do other stuff
}
}
}
My plugin keeps track of whether or not we're in the foreground. Also, it tries to get "food" after my receiver attempts to start the app.
public class MyPlugin extends CordovaPlugin {
public static boolean isForeground = false;
#Override
public void initialize(CordovaInterface cordova, CordovaWebView webview) {
super.initialize(cordova, webview);
isForeground = true;
}
#Override
public void onResume(boolean multitasking) {
isForeground = true;
String foo = activity.getIntent().getStringExtra("foo");
Log.d("MyPlugin", foo); //foo is null after clicking the notification!
}
#Override
public void onPause(boolean multitasking) {
isForeground = false;
}
#Override
public void onDestroy() {
isForeground = false;
}
}
Note: because I'm using cordova my activity has a singleTask launchMode.
Also, I'm new to Android development so any help about resuming activities not in the foreground vs resuming activities that have been destroyed and info about general concepts / best practices that I'm not understanding would be appreciated!
I don't think your Broadcast/Broadcast Receiver pattern is necessary.
Intents can be used to directly launch an activity, and when you build the Intent, you can add the extras. Then, your activity onResume() can extract them directly.
Here is a sample Intent and PendingIntent construction that can be sent in a notification:
Intent startActivity = new Intent(context, MyActivity.class);
// You can experiment with the FLAGs passed here to see what they change
startActivity.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("Extra1", myExtra1)
.putExtra("Extra2", myExtra2)
// ADDING THIS MAKES SURE THE EXTRAS ATTACH
.setAction("SomeString");
// Then, create the PendingIntent
// You can experiment with the FLAG passed here to see what it changes
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_UPDATE_CURRENT);
// Then, create and show the notification
Notification notif = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.my_small_icon)
.setContentTitle(myTitle)
.setContentText(myContent)
.setOngoing(isOngoingNotif)
.setAutoCancel(shouldAutoCancel)
.setOnlyAlertOnce(shouldAlertOnce)
.setContentIntent(pendingIntent)
.build();
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.notify(MY_NOTIFICATION_ID, notif);
In your code you are using a "launch Intent" to resume your application. You've added "extras" to the Intent but they will never be seen.
If your app is running, but in the background, and you call startActivity() with a "launch Intent", all this does it bring your task from the background to the foreground. It does not deliver the Intent to the Activity!.
A "launch Intent" does exactly the same thing as when you press the app icon of an app on the HOME screen (if it is already running, but in the background). This just brings the existing task in its current state, from the background to the foreground.
If you want to delivery "extras" to your app, you cannot use a "launch Intent". You must use a regular 'Intent. Depending on your architecture, you could either start a newActivity(which would get the "extras" inonCreate(), or you could start an existingActivity(which would get the "extras" inonNewIntent()`.

Click on notification to enter my app in android

Currently I am working on GCM (Google Cloud message), it allow user to push the message to user device. And I would like achieve the following requirement :
if the user has already enter app , ignore it
if the user has not enter the app , click on notification to enter the app
And the work flow of my app is:
WelcomePage (download json and create data set from it) => MainPage (Display base on the data set)
The code to handle notification
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
String notifyMsg = "";
JSONTokener tokener = new JSONTokener(msg);
if (tokener != null) {
try {
notifyMsg = new JSONObject(tokener).getString("msg");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Intent myintent = new Intent(this, WelcomePageActivity.class);
myintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, myintent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getResources().getString(R.string.notification_title))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(notifyMsg))
.setContentText(notifyMsg)
.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
The problem is if I use WelcomePageActivity class , it will create a new activity if I am at the main page, how can I adjust the code to fit my requirement ?
Thanks
For
1. if the user has already enter app , ignore it:
in the onReceive() , check if your app is running, do not notify.
It can be checked with something like:
ActivityManager activityManager =(ActivityManager)gpsService.this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList= activityManager.getRunningServices(Integer.MAX_VALUE);
if((serviceList.size() > 0)) {
boolean found = false;
for(int i = 0; i < serviceList.size(); i++) {
RunningServiceInfo serviceInfo = serviceList.get(i);
ComponentName serviceName = serviceInfo.service;
if(serviceName.getClassName().equals("Packagename.ActivityOrServiceName")) {
//Your service or activity is running
break;
}
}
if the user has not enter the app , click on notification to enter the app
from the code above, you'l know if you would like to resume the app or launch - call Splash Screen or in your case WelcomeActivity.
About the workflow of your app, i'd suggest check whether you need to download the data every time or not. Can save it maybe or update/download only when required, and rest of flow works as it is.
In your AndroidManifest.xml, define your WelcomePageActivity with the flag android:launchMode="singleTop". From the definition of this flag:
A new instance of a "singleTop" activity may also be created to handle
a new intent. However, if the target task already has an existing
instance of the activity at the top of its stack, that instance will
receive the new intent (in an onNewIntent() call); a new instance is
not created.
So with this flag, your activity will not be created again, rather it will receive a call in the onNewIntent() function with the Intent you used to create the PendingIntent for the notification. You could override this function, and use the intent to pass the activity new information.
You will not able to receive any notification click event so,
try this code :
Intent myintent = new Intent(this, TestActivity.class);
myintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, myintent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getResources().getString(R.string.notification_title))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(notifyMsg))
.setContentText(notifyMsg)
.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public class TestActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// check for your app state is running or not
if(appRunning == false) {
// start your WelcomePage activity.
}
}
}
1.Create an object in GcmIntentService
public static final Object CURRENTACTIVIYLOCK = new Object();
//for storing current activity
public static Activity currentActivity;
2.Update this object value in onPause and onResume of MainActivity to recognize Activity is running or not.
#Override
public void onResume() {
super.onResume();
System.out.println("onResume Home page");
synchronized (GcmIntentService.CURRENTACTIVIYLOCK) {
GcmIntentService.currentActivity = this;
}
}
#Override
public void onPause() {
super.onPause();
synchronized (GcmIntentService.CURRENTACTIVIYLOCK) {
GcmIntentService.currentActivity = null;
}
}
3.In GcmIntentService class, check for the current activity in onHandleIntent method.
synchronized (CURRENTACTIVIYLOCK) {
if (currentActivity != null) {
if (currentActivity.getClass() == HomePageActivity.class) {
} else {
sendNotification(extras.getString("message"));
}
} else {
sendNotification(extras.getString("message"));
}
I'm sure this will help you.

android notification in background if app closed?

I am trying to display a notification in the Android notifications bar even if my application is closed.
I've tried searching, but I have had no luck finding help.
An example of this is a news application. Even if the phone screen is off or the news application is closed, it can still send a notification for recent news and have it appear in the notification bar.
How might I go about doing this in my own application?
You have to build a Service that handles your news and shows notifications when it knows that are new news (Service Doc).
The service will run in background even if your application is closed.
You need a BroadcastReciever to run the service in background after the boot phase is completed. (Start service after boot).
The service will build your notifications and send them through the NotificationManager.
EDIT: This article may suit your needs
The selected answer is still correct, but only for devices running Android 7 versions and below.
As of Android 8+, you can no longer have a service running in the background while your app is idle/closed.
So, it now depends on how you set up your notifications from your GCM/FCM server. Ensure to set it to the highest priority. If your app is in the background or just not active and you only send notification data, the system process your notification and send it to the Notification tray.
I used this answer to write a service, and as an exmaple you need to call ShowNotificationIntentService.startActionShow(getApplicationContext()) inside one of your activities:
import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
public class ShowNotificationIntentService extends IntentService {
private static final String ACTION_SHOW_NOTIFICATION = "my.app.service.action.show";
private static final String ACTION_HIDE_NOTIFICATION = "my.app.service.action.hide";
public ShowNotificationIntentService() {
super("ShowNotificationIntentService");
}
public static void startActionShow(Context context) {
Intent intent = new Intent(context, ShowNotificationIntentService.class);
intent.setAction(ACTION_SHOW_NOTIFICATION);
context.startService(intent);
}
public static void startActionHide(Context context) {
Intent intent = new Intent(context, ShowNotificationIntentService.class);
intent.setAction(ACTION_HIDE_NOTIFICATION);
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_SHOW_NOTIFICATION.equals(action)) {
handleActionShow();
} else if (ACTION_HIDE_NOTIFICATION.equals(action)) {
handleActionHide();
}
}
}
private void handleActionShow() {
showStatusBarIcon(ShowNotificationIntentService.this);
}
private void handleActionHide() {
hideStatusBarIcon(ShowNotificationIntentService.this);
}
public static void showStatusBarIcon(Context ctx) {
Context context = ctx;
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx)
.setContentTitle(ctx.getString(R.string.notification_message))
.setSmallIcon(R.drawable.ic_notification_icon)
.setOngoing(true);
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, STATUS_ICON_REQUEST_CODE, intent, 0);
builder.setContentIntent(pIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = builder.build();
notif.flags |= Notification.FLAG_ONGOING_EVENT;
mNotificationManager.notify(STATUS_ICON_REQUEST_CODE, notif);
}
}

Categories

Resources