startActivity for URI in Lockscreen on Android Nougat - android

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();
}
}

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

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);
}

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!

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?

Related

setAlarmClock BroadcastReceiver does not reach onResume sometimes

I have created an alarm clock app using setAlarmClock. For some people the alarm does not start properly. I know you must disable all kind of energy saving modes on devices otherwise it could by that the alarm does not fire. But I have some rare cases where the alarm starts on time but it has time gaps until it finishes alls steps of onCreate and onPostCreate of my activity.
It could be related with the fact that my wakelock is activated very late. When the Broadcastreceiver is called with an alarm event it starts my main activity. The activity starts a thread which must run at least once. It checks if the alarm should fire. If yes then it creates a wakelock to keep the device awake.
I could try to create the wakelock ealier but I have seen log files where onResume has not been even called, only onStart. There is a gap of 5 minutes between onStart and onResume. So there is no chance to make it early enough.
Is there something wrong with my alarm and wakelock concept?
Is it possible/wise to start the wakelock in the BroadcastReceiver and stop it in the Activity?
public class AlarmManagement extends BroadcastReceiver implements Serializable
{
#Override
public void onReceive (Context context, Intent intent)
{
Intent i = new Intent(context, FullscreenActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
}
public void scheduleSystemAlarmApi21(Context context,long alarmTime, PendingIntent pi) {
AlarmManager am =( AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i2=new Intent(context, FullscreenActivity.class);
PendingIntent pi2=PendingIntent.getActivity(context, 0, i2, 0);
am.setAlarmClock(new AlarmManager.AlarmClockInfo(alarmTime,pi2),pi);
}
public void scheduleSystemAlarm(Context context,int id, long alarmTime) {
Intent i = new Intent(context, AlarmManagement.class);
PendingIntent pi = PendingIntent.getBroadcast(context, id, i, 0);
scheduleSystemAlarmApi21(context,alarmTime,pi);
}
}
public class FullscreenActivity extends AppCompatActivity {
Thread thread;
#Override
protected void onResume() {
super.onResume();
if (threadStopped)
alarmServiceThread();
}
void alarmServiceThread() {
thread = new Thread() {
#Override
public void run() {
...
if (needToStartWakeup(currentTime))
startWakeup();
...
}
}
thread.start();
}
PowerManager powerManager;
PowerManager.WakeLock wake;
public void startWakeup() {
powerManager = ((PowerManager) getSystemService(Context.POWER_SERVICE));
int levelAndFlags=PowerManager.ACQUIRE_CAUSES_WAKEUP;
levelAndFlags |= PowerManager.SCREEN_BRIGHT_WAKE_LOCK;
wake = powerManager.newWakeLock( levelAndFlags , WAKE_LOCK_TAG);
wake.acquire();
}
}
You are trying to start an activity from the receiver in the onReceive method, that is problematic if your app is in the background.
Please read the following documentation on the subject:
https://developer.android.com/guide/components/activities/background-starts
You should present a notification for the user and open the activity on user action.

AlarmManager + Service on Idle (screen off)

I call my Service with alarm manager
like this:
alarmManage.setExact(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + getPoolingInterval(), pendingIntentPolling);
On my ServicePooling i reschedule it of the same way, and this ServicePooling call another service to send data on my service.
Intent serviceSocket = new Intent(this.context, SenderService.class);
this.context.startService(serviceSocket);
All works very well every minut i receive on my server a polling communication, but when my device are screen off and without USB plugged, this stop work.
This is a bad idea to use Service for AlarmManager nowadays. Use WakefulBroadcastReceiver instead. your device fall asleep then unplugged.
public class BRMine extends WakefulBroadcastReceiver {
public static final String INTENT_FILTER = "com.example.BRMine";
#Override
public void onReceive(Context ctx, Intent intent) {
// TODO Auto-generated method stub
OWakeLocker.acquire(ctx, _.indexNOTS);
ComponentName comp = new ComponentName(ctx.getPackageName(),
SMine.class.getName());
startWakefulService(ctx, intent.setComponent(comp));
}
}
where:
public class OWakeLocker {
private static PowerManager.WakeLock[] wakeLocks = new PowerManager.WakeLock[_.indexNOTS_MAX];//Services count
#SuppressWarnings("deprecation")
public static void acquire(Context ctx, int index) {
WakeLock wakeLock = wakeLocks[index];
if (wakeLock != null) wakeLock.release();
PowerManager pm = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, _.APPNAME + Integer.toString(index));
if (wakeLock != null && wakeLock.isHeld()){
wakeLock.acquire();
}
}
public static void release(int index) {
WakeLock wakeLock = wakeLocks[index];
if (wakeLock != null)
wakeLock.release();
wakeLock = null;
}}
to start:
Intent intent = new Intent(BRMine.INTENT_FILTER);
PendingIntent pi = PendingIntent.getBroadcast(ctx, myintentalarm, intent, PendingIntent.FLAG_CANCEL_CURRENT):
am.setExact(AlarmManager.RTC_WAKEUP, nexttime, pi);
I solve my problem using a answer of Vyacheslav but wihtout AlarmManager because setExact didint work for me on idle and my android is a api lower then 23 (and don't have setExactAndAllowWhileIdle) i use a timertask on startapplication in my case works werry well, i just need this when my application are runnning.

Repetitive service design Android

I have following design in my app: I have a one activity that sets repeating alarm that launches receiver that starts my service. Every minute. In my service, I set Start Sticky but once Android decides to kill my service, I can't get it to restart. It's Android 4.4.2. Here is my code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent i1 = new Intent(MainActivity.this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(MainActivity.this, 0, i1, PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, 0, 60 * 1000, pi);
}
here is the receiver
public void onReceive(Context arg0, Intent arg1) {
// For our recurring task, we'll just display a message
Log.d(Constants.TAG, "Starting Service");
Intent intent = new Intent(arg0, MyLocationService.class);
arg0.startService(intent);
}
and service:
private static PowerManager.WakeLock wl;
private static OkHttpClient client;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(Constants.TAG, "Service onStartCommand");
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wl.acquire();
client = new OkHttpClient();
new GetLocation(MyLocationService.this).execute();
return START_STICKY;
}
You're most likely seeing an interaction with power management and alarms. Starting with API 19 all alarms are inexact by default, so they will be collated with other alarms. Further, once the device has entered into a lower power state, alarms are delivered to BroadcastReceivers and the device guaranteed to stay awake as long as the receiver is executing its onReceive() method. Once it returns from that method (and any other BroadcastReceiver associated with the alarm runs), the device will immediately go to a low power state again. Since your app had previously been killed, the Service is no longer running and will not get CPU time.
The way to resolve this is to use a WakefulReceiver which takes a wakelock when it runs onReceive(), starts your Service for processing. The Service will then release the wakelock when it is done processing. This article will give you a good explanation of this: http://po.st/7UpipA
Note that waking up every minute is going to seriously degrade the battery life of the device, so you should consider backing this off.

Wake Lock not working properly

I apologies in advance if I'm not good in writing English.
I'm writing a simple task app that remind me with alarm in specific time.
Below I set alarm with AlarmManager :
private static void setAlarm(Context context, Calendar calendar,
PendingIntent pIntent) {
AlarmManager alarmManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pIntent);
}
}
and then AlarmManagerHelper :
public class AlarmManagerHelper extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String title = intent.getStringExtra("Title");
int hour = intent.getIntExtra("Hour", 0);
int min = intent.getIntExtra("Minute", 0);
String alarmTone = intent.getStringExtra("AlarmTone");
Intent i = new Intent();
i.setClassName("com.example.tasks",
"com.example.tasks.AlarmScreenActivity");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("Title", title);
i.putExtra("Hour", hour);
i.putExtra("Minute", min);
i.putExtra("AlarmTone", alarmTone);
context.startActivity(i);
}
}
and AlarmScreenActivity is:
public class AlarmScreenActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get intent
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
mPlayer = new MediaPlayer();
try {
if (task_Tone != null && !task_Tone.equals("")) {
android.net.Uri toneUri = android.net.Uri.parse(task_Tone);
if (toneUri != null) {
mPlayer.setDataSource(this, toneUri);
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mPlayer.setLooping(true);
mPlayer.prepare();
mPlayer.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// some code
public void onClickDissmis(View view) {
mPlayer.stop();
finish();
}
protected void onDestroy() {
super.onDestroy();
wl.release();
}
}
then with AlarmManagerHelper and AlarmScreenActivity displaying it.
my problem is:
in the specific time that should wake up and ringing not do int, so when I press power button an turn screen on that is work???!
(when is in debug mode and the device , connected to system work properly)
I hope that describe my problem perfectly.
I don't understand your problem, exactly. I can say, though, that Android only guarantees that it is holding a wakelock while it delivers the Broadcast. Your code leave considerable time between the reception of the Broadcast, by the Receiver, and the time you seize the wakelock. There is nothing to prevent the device from going back to sleep, in that interval.
While AlarmManagerHelper.onReceive runs the system holds a lock (because of the Alarm manager) that will not fail. But between the context.startActivity(i); and the starting of the activity the system falls asleep again. You need to either use a WakefulBroadcastReceiver (see BroadcastReceiver Vs WakefulBroadcastReceiver) or (that's what I use) a WakefulIntentService.
In any case you need to start a service and start your activity from there. For the WakefulIntentService pattern see my answer PowerManager.PARTIAL_WAKE_LOCK android and links there.

How to force an android device to stay alive while the application is running

I need to force the android device to stay alive while the application is running. There any way to do this ?
I read here : Is there a way to force an android device to stay awake? about this, I tried to do this but probably I don't know to use correctly a Service.
This is the code I use :
public class WakeLockService extends Service {
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Tag");
wl.acquire();
}
#Override
public void onDestroy() {
wl.release();
}
and in the first Activity of my Application, I put this :
Intent s = new Intent(this, WakeLockService.class);
startService(s);
Is it correct what I'm doing? Anyone can help me to do this ?
Thanks in advance.
If you want the device to stay awake while it displays an activity of your app, you have to set the flag FLAG_KEEP_SCREEN_ON when creating the activity:
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); // Unlock the device if locked
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); // Turn screen on if off
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Keep screen on
.....
}
Add the permission WAKE_LOCK in the manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
EDIT After seeing your last comment: yes, you need a service: note that the device will go to sleep anyway, but your service can continue to run if you make it clear to the user (you have to display a notification) and declare it STICKY:
public class yourservice extends Service
{
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//The intent to launch when the user clicks the expanded notification
/////////////////////////////////////////////////////////////////////
Intent forPendingIntent = new Intent(this, si.test.app.activities.activity.class);
forPendingIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendIntent = PendingIntent.getActivity(this, 0, forPendingIntent, 0);
Notification notification = new Notification(R.drawable.icon, "testapp", System.currentTimeMillis());
notification.setLatestEventInfo(this, "testApp", "testApp is running", pendIntent);
notification.flags |= Notification.FLAG_NO_CLEAR;
startForeground (R.string.app_name, notification);
return START_STICKY;
}
...
}
In my application I have, for example, a syncronization server->mobile and this syncronization can run more then 5 minutes. I want to force the device to not enter into stand-by, to see when the syncronization process is finished
The synchronization operation should be managed by some Android component, such as a service. That component can manage a WakeLock. Do not create some separate component purely for the WakeLock, as that other component has nothing whatsoever to do with your synchronization work.
For example, if your synchronization is being conducted via an IntentService, you could use my WakefulIntentService to keep the device awake while the work in onHandleIntent() is being conducted.

Categories

Resources