Last active
August 29, 2015 14:03
-
-
Save blundell/bba18a128214d5fa1250 to your computer and use it in GitHub Desktop.
Unofficial Base WatchFace Listener
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
Good work! The best implementation of the ones I've seen so far!
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 afteronDestroy()
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
Thanks, hmm I can't see displayManager being null, perhaps getDisplay(id) returns null - the javadoc states "returns The display object, or null if there is no valid display with the given id." So that null check should be added! Nice spot, I'll add that check now. If there is no display for that id would you want a screen off callback perhaps?