startActivity for URI in Lockscreen on Android Nougat

779 views Asked by At

I'm using a BroadcastReceiver that receives a broadcast from an AlarmManager. In the receiver I am starting two activities. One activity is started from a URI like this and is a third-party app:

// Open spotify
Intent spotify = new Intent(Intent.ACTION_VIEW, Uri.parse(song));
spotify.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

try {
    context.startActivity(spotify);
} catch (ActivityNotFoundException e) {
    status = Status.SPOTIFY_NOT_INSTALLED;
}

After that I start another activity that belongs to the app with a 5 second delay using the AlarmManager again:

public static void setExact(
        Context context, PendingIntent pendingIntent, long time
) {

    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
        am.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
    else
        am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);

}

public static void setExactDelay(
        Context context, PendingIntent pendingIntent, long delay
) {
    setExact(context, pendingIntent, System.currentTimeMillis() + delay);
}

    PendingIntent pendingIntent = AlarmPlayActivity.makePendingIntent(context, alarm, status, startTime);
    AlarmSet.setExactDelay(context, pendingIntent, 5000);

The second activity starts in 5 seconds as expected. However the first activity only starts when the device is unlocked. If the device is locked it does not start on Android Nougat (7.0). This is the case even when the lock is not secured by password, pattern etc. This used to work on earlier Android versions, even with a secure lock.

Is there a method by which I can start the first activity without needing the screen to be on?

Edit: I've tried using the following IntentService. It works when the device is awake and unlocked but there is no luck when the device is locked:

public class AlarmService extends IntentService {

    static final int NOTIFICATION_ID = 1;

    public AlarmService() {
        super("AlarmService");
    }

    public static Intent makeIntent(Context context, Alarm alarm, AlarmReceiver.Status status, long startTime) {

        Intent intent = IntentFactory.alarmPlayIntent(alarm, status, startTime);
        intent.setClass(context, AlarmService.class);

        return intent;

    }

    private static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // Nothing
        }
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        // Acquire Wakelock immediately

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        PowerManager.WakeLock wakeLock = pm.newWakeLock(
                PowerManager.FULL_WAKE_LOCK |
                PowerManager.ACQUIRE_CAUSES_WAKEUP |
                PowerManager.ON_AFTER_RELEASE,
                "AlarmServiceWakeLock"
        );

        wakeLock.acquire();

        KeyguardManager.KeyguardLock lock = ((KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE)).newKeyguardLock(KEYGUARD_SERVICE);
        lock.disableKeyguard();

        // Get intent data

        final Alarm alarm = IntentFactory.getAlarm(intent);
        AlarmReceiver.Status status = IntentFactory.getStatus(intent);
        final long startTime = IntentFactory.getStartTime(intent, 0);

        // Get a random song for this alarm

        AlarmDatabase db = AlarmDatabase.getInstance(this);
        Song song = db.getRandomSong(alarm);

        String songName = song == null ? "backup sound" : song.getName();

        // Start a foreground notification

        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle(getText(R.string.alarm_starting_notification_title))
                .setContentText(getString(
                        R.string.alarm_starting_notification_message, alarm.getName(), songName
                ))
                .setSmallIcon(R.drawable.ic_launcher)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .build();

        startForeground(NOTIFICATION_ID, notification);

        // Potentially open Spotify if we can

        if (song != null) {

            // Open spotify

            Intent spotify = new Intent(Intent.ACTION_VIEW, Uri.parse(song.getUri()));
            spotify.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

            try {
                startActivity(spotify);
            } catch (ActivityNotFoundException e) {
                status = AlarmReceiver.Status.SPOTIFY_NOT_INSTALLED;
            }

        } else
            status = AlarmReceiver.Status.NO_SONGS;

        // Start play activity in 10 seconds, giving Spotify some chance to load up.

        sleep(10);
        startActivity(AlarmPlayActivity.makeIntent(this, alarm, status, startTime));

        // Keep alive for 5 more seconds
        sleep(5);

        // Stop notification
        stopForeground(true);

        // Release wakelock
        wakeLock.release();


    }

}
4

There are 4 answers

1
cokceken On

I experienced the same issue and i think Intent.ACTION_VIEW don't work until you unlock the screen because of safety reasons.

Same issue discussed here also. You can also check this link

0
Vyacheslav On

You have to use up-to-date methods: either am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nexttime, pendingintent);

or am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nexttime, pendingintent);

Do not forget to set your i awake mode: to start:

  PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
            wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
                    PowerManager.ACQUIRE_CAUSES_WAKEUP |
                    PowerManager.ON_AFTER_RELEASE, A_.APPNAME + Integer.toString(index));
        if (wakeLock != null && wakeLock.isHeld()){
            wakeLock.acquire();
        }

to release:

wakeLock.release();

you can set your activity as foreground (over lock screen) described here: https://stackoverflow.com/a/23611199/1979882

import android.view.Window;
import android.view.WindowManager.LayoutParams;


Window window = this.getWindow();
window.addFlags(LayoutParams.FLAG_DISMISS_KEYGUARD);
window.addFlags(LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(LayoutParams.FLAG_TURN_SCREEN_ON);

You can start your activity as shown here too:

https://stackoverflow.com/a/6468575/1979882

 @Override
    public void onReceive(Context context, Intent intent) {
        //start activity
        Intent i = new Intent();
        i.setClassName("com.test", "com.test.MainActivity");
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
    }
0
Trần Đức Tâm On

In my case, the second activity also could not open without wake your phone up.

Solution:

Start activity when screen is off

Bring app to front, turn on display and unlock from AlarmManager?

2
Nick Friskel On

Instead of starting your Spotify activity directly from the receiver, have the receiver instead start a Service from your own app.

Inside of the service, acquire your wakelock and then start a Foreground notification (build notification with NotificationCompat.Builder then use startForeground(mId, notification);, this notification will keep the service alive in cases where Nougat would otherwise stop it.

Then fire your Spotify Intent from the service, afterwards of course kill the service, foreground notification, and wakelock. This has worked for me before to avoid pesky Nougat problems like you are facing. Hope this helps!