I'm changing an existing application to use MediaStyle notifications to provide lock screen music information and transport controls in Android 5.0. It seems, however, that updating such notifications isn't working as expected. The following code snippet generates notifications each time a button is pressed, incrementing a counter displayed in the notification title:
public class MainActivity extends Activity {
private int serial;
private TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.textView1);
}
public void buttonClick(final View view) {
final Notification.Builder builder = new Notification.Builder(this)
.setContentTitle("Title " + serial)
.setContentText("Text")
.setContentInfo("Info")
.setSmallIcon(R.drawable.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher))
.setOngoing(true)
.setStyle(new Notification.MediaStyle())
.setVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE );
nm.notify(1, builder.build());
text.setText("Serial = " + serial);
serial++;
}
}
The problems are:
When executed in the emulator running 5.0, the notification text in the notification drawer or lock screen is out of sync with the serial displayed in the TextView. If MediaStyle is not set, notifications have correct numbering in the notification drawer, but not in the lock screen.
The MediaStyle notification is displayed correctly in the lock screen, until it's viewed in the notification drawer for the first time. After that, it's not displayed as a MediaStyle notification in the lock screen anymore (but not exactly like a standard, non-MediaStyle notification). Notably, buttons added with addAction() are not displayed anymore, until the emulator is restarted.
So I suspect I'm doing something very wrong (either that, or the Android image running on the emulator is broken, but that seems less likely). Any ideas?
It turned out that the emulator was broken after all. The notification behavior, including updating icons added with addAction(), title and info updates, and notifications in lock screen after being seen in the drawer, is correct in a real device (a Nexus 5 running 5.0). At the time this answer was written, however, the problems still happened in the emulator provided with the API 21 SDK, and no updates were available.
Related
Project Target API 30 Android 10, Min API 19 KitKat
I am creating a parental control app where parents can restrict certain apps.
I have a foreground service where I would I ideally trigger an activity-like notification from the service that would envelop the user's entire screen or take them to the home screen.
I have learned that starting activities, including going to the home screen, is no longer possible under normal circumstances as of API 29. https://developer.android.com/guide/components/activities/background-starts
After reading the documentation it seems creating a full screen intent notification is the most highly recommended workaround to the activity restriction.
I am currently working with the following code for my full screen intent notification:
Intent blockedIntent = new Intent(App.getContext(), BlockedItemReceiver.class);
blockedIntent.putExtra("currentApp", restrictedApp.name);
PendingIntent pendingBlockedIntent = PendingIntent.getActivity(App.getContext(), 50, blockedIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder blockedNotificationBuilder = new NotificationCompat.Builder(App.getContext(), CHANNEL_BLOCKED_FROM_ITEM)
.setSmallIcon(R.drawable.ic_baseline_lock_24)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentTitle(restrictedApp.packageName + " was Blocked!")
.setContentText(restrictedApp.name + " will be available again DAY at TIME")
.setCategory(NotificationCompat.CATEGORY_CALL)
.setFullScreenIntent(pendingBlockedIntent, true);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(5, blockedNotificationBuilder.build());
I looked at some documentation for customizing notifications, but the information I found does not have a full screen intent notification example, and when I attempt to add the methods in the documentation example for NotificationCompat.Builder such as setCustomContentView() with custom layouts, my notification fails to appear without an error message.
https://developer.android.com/training/notify-user/custom-notification
Besides, I do not want a collapsed and expanded version of my notification as the example in the link has, just one full screen view.
TLDR; I want a notification that always engulfs the screen until the user presses a button on the notification to dismiss it. How can I further customize my full screen intent notification to truly take up the full screen? Ideally with a layout. If I must have a collapsed version of my notification, I don't want the user to ever see it, because I always want my notification to engulf the screen while it's showing.
There are existing apps such as AppBlock that have found a workaround to launching an activity-like thing from a foreground service that takes up the full screen, so what I am trying to do is possible even if the specific question I'm asking won't lead me to that result. Please suggest another way of accomplishing what I am after if what I am asking to do in my question is "impossible". What I generally want to do is certainly possible.
I want to only display a notification icon, I don't want that notification in the app drawer also. I have an alarm clock app, where I only want to set the icon, but not have something in the drawer, like the default android alarm clock app. My current code is below.
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.jolt_action_bar_logo);
int mNotificationId = 001;
// Gets an instance of the NotificationManager service
NotificationManager mNotifyMgr =
(NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
// Builds the notification and issues it.
mNotifyMgr.notify(mNotificationId, mBuilder.build());
While it creates a notification icon. It also creates a notification in the drawer.
It's impossible to do what you are wanting to do. The idea of notification icons is so that the user can get any idea of what notifications that the user has yet to act upon. If a notification icon is displayed, the user expects to see a notification of it in the notification shade.
Also, the alarm clock icon you are referring to that the default alarm clock app puts out isn't a notification icon, rather it is an indicator icon to let you know that something is happening (or going to happen) with your device. Examples of indicator icons include the alarm clock icon, the WiFi connectivity icon, the mobile network icon, the NFC icon on some devices, etc. If your app is a alarm clock app, you could tell the Android system to display the alarm icon, as outlined here.
I can't seem to be able to create an Android Wear notification that updates with out blinking the app icon whereas the same code works fine on an Android phone.
Most referenced solutions talk about updating the same notification, use setAlertOnlyOnce, keeping the ID or when the same. However, whatever I do, every time the notification is updated it blinks (most noted by the App Icon).
As suggested here Android Wear: Timer like notification card on wear device you can use setHintHideIcon(true) to hide the app icon, which hides the blinking part, however in the limited world of Android Wear Notifications the app icon plays a large part in the branding of the application.
If you want a timer, you can use .setUsesChronometer(true) and let the system update the timer which works perfectly. Unfortunately if you want to update something else than time (like steps or messages received count) it seems to me you're out of luck.
Below you can find code that works fine when run as a phone app, but blinks when run as a wearable app.
Commented line below to demonstrate that the notification still blinks (when running on wear, not the phone) that the notification still blinks when posting an unchanged notification on the wearable. Uncomment to update the notification again.
mNotification = buildNotification(WearMainActivity.this);
Therefore my question is if anyone has any further idea's we can explore to keep the notification from blinking or if we can write this down as an Android Wear bug?
public class WearMainActivity extends Activity {
public final int NOTIFICATION_ID= 1;
public Notification mNotification;
public int count;
public long when;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
count = 0;
when = System.currentTimeMillis();
mNotification = buildNotification(WearMainActivity.this);
postDelayedHandler();
finish();
}
private void postDelayedHandler(){
new Handler().postDelayed(new Runnable() {
public void run() {
count++;
mNotification = buildNotification(WearMainActivity.this);
NotificationManager notifyMgr = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE));
notifyMgr.notify(NOTIFICATION_ID, mNotification);
postDelayedHandler();
}
}, 1000L);
}
private Notification buildNotification(Context context){
return new Notification.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(context.getString(R.string.app_name))
.setContentText("Count: "+count)
.setWhen(when)
// .setOngoing(true) //Don't do this, adds "Mute app" action
.setOnlyAlertOnce(true)
.setPriority(Notification.PRIORITY_MAX)
.extend(new Notification.WearableExtender()
// .setHintHideIcon(true) //Hides the icon, so kinda hides the blink
)
.build();
}
}
Tested on:
Wearable: Moto 360 (4.4W2) Wear Emulator (5.0.1)
Phones: Galaxy Nexus (4.3) and Nexus 5 (5.0.0)
Occurs: When running as a Wearable app or as phone notification displayed on Wearable. Works perfect on phone.
Referenced questions:
How can I avoid blinking notification update while changing button
Updating an ongoing notification quietly
How to properly update a notification post api 11?
Replace:
NotificationManager notifyMgr =
((NotificationManager)getSystemService(NOTIFICATION_SERVICE));
to:
NotificationManagerCompat notifyMgr =
NotificationManagerCompat.from(this);
More informations: https://developer.android.com/training/wearables/notifications/creating.html
You also making a lot of updates. Every update is send via bluetooth to Wear. You should create self-install app to Android Wear. The delay of sending is about 3 seconds.
I solved this problem a while back when I was working with Android Wear but the code in unfortunately gone. Anyway, what I did was to not BUILD the notification every time I wanted to update it, I just tagged the notification the first time I created it and then I retrieved it with the TAG and made my changes directly to that object. That completely stopped the flickering...
I have an IntentService that uploads a file. Everything works fine, but I'm a little confused about how to handle the notifications. When I start the notification I use startForeground() because the files can be rather large and I don't want the upload to get killed unless absolutely necessary. When I use startForeground() it displays two notifications in the notification area (one under Ongoing and one under Notifications):
I've read through a number of different Stack Overflow posts and web articles, but none of them answer the question I have...hopefully I haven't missed one that talks about ths.
It's my understanding that the ongoing notification in the image above (the one without the progress bar) is put there since this is running in the foreground (Services documentation). That's all well and good if you ask me, and I understand why. However, I don't need two notifications displayed and I'm pretty sure most people wouldn't want two notifications cluttering up the notification area either. I would like to know how to properly handle the notification so that only one displays and doesn't clutter up the notification area.
The only way I've been able to get around this is if I set the integer ID for startForeground (int id, Notification notification) (ONGOING_NOTIFICATION_ID in my code below) to zero. However, the documentation I quote above says:
Caution: The integer ID you give to startForeground() must not be 0
Setting it to 0 disables the Ongoing notification and then just shows the regular notification with the progress bar. I figure I could kind of "fix" this by setting .setOngoing(true) until it's done uploading the file and then setting .setOngoing(false) once it's finished, so it can be dismissed. I'm not sure exactly how "Cautious" one has to be with setting the integer ID to 0. To me it kind of seems like a lazy way to be able to get around the issue I'm having, but I don't know if there are other consequences for setting it to 0. Also, this only works if I only have one notification that I'm dealing with. If I have multiple, different notifications, then I'll need different IDs for each one and this won't work. Update: It looks like setting the ID to 0 won't work in Android 4.3, so now I'm back to square one.
What is a valid way to get around displaying both notifications?
Update/Solution: Duh, taking some time off and then coming back to this and double-checking what I had done based on #dsandler 's recommendation helped me figure out what I was doing wrong. I wasn't setting the correct ID when I was doing the progress update, so that's why it was creating two notifications and one wasn't getting updated. Using the same ID (ONGOING_NOTIFICATION_ID) for all the notifications solved the issue for me. See the code below for the additional pieces I hadn't included before and where I had made the mistake.
Relevant code from UploadFile.java:
public class UploadFile extends IntentService {
private static final int ONGOING_NOTIFICATION_ID = 1;
#Override
protected void onHandleIntent(Intent intent) {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentTitle(getText(R.string.upload))
.setContentText("0% " + getText(R.string.upload_in_progress))
.setSmallIcon(android.R.drawable.stat_sys_upload);
startForeground(ONGOING_NOTIFICATION_ID, mBuilder.build());
....
if (progress > 0){
percent = (Long.valueOf(progress) * 100) / totalSize;
mBuilder.setProgress(100, percent.intValue(), false);
mBuilder.setContentText(percent.intValue() + "% " + getText(R.string.upload_in_progress));
mNotifyManager.notify(ONGOING_NOTIFICATION_ID, mBuilder.build()); // <-- My problem was that I had set the ID here to 0 and did not make it the same as the ID I set above
}
....
}
First off, judging by your description, you might be able to get away with a regular service, recognizing that the out-of-memory killer won't come calling unless things are getting dire on the device and your service has really been running a very long time.
That said, if you must make the service foreground, the typical strategy here is to show your progress using the Notification you used when you put your service into the foreground state. Using NotificationManager.notify you can post as many updates to that notification as you like, including adjustments to progress bars via Notification.Builder.setProgress, for example.
In this way you only show one notification to the user, and it's the one required by startForeground.
When you want to update a Notification set by startForeground(), simply build a new notication and then use NotificationManager to notify it.
The KEY point is to use the same notification id.
I didn't test the scenario of repeatedly calling startForeground() to update the Notification, but I think that using NotificationManager.notify would be better.
Updating the Notification will NOT remove the Service from the foreground status (this can be done only by calling stopForground );
Example:
private static final int notif_id=1;
#Override
public void onCreate (){
this.startForeground();
}
private void startForeground() {
startForeground(notif_id, getMyActivityNotification(""));
}
private Notification getMyActivityNotification(String text){
// The PendingIntent to launch our activity if the user selects
// this notification
CharSequence title = getText(R.string.title_activity);
PendingIntent contentIntent = PendingIntent.getActivity(this,
0, new Intent(this, MyActivity.class), 0);
return new Notification.Builder(this)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(R.drawable.ic_launcher_b3)
.setContentIntent(contentIntent).getNotification();
}
/**
this is the method that can be called to update the Notification
*/
private void updateNotification() {
String text = "Some text that will update the notification";
Notification notification = getMyActivityNotification(text);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(notif_id, notification);
}
I'm trying to turn the LED-flashing on for my activity in foreground, but it works only when the screen is off.
Is it possible to turn the LED on for active activity with the screen on?
My code:
protected void led() {
Notification notif = new Notification();
notif.ledARGB = 0xFF0000ff;
notif.flags = Notification.FLAG_SHOW_LIGHTS | Notification.FLAG_ONGOING_EVENT;
notif.ledOnMS = 800;
notif.ledOffMS = 200;
notificationManager.notify( LED_NOTIFICATION_ID, notif );
}
At first Android LED indicator is very hardware dependent. Second - there is no API for managing LED instead of Notification class with its FLAG_SHOW_LIGHTS flag and several flags for managing flash duration and LED color which you use. Notification is a message you can display to the user outside of your application's normal UI the primary purpose of LED indicator is to present additional notification information for the user when the screen is off. So the answer is definite NO. The LED will only start flashing if your screen is off and it’ll stop when you turn it back on. And there is no way to turn on and off LED when you want and to turn it when any of the application activities is in foreground, because it is managed by the OS internally.