Creating dynamic always-on wearable apps

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.

How to do it...

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:

  1. We will need the wake lock permission on both, the handheld and wearable:
    [assembly: UsesPermission(Manifest.Permission.WakeLock)]
  2. If we want to support older wearables, we will need to set the wearable library as optional:
    [assembly: UsesLibrary(
      "com.google.android.wearable", false)]
  3. Next, we need to inherit it from WearableActivity:
    public class MainActivity : WearableActivity
  4. We are going to use the alarm manager to send us an 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:

  1. In OnCreate, we request ambient mode notifications by invoking the SetAmbientEnabled() method:
    SetAmbientEnabled();
  2. As we need to schedule an alarm, we get an instance of AlarmManager:
    alarmManager = AlarmManager.FromContext(this);
  3. Because we are just going to request that the alarm sends an 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);
  4. Now, we create a method to schedule our once-off alarm to fire several seconds in the future. We use the 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);
      }
    }
  5. When we enter the ambient mode, we schedule the alarm:
    public override void OnEnterAmbient(Bundle ambientDetails)
    {
      base.OnEnterAmbient(ambientDetails);
      ScheduleAlarm();
    }
  6. After the alarm manager sends the intent, we will receive it in the 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();
    }
  7. When we leave the ambient mode, we must make sure to cancel the alarm:
    public override void OnExitAmbient() {
      base.OnExitAmbient();
      alarmManager.Cancel(alarmPendingIntent);
    }
  8. We need to do the same for 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:

  • To avoid unnecessary wake operations, we can move the scheduled alarm to the next interval:
    public override void OnUpdateAmbient() {
      base.OnUpdateAmbient();
      // update the ambient UI
      ScheduleAlarm();
    }

How it works...

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.

Note

If an activity is set to only exist as a single instance, any attempt to start a new instance will get redirected to the OnNewIntent() method of this 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.

Tip

When in the ambient mode, the device should not be woken up more than once every 10 seconds, but should rather acquire a normal wakelock to prevent the device from going to sleep altogether.

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.

Tip

For intervals that are shorter than one minute, an exact alarm must be scheduled instead of a repeating alarm.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset