Launch screen from native background service - java

I have a simple Flutter app that start a native background service using MethodChannel. This native background service notify my Flutter app using BasicMessageChannel<String> to display text when a particular native information is catched. All of this work perfectly when my app is in foreground. When an other app is in foreground, I can't see the text without having to switch to my app.
I want that my native service can display a particular Flutter screen even if an other application is running in foreground.
It can be perceived as not user friendly, but this is an message of utmost importance.
Any suggestion or solution will be welcome !
Note : Native service is for the moment only available in Java for Android, I'm working on C# for IOS side.

On Android you need to display a high priority notification. This displays the slide-down notification panel which will appear over the lock screen or another app. Since you are using native code already, you can create this notification there, or send a message to the Dart side (as you are doing, using MethodChannel) where it can use the flutter_local_notifications plugin to display it. When the user click the notification, your flutter app is brought to the foreground. In Java you might use code similar to this:
// Create an intent which triggers the fullscreen notification
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.setAction("SELECT_NOTIFICATION");
Class mainActivityClass = getMainActivityClass(context);
intent.setClass(context, mainActivityClass);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Build the notification as an ongoing high priority item to ensures it will show as
// a heads up notification which slides down over top of the current content.
final Notification.Builder builder = new Notification.Builder(context, CHANNEL_ID);
builder.setOngoing(true);
// Set notification content intent to take user to fullscreen UI if user taps on the
// notification body.
builder.setContentIntent(pendingIntent);
// Set full screen intent to trigger display of the fullscreen UI when the notification
// manager deems it appropriate.
builder.setFullScreenIntent(pendingIntent, true);
// Setup notification content.
int resourceId = context.getResources().getIdentifier("app_icon", "drawable", context.getPackageName());
builder.setSmallIcon(resourceId);
builder.setContentTitle("Your notification title");
builder.setContentText("Your notification content.");
MyPlugin.notificationManager().notify(someId, builder.build());
Then, for Android 8.1 or higher, add the following to your MainActivity class, found under the android/app/src/main/java/packageName/ folder
GeneratedPluginRegistrant.registerWith(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true);
setTurnScreenOn(true);
}
(This shows the Flutter app even when the screen is locked.)
Flutter only has one activity, so the above code will bring that Flutter activity to the foreground (note that you don't always see the notification, but sometimes you do - if you set it to autoCancel then touching it clears it). It's up to you to build the correct screen in Flutter, which you can do as you send the notification. Use Navigator.push or equivalent to change the page that Flutter is showing.

Related

react native data only fcm on android: start foreground when app is killed

I'm building a react native app which should handle push notifications.
I'm using react-native-notifications to handle the notification logic, and notifee to show the received notifications. But I have a few problems.
There are 3 'states' in which the app is, when a notification should be handled: app in foreground, background or killed.
When the app is in foreground or in background, I can "catch" the notifications with the event handlers from react-native-notifications and based on the payload, create a notification with notifee to display.
The problem is when the app is killed.
I found a pull request of react-native-notifications (see PR here) that would "read" data-only messages on android when the app is killed.
This works, I console.log the output and it shows up in logcat.
My question now is: how can I create a custom notification with notifee when the app is killed? So no react native code can run?
In my index.js I have the following:
AppRegistry.registerHeadlessTask(
"JSNotifyWhenKilledTask",
() => {
return async (notificationBundle) => {
console.log('[JSNotifyWhenKilledTask] notificationBundle', notificationBundle);
}
}
);
the notification is shown in logcat, but now I want to add notifee to this to create a notification. But then I get the following error (in logcat):
android.app.BackgroundServiceStartNotAllowedException: Not allowed to start service Intent
The code that is a problem is this one:
private void notifyReceivedKilledToJS() {
Bundle bundle = new Bundle(mNotificationProps.asBundle());
Intent service = new Intent(mContext.getApplicationContext(), JSNotifyWhenKilledTask.class);
service.putExtras(bundle);
mContext.getApplicationContext().startService(service);
}
I modified the code to this, but now I get another error:
private void notifyReceivedKilledToJS() {
Bundle bundle = new Bundle(mNotificationProps.asBundle());
Intent service = new Intent(mContext.getApplicationContext(), JSNotifyWhenKilledTask.class);
service.putExtras(bundle);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mContext.startForegroundService(new Intent(mContext, JSNotifyWhenKilledTask.class));
} else {
mContext.startService(new Intent(mContext, JSNotifyWhenKilledTask.class));
}
}
android.app.ForegroundServiceStartNotAllowedException: startForegroundService() not allowed due to mAllowStartForeground false
I found this question on SO addressing this problem.
I changed the priority of the notification to "high", and now I get this final error:
android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground():
I'm guessing I should start this Service.startForeground() somewhere in my java code, but I really have no clue where.
Does anyone know a solution on how to "read" a data-only notification on android when the app is killed, and to run some JS code (notifee) to display a notification?
I'm not very good at java btw, any guidance would be welcome.

how to enable floating notification and lock screen notification in xamarin android(Java or Kotlin is okay)

I make simple notification android app using xamarin,
i want floating notification and lock screen notification
l add users permission in manifest, also i read all reference document about notification channel however i don't know how to enable floating notification and lock screen notification in code
outlook, gmail etc. some app enable all notification when it installed
above screenshot is my app
i want enable all notification when installed my app
necessarily Even if it is not in xamarin, a solution using kotlin or java is fine!
For floating notification, you should change Notification priority or NotificationChannel importance.
Android 5.0 - Android 7.1
Set notification priority to NotificationPriority.High or NotificationPriority.Max.
builder.SetPriority((int)NotificationPriority.High)
Set ringtone and vibrations. You can use SetDefaults.
// Turn on sound if the sound switch is on:
notification.Defaults |= NotificationDefaults.Sound;
// Turn on vibrate if the sound switch is on:
notification.Defaults |= NotificationDefaults.Vibrate;
Android 8.0 and higher
Set notification channel priority to NotificationImportance.High or NotificationImportance.Max.
var channel = new NotificationChannel(CHANNEL_ID1, name, NotificationImportance.Max)
{
Description = description
};
For lock notifications, you could set the Visibility.
Beginning with Android 5.0, the visibility setting is available to control how much notification content appears on the secure lock screen.
NotificationVisibility.Public – The full content of the notification
is displayed on the secure lock screen.
NotificationVisibility.Private – Only essential information is displayed on the secure lock screen (such as the notification icon and the name of the app that posted it), but the rest of the notification's details are hidden. All notifications default to NotificationVisibility.Private.

Is there possibility to "Auto-start" application when users become inactive?

I have to create an app, which detects user inactivity, and then start activity which displays some videos with WebView, and then when displaying with WebView is finished, it has to play videos from SDCard. I've already handled part with WebView and SDCard (with JavaScriptInterface etc.)
This application has to work with API 19 all the way to the newest one.
The question is - Is there a possibility to detect if user is inactive and start my application, or keep the app running in background, and then start activity in the foreground after the user becomes inactive for certain time?
I'm not trying to play ads, when user is not looking at his screen. Application is for my client, who have stores with all kind of electrical equipments, including smartphones. The goal is to play video presentations with hardware details specific for each smartphone (informations about processor, ram, camera, screen etc.).
In short: I have to make an app which is similar to "Demo Apps" created for example by Samsung (playing some kind of presentations on screen).
So far I've read and tested things like:
1) BroadcastReceiver with combination of ACTION_SCREEN_OFF / ACTION_SCREEN_ON events.
Receiver works properly, I can detect this event and then start activity, but... The screen is already off so i can't see the displayed activity - it's visible running in the foreground after unlocking the phone. Is there a way to unlock the phone when the event is received?
That's my code so far.
EventReceiver Class:
class EventReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
StringBuilder().apply {
append("Action: ${intent.action}\n")
append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
toString().also { log ->
Log.d(TAG, log)
Toast.makeText(context, log, Toast.LENGTH_LONG).show()
}
}
if (intent.action == Intent.ACTION_SCREEN_OFF) {
val i = Intent(context, MainActivity::class.java)
context.startActivity(i)
}
}
}
MainActivity Class:
val br : BroadcastReceiver = EventReceiver()
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(Intent.ACTION_SCREEN_OFF)
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_BOOT_COMPLETED)
}
2) Foreground Services - I read that this is a great way to make some asyc stuff in the background and show notifications to user. Is there a way to start the activity with it?
3) Job Scheduler
4) Daydream / Dream Service - it actually works great with almost every API and manufacturer, but.. there's no way to set the app as Screen Saver on Huawei/Honor smartphones, at least from phone settings, I've read that this is possible with ADB etc. but this is not an option that I can use here.
It seems that none of these fullfill my expectations.

Android foreground service notification not persistent

I have created a class which extends Service and runs as a foreground service. I would like my service notification to be persistent (i.e. not removed by swiping). However, my notification can be dismissed by swiping.
The Service documentation states: ...A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground...
I did put break points to check whether onDestroy() or stopSelf() is hit, but this is not the case. The service is running in foreground mode, but I can dismiss the notification by swiping.
I have found quite a few questions regarding the opposite case, where it was not possible to dismiss the notification after the service was stopped, but did not find any question similar to my problem.
The service is started via an Intent and initialized as follows:
#Override
public void onCreate()
{
super.onCreate();
initialize();
}
private void initialize()
{
Notification n = get_service_notification();
startForeground(10, n);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter(BroadcastCodes.service_broadcast_intent_name));
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
startServing();
return START_STICKY;
}
where startServing() subscribes to location updates
The notification is built like this
private Notification get_service_notification()
{
Intent intent = new Intent(this, LoginActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setAutoCancel(false)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.app_icon)
.setTicker("some text")
.setContentTitle("some text")
.setContentText("some text")
.setContentIntent(contentIntent)
.setContentInfo("")
.setOngoing(true);
Notification res = b.build();
res.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
return res;
}
I have tried playing around with the flags and setOngoing/setAutoCancel, but the result stayed the same.
SDK versions are configured as minSdkVersion 18, targetSdkVersion 25
I would really appreciate any input regarding this issue - it might be just some stupid mistake, but I already did spend more than a few hours trying to research on my own...unfortunately to no avail.
EDIT 1
I still have not figured out the problem. I decided to try a dirty hack, but still no luck.
I made a PendingIntent and registered it via setDeleteIntent() the idea being to display a new notification once this one was dismissed by swipe. Unfortunately, I was not able to get this to work (the intent never fires when swiping).
I would also like to clarify, that the notification CAN BE swiped away, but IS NOT AFFECTED (i.e. is not deleted) by the clear-all button in the notification pane (the trash icon).
EDIT 2
As mentioned above I was not able to use setDeleteIntent() to recreate my notification when it is dismissed by swipe.
For now I settled with a workaround - my service does some periodical tasks. I am now calling
notificationManager.notify(10, service_notification);
when my task runs so that even if swiped away, my notification will be recreated after a while.
I still have a strong feeling that I just misread the documentation, since there are several notifications on my test device (Honor API23) that can not be swiped away.
EDIT 3
I have tried the same code on a different device (Lenovo API23) and the notification works as expected (can not be dismissed and can not be swiped). My problem seems to be device specific.
I made a few further tests (besides the ones mentioned in updates). I tried running various examples/tutorials from around the internet like this page here
On the Honor I was initially running my tests the notification could always be swiped, on the Lenovo and others the "ongoing" flag works as stated in the documentation, i.e. it can not be dismissed via swipe action.
To sum it up, my problem really seems to be device specific.

Android: How to close application opened from my app when all sent to background

Here is my problem. I am developing app that loads some documents from server. I open document in another app via Intent.ACTION_VIEW. This is all working just fine. Problem is that whole app is pin protected so I have to capture events such "sent to background" or "screen lock" to bring up pin screen afterwards and this is not working when another app is opened above mine. So if user opens document then press home button, click on my launch icon from menu then he gets again external app with opened document and with back button access my app again. This is security issue that needs to be fixed.
Here are some code snippets:
Opening document:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(downloadedFile);
String mimeType = document.getMimeType();
intent.setDataAndType(uri, mimeType);
startActivityForResult(intent, 1);
capture sent to background:
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
IN_BACKGROUND = true;
Log.i(PinUtil.class.getSimpleName(), "App sent to background ");
} else {
IN_BACKGROUND = false;
}
}
My question is: Is it possible to detect if my app is sent to background when another app is opened? How not to open another app when my launcher icon is pressed.
Thanks for all responses.
Regards
Lubos
In order to fix this problem:
So if user opens document then press home button, click on my launch
icon from menu then he gets again external app with opened document
and with back button access my app again. This is security issue that
needs to be fixed. Here are some code snippets:
You need to make sure that, when you launch an external app for the user to view a document, that the external app does not run in the same task as your application. It needs to run in a new, separate task. You can do this like this:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Ensure app runs in separate task
Uri uri = Uri.fromFile(downloadedFile);
String mimeType = document.getMimeType();
intent.setDataAndType(uri, mimeType);
startActivity(intent); // Can't use startActivityForResult() here
However, you can't use startActivityForResult() when you launch the external viewer, because an activity running in another task cannot return a result to you. However, most external applications won't return a result when launched with ACTION_VIEW anyway, so it probably isn't a problem.
Then you asked this:
My question is: Is it possible to detect if my app is sent to
background when another app is opened? How not to open another app
when my launcher icon is pressed.
There should be some answers on StackOverflow that can help you determine if your application is in the background (it isn't actually that easy to determine this).
My explanation above should answer your 2nd question. If you don't launch other apps in your task, then only your app will be launched when your launcher icon is pressed.

Categories