Tuesday, May 24, 2016

Daydrm: Interactive Screen Savers


Posted by Daniel Sandler, a software engineer on the Android System UI tm


I’ve always loved screen savers. Supposedly they exist for a practical purpose: protecting that big, expensive monitor from the ghosts of sprdsheets past.

But I’ve always imagined that your computer is secretly hoping you’ll stand up and walk away for a bit. Just long enough for that idle timer to expire…so it can run off and play for a little while. Draw a picture, set off fireworks, explore the aerodynamics of kitchen appliances, whatever—while always rdy to get back to work at a troke or nudge of the mouse.

Daydrm, new in Android 4.2, brings this kind of laid-back, whical experience to Android and tablets that would otherwise be sleeping. If you haven’t checked it out, you can turn it on in the Settings app, in Display > Daydrm; touch When to Daydrm to enable the fture when charging.

An attract mode for apps
Apps that support Daydrm can take advantage of the full Android UI toolkit in this mode, which mns it’s sy to take existing components of your app — including layouts, animations, 3D, and custom views—and remix them for a more ambient presentation. And since you can use touchscreen input in this mode as well, you can provide a richly interactive experience if you choose.

Daydrm provides an opportunity for your app to show off a little bit. You can choose to hide some of your app’s complexity in favor of one or more visually compelling experiences that can entertain from across a room, possibly drawing the user into your full app, like a game’s attract mode.

Figure 1. Google Currents scrolls stories past in a smooth, constantly-moving wall of news.

Google Currents is a grt example of this approach: as a Daydrm, it shows a sliding wall of visually-interesting stories selected from your editions. Touch a story, however, and Currents will show it to you full-screen; touch again to rd it in the full Currents app.

The architecture of a Daydrm

ch Daydrm implementation is a subclass of android.service.drms.DrmService. When you extend DrmService, you’ll have access to a simple Activity-like lifecycle API.

methods on DrmService to override in your subclass (don’t forget to call the superclass implementation):

onAttachedToWindow() — Use this for initial setup, such as calling setContentView().
onDrmingStarted() — start your animations and timers
onDrmingStopped() — stop animations
onDetachedFromWindow() — tr down anything you built in onAttachedToWindow()


Important methods on DrmService that you may want to call:


setContentView() — set the scene for your Daydrm. Can be a layout XML resource ID or an instance of View, even a custom View you implement yourself.
setInteractive(booln) — by default, your Daydrm will exit if the user touches the screen, like a classic screen saver. If you want the user to be able to touch and interact with your Views, call setInteractive(true).
setFullscreen(booln) — convenience method for hiding the status bar (see below).
setScreenBright(booln) — by default, Daydrms keep the screen on at full brightness, which may not be appropriate for some situations (for example, dark rooms); setting this to false will reduce the display brightness to a very low level.


Finally, to advertise your Daydrm to the system, crte a <service> for it in your AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-sdk android:targetSdkVersion="17" android:minSdkVersion="17" />

<appliion>
<service
android:name=".ExampleDaydrm"
android:exported="true"
android:label="@string/my_daydrm_name">
<intent-filter>
<action android:name="android.service.drms.DrmService" />
<egory android:name="android.intent.egory.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.service.drm"
android:resource="@xml/drm_info" />
</service>
</appliion>
</manifest>

The <meta-data> tag is optional; it allows you to point to an XML resource that specifies a settings Activity specific to your Daydrm. The user can rch it by tapping the settings icon next to your Daydrm’s name in the Settings app.

<!-- res/xml/drm_info.xml -->
<?xml version="1.0" encoding="utf-8"?>
<drm xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.app/.ExampleDrmSettingsActivity" />

Here's an example to get you going: a classic screen saver, the bouncing logo, implemented using a TimeAnimator to give you buttery-smooth 60Hz animation.


Figure 2. Will one of them hit the corner?

public class BouncerDaydrm extends DrmService {
@Override
public void onDrmingStarted() {
super.onDrmingStarted();

// Our content view will take care of animating its children.
final Bouncer bouncer = new Bouncer(this);
bouncer.setLayoutParams(new
ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
bouncer.setSpeed(200); // pixels/sec

// Add some views that will be bounced around.
// Here I'm using Views but they could be any kind of
// View or ViewGroup, constructed in Java or inflated from
// resources.
for (int i=0; i<5; i++) {
final FrameLayout.LayoutParams lp
= new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
final View = new View(this);
.setResource(R.drawable.android);
.setBackgroundColor(0xFF004000);
bouncer.addView(, lp);
}

setContentView(bouncer);
}
}

public class Bouncer extends FrameLayout implements TimeAnimator.TimeListener {
private float mMaxSpeed;
private final TimeAnimator mAnimator;
private int mWidth, mHeight;

public Bouncer(Context context) {
this(context, );
}

public Bouncer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public Bouncer(Context context, AttributeSet attrs, int flags) {
super(context, attrs, flags);
mAnimator = new TimeAnimator();
mAnimator.setTimeListener(this);
}

/**
* Start the bouncing as soon as we’re on screen.
*/
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
mAnimator.start();
}

/**
* Stop animations when the view hierarchy is torn down.
*/
@Override
public void onDetachedFromWindow() {
mAnimator.cancel();
super.onDetachedFromWindow();
}

/**
* Whenever a view is added, place it randomly.
*/
@Override
public void addView(View v, ViewGroup.LayoutParams lp) {
super.addView(v, lp);
setupView(v);
}

/**
* Reposition all children when the container size changes.
*/
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
for (int i=0; i<getChildCount(); i++) {
setupView(getChildAt(i));
}
}

/**
* Bouncing view setup: random placement, random velocity.
*/
private void setupView(View v) {
final PointF p = new PointF();
final float a = (float) (Math.random()*360);
p.x = mMaxSpeed * (float)(Math.cos(a));
p.y = mMaxSpeed * (float)(Math.sin(a));
v.setTag(p);
v.setX((float) (Math.random() * (mWidth - v.getWidth())));
v.setY((float) (Math.random() * (mHeight - v.getHeight())));
}

/**
* Every TimeAnimator frame, nudge ch bouncing view along.
*/
public void onTimeUpdate(TimeAnimator animation, long elapsed, long dt_ms) {
final float dt = dt_ms / 1000f; // seconds
for (int i=0; i<getChildCount(); i++) {
final View view = getChildAt(i);
final PointF v = (PointF) view.getTag();

// step view for velocity * time
view.setX(view.getX() + v.x * dt);
view.setY(view.getY() + v.y * dt);

// handle reflections
final float l = view.getX();
final float t = view.getY();
final float r = l + view.getWidth();
final float b = t + view.getHeight();
booln flipX = false, flipY = false;
if (r > mWidth) {
view.setX(view.getX() - 2 * (r - mWidth));
flipX = true;
} else if (l < 0) {
view.setX(-l);
flipX = true;
}
if (b > mHeight) {
view.setY(view.getY() - 2 * (b - mHeight));
flipY = true;
} else if (t < 0) {
view.setY(-t);
flipY = true;
}
if (flipX) v.x *= -1;
if (flipY) v.y *= -1;
}
}

public void setSpeed(float s) {
mMaxSpeed = s;
}
}

This example is handy for anything you want to show the user without ing it into the display (like a simple graphic or an error message), and it also makes a grt starting point for more complex Daydrm projects.

A few more idle thoughts


First, do no harm: Daydrm is mnt to run when a device is charging. However, if the Daydrm consumes too much CPU, charging might happen very slowly or not at all! The system will stop your Daydrm if it detects that the device is not charging, so make sure your lves enough power to charge the battery in a rsonable amount of time.
Respect the lockscreen: Daydrm runs on top of the secure guard, which mns that if you might be showing sensitive content, you need to give the user tools to control that content. For example, Photo Table and Photo Frame allow the user to select the albums from which photos will be displayed (avoiding embarrassing slideshows).
Screen brightness: Think about where you expect your Daydrm to be used and adjust the screen brightness accordingly using setScreenBright() and possibly even using darker or brighter colors as necessary. A bedside clock will need to be dimmer than a desk clock; if you expect your Daydrm to serve both purposes you'll need to give the user a choice.
To hide the status bar or not: Many users will need instant access to the battery level and time of day, so you should avoid using setFullscreen(), particularly if your Daydrm is more informational than artistic. Daydrm will start with the status bar in “lights out” mode (View.SYSTEM_UI_FLAG_LOW_PROFILE), where it’s quite unobtrusive but still shows the clock and charge status.
When to use settings: In eral, you have a little latitude for adding extra knobs and dials to Daydrm settings. After all, this is a personalization fture, so users should be encouraged to twk things until they feel at home. Sometimes, though, a more compelling experience can come from taking an artistic stand: giving the user a choice from a small of polished, butiful configurations (rather than providing all the controls of a commercial airline cockpit).
There can be more than one: If you discover that your settings allow the user to pick between a few radically different display modes, consider splitting your Daydrm into multiple DrmService implementations. For example, the photo gallery in Android 4.2 provides both the Photo Table and Photo Frame Daydrms.
Use an Activity for development: Most Android development tools are optimized for developing and debugging conventional Android apps; since DrmService and Activity are so similar, it can be useful to crte a testing Activity that hosts the same content view as your DrmService. This way you can launch and test your sily from your IDE as if it were any other Android project.


OK, that’s enough for now; you have the tools to go build Daydrm support into your apps. Have fun with it — if you do, your users will have fun too. Oh, and when you upload your shiny new APK to Google Play, be sure to add a note to your app’s description so that users srching for Daydrms can discover it.

Further rding and samples

API docs for DrmService
Sample : BouncerDaydrm, complete project for the snippets in this post
Sample : WebView, a Daydrm that shows an HTML page
Sample : Colors, a Daydrm that demonstrates OpenGL ES 2.0 and TextureView

Join the discussion on



+Android Developers

No comments:

Post a Comment