An always-on app provides the user with the ability to view information at any time, without having to wake the device; however, the visible information may become stale if the actual information changes more frequently than once in a minute.
We may need to update the content on the wearable more frequently than once in a minute; thus, we can schedule an alarm to trigger an update outside the one-minute interval:
[assembly: UsesPermission(Manifest.Permission.WakeLock)]
[assembly: UsesLibrary( "com.google.android.wearable", false)]
WearableActivity
:public class MainActivity : WearableActivity
Intent
instance, which we will respond to by updating the UI. As the intent will try to launch a new instance of our activity, we will specify that we only want a single instance of our activity using the LaunchMode
property of the [Activity]
attribute on our activity:[Activity(..., LaunchMode = LaunchMode.SingleInstance)]
Now that the activity is set up, we schedule the alarm for several seconds in the future when we are in the ambient mode:
OnCreate
, we request ambient mode notifications by invoking the SetAmbientEnabled()
method:SetAmbientEnabled();
AlarmManager
:alarmManager = AlarmManager.FromContext(this);
Intent
instance to our activity, we create the PendingIntent
instance for the alarm manager:var alarmIntent = new Intent( ApplicationContext, typeof(MainActivity)); alarmPendingIntent = PendingIntent.GetActivity( ApplicationContext, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
IsAmbient
property to ensure that we only schedule the alarm when in the ambient mode:private void ScheduleAlarm() { if (IsAmbient) { long alarmInterval = 15 * 1000; // 15 seconds long time = JavaSystem.CurrentTimeMillis(); long delay = alarmInterval- (time % alarmInterval); long trigger = time + delay; alarmManager.SetExact( AlarmType.RtcWakeup, trigger, alarmPendingIntent); } }
public override void OnEnterAmbient(Bundle ambientDetails) { base.OnEnterAmbient(ambientDetails); ScheduleAlarm(); }
OnNewIntent()
method. As this type of alarm is a once-off alarm, we will have to reschedule when it fires:protected override void OnNewIntent(Intent intent) { base.OnNewIntent(intent); Intent = intent; if (IsAmbient) { // update the ambient UI } ScheduleAlarm(); }
public override void OnExitAmbient() { base.OnExitAmbient(); alarmManager.Cancel(alarmPendingIntent); }
OnDestroy
so that we cancel the alarm when the user exits the app:protected override void OnDestroy() { base.OnDestroy(); alarmManager.Cancel(alarmPendingIntent); }
Although, this is all that is needed to update the UI every few seconds while in the ambient mode, we can add a further improvement to take into consideration the default one-minute update provided by the framework:
public override void OnUpdateAmbient() { base.OnUpdateAmbient(); // update the ambient UI ScheduleAlarm(); }
Although, the Android Wear provides the WearableActivity
type to allow us to neatly create always-on apps, the default update interval of one minute may not be frequent enough. This maybe the case for a fitness app or some other app where we want the user to be able to glance at the information, which is updated fairly rapidly.
Creating a more dynamic always-on app is more of an extension of a typical always-on app. We still require the activity inherit from WearableActivity
, and we do need other various events that this type brings. What we add is a timer or alarm that triggers additional updates more frequently. Because we are still creating a traditional always-on app, we still need to specify the wake lock permission and the com.google.android.wearable
library requirement. Also, we must request that the activity be allowed to enter the ambient mode using the SetAmbientEnabled()
method in OnCreate
.
The simplest method to update the screen could just be to relaunch the activity. We can do this as Android provides us with a means to skip the actual launch if the activity is already visible, which will be the case. To prevent multiple instances of our activity, we can set the LaunchMode
property on the [Activity]
attribute to LaunchMode.SingleInstance
. Then, when the alarm fires, a new activity will not be created, but just the OnNewIntent()
method will be invoked on the existing activity.
In OnCreate
, we set up the AlarmManager
instance using the static FromContext()
method. And, as we don't need a dynamic intent, we can create the PendingIntent
instance that will hold the Intent
instance to launch our activity.
When the wearable enters the ambient mode, the OnEnterAmbient()
method will be invoked. Here, we will schedule the pending intent for several seconds into the future. We should make sure that we do not wake the device up more frequently than once in every 10 seconds. Waking up the device requires much more power than keeping the device awake. So, if we need the screen to get updated more frequently, we should rather not allow the device to enter the ambient mode.
To avoid unnecessary events, we should ensure that the device is in the ambient mode using the IsAmbient
property, before we schedule the alarm. Then, we must schedule an exact alarm for several seconds into the future. We can't use a repeating alarm as Android does not permit a more frequent alarm than once in a minute. Also, we must ensure that we use the RtcWakeup
alarm type so that the device will wake up when the alarm fires.
When the device wakes, the OnExitAmbient()
method will be invoked. To ensure that the alarm does not fire unnecessarily, we cancel it using the same pending intent that we used to schedule the alarm with. To prevent the activity from being relaunched when the user exits our app, we must make sure to cancel the alarm in the OnDestroy()
method.
The OnNewIntent()
method is invoked when the alarm fires. What actually happens is that the alarm fires and Android tries to launch the activity intent contained in the pending intent. When it sees that the activity is already visible, it then just invokes the OnNewIntent()
method.
The first thing we can do is to update the Intent
property of the activity to be the intent we just received. Then, we can update the screen with the information that we want to present to the user. Just before we allow the device to go back to sleep, we need to re-schedule the alarm as an exact alarm is only a once-off alarm.
One additional thing we can do to further optimize the app is to ensure that we don't allow the alarm to fire too soon after Android invokes the OnUpdateAmbient()
method. The OnUpdateAmbient()
method is still invoked every minute, because we have just added an additional mechanism to wake the device. In this method, we can update the screen and reschedule the alarm, just as we would in the OnNewIntent()
method.