Skip to content

Instantly share code, notes, and snippets.

@blundell
Last active August 29, 2015 14:03
Show Gist options
  • Save blundell/bba18a128214d5fa1250 to your computer and use it in GitHub Desktop.
Save blundell/bba18a128214d5fa1250 to your computer and use it in GitHub Desktop.
Unofficial Base WatchFace Listener
public class ExampleActivity extends Activity implements WatchFaceLifecycle.Listener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_layout);
WatchFaceLifecycle.attach(this, savedInstanceState, this);
}
@Override
public void onScreenDim() {
}
@Override
public void onScreenAwake() {
}
@Override
public void onWatchFaceRemoved() {
}
@Override
public void onScreenOff() {
}
}
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.view.Display;
/**
* A listener for watch faces that have callbacks for the various screen states (dim, awake, and off)
* as well as a callback for when the watch face is removed.
* <p/>
* Prefer composition over inheritance
* <p/>
* This was created by Paul Blundell based on the Unofficial Base WatchFace Activity by Tavon Gatling
* https://gist.github.com/blundell/bba18a128214d5fa1250
* https://gist.github.com/kentarosu/52fb21eb92181716b0ce
* http://gist.github.com/PomepuyN/cdd821eca163a3279de2.
*/
public class WatchFaceLifecycle {
interface Listener {
/**
* Used to detect when the watch is dimming.<br/>
* Remember to make gray-scale versions of your watch face so they won't burn
* and drain battery unnecessarily.
*/
void onScreenDim();
/**
* Used to detect when the watch is not in a dimmed state.<br/>
* This does not handle when returning "home" from a different activity/application.
*/
void onScreenAwake();
/**
* Used to detect when a watch face is being removed (switched).<br/>
* You can either do what you need here, or simply override {@code onDestroy()}.
*/
void onWatchFaceRemoved();
/**
* When the screen is OFF due to "Always-On" being disabled.
*/
void onScreenOff();
}
private WatchFaceLifecycle() {
}
public static void attach(Activity activity, Bundle savedInstanceState, Listener listener) {
RealWatchFaceLifecycleCallbacks.newInstance(activity, savedInstanceState, listener);
}
private static class RealWatchFaceLifecycleCallbacks implements Application.ActivityLifecycleCallbacks, DisplayManager.DisplayListener {
private final DisplayManager displayManager;
private final Listener listener;
public RealWatchFaceLifecycleCallbacks(DisplayManager displayManager, Listener listener) {
this.displayManager = displayManager;
this.listener = listener;
}
private static void newInstance(Activity activity, Bundle savedInstanceState, Listener listener) {
DisplayManager displayManager = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
RealWatchFaceLifecycleCallbacks watchFace = new RealWatchFaceLifecycleCallbacks(displayManager, listener);
activity.getApplication().registerActivityLifecycleCallbacks(watchFace);
watchFace.onActivityCreated(activity, savedInstanceState);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// Set up the display manager and register a listener (this activity).
displayManager.registerDisplayListener(this, null);
}
@Override
public void onActivityStarted(Activity activity) {
// not used
}
@Override
public void onActivityResumed(Activity activity) {
// not used
}
@Override
public void onDisplayAdded(int displayId) {
// In testing, this was never called, so the listener for this was removed.
}
@Override
public void onDisplayRemoved(int displayId) {
listener.onWatchFaceRemoved();
}
@Override
public void onDisplayChanged(int displayId) {
Display display = displayManager.getDisplay(displayId);
if(display == null) {
// No display found for this ID, treating this as an onScreenOff() but you could remove this line
// and swallow this exception quietly. What circumstance means 'there is no display for this id'?
listener.onScreenOff();
return;
}
switch (display.getState()) {
case Display.STATE_DOZING:
listener.onScreenDim();
break;
case Display.STATE_OFF:
listener.onScreenOff();
break;
default:
// Not really sure what to so about Display.STATE_UNKNOWN, so we'll treat it as if the screen is normal.
listener.onScreenAwake();
break;
}
}
@Override
public void onActivityPaused(Activity activity) {
// not used
}
@Override
public void onActivityStopped(Activity activity) {
// not used
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
// not used
}
@Override
public void onActivityDestroyed(Activity activity) {
activity.getApplication().unregisterActivityLifecycleCallbacks(this);
// Unregister the listener. If you don't, even after the watch face is gone, it will still accept your callbacks.
displayManager.unregisterDisplayListener(this);
}
}
}
@murphy2712
Copy link

I noticed that onDisplayAdded() is actually called on rare occasions, probably a race condition meaning the callback happens before you register.
Same problem for onDisplayRemoved(), I get it more often but not every time, it is unregistered before being triggered.
I was able to confirm these by commenting displayManager.unregisterDisplayListener():

  • onDisplayAdded() is called every time I switch with the same face (stays registered),
  • onDisplayRemoved() is called every time, sometimes right after onDestroy() and sometimes 1.8 seconds after!

I didn't find good enough solutions, I can't register before the first line of the Activity onCreate(), and delaying unregisterDisplayListener() can give weird callbacks.
I suppose we're better off relying on the Activity onCreate() and onDestroy() to start/stop things.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment