I use android 10 [android Q, galaxy 10],
I use android studio 3.3,
using AVD, and made a api 29 [android 10] virtual phone.
at the virtual machine,
I execute my app , after that, I launch other app like calendar, calculator.
so my app activity get into background mode.
when I receive a message at BroadcastReceiver.
I call startActivity.
here, code -->
public class myReceiver extends BroadcastReceiver {}
public void onReceive(Context context, Intent intent)
{
Intent intentRun = new Intent(context, LoginSuccess.class);
intentRun.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intentRun);
}
but LoginSuccess activity do not shows up.
[when my app is in background mode]
using same code, LoginSuccess activity show up very well
when my app is in foreground mode.
call stack capture image
above image shows call stack
right before I call startActivity in broadcast receiver.
I have read guide line for android 10 background activity issue.
[developer.android.com/~~ some location]
at the guide line,
I came to know that
if the activity exists in call stack, it can be started
even in background mode.
above code,
it try to start activity that exists in recent call stack.
why startActivity call fail in background mode ?
[maybe not fail , but anyway not activated into foreground]
With Android Q, it is impossible to start an activity from the background automatically if your app does not include those exceptions listed in the link below.
https://developer.android.com/guide/components/activities/background-starts
Possible Solutions:
1- You can choose just show a service notification, and start pending intent with a click
2- You can use full-screen intents to show your intent immediately as shown in the other answer and suggested by Google.
For full-screen intent solution, as described in the official document
The system UI may choose to display a heads-up notification, instead
of launching this intent, while the user is using the device.
3- To start the activity automatically in the background, The most possible solution in my view is adding "SYSTEM_ALERT_WINDOW" to the manifest file. And ask for user permission once when the app opened the first time. (The user can give this permission manually - (Settings-Apps-Your App-Advanced- Draw over other apps))
Example code to request permission :
In Manifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Somewhere in app:
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 2323;
//if the user already granted the permission or the API is below Android 10 no need to ask for permission
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
!Settings.canDrawOverlays(getContext()))
{RequestPermission()}
private void RequestPermission() {
// Check if Android M or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getActivity().getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(getContext())) {
PermissionDenied();
}
else
{
// Permission Granted-System will work
}
}
}
}
I'm open activity using the below logic. as google, blog says if you want to open activity in background service for use notification on android 10 or higher.
In Manifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Example:
private void startActivity() {
Uri sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.siren);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
AudioAttributes attributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build();
String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
String CHANNEL_NAME = BuildConfig.APPLICATION_ID.concat("_notification_name");
assert notificationManager != null;
NotificationChannel mChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
if (mChannel == null) {
mChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
mChannel.setSound(sound, attributes);
notificationManager.createNotificationChannel(mChannel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID);
builder.setSmallIcon(R.drawable.logo)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.login))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setFullScreenIntent(openScreen(Constants.NOTIFICATION_ID), true)
.setAutoCancel(true)
.setOngoing(true);
Notification notification = builder.build();
notificationManager.notify(Constants.NOTIFICATION_ID, notification);
} else {
startActivity(new Intent(BackgroundService.this, LoginActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
private PendingIntent openScreen(int notificationId) {
Intent fullScreenIntent = new Intent(this, LoginActivity.class);
fullScreenIntent.putExtra(Constants.NOTIFICATION_IDS, notificationId);
return PendingIntent.getActivity(this, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
If you have root permissions you can simply use the am command for this in the shell:
public static final void switchAcitivty (final Context context) throws IOException {
final Runtime runtime = Runtime.getRuntime();
final String intentCommand = "su -c am start -n yourpackage/.MainActivity -a android.intent.action.VIEW";
Log.i("TAG", intentCommand);
runtime.exec(intentCommand);
}
It gets blocked without root permission (silently, which is annoying).
Very strange but launching activity from foreground service worked in release build. Was not working in debug build (when debugging via Android Studio).
Related
I am trying to implement an alarm based application in Android Q using a broadcast receiver. I am running a foreground service using notification for triggering the alarm broadcast receiver. The service is working fine and it is also triggering the broadcast receiver. If we close the application or lock the screen after setting an alarm, the service will be running in the foreground with a notification.
When the alarm broadcast is called I am trying to open a new activity when the screen is locked to provide the functionality to stop the alarm and service. I tried disabling the keyguard, turn on the screen and then opening the activity from the broadcast receiver, but I couldn't succeed.
I tried using WindowManager flags but they are deprecated and do not make any difference in the code.
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
Since I am trying to start an activity from a BroadcastReceiver I won't be having any Activity to use KeyguardManager.requestDismissKeyguard(Activity activity, KeyguardDismissCallback callback)
Is there any way to start an activity when the screen is locked to turn off the alarm. My implementation is given as follows,
I also added permissions in the manifest file.
AndroidManifest.xml
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
AlarmBroadcastReceiver.class
public class AlarmBroadcastReceiver extends BroadcastReceiver {
public static MediaPlayer mp;
public static Vibrator vibrator;
private boolean isVibrationEnabled = false;
#Override
public void onReceive(Context context, Intent intent) {
long[] mVibratePattern = new long[]{0, 400, 400, 400, 400, 400, 400, 400};
final int[] mAmplitudes = new int[]{0, 128, 0, 128, 0, 128, 0, 128};
isVibrationEnabled = intent.getExtras().getBoolean(LocationAlertService.IS_VIBRATE);
mp=MediaPlayer.create(context, R.raw.ring1);
mp.setLooping(true);
mp.start();
if(isVibrationEnabled) {
vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(mVibratePattern, mAmplitudes, 0));
} else {
//deprecated in API 26
vibrator.vibrate(mVibratePattern, 3);
}
}
Intent wakeIntent = new Intent(context, WakeUpActivity.class);
wakeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(wakeIntent);
}
}
I know that I am missing something. I would be happy if there are any suggestions to overcome the issue which I am facing. Thanks in advance for helping me out.
With Android Q, it is impossible to start an activity from background automatically if your app is not includes those exceptions listed in the link below. You can choose just show a service notification, and start pending intent with click.
https://developer.android.com/guide/components/activities/background-starts
To make the system work. The most possible solution in my view is adding "SYSTEM_ALERT_WINDOW" to manifest file. And ask for user permission once when the app opened first time.(The user can give this permission manually - (Settings-Apps-Your App-Advanced- Draw over other apps)) Example code to request permission :
In Manifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Somewhere in app:
private void RequestPermission() {
// Check if Android M or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getActivity().getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(getContext())) {
PermissionDenied();
}
else
{
//Permission Granted-System will work
}
}
}
I have a web/android app, written using Ionic 4 and Capacitor, and I've been trying without success to re-enter the Ionic app to a specific page from a notification raised from an Android service (activated via a capacitor plugin).
Here's the code that creates the notification in the service:
private Notification getNotification() {
CharSequence contentTitle = "Fun App Background Mode Running";
CharSequence contentText = "Fun App";
long notificationTime = System.currentTimeMillis();
if (_NFC == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel("funapp", "FunApp", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights(false);
channel.enableVibration(false);
channel.setSound(null,null);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.setShowBadge(true);
manager.createNotificationChannel(channel);
}
Intent notificationIntent = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(notificationIntent);
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
_NFC = new NotificationCompat.Builder(getApplicationContext(),"funapp")
.setSmallIcon(R.drawable.ic_sheep_notif)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_foreground))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText).setBigContentTitle(contentTitle))
.setContentIntent(pendingIntent)
.setOngoing(true);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
_NFC.setPriority(NotificationCompat.PRIORITY_LOW);
}
}
_NFC.setContentTitle(contentTitle);
_NFC.setContentText(contentText);
_NFC.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText).setBigContentTitle(contentTitle));
_NFC.setWhen(notificationTime);
return _NFC.build();
}
I believe I need to put something in/around the new Intent(this, MainActivity.class) line to get Capacitor / Ionic to initialise the app to the right state, but I cannot work out what that should be!
I've poured over the Capacitor documentation and not been able to find the solution so far, I have a suspicion that I need to send a "view" intent to the activity with some sort of URL?
The current behavior is for it to start what appears to be a completely new instance of the application (it re-loads the splash screen etc), even if the app is still the foreground task on the phone.
UPDATE
My latest attempt is to create the intent like this:
Intent notificationIntent = new Intent(Intent.ACTION_VIEW,
Uri.parse("http://localhost/event/horse"),
this, MainActivity.class);
(assuming I have a valid route set up in Ionic/Angular for /event/horse, which I do)
No change though, this still expresses the same behavior as described above (re-entering the splash screen).
In order to realise this behavior, three different parts are needed.
Firstly, your Angular / Ionic code must hook into the events from the Capacitor App plugin and do the navigation when called with an open URL, for example:
import { Plugins, AppUrlOpen } from '#capacitor/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
constructor(
private platform: Platform,
private router: Router
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
if (this.platform.is('capacitor')) {
Plugins.SplashScreen.hide();
// THIS WILL BE USED IF THE APP IS ALREADY OPEN:
Plugins.App.addListener('appUrlOpen', (urlOpen: AppUrlOpen) => {
console.log('App URL Open', urlOpen);
this.navigate(urlOpen.url);
});
}
// THIS WILL BE USED IF THE APP HAS BEEN KILLED AND RE-OPENED:
this.getLaunchUrl();
});
}
async getLaunchUrl() {
const urlOpen = await Plugins.App.getLaunchUrl();
if(!urlOpen || !urlOpen.url) return;
console.log('Launch URL', urlOpen);
this.navigate(urlOpen.url);
}
navigate(uri: string) {
// THIS MUST EQUAL THE 'custom_url_scheme' from your Android intent:
if (!uri.startsWith('net.exampleapp.app:/')) return;
// Strip off the custom scheme:
uri = uri.substring(19);
this.router.navigateByUrl(uri);
}
}
Then, in the Android side, this is the incantation needed to get a PendingIntent to trigger this behavior:
Intent notificationIntent = getPackageManager()
.getLaunchIntentForPackage(getPackageName())
.setPackage(null)
.setAction(Intent.ACTION_VIEW)
.setData(Uri.parse(
getResources().getString(R.string.custom_url_scheme) +
"://events/" + _EventId))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 1234,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Lastly, in the AndroidManifest.xml for your app, you must also specify the launch mode as SingleTask or SingleTop for MainActivity (either seem to work):
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale"
android:label="#string/title_activity_main"
android:theme="#style/AppTheme.NoActionBarLaunch">
With this combination, if the app is still running, the relevant page will be navigated to correctly, and if it is not running, the app will be opened then the page will be navigated to.
Note, however, that this does not set up the "back" stack within the Ionic app sensibly in the case where the app wasn't running, so hitting back will not automatically navigate upwards. But that's a different problem...
I am targeting Oreo. As you know, oreo introduced limits on background task execution time. Workarounds are - according to google - to put the background task in the foreground. This is what I was trying to do, yet once the foreground service is running, it gets destroyed after some time.
First the phone switches off it's screen, then once I activate it again, the background task continues. Sometimes onDestroy on the foreground service is called without the task being completed.
My goal is to have all tasks being set by enqueueWork to be executed without ondestroy being called and without phone sleep mode to interrupt it.
ForeGroundService
public class ForeGroundService extends JobIntentService {
static final int JOB_ID = 1000;
static final int ONGOING_NOTIFICATION_ID = 33;
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, ForeGroundService.class, JOB_ID, work);
}
Notification.Builder notification;
NotificationManager mNotificationManager;
#RequiresApi(api = Build.VERSION_CODES.O)
void einleitung(String Titel, String Text)
{
Intent notificationIntent = new Intent(this, ForeGroundService.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);
mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(Titel,
Text,
NotificationManager.IMPORTANCE_HIGH);
channel.setSound(null,null);
mNotificationManager.createNotificationChannel(channel);
}
notification =
new Notification.Builder(this,Titel)
.setContentTitle(Titel)
.setContentText(Text)
.setSmallIcon(R.drawable.kleinesicon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent)
.setTicker("setTicker");
mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
startForeground(ONGOING_NOTIFICATION_ID, notification.build());
}
#RequiresApi(api = Build.VERSION_CODES.O)
void vordergrund(String Titel, String Text)
{
notification.setContentTitle(Titel);
notification.setContentText(Text);
mNotificationManager.notify(ONGOING_NOTIFICATION_ID, notification.build());
}
PowerManager.WakeLock wakeLock;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
protected void onHandleWork(Intent intent) {
if (beginn) {
einleitung("Test", "Test");
beginn = false;
}
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyWakelockTag");
wakeLock.acquire();
//Do Work
}
#Override
public void onDestroy() {
super.onDestroy();
Intent local = new Intent();
local.setAction("de.test.action");
this.sendBroadcast(local);
stopForeground(true);
//toast("Fertig");
if (wakeLock != null)
wakeLock.release();
}
final Handler mHandler = new Handler();
}
MainActivity
public class MainActivity extends AppCompatActivity {
private int JI = 1000;
private BroadcastReceiver updateUIReciver;
#RequiresApi(api = Build.VERSION_CODES.O)
void somefunction(someparameters)
{
Intent mServiceIntent = new Intent();
mServiceIntent.putExtra...
ForeGroundService.enqueueWork(getBaseContext(),ForeGroundService.class,JI,mServiceIntent);
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(updateUIReciver);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
IntentFilter filter = new IntentFilter();
filter.addAction("de.test.action");
updateUIReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
ForeGroundService.shouldContinue = false;
}
};
registerReceiver(updateUIReciver,filter);
btnB.setOnClickListener(new View.OnClickListener() {
#RequiresApi(api = Build.VERSION_CODES.O)
public void onClick(View v) {
if (startcondition)
{
Intent startIntent = new Intent(MainActivity.this, MyService.class);
startIntent.setAction(Constants.ACTION.START_ACTION);
startService(startIntent);
Intent serviceIntent = new Intent(MainActivity.this,ForeGroundService.class);
startForegroundService(serviceIntent);
somefunction(someparameters);
}
else
{
Intent stopIntent = new Intent(MainActivity.this, MyService.class);
stopIntent.setAction(Constants.ACTION.STOP_ACTION);
startService(stopIntent);
}
}
});
}
}
EDIT: I made it work with sandhya sasane's solution and
public int onStartCommand(Intent intent, int flags, int startId)
{
if (beginn) {
executorService = Executors.newFixedThreadPool(1);
beginn = false;
}
final Intent i2 = intent;
executorService.execute(new Runnable(){
#Override
public void run(){
abarbeiten(i2);
}
});
return START_STICKY;
}
Important is the 1 in newFixedThreadPool(1); to only have one thread run at once
I am targeting Oreo. As you know, oreo introduced limits on background task execution time.
Yes, it does. I can understand you, as google has made the things very odd and complex first..., then again complicated... then again... then again... And now developers like me and you, and your question and problem, denotes the outcome / result / proof of that.
Workarounds are - according to google ...
Please save time and yourself too... Google documentation is worst.. i have given -10 out of 10 for their documentation.
to put the background task in the foreground.
You have a wrong perception of what foreground concept is..!! Read complete answer word by word carefully, Your problem will get solved..!!
This is what I was trying to do, yet once the foreground service is running, it gets destroyed after some time...
Now very simply... Your Concept and implementation, both are wrong..., So Try with a new sample project and guidelines provided here along with sample working and tested code across 4.0 to latest android P
.
First the phone switches off it's screen, then once I activate it again, the background task continues. Sometimes onDestroy on the foreground service is called without the task being completed.
It does not relate to foreground service, in any way.... forget this.
My goal is to have all tasks being set by enqueueWork to be executed without ondestroy being called and without phone sleep mode to interrupt it.
Forget this too... Lets first see what a foreground service is and how it is created...
What is foreground service
A service which remains active (It does not mean... continuously
running like never ending do-while loop)
Remain active until next boot / reboot
Even if user removes app from recents, it remains
But It does not remain active post next boot
It needs to be restarted by user by opening app again or via a broadcast receiver of ON_BOOT_COMPLETE or by a AlarmManager or By a JobScedular
When to use
As per my view users do not like a permanent notification showing message ^This is running in foreground and may discharge your battery soon^ , Again user would not be able to swipe it away and can only force stop or uninstall app to stop it. So it is as per my implementations point of view , ^Developers must use this for implementing runtime receivers as post - oreo devices do not welcomes static receivers implemented by extending Broadcastreceiver and placing its intent entry in manifest.xml file... Even if developer tries to do this that receiver will never get called on post - oreo devices ..., Yes it will get called below oreo devices. So implement just a ON_BOOT_COMPLETE receiver and rest all in a service.
How to implement a foreground service
Right click on project structure and make a service named RunnerService and then generate all mandatory methods. it does not require you to type all code manually.. Generate it as said. Sample foreground service :
public class RunnerService extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "1";
public RunnerService() { }
#Override
public void onCreate()
{
super.onCreate();
Log.d("RUNNER : ", "PROGRAMMED.... \n");
Bitmap IconLg = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this, null);
mBuilder.setContentTitle("App Name")
.setContentText("Foreground service...")
.setTicker("Foreground service...")
.setSmallIcon(R.drawable.ic_menu_slideshow)
.setLargeIcon(IconLg)
.setPriority(Notification.PRIORITY_HIGH)
.setVibrate(new long[] {100})
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setOngoing(true)
.setAutoCancel(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{100});
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(notificationChannel);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(1, mBuilder.build());
}
else
{
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
mNotifyManager.notify(1, mBuilder.build());
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d("RUNNER : ", "\n IT IS ACTIVE UNTIL NEXT BOOT....");
return START_STICKY;
}
#Override
public void onDestroy()
{
Log.d("RUNNER : ", "\n IT WILL BE AGAIN ACTIVE BY ANDROID OS AUTOMATICALLY, DO NOT WORRY AND DONT CODE TO START IT AGAIN !!....");
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("NOT_YET_IMPLEMENTED_BY_DEVELOPER");
}
}
How to start it
It depends on which android you are targeting below oreo or post oreo ... I will prefer to on all like below :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
this.startForegroundService(new Intent(this, RunnerService.class));
}
else
{
this.startService(new Intent(this, RunnerService.class));
}
Either from MainActivity or any ON_BOOT_RECEIVER, or from wherever you want, just start it as said here...
How to test is in foreground
By removing it from recents... It will call onDestroy but it will be never destroyed you will not be able to swipe away notification. This means a success.
How to test it quick
With a sample new project with a MainActivity just calling service in said manner.
What next..?
Yes you can ask your next tasks here only..., I will keep updating and guiding... I hope you have kept enqueueWork concept and all your concepts aside and do not thinking on it...
Lets go step by step and let me know the updates....
UPDATE 2
You should try it on emulator only... If success then try it on actual devices... Here is a problem again...
There are many mobile phone manufacturers in the world now, which takes
stock android from google as it is open source and modifies it to disable all services on BOOT. It only keeps Google , WhatsApp, FaceBook , Twitter and major market leaders... As if they do not allow them no one will purchase their devices ...
Examples :
Vivo = FunTouchOs
Oppo = ColorOs
There is a huge list....
Do not check on this for BOOT_COMPLETE..., IT will not work as they are modified the android..
But i want to test it on actual device
Then test it on such device which os is purely from google and having android os.
Then what should i do for other os modified from android
There are tricks ..., But lets go step by step.... I will let you know , once you success in this..!!
UPDATE : 3
As it is not clear what is the requirement i am making some assumptions and writing answer :
What you can do to implement foreground execution is :
Implement foreground service as i depicted
Use local broadcastmanager to broadcast events of your own.
in onCreate of a foreground service register runtime receiver to receive that broadcasts
On receiving broadcasts call to the methods of user defined class with context of foreground service. And perform all tasks from there.
Unregister receiver from onDestroy of foreground service.
What you can do to implement background execution is :
If you are having repeating tasks and wants to execute it in background even if the app is removed from recents ... Then :
Use Firebase Job Dispatcher which uses GooglePLAYServices
If you use forever then that job will be triggered automatically even if system is rebooted and even if app is not in foreground or background or in recents...
As of now i do not see any need of JobIntentService and therefore its static enqueueWork method; More resolution and details are needed for solving your problem.
I am trying to "resume" a single task activity so it appears in the foreground when a user clicks my notification. (Same behavior as if the user tapped on the app icon from the applications menu.)
My notification creates a PendingIntent which broadcasts an action that is received by my broadcast receiver. If the app is in not in the foreground, I try to resume the app. Additionally, I'm trying to pass a message to my onResume function through the intent. However, I'm hitting an error:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Despite this error, my app is being resumed...don't understand why. However, my extras are not being passed to my onResume function.
So first I create a notification.
public static class MyNotificationCreator {
private static final int MY_NOTIFICATION_ID = 987;
public static void createNotification(Context context) {
Intent openAppIntent = new Intent(context, MyReceiver.class);
openAppIntent.setAction("PleaseOpenApp");
PendingIntent pi = PendingIntent.getBroadcast(context, /*requestCode*/0, openAppIntent, /*flags*/0);
Notification notification = ne Notification.Builder(context)
.setContentTitle("")
.setContentText("Open app")
.setSmallIcon(context.getApplicationInfo().icon)
.setContentIntent(pi)
.build();
NotificationManager notificationManager = (NotificationManager) applicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, notification); }
}
Which broadcasts "PleaseOpenApp" for MyReceiver.
public class MyReceiver extends BroadcastReceiver {
#Override
public void onRecieve(Context context, Intent intent) {
if (intent.action() == "PleaseOpenApp" && !MyPlugin.isForeground) {
PackageManager pm = context.getPackageManager();
//Perhaps I'm not supposed to use a "launch" intent?
Intent launchIntent = pm.getLaunchIntentForPackage(context.getPackageName());
//I'm adding the FLAG_ACTIVITY_NEW_TASK, but I'm still hitting an error saying my intent does not have the FLAG_ACTIVITY_NEW_TASK...
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.putExtra("foo", "bar");
context.startActivity(launchActivity);
} else {
//do other stuff
}
}
}
My plugin keeps track of whether or not we're in the foreground. Also, it tries to get "food" after my receiver attempts to start the app.
public class MyPlugin extends CordovaPlugin {
public static boolean isForeground = false;
#Override
public void initialize(CordovaInterface cordova, CordovaWebView webview) {
super.initialize(cordova, webview);
isForeground = true;
}
#Override
public void onResume(boolean multitasking) {
isForeground = true;
String foo = activity.getIntent().getStringExtra("foo");
Log.d("MyPlugin", foo); //foo is null after clicking the notification!
}
#Override
public void onPause(boolean multitasking) {
isForeground = false;
}
#Override
public void onDestroy() {
isForeground = false;
}
}
Note: because I'm using cordova my activity has a singleTask launchMode.
Also, I'm new to Android development so any help about resuming activities not in the foreground vs resuming activities that have been destroyed and info about general concepts / best practices that I'm not understanding would be appreciated!
I don't think your Broadcast/Broadcast Receiver pattern is necessary.
Intents can be used to directly launch an activity, and when you build the Intent, you can add the extras. Then, your activity onResume() can extract them directly.
Here is a sample Intent and PendingIntent construction that can be sent in a notification:
Intent startActivity = new Intent(context, MyActivity.class);
// You can experiment with the FLAGs passed here to see what they change
startActivity.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra("Extra1", myExtra1)
.putExtra("Extra2", myExtra2)
// ADDING THIS MAKES SURE THE EXTRAS ATTACH
.setAction("SomeString");
// Then, create the PendingIntent
// You can experiment with the FLAG passed here to see what it changes
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_UPDATE_CURRENT);
// Then, create and show the notification
Notification notif = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.my_small_icon)
.setContentTitle(myTitle)
.setContentText(myContent)
.setOngoing(isOngoingNotif)
.setAutoCancel(shouldAutoCancel)
.setOnlyAlertOnce(shouldAlertOnce)
.setContentIntent(pendingIntent)
.build();
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
manager.notify(MY_NOTIFICATION_ID, notif);
In your code you are using a "launch Intent" to resume your application. You've added "extras" to the Intent but they will never be seen.
If your app is running, but in the background, and you call startActivity() with a "launch Intent", all this does it bring your task from the background to the foreground. It does not deliver the Intent to the Activity!.
A "launch Intent" does exactly the same thing as when you press the app icon of an app on the HOME screen (if it is already running, but in the background). This just brings the existing task in its current state, from the background to the foreground.
If you want to delivery "extras" to your app, you cannot use a "launch Intent". You must use a regular 'Intent. Depending on your architecture, you could either start a newActivity(which would get the "extras" inonCreate(), or you could start an existingActivity(which would get the "extras" inonNewIntent()`.
When creating a recommendation (or Notification) in Lollipop on Android TV, I cannot get it to Auto-cancel.
I am using the "NotificationCompat.BigPictureStyle" as recommended in the Android TV developer pages. The notification works as designed, triggering the PendingIntent as expected but does not auto-cancel and dissappear from recommendations bar. A second selection of the recommendation brings up a blank screen, so I guess the PendingIntent is null at that point. (ADB shows android.content.IntentSender$SendIntentException on 2nd invocation.)
Tested on Nexus Player and Android TV Emulator.
private void buildAndroidTVRecommendation(String name, PendingIntent pIntent,
Context context2, Bundle extras) {
NotificationManager mNotificationManager = (NotificationManager)
context2.getSystemService(Context.NOTIFICATION_SERVICE);
Bitmap smallBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.air_share);
Notification notification = new NotificationCompat.BigPictureStyle(
( new NotificationCompat.Builder(context)
.setContentTitle("Air-Share - incoming share")
.setContentText("From: "+name)
.setContentInfo("Air-Share"))
.setGroup("Air-Share")
.setColor(0xFFFF2020)
.setCategory(Notification.CATEGORY_RECOMMENDATION)
.setLargeIcon(smallBitmap)
.setSmallIcon(R.drawable.air_share)
.setContentIntent(pIntent)
.setExtras(extras)
.setAutoCancel(true)
)
.build();
mNotificationManager.notify(pendingCounter, notification);
mNotificationManager = null;
}
For what it's worth I have created a work-around to get by this issue.
I created a new Activity whose sole purpose is to receive a Pending intent from the Recommendation.
I alter the original Pending Intent of the recommendation to invoke this new activity instead of my desired activity. (The desired activity is outside my app.) In the Extras, I bundle everything I need to know for my original desired intent as well as the notification ID.
When the new activity is launched (after user clicks on recommendation), I extract the ID and cancel the recommendation. I then extract the information for the desired intent, create the intent and finally finish the activity.
public class TVRecommendationActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent in = (Intent) getIntent();
String jsonString = in.getStringExtra("jsonString");
String fileUri = in.getStringExtra("fileUri");
int id =in.getIntExtra("notificationID", -1);
NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
if (id >= 0 ) nm.cancel(id);
JSONIntent newIntent = new JSONIntent(getApplicationContext());
newIntent.setIncomingLocalFileURI(fileUri);
Intent out = newIntent.buildIntentFromJSON(jsonString, fileUri);
if (out != null) startActivity(out);
finish();
}
}