None of the comments below worked for me.
However, I have found a fix. I needed to change my AlarmActivity and it seems the keyguardManager.requestDismissKeyguard was very important.
Code:
public class AlarmActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
this.setTurnScreenOn(true);
this.setShowWhenLocked(true);
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE);
keyguardManager.requestDismissKeyguard(this, null);
}
}
Initial problem was..
I am trying to display an activity (startActivity) from a BroadcastReciever.onReceive method but it isn't working consistently. The code I have seems to work well if the device is active but has issues when the device is locked. The following code should be minSdkVersion 28 compliant.
The existing code uses an AlarmManager.setExactAndAllowWhileIdle to trigger a BroadcastReciever. Depending on some values inside intent.getExtras the code should display either: AssessmentAlarmActivity or MATActivity. AssessmentAlarmActivity needs to ALWAYS display even when the device is snoozing and on the locked screen. MATActivity should display only if device is active (doesn't have to wake device or display on lock screen).
The following code seems to work on a lot of devices. Looking at the logcat data, I see the BroadcaseReceiver.onReceive is getting triggered but sometimes the activity can take a minute before it will display. On some devices the activity will never displays at all. I am assuming the following code has incorrect values in the intent.addFlags code, but I am not sure.
AlarmManager.setExactAndAllowWhileIdle code:
private static void setAlarm(Context context, ResponseInfo responseInfo, int requestCode) {
// Create a new PendingIntent and add it to the AlarmManager
Intent intent = new Intent(context, ResponseAlarmReceiver.class);
responseInfo.setRequestCode(requestCode);
intent.putExtras(responseInfo.createBundle());
// Create a new PendingIntent and add it to the AlarmManager
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Activity.ALARM_SERVICE);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, responseInfo.getAlarmTime(), pendingIntent);
pendingIntents.put(requestCode, pendingIntent);
}
BroadcastReceiver.onRecieve code:
public class ResponseAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// read the ResponseInfo from the extras
ResponseInfo responseInfo = new ResponseInfo();
responseInfo.readBundle(intent.getExtras());
// log requestInfo data
Log.i("ALARM", String.format("Now: %d, responseInfo: %s",
System.currentTimeMillis(), responseInfo.toString()) );
if (responseInfo.getAlarmType() == AlarmType.CANCEL) {
cancelAlarm(context);
}
else {
showAssessmentAlarm(context);
}
}
private static void showAssessmentAlarm(Context context) {
Log.i("ALARM", "showAssessmentAlarm top");
Intent nextIntent = new Intent(context, AssessmentAlarmActivity.class);
nextIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(nextIntent);
Log.i("ALARM", "showAssessmentAlarm bottom");
}
private void cancelAlarm(Context context) {
Log.i("ALARM", "cancelAlarm top");
Intent intent = new Intent(context, MATActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
AndroidManifest.xml definition of the activities:
<activity
android:name=".MATActivity"
android:label="#string/app_name"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AssessmentAlarmActivity" android:theme="#android:style/Theme.Dialog" android:screenOrientation="portrait" ></activity>
MATActivity is a pretty standard activity, so I will skip that code. AssessmentAlarmActivity does have a special settings and extends AlarmActivity:
public class AlarmActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
// not minSdkVersion 28 complient, but this is what I have tested most
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
}
public class AssessmentAlarmActivity extends AlarmActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.assessment_alarm);
..
}
...
}
I think it's because of background process limits.
try testing it again with phone plugged in (charging). if the issue solved so it's because of background process limits.
if you are making a alarm clock or a very important thing to do you can use alarmManager.setAlarmClock(). it will bypass the restriction.
don't forget to acquire a wakeLock immediately in onReceive. else android system may go to idle mode very soon.
note: you may just need the second part (acquiring a WakeLock). but implementing both is a stronger way.
Related
So, I've been trying to achieve that for at least five hours today and I've tried literally ANY solution found anywhere.
I'm programming a little clock app, using AlarmManager to make the app ring. It works fine when my app is open or minimized. I'm trying to make it work when the app is closed, and that's the problem. So, there is the piece of code that sets the AlarmManager :
AlarmManager am = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ctxt, AlarmService.class);
PendingIntent pen = PendingIntent.getBroadcast(ctxt, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.setExact(AlarmManager.RTC_WAKEUP, nextRing.getTime(), pen);
(Here, ctxt is the context, I've tried both getApplicationContext() and getBaseContext() and nextRing.getTime() is a long that represents the date)
Then, I have my AlarmService class (wich used to be a service, which explain the name, but is now a BroadcastReceiver and I just don't want to rename it now)
public class AlarmService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
/*Intent cust = new Intent(context, CustomIntent.class);
context.startService(cust);*/
Bundle extras = intent.getExtras();
Intent newIntent = new Intent(context, AlarmActivity.class);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
newIntent.putExtra("AlarmName", extras.getString("AlarmName"));
context.startActivity(newIntent);
}
}
So this is the try with only the BroadcastReceiver, wich doesn't work obviously, so I tried to add a IntentService (commented out code at the top) which has the following code
public class CustomIntent extends IntentService {
public CustomIntent() {
super("CustomIntent");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent newIntent = new Intent(getBaseContext(), AlarmActivity.class);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(newIntent);
}
}
... and that's not working either ! Finally, here's the manifest I use :
<application>
[Here goes stuff that have nothing to do with my bug]
<receiver android:name=".custom_classes.AlarmService"/>
<service
android:name="com.group9.abclock.custom_classes.CustomIntent"
android:enabled="true" >
<intent-filter>
<action android:name="com.group9.abclock.custom_classes.CustomIntent" />
</intent-filter>
</service>
</application>
Sorry for the (very) long post but I tough I should explain anything I tried. Thanks in advance if you can help !
If your application is closed, you can't trigger a BroadcastReceiver because it's not registered, and you can't use Context methods because there is no Context.
If you want to execute tasks while the application is closed, you have to create another project with a Service, and start it with your application.
A Service runs in background until someone kill it, and once it's started, the main application is not needed anymore. So the ring functionality have to be implemented in this service.
Just remember that the AlarmManager.setExact, is not that exact from API 23, due to Doze Mode.
I've got an app that registers itself as the default launcher and pins itself automatically when started.
This all works fine when installing the app. It pins itself and only the back button is visible.
The problem is that when the device first boots up, it does not pin properly. I see a series of toasts "Screen pinned" and "Screen unpinned" multiple times. The "Home" and "Recent Tasks" buttons are still visible as well.
--
Running "adb shell dumpsys activity activities" - the last lines indicate that it is not pinned:
mLockTaskModeState=NONE mLockTaskPackages (userId:packages)=
0:[com.example.myapp]
mLockTaskModeTasks[]
--
Testing device Asus ZenPad running Marshmallow/6.0/23
I'm relying on the MainActivity manifest attribute "lockTaskMode" to pin (rather than activity.startLockTask()):
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/launcher_main"
android:launchMode="singleTask"
android:lockTaskMode="if_whitelisted"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
Any help or pointers would be appreciated
I had the same problem and I could really only find one solution. I'm not sure why but yeah, something in android prevents task locking when booting up which boggles my mind since the task lock was designed to create these "kiosk" type of applications. The only solution I could find was to detect for a case when it didn't lock then restart the application. Its a little "hacky" but what else can you do?
To detect for the case where it didn't lock I created a state variable and assigning states (Locking, Locked, Unlocking, Unlocked). Then in the device admin receiver in onTaskModeExiting if the state isn't "Unlocking" then I know it unlocked on its own. So if this case happened where it failed, I then restart the application using this method (which schedules the application in the alarm manager then kills the application):
how to programmatically "restart" android app?
Here is some sample code:
DeviceAdminReceiver
#Override
public void onLockTaskModeEntering(Context context, Intent intent, String pkg) {
super.onLockTaskModeEntering(context, intent, pkg);
Lockdown.LockState = Lockdown.LOCK_STATE_LOCKED;
}
#Override
public void onLockTaskModeExiting(Context context, Intent intent) {
super.onLockTaskModeExiting(context, intent);
if (Lockdown.LockState != Lockdown.LOCK_STATE_UNLOCKING) {
MainActivity.restartActivity(context);
}
Lockdown.LockState = Lockdown.LOCK_STATE_UNLOCKED;
}
MainActivity
public static void restartActivity(Context context) {
if (context != null) {
PackageManager pm = context.getPackageManager();
if (pm != null) {
Intent intent = pm.getLaunchIntentForPackage(context.getPackageName());
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
int pendingIntentId = 223344;
PendingIntent pendingIntent = PendingIntent.getActivity(context, pendingIntentId, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
System.exit(0);
}
}
}
}
private void lock() {
Lockdown.LockState = Lockdown.LOCK_STATE_LOCKING;
startLockTask();
}
private void unlock() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
if (am.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_LOCKED) {
Lockdown.LockState = Lockdown.LOCK_STATE_UNLOCKING;
stopLockTask();
}
}
In truth this is a simplified version of what I implemented. But it should hopefully get you pointed towards a solution.
The only solution I found as for now : make another launcher app, without locktask, which will trigger main app every time when launcher appears. This prevent user for waiting few more seconds before LockTasked app is being called with on BOOT_COMPLETED receiver. So we can meet this problem only when lockTask app has launcher properties for some activity in manifest.
Sorry for late answering, but...
Anyone has this problem can do this tricky work in first (LAUNCHER/HOME) activity (e.g. MainActivity):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (mSharedPreferences.getBoolean(KEY_PREF_RECREATED, false)) {
mSharedPreferences.edit().putBoolean(KEY_PREF_RECREATED, false).apply();
// start LOCK TASK here
} else {
mSharedPreferences.edit().putBoolean(KEY_PREF_RECREATED, true).apply();
finish(); // close the app
startActivity(new Intent(this, MainActivity.class)); // reopen the app
return;
}
setContentView(R.layout.activity_main);
// other codes
}
I've found a lot of solutions here about the Alarm Manager but none of them seemed to work.
I create the background service with:
public void scheduleSync() {
Intent intent = new Intent(this, SyncReceiver.class);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), SYNC_INTERVAL, pendingIntent);
Log.d(TAG, "Sync scheduled.");
}
The SyncReceiver class is:
public class SyncReceiver extends BroadcastReceiver {
private static final String TAG = "SyncReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, WebBackendSyncService_.class);
context.startService(i);
Log.d(TAG, "WebBackendSyncService started.");
}
}
And that is the WebBackendSyncService defined with Android Annotations:
#EIntentService
public class WebBackendSyncService extends IntentService {
public static final String ACTION = "com.invoicing.networking.WebBackendSyncService";
private static final String TAG = "WebBackendSyncService";
#RestService
APIService restClient;
public WebBackendSyncService() { super(ACTION);}
#Override
protected void onHandleIntent(Intent intent) {
Log.d(TAG, "Handling sync intent.");
sendInvoices();
}
#Background
void sendInvoices() {
SyncData.sendInvoices(restClient);
}
}
Service and Broadcast receiver in the manifest:
<service
android:name=".networking.WebBackendSyncService_"
android:exported="false" />
<receiver
android:name=".networking.SyncReceiver"
android:process=":remote" />
Looking at those line for the past couple of hours pushed me to ask for help here. I hope you'll see something that I'm missing.
Looking at the console output it gets to "Sync scheduled."
First, your <service> element has a rogue underscore in the android:name attribute that you need to remove.
Second, get rid of android:process=":remote". Forking a whole process for a two-line BroadcastReceiver is not very efficient, and it will interfere with the next fix.
Third, switch from BroadcastReceiver to WakefulBroadcastReceiver and follow the instructions for using that class, as right now the device will readily fall asleep either before or during the service's work.
Resolved after removing unused dependencies and multi-dex config. Zero changes to the code itself, however, it works for some unknown reason.
I have application, which makes event in alarm manager, and at specific time its called.
Code looks like this
Intent intent = new Intent(this, AlarmActivity.class);
pendingIntent = PendingIntent.getActivity(this,req_code, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),AlarmManager.INTERVAL_DAY*7,
pendingIntent);
Intent calls this activity.
public class AlarmActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void onStart(){
super.onStart();
//Change ringer mode
//Add notification in status bar
//Other booring stuff here...
Toast.makeText(this,"Finishing",2000).show();
finish();
}
}
in booring stuffthere are is code which should run in background (change ringermode)
Everything works for me, except one thing. Whenever the alarm manager calls my activity - the application comes to foreground.
When it only should change ringer mode in background, and add notification in status bar.
Any way to not allow application come to foreground?
You should do all of this in a BroadCastReceiver. There is no UI, and there is a Context variable passed on to the Receiver's onReceive() method which allows you to basically do anything the Activity does, without having an actual UI. This means that you can set the ringer, show the status bar notification, etc.
Your BroadcastReceiver class should look something like:
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Change ringer mode
//Add notification in status bar
//Other boring stuff here...
Toast.makeText(context,"Finishing",2000).show();
}
}
Note that for your Toast, the variable named context is used.
And your AlarmManager code should look something like this:
Intent intent = new Intent(this, AlarmBroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this,req_code, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Activity.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),AlarmManager.INTERVAL_DAY*7,
pendingIntent);
Your manifest should have this:
<receiver android:name=".AlarmBroadcastReceiver" >
</receiver>
Add this line to the Activity in your AndroidManifest
android:theme="#android:style/Theme.NoDisplay"
and you have an Activity with nothing to display. Since you are already calling finish(); in your code, it will look like it is running in background.
I want to send my app to sleep and then wake it up at set times. I have it going to sleep but not waking up.
This sets the wakelock:
private void setWakeLock(){
System.out.println("wakelock");
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.ON_AFTER_RELEASE, "DoNotDimScreen");
wl.acquire();
}
This sets alarms for wake/sleep times:
private void setWakeSleep(){
java.util.Calendar c = java.util.Calendar.getInstance();
c.set(java.util.Calendar.HOUR_OF_DAY, 17);
c.set(java.util.Calendar.MINUTE, 53);
c.set(java.util.Calendar.MILLISECOND, 0);
Intent sleepIntent = new Intent("SLEEP_INTENT");
PendingIntent sleepPendingIntent = PendingIntent.getBroadcast(this, 0, sleepIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), sleepPendingIntent);
c.set(java.util.Calendar.HOUR_OF_DAY, 18);
c.set(java.util.Calendar.MINUTE, 14);
c.set(java.util.Calendar.MILLISECOND, 0);
Intent wakeIntent = new Intent("WAKE_INTENT");
PendingIntent wakePendingIntent = PendingIntent.getBroadcast(this, 0, wakeIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager2 = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager2.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), wakePendingIntent);
}
And this is the broadcast receiver:
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Time updateHour = new Time();
updateHour.set(System.currentTimeMillis());
if (intent.getAction().equals("SLEEP_INTENT")) {
System.out.println("sleep");
wl.release();
}
if (intent.getAction().equals("WAKE_INTENT")) {
wl.acquire();
System.out.println("wake");
//initialise();
}
}
};
Any help greatly appreciated!
First, you don't want a wakelock; those are for keeping the device from going to sleep, which is highly anti-social unless your app really requires it (it kills the battery).
Second, your code to set the wakeup time will fail if you call it after 18:14 since you'll now be defining a time in the past. Let's ignore that for now.
Next, your intent action should be something like "org.user1797190.WAKE_INTENT" rather than simply "WAKE_INTENT" which could cause collisions. If you anticipate making this intent public, consider registering it at http://openintents.org. That's not your problem either, though.
You don't need alarmManager2 -- there's only one alarm manager in the system, so just re-use the first one.
I've never heard of making an app go to "sleep" per se. Do you mean you want the app to go away, and then come back later?
Here is what I would do. Forget about the "SLEEP_INTENT" completely. Just schedule a "WAKE_INTENT" and then call finish(). Your app will simply leave the screen.
I would forget about the broadcast receiver entirely. Instead, I would use getActivity() instead of getBroadcast() to get a pending intent that will restart the activity. Modify your manifest so that your WAKE_INTENT will go to the activity. Also, you should set the "android:launchMode" property to "singleTask" so multiple instances of your activity aren't created. You'll also need to implement onNewIntent() to handle the wakeup intent if your activity is already running when it arrives.
Finally, if your activity is part of the same application that will be creating the intent, you don't need a named intent at all; you can create them by class. You'll need another way to let the receiver know that this is a wakeup intent though.
So, putting it all together:
Your manifest should contain:
<activity android:name=".TestActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Your code should contain:
/**
* Arrange for the activity to return at a specific time.
* Call finish() after calling this method().
* This function can be called from anywhere that has a valid Context.
*/
public static void scheduleWakeup(Context ctx, long timeMillis) {
if (DEBUG) Log.d(TAG, "Scheduling wakeup for " + timeMillis);
Intent intent = new Intent(ctx, TestActivity.class);
intent.putExtra("wakeup", true);
PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager mgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
mgr.cancel(pi); // Cancel any previously-scheduled wakeups
mgr.set(AlarmManager.RTC_WAKEUP, timeMillis, pi);
}
...
protected void onCreate(Bundle state) {
Intent intent = getIntent();
if (intent.getBooleanExtra("wakeup", false)) {
// We were woken up by the alarm manager
}
...
}
protected void onNewIntent(Intent intent) {
if (intent.getBooleanExtra("wakeup", false)) {
// We were woken up by the alarm manager, but were already running
}
}
This is pretty close to what I'm doing in my own apps, and it works pretty well for me.
You'll have to test this yourself, of course. Log.d() is your friend.
as above. The problem was that I was using a broadcast receiver within the calling activity.