cannot start activity background in android 10 [ android Q ]
Asked Answered
K

4

20

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]

Krems answered 20/12, 2019 at 3:59 Comment(2)
Android 10 (API level 29) and higher place restrictions on when apps can start activities when the app is running in the background. for more detail check out this blog. developer.android.com/guide/components/activities/…Creatine
Look at this post where EventBus is used https://mcmap.net/q/37214/-how-to-start-activity-from-service-in-android10Immaterial
M
30

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
            }

        }
    }
 }
Misspeak answered 20/12, 2019 at 7:6 Comment(6)
thank you ! I tested your code. it works good. but, it's not run-time permission. so, user have to go back to my app after overlay window settings finished by switching app.Krems
Yes They must. Android made starting activity in background very restricted.Gey
use shared preference to check whether you allow the permission or not , boolean checkPermission = SpUtil.getInstance().getBoolean(AppConstants.CHECK_PERMISSION, false);Kisor
Settings.canDrawOverlays(getContext() method returns if this permission granted. So no need to request permission more than one time if user grants itGey
what should i write in PermissionDenied() method? can you please send that method too?Relume
in my case, I created a dialog which explains the system wouldn't work without this permission and showed it to the userGey
C
10

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);
    }
Creatine answered 20/12, 2019 at 4:32 Comment(6)
thank you for your answer. but, my app should not use notification. because response time is very important in my app. users should respond in a few seconds, so after click notification message, already time passed by, so limit time is over. is there any other method in the respect of user response time ? activating my activity from background to foreground is very good , but is it impossible ?Krems
Read this blog developer.android.com/guide/components/activities/… .. and in your case try to handle some specific cases where the restriction doesn't apply to open activity.Creatine
thank you. I will read it. as you said, I think I have to handle some specific cases related to activity stack. I tested more cases, some activities show up, other activities not show up. I have to know what is different in each case.Krems
Works perfect! Thank you. You can call notificationManager.cancel(notId); right after notificationManager.notify to dismiss notification if it's not needed.Prerecord
This can not work for Android 10 Go device. developer.android.com/about/versions/10/…Silhouette
@Silhouette I have not to check this on go device. but above code almost all devices. give me some time I have to check and update my answer with a workable code on go device.Creatine
B
0

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).

Bijou answered 25/3, 2021 at 23:20 Comment(0)
O
0

Very strange but launching activity from foreground service worked in release build. Was not working in debug build (when debugging via Android Studio).

Owing answered 13/8, 2021 at 17:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.