From 4f23ee419f722e7e439af8ad64ec7c5d6106d352 Mon Sep 17 00:00:00 2001 From: spideythewebhead Date: Fri, 31 Jan 2020 20:53:58 +0200 Subject: [PATCH 1/3] better handling on background isolate initialization by guarding is the flutternativeview has been created once. --- .../FirebaseMessagingPlugin.java | 198 +++++++++--------- .../FlutterFirebaseMessagingService.java | 152 +++++++++----- 2 files changed, 200 insertions(+), 150 deletions(-) diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java index 7176daa75796..2a0c08c84751 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java @@ -11,8 +11,10 @@ import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; + import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.FirebaseApp; @@ -20,6 +22,7 @@ import com.google.firebase.iid.InstanceIdResult; import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.RemoteMessage; + import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -30,13 +33,16 @@ import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.NewIntentListener; import io.flutter.plugin.common.PluginRegistry.Registrar; + import java.io.IOException; import java.util.HashMap; import java.util.Map; -/** FirebaseMessagingPlugin */ +/** + * FirebaseMessagingPlugin + */ public class FirebaseMessagingPlugin extends BroadcastReceiver - implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware { + implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware { private static final String CLICK_ACTION_VALUE = "FLUTTER_NOTIFICATION_CLICK"; private static final String TAG = "FirebaseMessagingPlugin"; @@ -56,12 +62,7 @@ private void onAttachedToEngine(Context context, BinaryMessenger binaryMessenger this.applicationContext = context; FirebaseApp.initializeApp(applicationContext); channel = new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging"); - final MethodChannel backgroundCallbackChannel = - new MethodChannel(binaryMessenger, "plugins.flutter.io/firebase_messaging_background"); - channel.setMethodCallHandler(this); - backgroundCallbackChannel.setMethodCallHandler(this); - FlutterFirebaseMessagingService.setBackgroundChannel(backgroundCallbackChannel); // Register broadcast receiver IntentFilter intentFilter = new IntentFilter(); @@ -78,7 +79,7 @@ private void setActivity(Activity flutterActivity) { @Override public void onAttachedToEngine(FlutterPluginBinding binding) { onAttachedToEngine( - binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor()); + binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor()); } @Override @@ -122,7 +123,7 @@ public void onReceive(Context context, Intent intent) { channel.invokeMethod("onToken", token); } else if (action.equals(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE)) { RemoteMessage message = - intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); + intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); Map content = parseRemoteMessage(message); channel.invokeMethod("onMessage", content); } @@ -174,27 +175,28 @@ public void onMethodCall(final MethodCall call, final Result result) { e.printStackTrace(); } FlutterFirebaseMessagingService.setBackgroundSetupHandle(mainActivity, setupCallbackHandle); - FlutterFirebaseMessagingService.startBackgroundIsolate(mainActivity, setupCallbackHandle); FlutterFirebaseMessagingService.setBackgroundMessageHandle( - mainActivity, backgroundMessageHandle); - result.success(true); - } else if ("FcmDartService#initialized".equals(call.method)) { - FlutterFirebaseMessagingService.onInitialized(); + mainActivity, backgroundMessageHandle); + FlutterFirebaseMessagingService.startBackgroundIsolate( + new BackgroundIsolateMethodCallHandler(), + mainActivity, + setupCallbackHandle + ); result.success(true); } else if ("configure".equals(call.method)) { FirebaseInstanceId.getInstance() - .getInstanceId() - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); - return; - } - channel.invokeMethod("onToken", task.getResult().getToken()); - } - }); + .getInstanceId() + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); + return; + } + channel.invokeMethod("onToken", task.getResult().getToken()); + } + }); if (mainActivity != null) { sendMessageFromIntent("onLaunch", mainActivity.getIntent()); } @@ -202,84 +204,84 @@ public void onComplete(@NonNull Task task) { } else if ("subscribeToTopic".equals(call.method)) { String topic = call.arguments(); FirebaseMessaging.getInstance() - .subscribeToTopic(topic) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Exception e = task.getException(); - Log.w(TAG, "subscribeToTopic error", e); - result.error("subscribeToTopic", e.getMessage(), null); - return; - } - result.success(null); - } - }); + .subscribeToTopic(topic) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Exception e = task.getException(); + Log.w(TAG, "subscribeToTopic error", e); + result.error("subscribeToTopic", e.getMessage(), null); + return; + } + result.success(null); + } + }); } else if ("unsubscribeFromTopic".equals(call.method)) { String topic = call.arguments(); FirebaseMessaging.getInstance() - .unsubscribeFromTopic(topic) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Exception e = task.getException(); - Log.w(TAG, "unsubscribeFromTopic error", e); - result.error("unsubscribeFromTopic", e.getMessage(), null); - return; - } - result.success(null); - } - }); + .unsubscribeFromTopic(topic) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Exception e = task.getException(); + Log.w(TAG, "unsubscribeFromTopic error", e); + result.error("unsubscribeFromTopic", e.getMessage(), null); + return; + } + result.success(null); + } + }); } else if ("getToken".equals(call.method)) { FirebaseInstanceId.getInstance() - .getInstanceId() - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); - result.success(null); - return; - } - - result.success(task.getResult().getToken()); - } - }); + .getInstanceId() + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); + result.success(null); + return; + } + + result.success(task.getResult().getToken()); + } + }); } else if ("deleteInstanceID".equals(call.method)) { new Thread( - new Runnable() { - @Override - public void run() { - try { - FirebaseInstanceId.getInstance().deleteInstanceId(); - if (mainActivity != null) { - mainActivity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(true); - } - }); + new Runnable() { + @Override + public void run() { + try { + FirebaseInstanceId.getInstance().deleteInstanceId(); + if (mainActivity != null) { + mainActivity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(true); } - } catch (IOException ex) { - Log.e(TAG, "deleteInstanceID, error:", ex); - if (mainActivity != null) { - mainActivity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(false); - } - }); + }); + } + } catch (IOException ex) { + Log.e(TAG, "deleteInstanceID, error:", ex); + if (mainActivity != null) { + mainActivity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(false); } - } - } - }) - .start(); + }); + } + } + } + }) + .start(); } else if ("autoInitEnabled".equals(call.method)) { result.success(FirebaseMessaging.getInstance().isAutoInitEnabled()); } else if ("setAutoInitEnabled".equals(call.method)) { @@ -300,10 +302,12 @@ public boolean onNewIntent(Intent intent) { return res; } - /** @return true if intent contained a message to send. */ + /** + * @return true if intent contained a message to send. + */ private boolean sendMessageFromIntent(String method, Intent intent) { if (CLICK_ACTION_VALUE.equals(intent.getAction()) - || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { + || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { Map message = new HashMap<>(); Bundle extras = intent.getExtras(); diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java index 8c2759770a37..3b88fb52b7a2 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java @@ -12,15 +12,20 @@ import android.os.Handler; import android.os.Process; import android.util.Log; + import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; + +import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry; import io.flutter.view.FlutterCallbackInformation; import io.flutter.view.FlutterMain; import io.flutter.view.FlutterNativeView; import io.flutter.view.FlutterRunArguments; + import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -33,7 +38,7 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { public static final String ACTION_REMOTE_MESSAGE = - "io.flutter.plugins.firebasemessaging.NOTIFICATION"; + "io.flutter.plugins.firebasemessaging.NOTIFICATION"; public static final String EXTRA_REMOTE_MESSAGE = "notification"; public static final String ACTION_TOKEN = "io.flutter.plugins.firebasemessaging.TOKEN"; @@ -42,12 +47,14 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { private static final String SHARED_PREFERENCES_KEY = "io.flutter.android_fcm_plugin"; private static final String BACKGROUND_SETUP_CALLBACK_HANDLE_KEY = "background_setup_callback"; private static final String BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY = - "background_message_callback"; + "background_message_callback"; // TODO(kroikie): make isIsolateRunning per-instance, not static. private static AtomicBoolean isIsolateRunning = new AtomicBoolean(false); - /** Background Dart execution context. */ + /** + * Background Dart execution context. + */ private static FlutterNativeView backgroundFlutterView; private static MethodChannel backgroundChannel; @@ -55,7 +62,7 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { private static Long backgroundMessageHandle; private static List backgroundMessageQueue = - Collections.synchronizedList(new LinkedList()); + Collections.synchronizedList(new LinkedList()); private static PluginRegistry.PluginRegistrantCallback pluginRegistrantCallback; @@ -63,6 +70,13 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { private static Context backgroundContext; + /** + * flag, so we don't create multiple background views and communication channels as it seems that + * multiple {@link MethodChannel}s won't return an answer that is waited by @{link {@link + * LatchResult}} to release the thread. + */ + private static AtomicBoolean isBackgroundInitialized = new AtomicBoolean(false); + @Override public void onCreate() { super.onCreate(); @@ -74,7 +88,7 @@ public void onCreate() { if (!isIsolateRunning.get()) { SharedPreferences p = backgroundContext.getSharedPreferences(SHARED_PREFERENCES_KEY, 0); long callbackHandle = p.getLong(BACKGROUND_SETUP_CALLBACK_HANDLE_KEY, 0); - startBackgroundIsolate(backgroundContext, callbackHandle); + startBackgroundIsolate(new BackgroundIsolateMethodCallHandler(), backgroundContext, callbackHandle); } } @@ -99,14 +113,14 @@ public void onMessageReceived(final RemoteMessage remoteMessage) { } else { final CountDownLatch latch = new CountDownLatch(1); new Handler(getMainLooper()) - .post( - new Runnable() { - @Override - public void run() { - executeDartCallbackInBackgroundIsolate( - FlutterFirebaseMessagingService.this, remoteMessage, latch); - } - }); + .post( + new Runnable() { + @Override + public void run() { + executeDartCallbackInBackgroundIsolate( + FlutterFirebaseMessagingService.this, remoteMessage, latch); + } + }); try { latch.await(); } catch (InterruptedException ex) { @@ -120,7 +134,7 @@ public void run() { * Called when a new token for the default Firebase project is generated. * * @param token The token used for sending messages to this application instance. This token is - * the same as the one retrieved by getInstanceId(). + * the same as the one retrieved by getInstanceId(). */ @Override public void onNewToken(String token) { @@ -134,34 +148,55 @@ public void onNewToken(String token) { * side. Called either by the plugin when the app is starting up or when the app receives a * message while it is inactive. * - * @param context Registrar or FirebaseMessagingService context. - * @param callbackHandle Handle used to retrieve the Dart function that sets up background - * handling on the dart side. + * @param methodCallHandler a method call handler impl that handles the call from dart + * side. {@link BackgroundIsolateMethodCallHandler}. + * @param context Registrar or FirebaseMessagingService context. + * @param callbackHandle Handle used to retrieve the Dart function that sets up background + * handling on the dart side. */ - public static void startBackgroundIsolate(Context context, long callbackHandle) { + public static void startBackgroundIsolate( + final BackgroundIsolateMethodCallHandler methodCallHandler, + Context context, + long callbackHandle + ) { FlutterMain.ensureInitializationComplete(context, null); - String appBundlePath = FlutterMain.findAppBundlePath(); - FlutterCallbackInformation flutterCallback = + + if (!isBackgroundInitialized.get()) { + String appBundlePath = FlutterMain.findAppBundlePath(); + FlutterCallbackInformation flutterCallback = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle); - if (flutterCallback == null) { - Log.e(TAG, "Fatal: failed to find callback"); - return; - } + if (flutterCallback == null) { + Log.e(TAG, "Fatal: failed to find callback"); + + isBackgroundInitialized.set(false); + return; + } + + // Note that we're passing `true` as the second argument to our + // FlutterNativeView constructor. This specifies the FlutterNativeView + // as a background view and does not create a drawing surface. + backgroundFlutterView = new FlutterNativeView(context, true); + + backgroundChannel = + new MethodChannel( + backgroundFlutterView.getDartExecutor().getBinaryMessenger(), + "plugins.flutter.io/firebase_messaging_background" + ); + backgroundChannel.setMethodCallHandler(methodCallHandler); - // Note that we're passing `true` as the second argument to our - // FlutterNativeView constructor. This specifies the FlutterNativeView - // as a background view and does not create a drawing surface. - backgroundFlutterView = new FlutterNativeView(context, true); - if (appBundlePath != null && !isIsolateRunning.get()) { - if (pluginRegistrantCallback == null) { - throw new RuntimeException("PluginRegistrantCallback is not set."); + if (appBundlePath != null && !isIsolateRunning.get()) { + if (pluginRegistrantCallback == null) { + isBackgroundInitialized.set(false); + + throw new RuntimeException("PluginRegistrantCallback is not set."); + } + FlutterRunArguments args = new FlutterRunArguments(); + args.bundlePath = appBundlePath; + args.entrypoint = flutterCallback.callbackName; + args.libraryPath = flutterCallback.callbackLibraryPath; + backgroundFlutterView.runFromBundle(args); + pluginRegistrantCallback.registerWith(backgroundFlutterView.getPluginRegistry()); } - FlutterRunArguments args = new FlutterRunArguments(); - args.bundlePath = appBundlePath; - args.entrypoint = flutterCallback.callbackName; - args.libraryPath = flutterCallback.callbackLibraryPath; - backgroundFlutterView.runFromBundle(args); - pluginRegistrantCallback.registerWith(backgroundFlutterView.getPluginRegistry()); } } @@ -188,9 +223,9 @@ public static void onInitialized() { * * @param channel Background method channel. */ - public static void setBackgroundChannel(MethodChannel channel) { - backgroundChannel = channel; - } +// public static void setBackgroundChannel(MethodChannel channel) { +// backgroundChannel = channel; +// } /** * Set the background message handle for future use. When background messages need to be handled @@ -198,7 +233,7 @@ public static void setBackgroundChannel(MethodChannel channel) { * the incoming message. This method is called by the Dart side via `FcmDartService#start`. * * @param context Registrar context. - * @param handle Handle representing the Dart side method that will handle background messages. + * @param handle Handle representing the Dart side method that will handle background messages. */ public static void setBackgroundMessageHandle(Context context, Long handle) { backgroundMessageHandle = handle; @@ -215,9 +250,9 @@ public static void setBackgroundMessageHandle(Context context, Long handle) { * the Dart side needs to be able to retrieve the setup method. This method is called by the Dart * side via `FcmDartService#start`. * - * @param context Registrar context. + * @param context Registrar context. * @param setupBackgroundHandle Handle representing the dart side method that will setup the - * background method channel. + * background method channel. */ public static void setBackgroundSetupHandle(Context context, long setupBackgroundHandle) { // Store background setup handle in shared preferences so it can be retrieved @@ -238,8 +273,8 @@ public static void setBackgroundSetupHandle(Context context, long setupBackgroun */ public static Long getBackgroundMessageHandle(Context context) { return context - .getSharedPreferences(SHARED_PREFERENCES_KEY, 0) - .getLong(BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY, 0); + .getSharedPreferences(SHARED_PREFERENCES_KEY, 0) + .getLong(BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY, 0); } /** @@ -248,16 +283,16 @@ public static Long getBackgroundMessageHandle(Context context) { * a new background message is received or after background method channel setup for queued * messages received during setup. * - * @param context Application or FirebaseMessagingService context. + * @param context Application or FirebaseMessagingService context. * @param remoteMessage Message received from Firebase Cloud Messaging. - * @param latch If set will count down when the Dart side message processing is complete. Allowing - * any waiting threads to continue. + * @param latch If set will count down when the Dart side message processing is complete. Allowing + * any waiting threads to continue. */ private static void executeDartCallbackInBackgroundIsolate( - Context context, RemoteMessage remoteMessage, final CountDownLatch latch) { + Context context, RemoteMessage remoteMessage, final CountDownLatch latch) { if (backgroundChannel == null) { throw new RuntimeException( - "setBackgroundChannel was not called before messages came in, exiting."); + "setBackgroundChannel was not called before messages came in, exiting."); } // If another thread is waiting, then wake that thread when the callback returns a result. @@ -302,12 +337,12 @@ public static void setPluginRegistrant(PluginRegistry.PluginRegistrantCallback c * * @param context FlutterFirebaseMessagingService context. * @return True if the application is currently in a state where user interaction is possible, - * false otherwise. + * false otherwise. */ // TODO(kroikie): Find a better way to determine application state. private static boolean isApplicationForeground(Context context) { KeyguardManager keyguardManager = - (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); if (keyguardManager.isKeyguardLocked()) { return false; @@ -315,7 +350,7 @@ private static boolean isApplicationForeground(Context context) { int myPid = Process.myPid(); ActivityManager activityManager = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List list; @@ -330,3 +365,14 @@ private static boolean isApplicationForeground(Context context) { return false; } } + +class BackgroundIsolateMethodCallHandler implements MethodChannel.MethodCallHandler { + + @Override + public void onMethodCall(MethodCall call, MethodChannel.Result result) { + if ("FcmDartService#initialized".equals(call.method)) { + FlutterFirebaseMessagingService.onInitialized(); + result.success(true); + } + } +} \ No newline at end of file From 670b45b0693ddead7bfbb719a191c71021c82c32 Mon Sep 17 00:00:00 2001 From: spideythewebhead Date: Fri, 31 Jan 2020 21:04:44 +0200 Subject: [PATCH 2/3] set flag to true when entering the initialization phase --- .../firebasemessaging/FlutterFirebaseMessagingService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java index 3b88fb52b7a2..bcd8d7801000 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java @@ -162,6 +162,8 @@ public static void startBackgroundIsolate( FlutterMain.ensureInitializationComplete(context, null); if (!isBackgroundInitialized.get()) { + isBackgroundInitialized.set(true); + String appBundlePath = FlutterMain.findAppBundlePath(); FlutterCallbackInformation flutterCallback = FlutterCallbackInformation.lookupCallbackInformation(callbackHandle); From 06f1ca3ee5fe25065bb967ef87b0711cade2219f Mon Sep 17 00:00:00 2001 From: spideythewebhead Date: Mon, 3 Feb 2020 23:27:03 +0200 Subject: [PATCH 3/3] format java code --- .../FirebaseMessagingPlugin.java | 189 +++++++++--------- .../FlutterFirebaseMessagingService.java | 95 ++++----- 2 files changed, 133 insertions(+), 151 deletions(-) diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java index 2a0c08c84751..5c5143ddb6d0 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FirebaseMessagingPlugin.java @@ -11,10 +11,8 @@ import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; - import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.google.android.gms.tasks.OnCompleteListener; import com.google.android.gms.tasks.Task; import com.google.firebase.FirebaseApp; @@ -22,7 +20,6 @@ import com.google.firebase.iid.InstanceIdResult; import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.RemoteMessage; - import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -33,16 +30,13 @@ import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.NewIntentListener; import io.flutter.plugin.common.PluginRegistry.Registrar; - import java.io.IOException; import java.util.HashMap; import java.util.Map; -/** - * FirebaseMessagingPlugin - */ +/** FirebaseMessagingPlugin */ public class FirebaseMessagingPlugin extends BroadcastReceiver - implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware { + implements MethodCallHandler, NewIntentListener, FlutterPlugin, ActivityAware { private static final String CLICK_ACTION_VALUE = "FLUTTER_NOTIFICATION_CLICK"; private static final String TAG = "FirebaseMessagingPlugin"; @@ -79,7 +73,7 @@ private void setActivity(Activity flutterActivity) { @Override public void onAttachedToEngine(FlutterPluginBinding binding) { onAttachedToEngine( - binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor()); + binding.getApplicationContext(), binding.getFlutterEngine().getDartExecutor()); } @Override @@ -123,7 +117,7 @@ public void onReceive(Context context, Intent intent) { channel.invokeMethod("onToken", token); } else if (action.equals(FlutterFirebaseMessagingService.ACTION_REMOTE_MESSAGE)) { RemoteMessage message = - intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); + intent.getParcelableExtra(FlutterFirebaseMessagingService.EXTRA_REMOTE_MESSAGE); Map content = parseRemoteMessage(message); channel.invokeMethod("onMessage", content); } @@ -176,27 +170,24 @@ public void onMethodCall(final MethodCall call, final Result result) { } FlutterFirebaseMessagingService.setBackgroundSetupHandle(mainActivity, setupCallbackHandle); FlutterFirebaseMessagingService.setBackgroundMessageHandle( - mainActivity, backgroundMessageHandle); + mainActivity, backgroundMessageHandle); FlutterFirebaseMessagingService.startBackgroundIsolate( - new BackgroundIsolateMethodCallHandler(), - mainActivity, - setupCallbackHandle - ); + new BackgroundIsolateMethodCallHandler(), mainActivity, setupCallbackHandle); result.success(true); } else if ("configure".equals(call.method)) { FirebaseInstanceId.getInstance() - .getInstanceId() - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); - return; - } - channel.invokeMethod("onToken", task.getResult().getToken()); - } - }); + .getInstanceId() + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); + return; + } + channel.invokeMethod("onToken", task.getResult().getToken()); + } + }); if (mainActivity != null) { sendMessageFromIntent("onLaunch", mainActivity.getIntent()); } @@ -204,84 +195,84 @@ public void onComplete(@NonNull Task task) { } else if ("subscribeToTopic".equals(call.method)) { String topic = call.arguments(); FirebaseMessaging.getInstance() - .subscribeToTopic(topic) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Exception e = task.getException(); - Log.w(TAG, "subscribeToTopic error", e); - result.error("subscribeToTopic", e.getMessage(), null); - return; - } - result.success(null); - } - }); + .subscribeToTopic(topic) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Exception e = task.getException(); + Log.w(TAG, "subscribeToTopic error", e); + result.error("subscribeToTopic", e.getMessage(), null); + return; + } + result.success(null); + } + }); } else if ("unsubscribeFromTopic".equals(call.method)) { String topic = call.arguments(); FirebaseMessaging.getInstance() - .unsubscribeFromTopic(topic) - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Exception e = task.getException(); - Log.w(TAG, "unsubscribeFromTopic error", e); - result.error("unsubscribeFromTopic", e.getMessage(), null); - return; - } - result.success(null); - } - }); + .unsubscribeFromTopic(topic) + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Exception e = task.getException(); + Log.w(TAG, "unsubscribeFromTopic error", e); + result.error("unsubscribeFromTopic", e.getMessage(), null); + return; + } + result.success(null); + } + }); } else if ("getToken".equals(call.method)) { FirebaseInstanceId.getInstance() - .getInstanceId() - .addOnCompleteListener( - new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (!task.isSuccessful()) { - Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); - result.success(null); - return; - } - - result.success(task.getResult().getToken()); - } - }); + .getInstanceId() + .addOnCompleteListener( + new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "getToken, error fetching instanceID: ", task.getException()); + result.success(null); + return; + } + + result.success(task.getResult().getToken()); + } + }); } else if ("deleteInstanceID".equals(call.method)) { new Thread( - new Runnable() { - @Override - public void run() { - try { - FirebaseInstanceId.getInstance().deleteInstanceId(); - if (mainActivity != null) { - mainActivity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(true); + new Runnable() { + @Override + public void run() { + try { + FirebaseInstanceId.getInstance().deleteInstanceId(); + if (mainActivity != null) { + mainActivity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(true); + } + }); } - }); - } - } catch (IOException ex) { - Log.e(TAG, "deleteInstanceID, error:", ex); - if (mainActivity != null) { - mainActivity.runOnUiThread( - new Runnable() { - @Override - public void run() { - result.success(false); + } catch (IOException ex) { + Log.e(TAG, "deleteInstanceID, error:", ex); + if (mainActivity != null) { + mainActivity.runOnUiThread( + new Runnable() { + @Override + public void run() { + result.success(false); + } + }); } - }); - } - } - } - }) - .start(); + } + } + }) + .start(); } else if ("autoInitEnabled".equals(call.method)) { result.success(FirebaseMessaging.getInstance().isAutoInitEnabled()); } else if ("setAutoInitEnabled".equals(call.method)) { @@ -302,12 +293,10 @@ public boolean onNewIntent(Intent intent) { return res; } - /** - * @return true if intent contained a message to send. - */ + /** @return true if intent contained a message to send. */ private boolean sendMessageFromIntent(String method, Intent intent) { if (CLICK_ACTION_VALUE.equals(intent.getAction()) - || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { + || CLICK_ACTION_VALUE.equals(intent.getStringExtra("click_action"))) { Map message = new HashMap<>(); Bundle extras = intent.getExtras(); diff --git a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java index bcd8d7801000..ecc13f59ab67 100644 --- a/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java +++ b/packages/firebase_messaging/android/src/main/java/io/flutter/plugins/firebasemessaging/FlutterFirebaseMessagingService.java @@ -12,12 +12,9 @@ import android.os.Handler; import android.os.Process; import android.util.Log; - import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; - import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.PluginRegistry; @@ -25,7 +22,6 @@ import io.flutter.view.FlutterMain; import io.flutter.view.FlutterNativeView; import io.flutter.view.FlutterRunArguments; - import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -38,7 +34,7 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { public static final String ACTION_REMOTE_MESSAGE = - "io.flutter.plugins.firebasemessaging.NOTIFICATION"; + "io.flutter.plugins.firebasemessaging.NOTIFICATION"; public static final String EXTRA_REMOTE_MESSAGE = "notification"; public static final String ACTION_TOKEN = "io.flutter.plugins.firebasemessaging.TOKEN"; @@ -47,14 +43,12 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { private static final String SHARED_PREFERENCES_KEY = "io.flutter.android_fcm_plugin"; private static final String BACKGROUND_SETUP_CALLBACK_HANDLE_KEY = "background_setup_callback"; private static final String BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY = - "background_message_callback"; + "background_message_callback"; // TODO(kroikie): make isIsolateRunning per-instance, not static. private static AtomicBoolean isIsolateRunning = new AtomicBoolean(false); - /** - * Background Dart execution context. - */ + /** Background Dart execution context. */ private static FlutterNativeView backgroundFlutterView; private static MethodChannel backgroundChannel; @@ -62,7 +56,7 @@ public class FlutterFirebaseMessagingService extends FirebaseMessagingService { private static Long backgroundMessageHandle; private static List backgroundMessageQueue = - Collections.synchronizedList(new LinkedList()); + Collections.synchronizedList(new LinkedList()); private static PluginRegistry.PluginRegistrantCallback pluginRegistrantCallback; @@ -88,7 +82,8 @@ public void onCreate() { if (!isIsolateRunning.get()) { SharedPreferences p = backgroundContext.getSharedPreferences(SHARED_PREFERENCES_KEY, 0); long callbackHandle = p.getLong(BACKGROUND_SETUP_CALLBACK_HANDLE_KEY, 0); - startBackgroundIsolate(new BackgroundIsolateMethodCallHandler(), backgroundContext, callbackHandle); + startBackgroundIsolate( + new BackgroundIsolateMethodCallHandler(), backgroundContext, callbackHandle); } } @@ -113,14 +108,14 @@ public void onMessageReceived(final RemoteMessage remoteMessage) { } else { final CountDownLatch latch = new CountDownLatch(1); new Handler(getMainLooper()) - .post( - new Runnable() { - @Override - public void run() { - executeDartCallbackInBackgroundIsolate( - FlutterFirebaseMessagingService.this, remoteMessage, latch); - } - }); + .post( + new Runnable() { + @Override + public void run() { + executeDartCallbackInBackgroundIsolate( + FlutterFirebaseMessagingService.this, remoteMessage, latch); + } + }); try { latch.await(); } catch (InterruptedException ex) { @@ -134,7 +129,7 @@ public void run() { * Called when a new token for the default Firebase project is generated. * * @param token The token used for sending messages to this application instance. This token is - * the same as the one retrieved by getInstanceId(). + * the same as the one retrieved by getInstanceId(). */ @Override public void onNewToken(String token) { @@ -148,17 +143,16 @@ public void onNewToken(String token) { * side. Called either by the plugin when the app is starting up or when the app receives a * message while it is inactive. * - * @param methodCallHandler a method call handler impl that handles the call from dart - * side. {@link BackgroundIsolateMethodCallHandler}. - * @param context Registrar or FirebaseMessagingService context. - * @param callbackHandle Handle used to retrieve the Dart function that sets up background - * handling on the dart side. + * @param methodCallHandler a method call handler impl that handles the call from dart side. + * {@link BackgroundIsolateMethodCallHandler}. + * @param context Registrar or FirebaseMessagingService context. + * @param callbackHandle Handle used to retrieve the Dart function that sets up background + * handling on the dart side. */ public static void startBackgroundIsolate( - final BackgroundIsolateMethodCallHandler methodCallHandler, - Context context, - long callbackHandle - ) { + final BackgroundIsolateMethodCallHandler methodCallHandler, + Context context, + long callbackHandle) { FlutterMain.ensureInitializationComplete(context, null); if (!isBackgroundInitialized.get()) { @@ -166,7 +160,7 @@ public static void startBackgroundIsolate( String appBundlePath = FlutterMain.findAppBundlePath(); FlutterCallbackInformation flutterCallback = - FlutterCallbackInformation.lookupCallbackInformation(callbackHandle); + FlutterCallbackInformation.lookupCallbackInformation(callbackHandle); if (flutterCallback == null) { Log.e(TAG, "Fatal: failed to find callback"); @@ -180,10 +174,9 @@ public static void startBackgroundIsolate( backgroundFlutterView = new FlutterNativeView(context, true); backgroundChannel = - new MethodChannel( - backgroundFlutterView.getDartExecutor().getBinaryMessenger(), - "plugins.flutter.io/firebase_messaging_background" - ); + new MethodChannel( + backgroundFlutterView.getDartExecutor().getBinaryMessenger(), + "plugins.flutter.io/firebase_messaging_background"); backgroundChannel.setMethodCallHandler(methodCallHandler); if (appBundlePath != null && !isIsolateRunning.get()) { @@ -225,9 +218,9 @@ public static void onInitialized() { * * @param channel Background method channel. */ -// public static void setBackgroundChannel(MethodChannel channel) { -// backgroundChannel = channel; -// } + // public static void setBackgroundChannel(MethodChannel channel) { + // backgroundChannel = channel; + // } /** * Set the background message handle for future use. When background messages need to be handled @@ -235,7 +228,7 @@ public static void onInitialized() { * the incoming message. This method is called by the Dart side via `FcmDartService#start`. * * @param context Registrar context. - * @param handle Handle representing the Dart side method that will handle background messages. + * @param handle Handle representing the Dart side method that will handle background messages. */ public static void setBackgroundMessageHandle(Context context, Long handle) { backgroundMessageHandle = handle; @@ -252,9 +245,9 @@ public static void setBackgroundMessageHandle(Context context, Long handle) { * the Dart side needs to be able to retrieve the setup method. This method is called by the Dart * side via `FcmDartService#start`. * - * @param context Registrar context. + * @param context Registrar context. * @param setupBackgroundHandle Handle representing the dart side method that will setup the - * background method channel. + * background method channel. */ public static void setBackgroundSetupHandle(Context context, long setupBackgroundHandle) { // Store background setup handle in shared preferences so it can be retrieved @@ -275,8 +268,8 @@ public static void setBackgroundSetupHandle(Context context, long setupBackgroun */ public static Long getBackgroundMessageHandle(Context context) { return context - .getSharedPreferences(SHARED_PREFERENCES_KEY, 0) - .getLong(BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY, 0); + .getSharedPreferences(SHARED_PREFERENCES_KEY, 0) + .getLong(BACKGROUND_MESSAGE_CALLBACK_HANDLE_KEY, 0); } /** @@ -285,16 +278,16 @@ public static Long getBackgroundMessageHandle(Context context) { * a new background message is received or after background method channel setup for queued * messages received during setup. * - * @param context Application or FirebaseMessagingService context. + * @param context Application or FirebaseMessagingService context. * @param remoteMessage Message received from Firebase Cloud Messaging. - * @param latch If set will count down when the Dart side message processing is complete. Allowing - * any waiting threads to continue. + * @param latch If set will count down when the Dart side message processing is complete. Allowing + * any waiting threads to continue. */ private static void executeDartCallbackInBackgroundIsolate( - Context context, RemoteMessage remoteMessage, final CountDownLatch latch) { + Context context, RemoteMessage remoteMessage, final CountDownLatch latch) { if (backgroundChannel == null) { throw new RuntimeException( - "setBackgroundChannel was not called before messages came in, exiting."); + "setBackgroundChannel was not called before messages came in, exiting."); } // If another thread is waiting, then wake that thread when the callback returns a result. @@ -339,12 +332,12 @@ public static void setPluginRegistrant(PluginRegistry.PluginRegistrantCallback c * * @param context FlutterFirebaseMessagingService context. * @return True if the application is currently in a state where user interaction is possible, - * false otherwise. + * false otherwise. */ // TODO(kroikie): Find a better way to determine application state. private static boolean isApplicationForeground(Context context) { KeyguardManager keyguardManager = - (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); if (keyguardManager.isKeyguardLocked()) { return false; @@ -352,7 +345,7 @@ private static boolean isApplicationForeground(Context context) { int myPid = Process.myPid(); ActivityManager activityManager = - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List list; @@ -377,4 +370,4 @@ public void onMethodCall(MethodCall call, MethodChannel.Result result) { result.success(true); } } -} \ No newline at end of file +}