5

I have implemented FCM Push notifications in my Android App. I get the all the notification JSON in data payload. And I have 'not added' 'Notification' tag on api. So in all the states (Foreground/Background/Killed) I got the notification in data payload only.

It is Working fine on Non-Customised OS phones like Moto, Google etc, In All States when an app is Foreground/Background/Killed. But the Problem is when I testing on the customised OS phones like Oppo, Vivo or MIUI the notification is arrived only when the app is in Foreground or Background (App is in Memory), Not arrived/appearing when App is "killed" (Not in Memory).

What should I do?

In any case, thank you for your time.

public class MyFirebaseMessagingService extends FirebaseMessagingService{
    private static final String TAG = "MyFirebaseMsgService";

    /**
     * Called when message is received.
     *
     * @param remoteMessage Object representing the message received from Firebase Cloud Messaging.
     */
    // [START receive_message]
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // [START_EXCLUDE]
        // There are two types of messages data messages and notification messages. Data messages are handled
        // here in onMessageReceived whether the app is in the foreground or background. Data messages are the type
        // traditionally used with GCM. Notification messages are only received here in onMessageReceived when the app
        // is in the foreground. When the app is in the background an automatically generated notification is displayed.
        // When the user taps on the notification they are returned to the app. Messages containing both notification
        // and data payloads are treated as notification messages. The Firebase console always sends notification

        // [END_EXCLUDE]

        // TODO(developer): Handle FCM messages here.

        Log.e(TAG, "From: " + remoteMessage.getFrom());

        // Check if message contains a data payload.
        if (remoteMessage.getData().size() > 0)
        {
            Log.d(TAG, "Message data payload: " + remoteMessage.getData());

            if (/* Check if data needs to be processed by long running job */ true) {
                // For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
                scheduleJob();
            } else {
                // Handle message within 10 seconds
                handleNow();
            }

            if (remoteMessage.getNotification()!=null)
            sendNotification(remoteMessage.getNotification().getBody());
            else
                sendNotification("Body");

        }

        // Check if message contains a notification payload.
        if (remoteMessage.getNotification() != null)
        {
            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
            if (remoteMessage.getNotification()!=null)
                sendNotification(remoteMessage.getNotification().getBody());
            else
                sendNotification("Body");

        }

        // Also if you intend on generating your own notifications as a result of a received FCM
        // message, here is where that should be initiated. See sendNotification method below.
    }
    // [END receive_message]

    /**
     * Schedule a job using FirebaseJobDispatcher.
     */
    private void scheduleJob() {
        // [START dispatch_job]
        FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(this));
        Job myJob = dispatcher.newJobBuilder()
                .setService(MyJobService.class)
                .setTag("my-job-tag")
                .build();
        dispatcher.schedule(myJob);
        // [END dispatch_job]
    }

    /**
     * Handle time allotted to BroadcastReceivers.
     */
    private void handleNow() {
        Log.d(TAG, "Short lived task is done.");
    }

    /**
     * Create and show a simple notification containing the received FCM message.
     *
     * @param messageBody FCM message body received.
     */
    private void sendNotification(String messageBody)
    {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);

        String channelId = getString(R.string.default_notification_channel_id);
        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder =
                new NotificationCompat.Builder(this, channelId)
                        .setSmallIcon(R.drawable.ic_launcher_background)
                        .setContentTitle("FCM Message")
                        .setContentText(messageBody)
                        .setAutoCancel(true)
                        .setSound(defaultSoundUri)
                        .setContentIntent(pendingIntent);

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        // Since android Oreo notification channel is needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(channelId,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }

        if (notificationManager != null) {
            notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
        }
    }

}

My AndroidManifest.xml file as below:

<!-- [START firebase_iid_service] -->
    <service
        android:name=".Firebase.FirebaseId">
        <intent-filter>
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
        </intent-filter>
    </service>
    <service
        android:name="Firebase.MyFirebaseMessagingService"
        android:stopWithTask="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT" />
        </intent-filter>
    </service>

    <!-- [END firebase_iid_service] -->

    <!--
   Set custom default icon. This is used when no icon is set for incoming notification messages.

   -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_icon"
        android:resource="@drawable/ic_launcher_background" />
    <!--
         Set color used with incoming notification messages. This is used when no color is set for the incoming
         notification message.
    -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_color"
        android:resource="@color/colorAccent" />

    <!-- [START fcm_default_channel] -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_channel_id"
        android:value="@string/default_notification_channel_id" />
    <!-- [END fcm_default_channel] -->
Rohan Shinde
  • 374
  • 1
  • 6
  • 16
  • add your code, it will make a better sense – Maddy Mar 23 '18 at 05:19
  • Your service must be running to catch the notifications. Make sure when the app is killed your service has not stopped. Use "stickyIntent" to make your service start automatically when its killed manually. – Akash Khatri Mar 23 '18 at 05:21
  • @AkashKhatri can you please send me the sample code of it? – Rohan Shinde Mar 23 '18 at 05:37
  • you can find what you're looking for here: https://stackoverflow.com/questions/37711082/how-to-handle-notification-when-app-in-background-in-firebase – Akash Khatri Mar 23 '18 at 05:42
  • @AkashKhatri my server side is code is exact same as given in code snipest of your link. I have added only 'data' tag to receive custom key-value pair of notification. – Rohan Shinde Mar 23 '18 at 05:50
  • And in your intent-filter did you use the same action as the one you're sending from server? – Akash Khatri Mar 23 '18 at 06:03
  • Yes, have also done this. See my AndroidManifest.xml file – Rohan Shinde Mar 23 '18 at 06:10
  • @Rohan make sure you are using high priority message. You can add this in json payload `android:{"priority":"high", "ttl":"86400s" }` – global_warming Mar 23 '18 at 06:20
  • @global_warming let me test this one, i have added only the "priority" tag, not ''ttl". – Rohan Shinde Mar 23 '18 at 06:29
  • @global_warming Tested this one, but not working for me. I killed the app from memory and tested through Postman, notification not yet appeared.! – Rohan Shinde Mar 23 '18 at 06:39
  • @Rohan It appears in my case on MIUI 8 even after clearing app from memory. – global_warming Mar 23 '18 at 06:45
  • @Rohan Try with this [link](https://stackoverflow.com/questions/45602194/not-receiving-any-notification-from-fcm-firebase-cloud-messaging-when-applicatio?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) – Onkar Mar 23 '18 at 07:03
  • @global_warming What is Happening is.. You are right at your side. I have tested on Vivo Y51L and MI A1 phone. I have write a simple started service for my app and tested, When an app is in foreground the service is started and it is displayed in my 'Running' app '1 Process and 1 Service is Started'. But when i killed the app from both the phones, the service get killed from vivo phone but still alive in MI A1 phone. It is the problem of customization of OS. The manufacturer of this OS have designed this OS is like, once the app is killed, the background service also get Killed. – Rohan Shinde Mar 23 '18 at 07:38
  • Do you have any solution on this? How we can run the service still app is killed? – Rohan Shinde Mar 23 '18 at 07:40
  • I get it what you are saying, same happens with Xiaomi MIUI, but in my case if I get FCM with data only when app is killed, then it is working for me on Honor and Xiaomi. Haven't tested on Vivo. Though I am not using FIrebase Job Dispatcher. – global_warming Mar 23 '18 at 08:07
  • @global_warming, Yes, Same issue on Oppo phone. Not appearing notifications when app is killed. It is actually killing the background services with the app on some phone, but on some phone it is still running the service. But Whatsapp has achive that solution. Their notifications are appearing still app is killed. – Rohan Shinde Mar 23 '18 at 08:12
  • Are you getting anything in logs? – global_warming Mar 23 '18 at 08:13
  • @global_warming Nope! No logcats displayed when an app is killed. – Rohan Shinde Mar 23 '18 at 08:35
  • Facing the same problems on Vivo 1610 with Funtouch OS_30 vivo ROM. FCM isn't received at all. Samsung device is receiving the FCM eventhough the app is cleared from memory. – ישו אוהב אותך Mar 23 '18 at 08:56

2 Answers2

2

Well i have found a solution to this problem. Write a custom service for your application which continuously running in background and write one Broadcast Receiver to restart the service once it is killed. This works fine for me. I have tested this on Vivo, Oppo, Redmi phones. It is working!

My Service Code is as below--

public class MyService extends Service
{

private static final String TAG = "MyService";


@Override
public void onStart(Intent intent, int startId)
{
    // TODO Auto-generated method stub
    super.onStart(intent, startId);
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}


@Override
public void onCreate()
{
    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    //call to onTaskRemoved
    onTaskRemoved(intent);
    //return super.onStartCommand(intent, flags, startId);
    Toast.makeText(this, "Service Started!", Toast.LENGTH_SHORT).show();

    return START_NOT_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent)
{
    return null;
}

@Override
public void onDestroy()
{
    Toast.makeText(this, "Service Destroyed!", Toast.LENGTH_SHORT).show();
    Intent intent = new Intent("com.myapp.startservice");
    //Intent intent = new Intent("android.intent.action.BOOT_COMPLETED");
    intent.putExtra("yourvalue", "torestore");
    sendBroadcast(intent);
    super.onDestroy();

}



@Override public void onTaskRemoved(Intent rootIntent)
{
    Log.e("onTaskRemoved", "Called!");

    //thread = new Thread(this);
    //startThread();

    /*Intent alarm = new Intent(this.getApplicationContext(), MyBroadCastReceiver.class);
    boolean alarmRunning = (PendingIntent.getBroadcast(this.getApplicationContext(), 0, alarm, PendingIntent.FLAG_NO_CREATE) != null);
    //if(!alarmRunning)
    {
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 0, alarm, 0);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        if (alarmManager != null) {
            alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 10000, pendingIntent);
        }
    }*/

     //send broadcast to your BroadcastReciever
    Intent intent = new Intent("com.myapp.startservice"); //unique String to uniquely identify your broadcastreceiver
    //Intent intent = new Intent("android.intent.action.BOOT_COMPLETED");
    intent.putExtra("yourvalue", "torestore");
    sendBroadcast(intent);

     //intent to restart your service.
    Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
    restartServiceIntent.setPackage(getPackageName());

    PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
    AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    if (alarmService != null) {
        alarmService.set(
                AlarmManager.ELAPSED_REALTIME,
                SystemClock.elapsedRealtime() + 1000,
                restartServicePendingIntent);
    }

    super.onTaskRemoved(rootIntent);

}}

My BroadcastReceiver is as Below --

public class MyBroadCastReceiver extends BroadcastReceiver
{

@Override
public void onReceive(Context context, Intent intent)
{
    Log.e("MyBroadCastReceiver", "onReceive");

    //if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction()))
    {
        Intent service = new Intent(context, MyService.class);
        context.startService(service);
        Log.e("BootCompleteReceiver", " __________BootCompleteReceiver _________");

    }
}}

My AndroidManifest.xml file as below--

 <!-- My Service -->
    <service
        android:name=".Service.MyService"
        android:exported="false"
        android:stopWithTask="false" />


    <!-- My Broadcast Receiver -->
    <receiver
        android:name=".Service.MyBroadCastReceiver"
        android:enabled="true"
        android:exported="false">

        <intent-filter>
            <action android:name="com.myapp.startservice" />
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE"/>
            <category android:name="android.intent.category.DEFAULT"/>

        </intent-filter>

    </receiver>

And my MainActivity.java file code to start the service ---

public class MainActivity extends AppCompatActivity
{

Button btnStopService;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    btnStopService = findViewById(R.id.btnStopService);

    //get FirebaseToken
    getToken();

    //start Service
    startService();



    btnStopService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MainActivity.this, MyService.class);
            stopService(intent);
        }
    });

}


private void getToken()
{
    FirebaseId firebaseId=new FirebaseId();
    String token_firebase=firebaseId.getFireBaseToken();
}


private void startService()
{

    Intent myIntent = new Intent(this, MyService.class);
    PendingIntent pendingIntent = PendingIntent.getService(this, 0, myIntent, 0);
    Log.e("TAG", "++++++++++222222++++++++");
    AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
    Calendar calendar = Calendar.getInstance();
    // calendar.setTimeInMillis(System.currentTimeMillis());
    //calendar.add(Calendar.SECOND, 10);
    if (alarmManager != null) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
    }

    Toast.makeText(this, "Start Alarm", Toast.LENGTH_LONG).show();

}

private void s()
{
    Intent intent = new Intent(this, MyService.class);
    startService(intent);
}}
Rohan Shinde
  • 374
  • 1
  • 6
  • 16
0

This is an age old story with custom OS providers like MIUI, Vivo etc They're very strict with their battery optimization policies and hence they won't allow even sticky services to restart when an app is closed and this is the primary reason why you're facing this issue. Although there is nothing that you can do from your code that can help your users here but you can take them to their Security Center and ask them to enable auto-start feature. To do this you have to add this piece of code:

try {
    Intent intent = new Intent();
    String manufacturer = android.os.Build.MANUFACTURER;
    if ("xiaomi".equalsIgnoreCase(manufacturer)) {
        intent.setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"));
    } else if ("oppo".equalsIgnoreCase(manufacturer)) {
        intent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
    } else if ("vivo".equalsIgnoreCase(manufacturer)) {
        intent.setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
    } else if("oneplus".equalsIgnoreCase(manufacturer)) { 
        intent.setComponent(new ComponentName("com.oneplus.security", "com.oneplus.security.chainlaunch.view.ChainLaunchAppListAct‌​ivity")); }

    List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    if  (list.size() > 0) {
        context.startActivity(intent);
        } 
    } catch (Exception e) {
        Crashlytics.logException(e);
}

this app will take the user to the security center where you have to ask them to enable auto-start feature for your app. Now apps like whatsapp and instagram don't have such issues but i don't clearly know the reason for this, As i have seen on my devices, auto-start is by default enabled for these apps.

Abhishek Tiwari
  • 936
  • 10
  • 25
  • if you want add these lines at your splash screen and run it once, do note that your app won't know if user has enabled auto enable or not, so you may run this code only once. Also you should consider providing a nice disclaimer to your users why you need this permission to be enabled and what they may lose if they choose to disable it. – Abhishek Tiwari Mar 23 '18 at 10:08
  • Not Working not finding the activity.! Unable to find explicit activity class {com.vivo.permissionmanager/com.vivo.permissionmanager.activity.BgStartUpManagerActivity}; – Rohan Shinde Mar 23 '18 at 10:20
  • They may have refactored their packages, in order to find what packages are being opened, you'll have to make an app that logs every app that is opened in your device, you can do that by extending AccessibilityService and log the packages that are being opened, that's how i was able to get the package names for the setting files, although this is a bit complex. – Abhishek Tiwari Mar 23 '18 at 10:30