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.
Related
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.
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?
I am experiencing some lag/black screen in my application since I start using AlarmManager + BroadcastReceiver. I have 2 BroadcastReceivers, one to when the phone gets restarted and another that AlarmManager call in the given period of time to send data to the server.
This is the code for BootReceiver to start the alarmManager once the cellphone is rebooted (it is working so far):
private final String BOOT_COMPLETED_ACTION = "android.intent.action.BOOT_COMPLETED";
#Override
public void onReceive(Context context, Intent intent) {
// when the boot is completed, restart the alarm manager
if(intent.getAction().equals(BOOT_COMPLETED_ACTION)){
SharedPreferences mPrefs = context.getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
if (mPrefs.getBoolean("hasGeolocation", false) &&
!mPrefs.getBoolean("isThreadOn", false)){
EngineerTracker tracker = new EngineerTracker(context);
try {
tracker.startEngineerTrackingLocation();
} catch (ApplicationException e) {
e.printStackTrace();
}
}
}
}
The method to start and stop the alarm manager is this:
public void startEngineerTrackingLocation() throws ApplicationException{
PendingIntent pendingIntent = null;
AlarmManager manager = null;
ProjectGeospatialConfig geospatialConfig;
// check if the intent is running, if it is not, start it
if (PendingIntent.getBroadcast(context, 0,
new Intent(context, EngineerGeospatialTrackingReceiver.class),
PendingIntent.FLAG_NO_CREATE) == null){
// fetch the geospatial configuration, it may come null, so verify before using
geospatialConfig = getFirstFoundGeospatialConfiguration();
// if not null and use gps
if (geospatialConfig != null && geospatialConfig.isUseGps()){
// session information
SessionInformationDTO sessionInformation = dao.getObjectForKey(SqlLiteStorageKey.USER_INFORMATION);
Integer currentResourceId = sessionInformation.getSecurityHandler().getCurrentUser().getId();
// Retrieve a PendingIntent that will perform a broadcast and add resource id as extra
Intent alarmIntent = new Intent(context, EngineerGeospatialTrackingReceiver.class);
alarmIntent.putExtra("resourceId", currentResourceId.toString());
// set pending intent
if (pendingIntent == null){
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
}
// set manager
if (manager == null){
manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
// set interval between alarms
int interval = (geospatialConfig.getGpsTrackingInterval() *1000) * 60;
// set alarm repetition
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
interval, pendingIntent);
// set variables for gps tracking
SharedPreferences mPrefs = getApplicationContext().getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
Editor editor = mPrefs.edit();
// these variables will be measured once db is set
editor.putBoolean("hasExecuted", false);
editor.commit();
}
}
}
both are also working so far, the flag is meant to know when the service has been executed once and will not attempt again at the basic activity (template for all activitied)
The broadcast that is invoked in the alarm manager to send the information in the defined interval is this:
public class EngineerGeospatialTrackingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String resourceId = intent.getStringExtra("id");
sendLocation(context, resourceId);
}
private void sendLocation(final Context context, final String resourceId){
new RemoteRequestTask<Void>(null, false, null) {
#Override
public Void executeTask() throws ApplicationException {
// working code
}
#Override
public void completed(Void refreshed) {
}
#Override
public void onException(final ApplicationException ex) {
}
}.start();
}}
Both receivers were added to the AndroidManifest. Beside the slowness, i also get a black screen when transitioning from an activity to another.
Use Traceview to determine where you are spending your time, and consider enabling StrictMode to point out where you are doing unfortunate things on the main application thread.
You want onReceive() to be very quick, ideally under 1ms, as. However, it looks like you might be doing database I/O in there (e.g., references to dao), which means that work should be handled off the main application thread, perhaps by an IntentService that you start from onReceive().
I'm working on an Android app that sues a background task to fetch XML from a URL every x seconds (user defined interval, defaulting to 60). My structure is as so:
MainActivity
This schedules an Alarm via AlarmManager:
public static void scheduleAlarm(Context voContext, int viCheckInterval)
{
try {
moAlarmManager = (AlarmManager) voContext.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(voContext, AlarmReceiver.class);
moAlarmIntent = PendingIntent.getBroadcast(voContext, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, viCheckInterval);
moAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),
time.getTimeInMillis(), moAlarmIntent);
} catch (Exception e) {
Log.e("MessageCheckAlarmHandler", e.toString());
}
}
AlarmReceiver
This is a BroadcastReceiver that starts the service:
#Override
public void onReceive(Context context, Intent intent)
{
Context oAppContext = context.getApplicationContext();
if (oAppContext == null) {
oAppContext = context;
}
Intent serviceIntent = new Intent(oAppContext, MessagingService.class);
oAppContext.startService(serviceIntent);
}
MessagingService
This creates our in-house logger (logging over TCP) and starts an AsyncTask called FetchPageTask:
public class MessagingService extends Service
{
#Override
public void onCreate()
{
super.onCreate();
...
this.acquireLocks();
try {
String sCheckerUrl = oPreferences.getString("pref_server", "");
int sCheckerPort = Integer.parseInt(oPreferences.getString("pref_server_port",
"8050"));
sCheckerUrl = String.format(URL, sCheckerUrl, sCheckerPort);
this.moFetchInboxTask = new FetchPageTask(this.logger, this);
this.moFetchInboxTask.execute(sCheckerUrl);
} finally {
this.releaseLocks();
this.stopSelf();
}
}
#Override
public void onDestroy()
{
super.onDestroy();
this.logger.close();
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
/**
* Acquire a WakeLock and a WifiLock.
*/
private void acquireLocks()
{
try {
// Acquire a wake lock to prevent the device from entering "deep sleep"
PowerManager oPowerManager = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
this.moWakeLock = oPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
this.moWakeLock.acquire();
// Acquire a WiFi lock to ensure WiFi is enabled
WifiManager wm = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
this.moWifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
this.moWifiLock.acquire();
} catch (Exception e) {
this.logger.error(TAG + "->onCreate()", "Error acquiring locks: " + e.toString());
}
}
/**
* Release our WakeLock and WifiLock.
*/
private void releaseLocks()
{
try {
this.moWakeLock.release();
this.moWifiLock.release();
} catch (Exception e) {
this.logger.error(TAG + "->releaseLocks()", e.toString());
}
}
}
FetchPageTask
This extends AsyncTask and does all the work of fetching the page and parsing the XML. It also then adds notifications and performs actions on the data retreived if need be.
This all works fine once, but does not run subsequently. I know that the AsyncTask works as I was previously doing it using pure Java via ScheduledExecutorService and ScheduledFuture and it worked. The only reason I decided to change to using AlarmManager is for maintainability purposes.
First, you are setting up your alarm to occur immediately, and then every ~43 years thereafter. That is unlikely to be what you want. Modify the third parameter of your call to setRepeating() to be the desired period in milliseconds, which right now is set to the number of milliseconds since midnight 1 January 1970.
Second, you are acquiring your WakeLock too late. There is no guarantee that your acquireLocks() will get a chance to run before the device falls asleep. My WakefulIntentService or the new WakefulBroadcastReceiver offer better patterns for passing control to an IntentService.
I think you don't need the Calendar here. You just want to run the action every x seconds, so It would be something like this:
moAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
viCheckInterval, moAlarmIntent);
// viCheckInterval should be long miliseconds
I have a startApplicationService method in an activity. Therefore I have an alarm manager. startApplicationService method is calling background service. I want to use/call that method in onReceive method from my alarm manager. How to do it? Please help me. :)
EDITED: Added alarm manager class:
public class WatcherAlarm extends BroadcastReceiver
{
private final static String LOG_TAG = WatcherAlarm.class.getSimpleName();
private AccessActivity activity = null;
#Override
public void onReceive(Context context, Intent intent)
{
Log.d(LOG_TAG, "-------------------------- WATCHER ALARM ------ ON RECEIVE");
if(activity != null) {
activity.startApplicationService(intent.getExtras());
}
}
public void startAlarm(AccessActivity activity, Bundle bundle)
{
this.activity = activity;
AlarmManager alarmManager = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(activity, WatcherAlarm.class); // explicit
i.putExtras(bundle);
PendingIntent pi = PendingIntent.getBroadcast(activity, 0, i, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 20, pi); // Millisec * Second * Minute
}
public void stopAlarm(Context context)
{
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, WatcherAlarm.class); // explicit
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
alarmManager.cancel(pi);
}
}
And here is the my startApplicationService method: It's starting AsyncTask.
public void startApplicationService(Bundle bundle)
{
try {
task = new ApplicationServiceTask(this);
requestId = task.execute(bundle).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
I'm tried get activiy from context. But it's not possible. :(. Is there any way to say call startApplicationService method to activity from alarm manager?
Thank you for every advice.
Zeck
No... because you have no guarantee that the Activity you are trying to call will even exist when your onReceive() is called.
What you can do is start that Activity using an Intent with an Extra indicating that the the caller is you BroadcastReceiver. The Activity can then check that Extra and call the method.
However, considering what you appear to want to do. I would recommend going with a Service. Since you are doing the work in the background anyways, I don't see a reason for wanting to do it in an Activity (unless, of course, you have a valid reason that does not show here).
See my answer here for an example of something similar.