How to make notification persist in notification bar even after device is shut down and then restarted? - android-notification-bar

PendingIntent pendingIntent = PendingIntent.getActivity(context, (int)(Math.random() * 100),MyIntent,PendingIntent.FLAG_UPDATE_CURRENT);
//A PendingIntent will be fired when the notification is clicked. The FLAG_CANCEL_CURRENT flag cancels the pendingintent
mNotification.setLatestEventInfo(getApplicationContext(), MyNotificationTitle, MyNotificationText,pendingIntent);
mNotification.flags |= Notification.FLAG_NO_CLEAR;
notificationManager.notify(0, mNotification);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.finish();
startActivity(intent);
I need to save the notification in notification bar even after device is restarted.

show notification when the device reboot you can use following approach. When the system reboots the notification tray becomes empty. There is no way to make such notification which stays in memory even when system shuts down. The only way to show persistent notification is to store data some where like database or shared preferences and show the same notification when system reboots.
Android BroadcastReceiver on startup - keep running when Activity is in Background
just write code inside onRecieve method which will be called when the device reboots.
First You need to define a receiver in manifest with action name android.intent.action.BOOT_COMPLETED.
<!-- Start the Service if applicable on boot -->
<receiver android:name="com.prac.test.ServiceStarter">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver
>
Make sure also to include the completed boot permission.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceStarter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
/* show notification here*/
}
}

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.

android 12, how replace the notification trampolines

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.

How to wake up flutter android app with firebase push notification

How can i use broadcast receiver/intent to open flutter android app when receive firebase message.
Implment FirebaseMessagingService and start Main activity from onMessageReceived:
public class FirebaseMsgService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//...
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
and also in MainActivity you may want to unlock device:
public class MainActivity extends FlutterActivity {
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
}
The answer provided by #Develocode777 will work until android 9. From android 10, this behavior is not allowed anymore. You can learn more about it in this page, Restrictions on starting activities from the background.
If you are planning to show a notification, and upon clicking the notification you want to open the app, then it it will work normally, but opening the app without no user interaction, not allowed in Android 10 and above.

Can I use Firebase to control my app behavior?

A while back, I had a crash course on Advanced Android, where the instructor taught about Firebase. Although, I don't remember the details, he said we could control our apps' behavior from Firebase console (e.g. by calling a Cloud function?). For example, then I can get a trigger in my app and in response, I can decide whether to serve certain functions and features to the user or not (like remove an onClickListener from a button) based on e.g. they have paid the project price or subscription fee.
Is it possible to do using Firebase?
After some research and experiment I was able to implement this functionality into my app.
First you need to add Firebase Messaging to your project. Then, create a subclass of FirebaseMessagingService. Inside the class, implement onMessageReceived, where you can access messages received from server:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = MyFirebaseMessagingService.class.getSimpleName();
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.d(TAG, "onNewToken: " + s);
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG, "onMessageReceived: " + remoteMessage.getData());
Log.d(TAG, "onMessageReceived: activation: "+ remoteMessage.getData().get(Constants.ACTIVATION_KEY));
boolean activation = Boolean.valueOf(remoteMessage.getData().get(Constants.ACTIVATION_KEY));
//Save in preferences
SharedPreferences prefs = getApplication().getSharedPreferences(Constants.PREFERENCE_FILE, MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(Constants.ACTIVATION_KEY, activation);
editor.apply();
}
}
Make sure you add this service in your Manifest file:
<service android:name="me.mehdi.utils.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
In your Firebase Console select Cloud Messaging from the left and hit "New message". While composing a new message, you'll notice a section "Advanced options" in addition to message text and message label. Under Advanced options, you can enter key-value pairs and send to the device you select from the dropdown menu and finally hit Send message. In your onMessageReceived method, you can read the key-value pair and update your app's shared preferences to activate or deactivate certain features.

App.xaml.cs ctor is called when OnResume() should be called

If I navigate in my App and go back to the Android LaunchScreen with the HomeButton the OnSleep() Hook is called, which is fine. If I navigate back into the App using the Android TaskManager OnResume() is called.
If I navigate in my App with the Hardware BackButton then also OnSleep() is called which is fine, but if I navigate back into my app then the OnCreate Method in the MainActivity is called which recreated the App.
global::Xamarin.Forms.Forms.Init (this, bundle);
DisplayCrashReport();
LoadApplication (new Gtue.Mobile.App ());
That should not happen. In the ctor of App.xaml I initialize stuff which only should be initialized once. I tried every LaunchMode for the MainAcitivity, nothing helped.
Is there a way to find out if the App was already initialized?
Your App class extends from Xamarin.Forms.Application and the Xamarin.Forms.Application life cycle is bind with the Activity life cycle. You could find it in the source code :
async Task OnStateChanged()
{
if (_application == null)
return;
if (_previousState == AndroidApplicationLifecycleState.OnCreate && _currentState == AndroidApplicationLifecycleState.OnStart)
_application.SendStart();
else if (_previousState == AndroidApplicationLifecycleState.OnStop && _currentState == AndroidApplicationLifecycleState.OnRestart)
_application.SendResume();
else if (_previousState == AndroidApplicationLifecycleState.OnPause && _currentState == AndroidApplicationLifecycleState.OnStop)
await _application.SendSleepAsync();
}
So you just need to care about your Activity's life cycle. When you touch the HomeButton your application don't remove the MainActivity from the task's stack and the MainActivity will be available when you re-enter the app, so it didn't execute the OnCreate method.
When you touch the HomeButton :
[0:] MainActivity OnPause
[0:] MainActivity OnStop
[0:] Forms App OnSleep
When you use the Android TaskManager open your application :
[0:] MainActivity OnRestart
[0:] Forms App OnResume
[0:] MainActivity OnResume
But when you touch the hardware BackButton, the MainActivity will be removed from the task's stack :
[0:] MainActivity OnPause
[0:] MainActivity OnStop
[0:] Forms App OnSleep
[0:] MainActivity OnDestroy
When the MainActivity is destoryed, your application will dispose resource and it including your Gtue.Mobile.App instance. And you could see the source code about OnDestroy() method :
protected override void OnDestroy()
{
PopupManager.Unsubscribe(this);
_platform?.Dispose();
// call at the end to avoid race conditions with Platform dispose
base.OnDestroy();
}
So the next time when you open your application, it is necessary to recreated the App class. Actually, the App did only be initialized once.
I came across this issue when observing my App.xaml.cs constructor was being called when I was pressing the home button on my Android device and then clicking my app.
The well detailed answer above details the correct "anticipated behaviour", but this was not the case for me.
I was getting this kind of flow when using the home button or back button -
**On Debug Start**
MainActivity:OnCreate
XF:AppCtor
XF:OnStart
MainActivity:OnResume
**Press Home Button**
MainActivity:OnPause
XF:OnSleep
**Launch App via Click on App Icon**
MainActivity:OnCreate
XF:AppCtor
Searching eventually led me to -
App restarts rather than resumes
Which in turn resulted in the following Xamarin code at the top of MainActivity OnCreate -
if (!IsTaskRoot)
{
string action = Intent.Action;
if (Intent.HasCategory(Android.Content.Intent.CategoryLauncher) &&
!string.IsNullOrEmpty(Intent.Action) &&
action == Android.Content.Intent.ActionMain)
{
Finish();
return;
}
}
Which seems to solve the initial reload at least.
I also combined this with -
LaunchMode = LaunchMode.SingleTask
To stop some double activity shenanigans when clicking on a notification in notification screen.

Resources