android 12, how replace the notification trampolines - android-notifications

Having android sdk which intercept the push notification, and has been using notification trampoline to further open the end activity. In the case of deeplink the app who uses this sdk will open the configured deeplink handler activity.
Snippet for the trampoline:
public class NotificationTrampolineReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
final PendingResult asyncResult = goAsync();
ExecutorService executor = Executors.newSingleThreadExecutor();
asycTask(executor, new Runnable() {
#Override
public void run() {
String urlStr = getStringExtra(intent, PUSH_URL);
if (urlStr != null) {
var intent2: Intent = Intent(Intent.ACTION_VIEW, Uri.parse(urlStr));
if (intent2 != null) {
intent2.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent2.addFlags(FLAG_ACTIVITY_BROUGHT_TO_FRONT);
context.startActivity(intent2);
logAnalytics(intent, Message.MessageAction.OPEN);
}
}
asyncResult.finish();
}
});
}
void asycTask(ExecutorService executor, final Runnable task) {
try {
executor.execute(task);
} catch (Throwable ex) {}
}
}
The notification trampolines is not working in Android 12 anymore.
The notification trampolines is needed in the sdk to intercept the click and do something like to log analytics event; closing the notification drawer when clicking at the Action button on the notification, etc. And this sdk does not know what activities the app may configure to handle the deeplinks.
Using a dummy activity to replace the trampoline would work, but not feel right, i.e. open the activity and inside to open another one then finish this one.
When android 12 puts restriction on the notification tramoline, does it suggest a replacement for the use case like the one here? Haven't find one.
What is the suggested new solution for intercepting the push notification tap first and then open the activity?

You are better off launching an Activity directly from the Notification. The Activity could then do the analytics and figure out what Activity needs to be launched and then delegate to that. As an alternative, the launched Activity could send the broadcast Intent to your BroadcastReceiver which could do the work.
NOTE: If you launch an Activity directly from a Notification, that is not a trampoline. The trampoline occurs when you launch a Service or a BroadcastReceiver directly from the Notification and that component then launches an Activity. The idea is that the user needs to be in control of what pops onto his screen. If he taps a notification, he expects that something will appear on his screen, if your Service launches an Activity, that can happen at any time and could possibly interrupt him.

Related

Flutter firebase receiving notification when closed or in background - how to pass message to class

I'm building a Flutter app with Firebase push notifications.
When a message is received I want the app to show a popup modal with the text.
When the app is in the foreground the popup modal displays - this works
When the app is the background and the message is received by the mobile it appears in the system tray, the user clicks on it, the app opens and the initial message is found and displayed to the user in the popup modal - eg. FirebaseMessaging.onMessageOpenedApp function - this works.
When the app is in the background, the notification is received by the phone (and the firebase listener is working because it outputs the message data using debugPrint to test), it appears in the system tray, but the user chooses NOT to click the message - when the app is brought back to the foreground the message is ignored - This is a problem.
The "FirebaseMessaging.onBackgroundMessage" function needs to be placed in the TOP LEVEL (outside of any class). Therefore when the app is once again placed in the foreground, how do I push message data from a message that may have been received whilst the app is in the background, in to my App Class to display the message content? I'm using "AppLifecycleState" to detect when the app is returned to the foreground, but I can't grab the message data because it is received in the top level, not in the class.
Please see my code below (see last few lines for the bit I'm stuck on)...
//TOP LEVEL-----
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (message.messageId!="") {
debugPrint("Have received a background message! Will have to grab the message from here somehow if the user didn't interact with the system tray message link");
}
}
Future<void> main() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
//APP CLASS-----
class MyAppextends StatefulWidget {
State<MyApp> createState() => _MyAppState();
}
//APP STATE CLASS
class _MyAppState extends State<MyApp> with WidgetsBindingObserver{
#override
void initState() {
super.initState();
_initiateNotificationForegroundListener();
_initiateInteractedMessage();
}
// This variable will tell you whether the application is in foreground or not.
bool _isInForeground = true;
//Initiate Foreground Notification Listener (works)
void _initiateNotificationForegroundListener() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_handleNotificationInstruction(message);
});
}
//Initiate Background/Closed Notification Listener if user clicks the message in the system try (works)
Future<void> _initiateInteractedMessage() async {
RemoteMessage? message = await FirebaseMessaging.instance.getInitialMessage();
if (message != null) {
_handleNotificationInstruction(message);
}
// When app is in background (Stream listener)
FirebaseMessaging.onMessageOpenedApp
.listen(_handleNotificationInstruction);
}
void _handleNotificationInstruction(RemoteMessage message) {
//Create popup to display message info (works)
}
//Detect when an app moves in to the foreground
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_isInForeground = state == AppLifecycleState.resumed;
if(_isInForeground){
/** HELP!!!
/* How can I check what message might have been received while app was in the background?? ie. the top-level _firebaseMessagingBackgroundHandler function??
**/
}
}
I was facing this problem too, this is annoying but thanks to this thread, I found the solution. The FCM's document states:
When received, an isolate is spawned (Android only, iOS/macOS does not
require a separate isolate) allowing you to handle messages even when
your application is not running.
An important note is that each isolate has its own memory so that your shared preferences in foreground will be different from it in background. You can "synchronize" the data by calling SharedPreference.reload() when your app resumes.
Save a message to a persistent storage and when the application starts check if the storage has pending messages.

Xamarin.Forms (iOS) MessagingCenter message from AppDelegate is not delivered

I have next goal: my app receives push notifications (FCM) and I need to handle user manipulations with them in all possible scenarios.
When I receive foreground push and handle it in WillPresentNotification - everything works as expected.
When I sent app to the background mode (clicking home button) and receive a push, click on that push at Notification Center I have next code in DidReceiveNotificationResponse method:
MessagingCenter.Send<object, PushNotificationObject>(this, "PushNavigationToRootPage", push);
So it just sends a message to the RootPage, which is subscribed/unsubscribed as below:
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Unsubscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage");
MessagingCenter.Subscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage", (sender, _push) => { ... });
}
protected override void OnDisappearing()
{
MessagingCenter.Unsubscribe<object, PushNotificationObject>(this, "PushNavigationToRootPage");
base.OnDisappearing();
}
enter code here
It also works! BUT!
If I completely close the app (swipe it off from App Switcher) and then click on the received push - it will only Send a message (from the code above), but will never reach code which is placed inside Subscribe of a RootPage.
Any ideas of what is wrong?
Thanks in advance!

Signout with Google Sign-In option crashes with java.lang.IllegalStateException: GoogleApiClient is not connected yet

I am trying to use Google Sign-In for my android app from here.
I am able to log-in succesfully with the google account & able to fetch all the details. However, when ever I try to logout it fails with following error :
java.lang.IllegalStateException: GoogleApiClient is not connected yet.
I have read many answers suggesting to create googleClientApi object inside onCreate() and that's what I am doing.I have added callbacks for connected and suspended but the connect never goes into suspended mode.
Following is my code snippet :
public static void doInit(Context ctx, FragmentActivity fragmentActivity) {
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(
GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(ctx)
.enableAutoManage(fragmentActivity , googleAuth)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.addConnectionCallbacks(googleAuth)
.build();
}
public static Intent doGoogleLogIn() {
return Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
}
public static boolean doGoogleLogOut() {
Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
}
});
return true;
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// An unresolvable error has occurred and Google APIs (including Sign-In) will not
// be available.
Log.d("Signin", "onConnectionFailed:" + connectionResult);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
System.out.println("Connected...");
}
#Override
public void onConnectionSuspended(int i) {
System.out.println("Suspened....");
}
The only thing that is doubtful to me is, when I login and create googleApiClient object, its created from different activity that the one which I am using for logout. I don't suspect this is the reason because when the activity loaded, the isConnected on googleApiClient is returning true. However, the moment I do some UI action(Click on Logout), it starts returning false.
Primary requirement was to login and logout from different activities.
Finally I managed to make it work.
The actual cause of the error is "enableAutoManage" invocation at the time of Building Client object.
The API doc here suggests that it would automatically do the life cycle management by calling methods on onStart & onStop of the activity.
Therefore, if you want to use the same object across different activities then you should avoid calling "enableAutoManage" and invoke apiObject.connect(preferably in onStart of activity) and apiObject.disconnect() or logout (preferably in onStop of activity) manually.

JavaFx Turn-based Multiplayer Application with Google Service

Now I am using Gluon plugin and it is very helpful to start working with JavaFXPorts. I have my application ready and I can use it on computers and phones. I tested on phone with system android. Application is working good but only with my server.
I take care of the different resolutions and I think that it is good for now.
I want to have Turn-based Multiplayer Application but I have still big problem with using Google Play Service. Tutorial which show how to use this services for turn-based multiplayer application is written in pure android and use Activity. My question is maybe very simple but If I have my application view from "fxml" how to use it as an tutorial Activity?
I want to do auto-matching for my application and next I want override method takeTurn() to suit it to my application.
For example, how can I change thing like that (code below) to application in JavaFX?
I must use google services from my JavaFX(src/main/java folder) class in addition AndroidPlatformProvider.java and all methods must be in src/android/java folder. I know that I must use PlatformService and PlatformProvider. I did it as in the examples: HelloPlatform and SMSTracker .
I use methods from my interface PlatformProvider but application still crashes. :(
I only use Provider from my code of JavaFX and I don't have android Activity. I don't know how to use these method without Activity or View:
- public void onActivityResult(int request, int response, Intent data)
- public void playTurn(View view)
Can I call from google service methods to methods from my controller for view (fxml).
I don't know how these methods should working with JavaFX.
public class TbmpGameActivity extends Activity {
...
#Override
public void onActivityResult(int request, int response, Intent data) {
super.onActivityResult(request, response, data);
...
if (request == RC_SELECT_PLAYERS) {
if (response != Activity.RESULT_OK) {
// user canceled
return;
}
// Get the invitee list.
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// Get auto-match criteria.
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(
Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
TurnBasedMatchConfig tbmc = TurnBasedMatchConfig.builder()
.addInvitedPlayers(invitees)
.setAutoMatchCriteria(autoMatchCriteria)
.build();
// Create and start the match.
Games.TurnBasedMultiplayer
.createMatch(mGoogleApiClient, tbmc)
.setResultCallback(new MatchInitiatedCallback());
}
}
}
or something like that:
// Call this method when a player has completed his turn and wants to
// go onto the next player, which may be himself.
public void playTurn(View view) {
// Get the next participant in the game-defined way, possibly round-robin.
String nextParticipantId = getNextParticipantId();
// Get the updated state. In this example, we simply retrieve a
// text string from the view. In your game, there may be more
// complicated state.
mTurnData = mDataView.getText().toString();
// At this point, you might want to show a waiting dialog so that
// the current player does not try to submit turn actions twice.
showSpinner();
// Invoke the next turn. We are converting our data to a byte array.
Games.TurnBasedMultiplayer
.takeTurn(mGoogleApiClient, mMatch.getMatchId(),
mTurnData.getBytes(Charset.forName("UTF-16")),
nextParticipantId)
.setResultCallback(this);
}
Latest version of the jfxmobile plugin (1.0.3) includes recent changes in Dalvik SDK that contains the port of JavaFX 8 to android.
In FXActivity class, it has been added a very convenient method to listen to results of intents: setOnActivityResultHandler().
This means you shouldn't add new activities to your app, and only use FXActivity. You should add an Intent to it, and set a proper result handler to the FXActivity.
Have a look at this recent post at Gluon's site. It explains how to access native services, based on the case of taking a picture with the device's camera, and waiting to get the result of it to resume the app.
Basically, this is what it is required in this case:
public void takePicture() {
// create intent
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
...
// Add result handler
FXActivity.getInstance().setOnActivityResultHandler((requestCode, resultCode, data) -> {
if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK) {
...
}
});
// launch activity
FXActivity.getInstance().startActivityForResult(intent, TAKE_PICTURE);
}
Try this approach with your app.

How to clear push notification from Ionic Push?

The push notification clears when the user taps on the notification to open app. However if the user goes and opens the app, the push notification is still there. How can I get rid of the notification? I can't seem to find anywhere in the documentation that addresses this.
Thanks a lot in advance
I did some digging as I too had this problem, and it looks like a bug in the Push Plugin. Basically they added the removal code to the pause event instead of the resume event.
You just have to change the code in src/android/com/plugin/gcm/PushPlugin.java
from:
#Override
public void onPause(boolean multitasking) {
super.onPause(multitasking);
gForeground = false;
final NotificationManager notificationManager = (NotificationManager) cordova.getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
}
#Override
public void onResume(boolean multitasking) {
super.onResume(multitasking);
gForeground = true;
}
to:
#Override
public void onPause(boolean multitasking) {
super.onPause(multitasking);
gForeground = false;
}
#Override
public void onResume(boolean multitasking) {
super.onResume(multitasking);
gForeground = true;
final NotificationManager notificationManager = (NotificationManager) cordova.getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
}
This will create a more standard behaviour, where the notifications will be removed on app resume (instead of on pause). Note though, I haven't fully tested the effects on the internal app messages yet, which may require more changes.

Resources