Bad notification for startForeground in Android app - android

I am developing a service using Xamarin Android 3.5. Our app targets Android 8.1 (API 27 - Oreo). I want the service to run as a foreground service. However I am getting the following error when I run the service.
Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=null pri=1 contentView=null vibrate=null sound=null defaults=0x0 flags=0x42 color=0x00000000 vis=PRIVATE)
Here is the code for the service.
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
base.OnStartCommand(intent, flags, startId);
var context = Application.Context;
const int pendingIntentId = 0;
PendingIntent pendingIntent = PendingIntent.GetActivity(context, pendingIntentId, intent, PendingIntentFlags.OneShot);
var notification = new NotificationCompat.Builder(context)
.SetContentTitle("Testing")
.SetContentText("location tracking has begun.")
.SetSmallIcon(Resource.Drawable.icon)
.SetContentIntent(pendingIntent)
.SetOngoing(true)
.Build();
// Enlist this instance of the service as a foreground service
const int Service_Running_Notification_ID = 935;
StartForeground(Service_Running_Notification_ID, notification);
return StartCommandResult.NotSticky;
}
I have updated the AndroidManifest.xml with the following.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
In the MainActivity.cs I have the follwing code which we use to create a notification channel for sending app notifications (and which correctly creates the notification channel).
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channel = new NotificationChannel(ApplicationConstants.ChannelId, ApplicationConstants.ChannelName, NotificationImportance.Default)
{
Description = ApplicationConstants.ChannelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}

For Xamarin.Forms and Xamarin.Android
========Put this code in public override StartCommandResult OnStartCommand ===========
if (Build.VERSION.SdkInt >= Build.VERSION_CODES.O)
RegisterForegroundServiceO();
else { RegisterForegroundService(); }
========================================END=============================
void RegisterForegroundService()
{
var notification = new Notification.Builder(this)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText(Resources.GetString(Resource.String.notification_text))
.SetSmallIcon(Resource.Drawable.icon_userProfile)
.SetContentIntent(BuildIntentToShowMainActivity())
.SetOngoing(true)
.Build();
const int Service_Running_Notification_ID = 936;
StartForeground(Service_Running_Notification_ID, notification);
}
void RegisterForegroundServiceO()
{
String NOTIFICATION_CHANNEL_ID = "com.Your.project.id";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Your Channel Name", NotificationManager.ImportanceHigh);
NotificationManager manager = (NotificationManager)GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification= notificationBuilder.SetOngoing(true)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText(Resources.GetString(Resource.String.notification_text))
.SetSmallIcon(Resource.Drawable.icon_userProfile)
.SetContentIntent(BuildIntentToShowMainActivity())
.SetOngoing(true)
.Build();
const int Service_Running_Notification_ID = 936;
StartForeground(Service_Running_Notification_ID, notification);
}
Happy Coding. :-)

invalid channel for service notification
You are creating a notification channel but never assigning it in your NotificationCompat.Builder:
var notification = new NotificationCompat.Builder(context)
~~~
.SetChannelId(ApplicationConstants.ChannelId)
~~~
Docs: https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder

Here is a shot solution.
NotificationChannel chan = new NotificationChannel( "my_service_urgent", "My Channel", NotificationImportance.None );
chan.EnableVibration( false );
chan.LockscreenVisibility = NotificationVisibility.Secret;
NotificationManager notificationManager = GetSystemService( NotificationService ) as NotificationManager;
notificationManager.CreateNotificationChannel( chan );
var notification = new Notification.Builder( this, "my_service_urgent" )
.SetContentTitle( Resources.GetString( Resource.String.app_name ) )
.SetContentText( Resources.GetString( Resource.String.notification_text ) )
.SetSmallIcon( Resource.Drawable.ic_stat_name )
.SetContentIntent( BuildIntentToShowMainActivity() )
.SetOngoing( true )
.AddAction( BuildRestartTimerAction() )
.AddAction( BuildStopServiceAction() )
.Build();
// SERVICE_RUNNING_NOTIFICATION_ID = 101
// Enlist this instance of the service as a foreground service
StartForeground( Constants.SERVICE_RUNNING_NOTIFICATION_ID, notification );

In my case it was the missing of .SetSmallIcon(Resource.Drawable.icon)
After adding this line to Notification.Builder() it worked.
Strangely I did not face the problem in Debug builds but only in Release builds.

Related

NotificationChannel sound needs restart phone

I am trying to provide local notification into my App with my custom sound.
I created simple app in Visual Studio, and I want show notification on FAB Click.
My problem is that, notification is displaying, but without sound.
But... After phone restart - notification works properly with sound!
My code - create channel method called in OnCreate():
string channelId = "1";
string channelName = "a";
int notificationId = 0;
private void createNotificationChannel()
{
var notMgr = (NotificationManager)GetSystemService(NotificationService);
var uri = new Android.Net.Uri.Builder()
.Scheme(ContentResolver.SchemeAndroidResource)
.Authority(Resources.GetResourcePackageName(Resource.Raw.neworder4))
.AppendPath(Resources.GetResourceTypeName(Resource.Raw.neworder4))
.AppendPath(Resources.GetResourceEntryName(Resource.Raw.neworder4))
.Build();
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
if (notMgr.GetNotificationChannel(channelId) == null)
{
var channel = new NotificationChannel(channelId, channelName, NotificationImportance.Default);
channel.Description = channelName;
var aa = new AudioAttributes.Builder()
.SetContentType(AudioContentType.Sonification)
.SetUsage(AudioUsageKind.Notification).Build();
channel.SetSound(uri, aa);
notMgr.CreateNotificationChannel(channel);
}
}
}
Show notification on FAB click:
private void FabOnClick(object sender, EventArgs eventArgs)
{
var notMgr = (NotificationManager)GetSystemService(NotificationService);
notificationId = notificationId + 1;
var not = new NotificationCompat.Builder(this, channelId)
.SetSmallIcon(Resource.Mipmap.ic_launcher)
.SetContentText("test")
.SetContentTitle("test")
.Build();
notMgr.Notify(notificationId, not);
}
What I'm doing wrong?
I don't want to require my users to reboot phones after app install.
Resource.Raw.neworder4 is mp3 file, and is set as Android Resource.
I use the following code to play custom sound for Local Notification, it works fine, you can take a look:
Creating channel:
string channelId = "location_notification";
string channelName = "localnotification";
int notificationId = 1000;
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
Android.Net.Uri sound = Android.Net.Uri.Parse("android.resource://" + Application.Context.PackageName + "/" + Resource.Raw.MyAudio);
var aa = new AudioAttributes.Builder()
.SetContentType(AudioContentType.Sonification)
.SetUsage(AudioUsageKind.Notification).Build();
var channelDescription = "local notification!!!!";
var channel = new NotificationChannel(channelId, channelName, NotificationImportance.Max)
{
Description = channelDescription
};
channel.SetSound(sound,aa);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
Building the notification:
private void Button3_Click(object sender, System.EventArgs e)
{
// Build the notification:
var builder = new NotificationCompat.Builder(this, channelId)
.SetAutoCancel(true) // Dismiss the notification from the notification area when the user clicks on it
.SetDefaults((int)NotificationDefaults.All)
.SetContentTitle("Button Clicked") // Set the title
//.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Alarm))
.SetSmallIcon(Resource.Drawable.addreminder); // This is the icon to display
// Finally, publish the notification:
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(notificationId, builder.Build());
}
I found solution for one part of my problem (for Emulator):
https://stackoverflow.com/a/56935747
I tested notifications on physical phone with android 9.0, and there is no sound for notifications without restart phone.
For now I think provided solution is close enough for me.

ListenableWorker done, but ongoing foreground notification still exists?

I am trying to use ListenableWorker to perform some background API calls.
During this process I want to display notification with a progress.
I use setForegroundAsync() function as per this documentation google docs
The problem is when my ListenableWorker stops, i can still see my ongong notification and i cant remove it.
This is my refresh fucntion where I change notification parameters:
private void updateRefreshStatus(float objectsProcessed, int totalObjects) {
if (totalObjects == 0) {
setProgressAsync(new Data.Builder().putFloat(PROGRESS_CONSTANT_ID, 100.0f).build());
setForegroundAsync(createForegroundInfo(FloatFormatter.roundFloatTwoDigitsAsString(100f)));
} else {
setProgressAsync(new Data.Builder().putFloat(PROGRESS_CONSTANT_ID, (objectsProcessed / (float) totalObjects) * 100f).build());
setForegroundAsync(createForegroundInfo(FloatFormatter.roundFloatTwoDigitsAsString((objectsProcessed / (float) totalObjects) * 100f)));
}
}
This is how I create my foregroundInfo:
private ForegroundInfo createForegroundInfo(#NonNull String progress) {
Notification notification = WorkerUtils.prepareProgressNotification("Update",
"Update progress " + progress + " %",
getApplicationContext());
return new ForegroundInfo(NOTIFICATION_PROGRESS_ID,notification);
}
This is my progress notification code:
static Notification prepareProgressNotification(String title, String message, Context context) {
// Make a channel if necessary
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
CharSequence name = WorkerConstants.VERBOSE_NOTIFICATION_CHANNEL_NAME;
String description = WorkerConstants.VERBOSE_NOTIFICATION_CHANNEL_DESCRIPTION;
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel =
new NotificationChannel(WorkerConstants.CHANNEL_ID, name, importance);
channel.setDescription(description);
// Add the channel
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
// Create the notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, WorkerConstants.CHANNEL_ID)
.setContentTitle(title)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText(message)
.setTicker(title)
.setOngoing(true)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setVibrate(new long[0]);
// Show the notification
return builder.build();
}
I tried calling cancelAll, cancel(id), but it does nothing in my case.
UPDATE: Removing .setOngoing(true) from builder, does nothing for me its seems like setForegroundAsync() issue?

"Bad notification for startForeground: Invalid channel for service notification" even though channel has been set

What could be the possible other causes of a
Fatal Exception: android.app.RemoteServiceException
Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification
besides not having a channel set? It seems to happen only on Android 8 and 9
My stacktrace shows that channel has a value:
invalid channel for service notification: Notification(channel=com.myapp.notifications pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x52 color=0x00000000 category=service number=0 vis=PRIVATE semFlags=0x0 semPriority=0 semMissedCount=0)
so it seems that the channel has been created correctly.
My background service is set with the usual
public static final String NOTIFICATION_CHANNEL_ID = "com.myapp.notifications";
public static final String SERVICE_CHANNEL_ID = "com.myapp.services";
public static final int NOTIFICATION_ID = 100;
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(2, buildNotification(getApplicationContext()));
}
}
#RequiresApi(Build.VERSION_CODES.O)
Notification buildNotification(Context context) {
String channelId = SERVICE_CHANNEL_ID;
setupNotificationChannel(context, channelId);
return NotificationCompat.Builder(context, channelId)
.setOngoing(true)
.setSmallIcon(R.drawable.app_icon)
.setCategory(Notification.CATEGORY_SERVICE)
.setAutoCancel(true)
.setChannelId(channelId)
.build();
}
#RequiresApi(Build.VERSION_CODES.O)
void setupNotificationChannel(Context context, String channelId) {
NotificationManager notificationManager = getNotificationManager(context);
if (notificationManager.getNotificationChannel(channelId) == null) {
NotificationChannel channel = NotificationChannel(channelId, getChannelName(channelId), getChannelImportance())
channel.setDescription(getChannelDescription(channelId))
notificationManager.createNotificationChannel(channel)
}
}
I also display some push notifications in a similar way:
public void showNotification(Context context) {
NotificationManager notificationManager = getNotificationManager(context);
String channelId = SHRNotificationService.NOTIFICATION_CHANNEL_ID;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setupNotificationChannel(context, channelId);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(getNotificationTitle())
.setColor(ContextCompat.getColor(context, R.color.BLUE))
.setChannelId(channelId)
.setPriority(getNotificationPriority(channelId))
.setAutoCancel(false)
.setOngoing(true)
.setOnlyAlertOnce(true);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
I have the
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
in the Manifest.
What is also unclear is that the stacktrace refers to a notification from the service category, with com.myapp.notifications as the channel Id, but none of the background services or notifications meet both these conditions.
It's been a while, but I think this is how it you want to make it look:
fun sendToForegroundWithNotification() {
val CHANNEL_ID = "YourChannelId"
#Suppress("DEPRECATION") //for the NotificationBuilder < API26 ctor
val notificationBuilder: Notification.Builder
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// O > need a channel, create one
notificationManager.createNotificationChannel(
NotificationChannel(
CHANNEL_ID,
"Title",
NotificationManager.IMPORTANCE_DEFAULT
)
)
notificationBuilder = Notification.Builder(this, CHANNEL_ID)
} else notificationBuilder = Notification.Builder(this)
val notification = notificationBuilder
.setContentTitle(getText(R.string.app_name))
.setContentText(getText(R.string.your_content))
.setSmallIcon(R.drawable.some_icon)
.etc(whatever you need)
.build()
// since you're in your Service, you can call startFg directly:
startForeground(666, notification) // ;-)
}

FCM no notification on oreo when app is in foreground (below oreo it works)

I want to receive FCM notifications on my phone. I am using the code below. Now my problem is that when I am on android oreo and the app is running in foreground I dont receive the message (but on android 7 for example it works fine!). What do I have to change to get it to work? Background and when the app is killed works on both os.
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
//if the message contains data payload
//It is a map of custom keyvalues
//we can read it easily
if(remoteMessage.getData() != null){
sendNotification(remoteMessage);
}
}
private void sendNotification(RemoteMessage remoteMessage) {
Map<String, String> data = remoteMessage.getData();
String title = remoteMessage.getNotification().getTitle();
String body = remoteMessage.getNotification().getBody();
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
String NOTIFICATION_CHANNEL_ID = "TestID";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
#SuppressLint("WrongConstant")
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription("Description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.BLUE);
notificationChannel.setVibrationPattern(new long[]{0,1000,500,1000});
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID);
notificationBuilder.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.app_icon)
.setTicker("Ticker")
.setContentTitle(title)
.setContentText(body)
.setContentInfo("info");
notificationManager.notify(1,notificationBuilder.build());
}
}
Thanks!

Firebase push notification toast message fatal exception

I'm getting a weird crash report from Crashlytics saying that my app has crashed because of a Toast message not called from the UI thread. It's weird because I don't show Toast messages from push notifications. It looks like it's working for thousands of different users, but for this one is crashing. I don't know what's going on. Below is the report:
Fatal Exception: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.(Handler.java:209)
at android.os.Handler.(Handler.java:123)
at android.widget.Toast$TN.(Toast.java:350)
at android.widget.Toast.(Toast.java:106)
at android.widget.Toast.makeText(Toast.java:264)
at android.media.RingtoneManager.isRingtoneExist(RingtoneManager.java:1195)
at android.app.NotificationManager.notify(NotificationManager.java:235)
at com.google.firebase.messaging.zza.zzt(Unknown Source)
at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(Unknown Source)
at com.google.firebase.iid.zzc.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
app dependencies:
implementation 'com.google.firebase:firebase-core:12.0.1'
implementation 'com.google.firebase:firebase-messaging:12.0.1'
Device: Android 6.0 - Alcatel Shine Lite 5080X
The firebase push service:
public class FirebasePushService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(App.NAME, "message received");
Map<String, String> map = remoteMessage.getData();
JSONObject json = new JSONObject();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
json.put(key, value);
}
int pushType = json.getInt("push_type", -1);
if (pushType > 0) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 100, intent, PendingIntent.FLAG_ONE_SHOT);
createNotification();
}
}
private void createNotification() {
// create the channel first
createChannels();
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Build b = new NotificationCompat.Builder(context, CHANNEL_ID)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setSound(defaultSoundUri)
.setColor(ContextCompat.getColor(context, R.color.primaryColor));
b.setContentIntent(pi);
b.setAutoCancel(true);
Notification not = b.build();
not.flags |= Notification.FLAG_AUTO_CANCEL;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
not.ledARGB = Color.GREEN;
not.flags = Notification.FLAG_SHOW_LIGHTS;
not.ledOnMS = 1000;
not.ledOffMS = 1000;
not.defaults |= Notification.DEFAULT_VIBRATE;
not.defaults |= Notification.DEFAULT_SOUND;
}
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(PUSH_NOTIFICATION_ID, not);
}
#TargetApi(Build.VERSION_CODES.O)
private void createChannels() {
// create android channel
NotificationChannel androidChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
// Sets whether notifications posted to this channel should display notification lights
androidChannel.enableLights(true);
// Sets whether notification posted to this channel should vibrate.
androidChannel.enableVibration(true);
// Sets the notification light color for notifications posted to this channel
androidChannel.setLightColor(Color.GREEN);
// Sets whether notifications posted to this channel appear on the lockscreen or not
androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
getManager().createNotificationChannel(androidChannel);
}
}
Does anyone have any clue what's going on?

Categories

Resources