diff options
23 files changed, 1633 insertions, 0 deletions
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 7a0e4138595c..f883e25b3191 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -834,6 +834,38 @@ include the action bar on devices running Android 2.1 or higher." </li> </ul> </li> + + <li class="nav-section"> + <div class="nav-section-header"> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/index.html" + description="How to create custom watch faces for wearables." + >Creating Custom Watch Faces</a> + </div> + <ul> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/designing.html">Designing Watch Faces</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/service.html">Building a Watch Face Service</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/drawing.html">Drawing Watch Faces</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/information.html">Showing Information in Watch Faces</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/configuration.html">Providing Configuration Activities</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/issues.html">Addressing Common Issues</a> + </li> + <li> + <a href="<?cs var:toroot ?>training/wearables/watch-faces/performance.html">Optimizing Performance and Battery Life</a> + </li> + </ul> + </li> + <li> <a href="<?cs var:toroot ?>training/articles/wear-location-detection.html" description= diff --git a/docs/html/training/wearables/watch-faces/configuration.jd b/docs/html/training/wearables/watch-faces/configuration.jd new file mode 100644 index 000000000000..edc7eac54f65 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/configuration.jd @@ -0,0 +1,144 @@ +page.title=Providing Configuration Activities + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#Intent">Specify an Intent for Configuration Activities</a></li> + <li><a href="#WearableActivity">Create a Wearable Configuration Activity</a></li> + <li><a href="#CompanionActivity">Create a Companion Configuration Activity</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>When users install a handheld app that contains a <a +href="{@docRoot}training/wearables/apps/index.html">wearable app</a> with watch faces, these +watch faces become available in the Android Wear companion app on the companion device and in +the watch face picker on the wearable. Users can choose the active watch face for their wearable +device by selecting it on the companion app or using the watch face picker on the wearable +device.</p> + +<p>Some watch faces support configuration parameters to let users customize how the watch face +looks and behaves. For example, some watch faces let users pick a custom background color, and +watch faces that tell time for two different time zones can let users select which time zones +they are interested in.</p> + +<p>Watch faces that support configuration parameters can let users customize a watch face using +an activity in the wearable app, an activity on the handheld app, or both. Users can start the +wearable configuration activity on the wearable device, and they can start the companion +configuration activity from the Android Wear companion app.</p> + +<p>The digital watch face from the <em>WatchFace</em> sample in the Android SDK demonstrates how to +implement handheld and wearable configuration activities and how to update a watch face in +response to configuration changes. This sample is located in the +<code>android-sdk/samples/android-21/wearable/WatchFace</code> directory.</p> + + + +<h2 id="Intent">Specify an Intent for Configuration Activities</h2> + +<p>If your watch face includes configuration activities, add the following metadata entries to +the service declaration in the manifest file of the wearable app:</p> + +<pre> +<service + android:name=".DigitalWatchFaceService" ... /> + <!-- companion configuration activity --> + <meta-data + android:name= + "com.google.android.wearable.watchface.companionConfigurationAction" + android:value= + "com.example.android.wearable.watchface.CONFIG_DIGITAL" /> + <!-- wearable configuration activity --> + <meta-data + android:name= + "com.google.android.wearable.watchface.wearableConfigurationAction" + android:value= + "com.example.android.wearable.watchface.CONFIG_DIGITAL" /> + ... +</service> +</pre> + +<p>Provide values for these entries that are preceded by the package name of your app. +Configuration activities register intent filters for this intent, and the system fires this +intent when users want to configure your watch face.</p> + +<p>If your watch face only includes a companion or a wearable configuration activity, you only +need to include the corresponding metadata entry from the example above.</p> + + + +<h2 id="WearableActivity">Create a Wearable Configuration Activity</h2> + +<p>Wearable configuration activities provide a limited set of customization choices for a +watch face, because complex menus are hard to navigate on smaller screens. Your wearable +configuration activity should provide binary choices and just a few selections to customize +the main aspects of your watch face.</p> + +<p>To create a wearable configuration activity, add a new activity to your wearable app module +and declare the following intent filter in the manifest file of the wearable app:</p> + +<pre> +<activity + android:name=".DigitalWatchFaceWearableConfigActivity" + android:label="@string/digital_config_name"> + <intent-filter> + <action android:name= + "com.example.android.wearable.watchface.CONFIG_DIGITAL" /> + <category android:name= + "com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> +</activity> +</pre> + +<p>The name of the action in this intent filter must match the intent name you defined in +<a href="#Intent">Specify an Intent for Configuration Activities</a>.</p> + +<p>In your configuration activity, build a simple UI that provides selections for users to +customize your watch face. When users make a selection, use the <a +href="{@docRoot}training/wearables/data-layer/index.html">Wearable Data Layer API</a> to +communicate the configuration change to the watch face activity.</p> + +<p>For more details, see the <code>DigitalWatchFaceWearableConfigActivity</code> and +<code>DigitalWatchFaceUtil</code> classes in the <em>WatchFace</em> sample.</p> + + + +<h2 id="CompanionActivity">Create a Companion Configuration Activity</h2> + +<p>Companion configuration activities give users access to the full set of configuration choices +for a watch face, because it is easier to interact with complex menus on the larger screen of +a handheld device. For example, a configuration activity on a handheld device enables you to +present users with elaborate color pickers to select the background color of a watch face.</p> + +<p>To create a companion configuration activity, add a new activity to your handheld app module and +declare the same intent filter for this activity as the one in <a href="#WearableActivity">Create +a Wearable Configuration Activity</a>.</p> + +<p>In your configuration activity, build a UI that provides options to customize all the +configurable elements of your watch face. When users make a selection, use the <a +href="{@docRoot}training/wearables/data-layer/index.html">Wearable Data Layer API</a> to +communicate the configuration change to the watch face activity.</p> + +<p>For more details, see the <code>DigitalWatchFaceCompanionConfigActivity</code> class in the +<em>WatchFace</em> sample.</p> + + + +<h2 id="Listener">Create a Listener Service in the Wearable App</h2> + +<p>To receive updated configuration parameters from the configuration activities, create a +service that implements the <code>WearableListenerService</code> interface from the <a +href="{@docRoot}training/wearables/data-layer/index.html">Wearable Data Layer API</a> in your +wearable app. Your watch face implementation can redraw the watch face when the configuration +parameters change.</p> + +<p>For more details, see the <code>DigitalWatchFaceConfigListenerService</code> and +<code>DigitalWatchFaceService</code> classes in the <em>WatchFace</em> sample.</p> diff --git a/docs/html/training/wearables/watch-faces/designing.jd b/docs/html/training/wearables/watch-faces/designing.jd new file mode 100644 index 000000000000..b7fcfd42382c --- /dev/null +++ b/docs/html/training/wearables/watch-faces/designing.jd @@ -0,0 +1,108 @@ +page.title=Designing Watch Faces + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#DesignGuidelines">Conform to the Design Guidelines</a></li> + <li><a href="#ImplementationStrategy">Create an Implementation Strategy</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>Similar to the process of designing a traditional watch face, creating one for +Android Wear is an exercise in visualizing time clearly. Android Wear devices +provide advanced capabilities for watch faces that you can leverage in your designs, such as +vibrant colors, dynamic backgrounds, animations, and data integration. However, there are +also many design considerations that you must take into account.</p> + +<p>This lesson provides a summary of the design considerations for watch faces and general +guidelines to get started implementing a design. For more information, read the <a +href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a> design guide.</p> + + + +<h2 id="DesignGuidelines">Conform to the Design Guidelines</h2> + +<p>As you plan the look of your watch face and what kind of information it should present +to users, consider these design guidelines:</p> + +<div style="float:right;margin-top:-5px;margin-left:20px"> + <img src="{@docRoot}training/wearables/watch-faces/images/Render_Next.png" + width="200" height="195" alt="" style="margin-right:5px"/><br> + <img src="{@docRoot}training/wearables/watch-faces/images/Render_Interactive.png" + width="200" height="195" alt="" style="margin-right:5px"/> + <p class="img-caption" style="margin-top:0px;margin-left:10px"> + <strong>Figure 1.</strong> Example watch faces.</p> +</div> + +<dl> +<dt><em>Plan for square and round devices</em></dt> +<dd>Your design should work for both square and round Android Wear devices, including devices with +<a href="{@docRoot}training/wearables/ui/layouts.html#same-layout">insets on the bottom of the +screen</a>.</dd> + +<dt><em>Support all display modes</em></dt> +<dd>Your watch face should support ambient mode with limited color and interactive mode with +full color and animations.</dd> + +<dt><em>Optimize for special screen technologies</em></dt> +<dd>In ambient mode, your watch face should keep most pixels black. Depending on the screen +technology, you may need to avoid large blocks of white pixels, use only black and white, and +disable anti-aliasing.</dd> + +<dt><em>Accomodate system UI elements</em></dt> +<dd>Your design should ensure that system indicators remain visible and that users can still +read the time when notification cards appear on the screen.</dd> + +<dt><em>Integrate data</em></dt> +<dd>Your watch face can leverage sensors and cellular connectivity on the companion mobile +device to show user data relevant to the context, such as the weather for the day or their next +calendar event.</dd> + +<dt><em>Provide configuration options</em></dt> +<dd>You can let users configure some aspects of your design (like colors and sizes) on the +wearable or on the Android Wear companion app.</dd> +</dl> + +<p>For more information about designing watch faces for Android Wear, see the <a +href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a> design guide.</p> + + + +<h2 id="ImplementationStrategy">Create an Implementation Strategy</h2> + +<p>After you finalize the design for your watch face, you need to determine how to obtain any +necessary data and draw the watch face on the wearable device. Most implementations +consist of the following components:</p> + +<ul> +<li>One or more background images</li> +<li>Application code to retrieve the required data</li> +<li>Application code to draw text and shapes over the background images</li> +</ul> + +<p>You typically use one background image in interactive mode and a different background image +in ambient mode. The background in ambient mode is often completely black. Background images for +Android Wear devices with a screen density of hdpi should be 320 by 320 pixels in size to fit +both square and round devices. The corners of the background image are not visible on round +devices. In your code, you can detect the size of the device screen and scale down the background +image if the device has a lower resolution than your image. To improve performance, you should +scale the background image only once and store the resulting bitmap.</p> + +<p>You should run the application code to retrieve contextual data only as often as required +and store the results to reuse the data every time you draw the watch face. For example, you +don't need to fetch weather updates every minute.</p> + +<p>To increase battery life, the application code that draws your watch face in ambient mode +should be relatively simple. You usually draw outlines of shapes using a limited set of colors +in this mode. In interactive mode, you can use full color, complex shapes, gradients, and +animations to draw your watch face.</p> + +<p>The remaining lessons in this class show you how to implement watch faces in detail.</p> diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd new file mode 100644 index 000000000000..9afdd80fa276 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/drawing.jd @@ -0,0 +1,509 @@ +page.title=Drawing Watch Faces + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#Initialize">Initialize Your Watch Face</a></li> + <li><a href="#SystemUI">Configure the System UI</a></li> + <li><a href="#Screen">Obtain Information About the Device Screen</a></li> + <li><a href="#Modes">Respond to Changes Between Modes</a></li> + <li><a href="#Drawing">Draw Your Watch Face</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>After you have configured your project and added a class that implements the watch +face service, you can start writing code to initialize and draw your custom watch face.</p> + +<p>This lesson explains how the system invokes the methods in the +watch face service using examples from the <em>WatchFace</em> sample +included in the Android SDK. This sample is located in the +<code>android-sdk/samples/android-21/wearable/WatchFace</code> directory. Many aspects of the +service implementations described here (such as initialization and detecting device features) +apply to any watch face, so you can reuse some of the code in your own watch faces.</p> + + +<img src="{@docRoot}training/wearables/watch-faces/images/preview_analog.png" + width="180" height="180" alt="" style="margin-top:12px"/> +<img src="{@docRoot}training/wearables/watch-faces/images/preview_digital.png" + width="180" height="180" alt="" style="margin-left:25px;margin-top:12px"/> +<p class="img-caption"> +<strong>Figure 1.</strong> The analog and digital watch faces in +the <em>WatchFace</em> sample.</p> + + +<h2 id="Initialize">Initialize Your Watch Face</h2> + +<p>When the system loads your service, you should allocate and initialize most of the resources +that your watch face needs, including loading bitmap resources, creating timer objects to run +custom animations, configuring paint styles, and performing other computations. You can usually +perform these operations only once and reuse their results. This practice improves the performance +of your watch face and makes it easier to maintain your code.</p> + +<p>To initialize your watch face, follow these steps:</p> + +<ol> +<li>Declare variables for a custom timer, graphic objects, and other elements.</li> +<li>Initialize the watch face elements in the <code>Engine.onCreate()</code> method.</li> +<li>Initialize the custom timer in the <code>Engine.onVisibilityChanged()</code> method.</li> +</ol> + +<p>The following sections describe these steps in detail.</p> + +<h3 id="Variables">Declare variables</h3> + +<p>The resources that you intialize when the system loads your service need to be accessible +at different points throughout your implementation, so you can reuse them. You achieve this +by declaring member variables for these resources in your <code>WatchFaceService.Engine</code> +implementation.</p> + +<p>Declare variables for the following elements:</p> + +<dl> +<dt><em>Graphic objects</em></dt> +<dd>Most watch faces contain at least one bitmap image used as the background of the watch face, +as described in +<a href="{@docRoot}training/wearables/watch-faces/designing.html#ImplementationStrategy">Create an +Implementation Strategy</a>. You can use additional bitmap images that represent clock hands or +other design elements of your watch face.</dd> +<dt><em>Periodic timer</em></dt> +<dd>The system notifies the watch face once a minute when the time changes, but some watch faces +run animations at custom time intervals. In these cases, you need to provide a custom timer that +ticks with the frequency required to update your watch face.</dd> +<dt><em>Time zone change receiver</em></dt> +<dd>Users can adjust their time zone when they travel, and the system broadcasts this event. +Your service implementation must register a broadcast receiver that is notified when the time +zone changes and update the time accordingly.</dd> +</dl> + +<p>The <code>AnalogWatchFaceService.Engine</code> class in the <em>WatchFace</em> sample defines +these variables as shown in the snippet below. The custom timer is implemented as a +{@link android.os.Handler} instance that sends and processes delayed messages using the thread's +message queue. For this particular watch face, the custom timer ticks once every second. When the +timer ticks, the handler calls the <code>invalidate()</code> method and the system then calls +the <code>onDraw()</code> method to redraw the watch face.</p> + +<pre> +private class Engine extends CanvasWatchFaceService.Engine { + static final int MSG_UPDATE_TIME = 0; + + /* a time object */ + Time mTime; + + /* device features */ + boolean mLowBitAmbient; + + /* graphic objects */ + Bitmap mBackgroundBitmap; + Bitmap mBackgroundScaledBitmap; + Paint mHourPaint; + Paint mMinutePaint; + ... + + /* handler to update the time once a second in interactive mode */ + final Handler mUpdateTimeHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_UPDATE_TIME: + invalidate(); + if (shouldTimerBeRunning()) { + long timeMs = System.currentTimeMillis(); + long delayMs = INTERACTIVE_UPDATE_RATE_MS + - (timeMs % INTERACTIVE_UPDATE_RATE_MS); + mUpdateTimeHandler + .sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs); + } + break; + } + } + }; + + /* receiver to update the time zone */ + final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mTime.clear(intent.getStringExtra("time-zone")); + mTime.setToNow(); + } + }; + + /* service methods (see other sections) */ + ... +} +</pre> + +<h3 id="InitializeElements">Initialize watch face elements</h3> + +<p>After you have declared member variables for bitmap resources, paint styles, and other +elements that you reuse every time your redraw your watch face, initialize them when the system +loads your service. Initializing these elements only once and reusing them improves performance +and battery life.</p> + +<p>In the <code>Engine.onCreate()</code> method, initialize the following elements:</p> + +<ul> +<li>Load the background image.</li> +<li>Create styles and colors to draw graphic objects.</li> +<li>Allocate an object to hold the time.</li> +<li>Configure the system UI.</li> +</ul> + +<p>The <code>Engine.onCreate()</code> method in the <code>AnalogWatchFaceService</code> class +initializes these elements as follows:</p> + +<pre> +@Override +public void onCreate(SurfaceHolder holder) { + super.onCreate(holder); + + /* configure the system UI (see next section) */ + ... + + /* load the background image */ + Resources resources = AnalogWatchFaceService.this.getResources(); + Drawable backgroundDrawable = resources.getDrawable(R.drawable.bg); + mBackgroundBitmap = ((BitmapDrawable) backgroundDrawable).getBitmap(); + + /* create graphic styles */ + mHourPaint = new Paint(); + mHourPaint.setARGB(255, 200, 200, 200); + mHourPaint.setStrokeWidth(5.0f); + mHourPaint.setAntiAlias(true); + mHourPaint.setStrokeCap(Paint.Cap.ROUND); + ... + + /* allocate an object to hold the time */ + mTime = new Time(); +} +</pre> + +<p>The background bitmap is loaded only once when the system initializes the watch face. The +graphic styles are instances of the {@link android.graphics.Paint} class. You later use these +styles to draw the elements of your watch face inside the <code>Engine.onDraw()</code> method, +as described in <a href="#Drawing">Drawing Your Watch Face</a>.</p> + +<h3 id="Timer">Initialize the custom timer</h3> + +<p>As a watch face developer, you can decide how often you want to update your watch face by +providing a custom timer that ticks with the required frequency while the device is in +interactive mode. This enables you to create custom animations and other visual effects. In +ambient mode, you should disable the timer to let the CPU sleep and update the watch face +only when the time changes. For more information, see +<a href="{@docRoot}training/wearables/watch-faces/performance.html">Optimizing Performance and +Battery Life</a>.</p> + +<p>An example timer definition from the <code>AnalogWatchFaceService</code> class that ticks once +every second is shown in <a href="#Variables">Declare variables</a>. In the +<code>Engine.onVisibilityChanged()</code> method, start the custom timer if these two +conditions apply:</p> + +<ul> +<li>The watch face is visible.</li> +<li>The device is in interactive mode.</li> +</ul> + +<p>The timer should not run under any other conditions, since this watch face does not +draw the second hand in ambient mode to conserve power. The <code>AnalogWatchFaceService</code> +class schedules the next timer tick if required as follows:</p> + +<pre> +private void updateTimer() { + mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME); + if (shouldTimerBeRunning()) { + mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME); + } +} + +private boolean shouldTimerBeRunning() { + return isVisible() && !isInAmbientMode(); +} +</pre> + +<p>This custom timer ticks once every second, as described in <a href="#Variables">Declare +variables</a>.</p> + +<p>In the <code>Engine.onVisibilityChanged()</code> method, start the timer if required and +and register the receiver for time zone changes as follows:</p> + +<pre> +@Override +public void onVisibilityChanged(boolean visible) { + super.onVisibilityChanged(visible); + + if (visible) { + registerReceiver(); + + // Update time zone in case it changed while we weren't visible. + mTime.clear(TimeZone.getDefault().getID()); + mTime.setToNow(); + } else { + unregisterReceiver(); + } + + // Whether the timer should be running depends on whether we're visible and + // whether we're in ambient mode), so we may need to start or stop the timer + updateTimer(); +} +</pre> + +<p>When the watch face is visible, the <code>onVisibilityChanged()</code> method registers +the receiver for time zone changes and starts the custom timer if the device is in interactive +mode. When the watch face is not visible, this method stops the custom timer and unregisters +the receiver for time zone changes. The <code>registerReceiver()</code> and +<code>unregisterReceiver()</code> methods are implemented as follows:</p> + +<pre> +private void registerReceiver() { + if (mRegisteredTimeZoneReceiver) { + return; + } + mRegisteredTimeZoneReceiver = true; + IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED); + AnalogWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter); +} + +private void unregisterReceiver() { + if (!mRegisteredTimeZoneReceiver) { + return; + } + mRegisteredTimeZoneReceiver = false; + AnalogWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver); +} +</pre> + + + +<h3 id="TimeTick">Invalidate the canvas when the time changes</h3> + +<p>The system calls the <code>Engine.onTimeTick()</code> method every minute. In ambient mode, +it is usually sufficient to update your watch face once per minute. To update your watch face +more often while in interactive mode, you provide a custom timer as described in +<a href="#Timer">Initialize the custom timer</a>.</p> + +<p>Most watch face implementations just invalidate the canvas to redraw the watch face when +the time changes:</p> + +<pre> +@Override +public void onTimeTick() { + super.onTimeTick(); + + invalidate(); +} +</pre> + + + +<h2 id="SystemUI">Configure the System UI</h2> + +<p>Watch faces should not interfere with system UI elements, as described in +<a href="{@docRoot}design/wear/watchfaces.html#SystemUI">Accommodate System UI Elements</a>. +If your watch face has a light background or shows information near the bottom of the screen, +you may have to configure the size of notification cards or enable background protection.</p> + +<p>Android Wear enables you to configure the following aspects of the system UI when your watch +face is active:</p> + +<ul> +<li>Specify how far the first notification card peeks into the screen.</li> +<li>Specify whether the system draws the time over your watch face.</li> +<li>Show or hide cards when in ambient mode.</li> +<li>Protect the system indicators with a solid background around them.</li> +<li>Specify the positioning of the system indicators.</li> +</ul> + +<p>To configure these aspects of the system UI, create a <code>WatchFaceStyle</code> instance +and pass it to the <code>Engine.setWatchFaceStyle()</code> method.</p> + +<p>The <code>AnalogWatchFaceService</code> class configures the system UI as follows:</p> + +<pre> +@Override +public void onCreate(SurfaceHolder holder) { + super.onCreate(holder); + + /* configure the system UI */ + setWatchFaceStyle(new WatchFaceStyle.Builder(AnalogWatchFaceService.this) + .setCardPeekMode(WatchFaceStyle.PEEK_MODE_SHORT) + .setBackgroundVisibility(WatchFaceStyle + .BACKGROUND_VISIBILITY_INTERRUPTIVE) + .setShowSystemUiTime(false) + .build()); + ... +} +</pre> + +<p>The code snippet above configures peeking cards to be a single line tall, the background +of a peeking card to show only briefly and only for interruptive notifications, and the system +time not to be shown (since this watch face draws its own time representation).</p> + +<p>You can configure the style of the system UI at any point in your watch face implementation. +For example, if the user selects a white background, you can add background protection for the +system indicators.</p> + +<p>For more details about configuring the system UI, see the +<a href="{@docRoot}shareables/training/wearable-support-docs.zip">API reference</a> for the +<code>WatchFaceStyle</code> class.</p> + + + +<h2 id="Screen">Obtain Information About the Device Screen</h2> + +<p>The system calls the <code>Engine.onPropertiesChanged()</code> method when it determines +the properties of the device screen, such as whether the device uses low-bit ambient mode and +whether the screen requires burn-in protection.</p> + +<p>The following code snippet shows how to obtain these properties:</p> + +<pre> +@Override +public void onPropertiesChanged(Bundle properties) { + super.onPropertiesChanged(properties); + mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false); + mBurnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, + false); +} +</pre> + +<p>You should take these device properties into account when drawing your watch face:</p> + +<ul> +<li>For devices that use low-bit ambient mode, the screen supports fewer bits for each color +in ambient mode, so you should disable anti-aliasing.</li> +<li>For devices that require burn-in protection, avoid using large blocks of white pixels in +ambient mode and do not place content within 10 pixels of the edge of the screen, since the +system shifts the content periodically to avoid pixel burn-in.</li> +</ul> + +<p>For more information about low-bit ambient mode and burn-in protection, see +<a href="{@docRoot}design/wear/watchfaces.html#SpecialScreens">Optimize for Special +Screens</a>.</p> + + +<h2 id="Modes">Respond to Changes Between Modes</h2> + +<p>When the device switches between ambient and interactive modes, the system calls the +<code>Engine.onAmbientModeChanged()</code> method. Your service implementation should make +any necessary adjustments to switch between modes and then call the <code>invalidate()</code> +method for the system to redraw the watch face.</p> + +<p>The following snippet shows how this method is implemented in the +<code>AnalogWatchFaceService</code> class inside the <em>WatchFace</em> sample:</p> + +<pre> +@Override +public void onAmbientModeChanged(boolean inAmbientMode) { + + boolean wasInAmbientMode = isInAmbientMode(); + super.onAmbientModeChanged(inAmbientMode); + + if (inAmbientMode != wasInAmbientMode) { + if (mLowBitAmbient) { + boolean antiAlias = !inAmbientMode; + mHourPaint.setAntiAlias(antiAlias); + mMinutePaint.setAntiAlias(antiAlias); + mSecondPaint.setAntiAlias(antiAlias); + mTickPaint.setAntiAlias(antiAlias); + } + invalidate(); + updateTimer(); + } +} +</pre> + +<p>This example makes adjustments to some graphic styles and invalidates the canvas so the +system can redraw the watch face.</p> + + + +<h2 id="Drawing">Draw Your Watch Face</h2> + +<p>To draw a custom watch face, the system calls the <code>Engine.onDraw()</code> method with a +{@link android.graphics.Canvas} instance and the bounds in which you should draw your watch face. +The bounds account for any inset areas, such as the "chin" on the bottom of some round devices. +You can use this canvas to draw your watch face directly as follows:</p> + +<ol> +<li>If this is the first invocation of the <code>onDraw()</code> method, scale your background +to fit.</li> +<li>Check whether the device is in ambient mode or interactive mode.</li> +<li>Perform any required graphic computations.</li> +<li>Draw your background bitmap on the canvas.</li> +<li>Use the methods in the {@link android.graphics.Canvas} class to draw your watch face.</li> +</ol> + +<p>The <code>AnalogWatchFaceService</code> class in the <em>WatchFace</em> sample follows these +steps to implement the <code>onDraw()</code> method as follows:</p> + +<pre> +@Override +public void onDraw(Canvas canvas, Rect bounds) { + // Update the time + mTime.setToNow(); + + int width = bounds.width(); + int height = bounds.height(); + + // Draw the background, scaled to fit. + if (mBackgroundScaledBitmap == null + || mBackgroundScaledBitmap.getWidth() != width + || mBackgroundScaledBitmap.getHeight() != height) { + mBackgroundScaledBitmap = Bitmap.createScaledBitmap(mBackgroundBitmap, + width, height, true /* filter */); + } + canvas.drawBitmap(mBackgroundScaledBitmap, 0, 0, null); + + // Find the center. Ignore the window insets so that, on round watches + // with a "chin", the watch face is centered on the entire screen, not + // just the usable portion. + float centerX = width / 2f; + float centerY = height / 2f; + + // Compute rotations and lengths for the clock hands. + float secRot = mTime.second / 30f * (float) Math.PI; + int minutes = mTime.minute; + float minRot = minutes / 30f * (float) Math.PI; + float hrRot = ((mTime.hour + (minutes / 60f)) / 6f ) * (float) Math.PI; + + float secLength = centerX - 20; + float minLength = centerX - 40; + float hrLength = centerX - 80; + + // Only draw the second hand in interactive mode. + if (!mAmbient) { + float secX = (float) Math.sin(secRot) * secLength; + float secY = (float) -Math.cos(secRot) * secLength; + canvas.drawLine(centerX, centerY, centerX + secX, centerY + + secY, mSecondPaint); + } + + // Draw the minute and hour hands. + float minX = (float) Math.sin(minRot) * minLength; + float minY = (float) -Math.cos(minRot) * minLength; + canvas.drawLine(centerX, centerY, centerX + minX, centerY + minY, + mMinutePaint); + float hrX = (float) Math.sin(hrRot) * hrLength; + float hrY = (float) -Math.cos(hrRot) * hrLength; + canvas.drawLine(centerX, centerY, centerX + hrX, centerY + hrY, + mHourPaint); +} +</pre> + +<p>This method computes the required positions for the clock hands based on the current time +and draws them on top of the background bitmap using the graphic styles initialized in the +<code>onCreate()</code> method. The second hand is only drawn in interactive mode, not in +ambient mode.</p> + +<p>For more information about drawing on a Canvas instance, see <a +href="{@docRoot}guide/topics/graphics/2d-graphics.html">Canvas and Drawables</a>.</p> + +<p>The <em>WatchFace</em> sample in the Android SDK includes additional watch faces that you +can refer to as examples of how to implement the <code>onDraw()</code> method.</p> diff --git a/docs/html/training/wearables/watch-faces/images/AnalogNoCard.png b/docs/html/training/wearables/watch-faces/images/AnalogNoCard.png Binary files differnew file mode 100644 index 000000000000..a6b5d4f6cca5 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/AnalogNoCard.png diff --git a/docs/html/training/wearables/watch-faces/images/AnalogWithCard.png b/docs/html/training/wearables/watch-faces/images/AnalogWithCard.png Binary files differnew file mode 100644 index 000000000000..242d3fe78eb1 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/AnalogWithCard.png diff --git a/docs/html/training/wearables/watch-faces/images/BitmapFilterDisabled.png b/docs/html/training/wearables/watch-faces/images/BitmapFilterDisabled.png Binary files differnew file mode 100644 index 000000000000..32911370aa2e --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/BitmapFilterDisabled.png diff --git a/docs/html/training/wearables/watch-faces/images/BitmapFilterEnabled.png b/docs/html/training/wearables/watch-faces/images/BitmapFilterEnabled.png Binary files differnew file mode 100644 index 000000000000..64923180c9ec --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/BitmapFilterEnabled.png diff --git a/docs/html/training/wearables/watch-faces/images/ClockHandCropped.png b/docs/html/training/wearables/watch-faces/images/ClockHandCropped.png Binary files differnew file mode 100644 index 000000000000..69c746b45e7f --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/ClockHandCropped.png diff --git a/docs/html/training/wearables/watch-faces/images/ClockHandFull.png b/docs/html/training/wearables/watch-faces/images/ClockHandFull.png Binary files differnew file mode 100644 index 000000000000..2d0892017651 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/ClockHandFull.png diff --git a/docs/html/training/wearables/watch-faces/images/Indicators_Cropped.png b/docs/html/training/wearables/watch-faces/images/Indicators_Cropped.png Binary files differnew file mode 100644 index 000000000000..9449c02c74a0 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/Indicators_Cropped.png diff --git a/docs/html/training/wearables/watch-faces/images/Render_Episode.png b/docs/html/training/wearables/watch-faces/images/Render_Episode.png Binary files differnew file mode 100644 index 000000000000..2d545bbda1cd --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/Render_Episode.png diff --git a/docs/html/training/wearables/watch-faces/images/Render_Interactive.png b/docs/html/training/wearables/watch-faces/images/Render_Interactive.png Binary files differnew file mode 100644 index 000000000000..a1e260bd5a7a --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/Render_Interactive.png diff --git a/docs/html/training/wearables/watch-faces/images/Render_Next.png b/docs/html/training/wearables/watch-faces/images/Render_Next.png Binary files differnew file mode 100644 index 000000000000..e08094391190 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/Render_Next.png diff --git a/docs/html/training/wearables/watch-faces/images/Render_Saturn.png b/docs/html/training/wearables/watch-faces/images/Render_Saturn.png Binary files differnew file mode 100644 index 000000000000..500018c52a3f --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/Render_Saturn.png diff --git a/docs/html/training/wearables/watch-faces/images/preview_analog.png b/docs/html/training/wearables/watch-faces/images/preview_analog.png Binary files differnew file mode 100644 index 000000000000..fa715464dfcb --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/preview_analog.png diff --git a/docs/html/training/wearables/watch-faces/images/preview_calendar.png b/docs/html/training/wearables/watch-faces/images/preview_calendar.png Binary files differnew file mode 100644 index 000000000000..928aa1f4f963 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/preview_calendar.png diff --git a/docs/html/training/wearables/watch-faces/images/preview_digital.png b/docs/html/training/wearables/watch-faces/images/preview_digital.png Binary files differnew file mode 100644 index 000000000000..4853a6454093 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/images/preview_digital.png diff --git a/docs/html/training/wearables/watch-faces/index.jd b/docs/html/training/wearables/watch-faces/index.jd new file mode 100644 index 000000000000..c510fb20012b --- /dev/null +++ b/docs/html/training/wearables/watch-faces/index.jd @@ -0,0 +1,62 @@ +page.title=Creating Custom Watch Faces + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + <h2>Dependencies and Prerequisites</h2> + <ul> + <li>Android Studio 1.0.0 or later and Gradle 1.0 or later</li> + <li>Android 4.3 (API level 18) or higher on the handheld device</li> + <li>Android 5.0 (API level 21) or higher on the wearable device</li> + </ul> +</div> +</div> + +<p>Watch faces in Android Wear leverage a dynamic digital canvas to tell time using colors, +animations, and relevant contextual information. The <a +href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app">Android +Wear companion app</a> provides watch faces with different styles and shapes. When +users select one of the available watch faces on the wearable or on the companion app, the +wearable device previews the watch face and lets the user set configuration options.</p> + +<p>Android Wear enables you to create custom watch faces for Wear devices. When users install a +handheld app that contains a <a href="{@docRoot}training/wearables/apps/index.html">wearable +app</a> with watch faces, they become available in the Android Wear companion app +on the handheld device and in the watch face picker on the wearable device.</p> + +<p>This class teaches you to implement custom watch faces and to package them inside a wearable +app. This class also covers design considerations and implementation tips to ensure that your +designs integrate with system UI elements and are power-efficient.</p> + +<p class="note"><strong>Note:</strong> We recommend using <a +href="{@docRoot}sdk/index.html">Android Studio</a> for Android Wear development as +it provides project setup, library inclusion, and packaging conveniences that aren't available +in the Eclipse Android Developer Tools. This training assumes you are using Android Studio.</p> + + +<h2>Lessons</h2> + +<dl> +<dt><a href="{@docRoot}training/wearables/watch-faces/designing.html"> +Designing Watch Faces</a></dt> +<dd>Learn how to design a watch face that works on any Android Wear device.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/service.html"> +Building a Watch Face Service</a></dt> +<dd>Learn how to respond to important events during the lifecycle of your watch face.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/drawing.html"> +Drawing Watch Faces</a></dt> +<dd>Learn how to draw your watch face on a Wear device screen.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/information.html"> +Showing Information in Watch Faces</a></dt> +<dd>Learn how to incorporate contextual information into your watch face.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/configuration.html"> +Providing Configuration Activities</a></dt> +<dd>Learn how to create watch faces with configurable parameters.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/issues.html"> +Addressing Common Issues</a></dt> +<dd>Learn how to fix common problems when developing a watch face.</dd> +<dt><a href="{@docRoot}training/wearables/watch-faces/performance.html"> +Optimizing Performance and Battery Life</a></dt> +<dd>Learn how to improve the frame rate of your animations and how to save power.</dd> +</dl> diff --git a/docs/html/training/wearables/watch-faces/information.jd b/docs/html/training/wearables/watch-faces/information.jd new file mode 100644 index 000000000000..41319a10f9d7 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/information.jd @@ -0,0 +1,213 @@ +page.title=Showing Information in Watch Faces + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#Experience">Create a Compelling Experience</a></li> + <li><a href="#AddData">Add Data to Your Watch Face</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>In addition to telling time, Android Wear devices provide users with contextually relevant +information in the form of cards, notifications, and other wearable apps. Creating a custom +watch face not only gives you the opportunity to tell time in visually compelling ways, but +also to show users relevant information whenever they glance at their device.</p> + +<p>Like any other wearable app, your watch face can communicate with apps running on the handheld +device using the <a href="{@docRoot}training/wearables/data-layer/index.html">Wearable Data Layer +API</a>. In some cases, you need to create an activity in the handheld app module of your project +that retrieves data from the Internet or from the user's profile and then shares it with your +watch face.</p> + +<img src="{@docRoot}training/wearables/watch-faces/images/Render_Saturn.png" + width="200" height="196" alt="" style="margin-top:12px;margin-left:-20px"/> +<img src="{@docRoot}training/wearables/watch-faces/images/Render_Episode.png" + width="200" height="196" alt="" style="margin-top:12px;margin-left:-25px"/> +<p class="img-caption"> +<strong>Figure 1.</strong> Examples of watch faces with integrated data.</p> + + +<h2 id="Experience">Create a Compelling Experience</h2> + +<p>Before you design and implement a contextually-aware watch face, answer the following +questions:</p> + +<ul> +<li>What kind of data do you want to incorporate?</li> +<li>Where can you obtain this data?</li> +<li>How often does the data change significantly?</li> +<li>How can you present the data such that users understand it at a glance?</li> +</ul> + +<p>Android Wear devices are usually paired with a companion device that has a GPS sensor and +cellular connectivity, so you have endless possibilities to integrate different kinds of data +in your watch face, such as location, calendar events, social media trends, picture feeds, stock +market quotes, news events, sports scores, and so on. However, not all kinds of data are +appropriate for a watch face, so you should consider what kinds of data are most relevant to +your users throughout the day. Your watch face should also gracefully handle the case where the +wearable is not paired with a companion device or when an Internet connection is not available.</p> + +<p>The active watch face on an Android Wear device is an app that runs continuously, so you +must retrieve data in a battery-efficient manner. For example, you can obtain the current +weather every ten minutes and store the results locally, instead of requesting an update every +minute. You can also refresh contextual data when the device switches from ambient to interactive +mode, since the user is more likely to glance at the watch when this transition occurs.</p> + +<p>You should summarize contextual information on your watch face, since there is limited +space available on the screen and users just glance at their watch for a second or two at a +time. Sometimes the best way to convey contextual information is to react to it using graphics +and colors. For example, a watch face could change its background image depending on the current +weather.</p> + + + +<h2 id="AddData">Add Data to Your Watch Face</h2> + +<div style="float:right;margin-left:20px"> +<img src="{@docRoot}training/wearables/watch-faces/images/preview_calendar.png" + width="180" height="180" alt="" style="margin-left:10px;margin-top:10px"/> +<p class="img-caption"><strong>Figure 2.</strong> The calendar watch face.</p> +</div> + +<p>The <em>WatchFace</em> sample in the Android SDK demonstrates how to obtain calendar data +from the user’s profile in the <code>CalendarWatchFaceService</code> class and shows how many +meetings there are in the following twenty-four hours. This sample is located in the +<code>android-sdk/samples/android-21/wearable/WatchFace</code> directory.</p> + +<p>To implement a watch face that incorporates contextual data, follow these steps:</p> + +<ol> +<li>Provide a task that retrieves the data.</li> +<li>Create a custom timer to invoke your task periodically, or notify your watch face service + when external data changes.</li> +<li>Redraw your watch face with the updated data.</li> +</ol> + +<p>The following sections describe these steps in detail.</p> + +<h3 id="Task">Provide a task to retrieve data</h3> + +<p>Create a class inside your <code>CanvasWatchFaceService.Engine</code> implementation that +extends {@link android.os.AsyncTask} and add the code to retrieve the data you’re interested +in.</p> + +<p>The <code>CalendarWatchFaceService</code> class obtains the number of meetings in the next +day as follows:</p> + +<pre> +/* Asynchronous task to load the meetings from the content provider and + * report the number of meetings back using onMeetingsLoaded() */ +private class LoadMeetingsTask extends AsyncTask<Void, Void, Integer> { + @Override + protected Integer doInBackground(Void... voids) { + long begin = System.currentTimeMillis(); + Uri.Builder builder = + WearableCalendarContract.Instances.CONTENT_URI.buildUpon(); + ContentUris.appendId(builder, begin); + ContentUris.appendId(builder, begin + DateUtils.DAY_IN_MILLIS); + final Cursor cursor = getContentResolver() .query(builder.build(), + null, null, null, null); + int numMeetings = cursor.getCount(); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Num meetings: " + numMeetings); + } + return numMeetings; + } + + @Override + protected void onPostExecute(Integer result) { + /* get the number of meetings and set the next timer tick */ + onMeetingsLoaded(result); + } +} +</pre> + +<p>The <code>WearableCalendarContract</code> class from the Wearable Support Library provides +direct access to the user's calendar events from the companion device.</p> + +<p>When the task finishes retrieving data, your code invokes a callback method. The following +sections describe how to implement the callback method in detail.</p> + +<p>For more information about obtaining data from the calendar, see the <a +href="{@docRoot}guide/topics/providers/calendar-provider.html">Calendar Provider</a> API +guide.</p> + +<h3 id="Timer">Create a custom timer</h3> + +<p>You can implement a custom timer that ticks periodically to update your data. +The <code>CalendarWatchFaceService</code> class uses a {@link android.os.Handler} instance +that sends and processes delayed messages using the thread's message queue:</p> + +<pre> +private class Engine extends CanvasWatchFaceService.Engine { + ... + int mNumMeetings; + private AsyncTask<Void, Void, Integer> mLoadMeetingsTask; + + /* Handler to load the meetings once a minute in interactive mode. */ + final Handler mLoadMeetingsHandler = new Handler() { + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_LOAD_MEETINGS: + cancelLoadMeetingTask(); + mLoadMeetingsTask = new LoadMeetingsTask(); + mLoadMeetingsTask.execute(); + break; + } + } + }; + ... +} +</pre> + +<p>This method initializes the timer when the watch face becomes visible:</p> + +<pre> +@Override +public void onVisibilityChanged(boolean visible) { + super.onVisibilityChanged(visible); + if (visible) { + mLoadMeetingsHandler.sendEmptyMessage(MSG_LOAD_MEETINGS); + } else { + mLoadMeetingsHandler.removeMessages(MSG_LOAD_MEETINGS); + cancelLoadMeetingTask(); + } +} +</pre> + +<p>The next timer tick is set in the <code>onMeetingsLoaded()</code> method, as shown in the next +section.</p> + +<h3 id="Redraw">Redraw your watch face with the updated data</h3> + +<p>When the task that retrieves your data finishes, call the <code>invalidate()</code> method +so the system redraws your watch face. Store your data inside member variables of the +<code>Engine</code> class so you can access it inside the <code>onDraw()</code> method.</p> + +<p>The <code>CalendarWatchFaceService</code> class provides a callback method for the task to +invoke when it finishes retrieving calendar data:</p> + +<pre> +private void onMeetingsLoaded(Integer result) { + if (result != null) { + mNumMeetings = result; + invalidate(); + } + if (isVisible()) { + mLoadMeetingsHandler.sendEmptyMessageDelayed( + MSG_LOAD_MEETINGS, LOAD_MEETINGS_DELAY_MS); + } +} +</pre> + +<p>The callback method stores the result in a member variable, invalidates the view, and +schedules the next timer tick to run the task again.</p> diff --git a/docs/html/training/wearables/watch-faces/issues.jd b/docs/html/training/wearables/watch-faces/issues.jd new file mode 100644 index 000000000000..47c5e8c31f69 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/issues.jd @@ -0,0 +1,125 @@ +page.title=Addressing Common Issues + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#ScreenShape">Detect the Shape of the Screen</a></li> + <li><a href="#PeekCards">Accomodate Peek Cards</a></li> + <li><a href="#RelativeMeasurements">Use Relative Measurements</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>Creating a custom watch face for Android Wear is substantially different from creating +notifications and wearable-specific activities. This class shows you how to resolve some +issues that you may encounter as you implement your first few watch faces.</p> + + + +<h2 id="ScreenShape">Detect the Shape of the Screen</h2> + +<p>Some Android Wear devices have square screens, while others have round screens. Devices with +round screens can contain an inset (or "chin") at the bottom of the screen. Your watch face +should adapt to and take advantage of the particular shape of the screen, as described in the +<a href="{@docRoot}design/wear/watchfaces.html">design guidelines</a>.</p> + +<p>Android Wear lets your watch face determine the screen shape at runtime. To detect whether +the screen is square or round, override the <code>onApplyWindowInsets()</code> method in the +<code>CanvasWatchFaceService.Engine</code> class as follows:</p> + +<pre> +private class Engine extends CanvasWatchFaceService.Engine { + boolean mIsRound; + int mChinSize; + + @Override + public void onApplyWindowInsets(WindowInsets insets) { + super.onApplyWindowInsets(insets); + mIsRound = insets.isRound(); + mChinSize = insets.getSystemWindowInsetBottom(); + } + ... +} +</pre> + +<p>To adapt your design when you draw your watch face, check the value of the +<code>mIsRound</code> and <code>mChinSize</code> member variables.</p> + + + +<h2 id="PeekCards">Accomodate Peek Cards</h2> + +<div style="float:right;margin-left:30px;width:340px"> +<img src="{@docRoot}training/wearables/watch-faces/images/AnalogNoCard.png" alt="" + width="160" height="145" style="margin-right:7px"/> +<img src="{@docRoot}training/wearables/watch-faces/images/AnalogWithCard.png" alt="" + width="160" height="145"/> +<p class="img-caption"><strong>Figure 1.</strong> Some analog watch faces require adjustments +when notification cards appear.</p> +</div> + +<p>When users receive a notification, the notification card may cover a significant portion of the +screen, depending on the +<a href="{@docRoot}training/wearables/watch-faces/drawing.html#SystemUI">system UI style</a>. Your +watch face should adapt to these situations by ensuring that users can still tell the time while +the notification card is present.</p> + +<p>Analog watch faces can make adjustments when a notification card is present, like scaling +down the watch face to fit inside the portion of the screen not covered by the peek card. Digital +watch faces that display the time in the area of the screen not covered by peek cards do not +usually require adjustments. To determine the free space above the peek card so you can adapt +your watch face, use the <code>WatchFaceService.getPeekCardPosition()</code> method.</p> + +<p>In ambient mode, peek cards have a transparent background. If your watch face contains details +near the card in ambient mode, consider drawing a black rectangle over them to ensure that users +can read the contents of the card.</p> + + + +<h2 id="SystemIndicators">Configure the System Indicators</h2> + +<div style="width:200px;float:right;margin-left:10px"> +<img src="{@docRoot}training/wearables/watch-faces/images/Indicators_Cropped.png" alt="" + width="200" height="195"/> +<p class="img-caption" style="margin-left:25px;margin-top:-25px"> +<strong>Figure 2.</strong> The status bar.</p> +</div> + +<p>To ensure that the system indicators remain visible, you can configure their position on the +screen and whether they need background protection when you create a <code>WatchFaceStyle</code> +instance:</p> + +<ul> +<li>To set the position of the status bar, use the <code>setStatusBarGravity()</code> method.</li> +<li>To set the position of the hotword, use the <code>setHotwordIndicatorGravity()</code> +method.</li> +<li>To protect the status bar and hotword with a semi-transparent gray background, use the +<code>setViewProtection()</code> method. This is usually necessary if your watch face has a +light background, since the system indicators are white.</li> +</ul> + +<p>For more information about the system indicators, see <a +href="{@docRoot}training/wearables/watch-faces/drawing.html#SystemUI">Configure the System UI</a> +and read the <a href="{@docRoot}design/wear/watchfaces.html">design guidelines</a>.</p> + + + +<h2 id="RelativeMeasurements">Use Relative Measurements</h2> + +<p>Android Wear devices from different manufacturers feature screens with a variety of sizes and +resolutions. Your watch face should adapt to these variations by using relative measurements +instead of absolute pixel values.</p> + +<p>When you draw your watch face, obtain the size of the canvas with the {@link +android.graphics.Canvas#getWidth Canvas.getWidth()} and {@link +android.graphics.Canvas#getHeight Canvas.getHeight()} methods and set the positions of your +graphic elements using values that are some fraction of the detected screen size. If you resize +the elements of your watch face in response to a peek card, use values that are some fraction +of the remaining space above the card to redraw your watch face.</p> diff --git a/docs/html/training/wearables/watch-faces/performance.jd b/docs/html/training/wearables/watch-faces/performance.jd new file mode 100644 index 000000000000..68438fee5702 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/performance.jd @@ -0,0 +1,160 @@ +page.title=Optimizing Performance and Battery Life + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#ReduceSize">Reduce the Size of Your Bitmap Assets</a></li> + <li><a href="#CombineBitmaps">Combine Bitmap Assets</a></li> + <li><a href="#AntiAlias">Disable Anti-Aliasing when Drawing Scaled Bitmaps</a></li> + <li><a href="#OutDrawing">Move Expensive Operations Outside the Drawing Method</a></li> + <li><a href="#SavePower">Follow Best Practices to Save Power</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>In addition to accommodating notification cards and system indicators, you need to ensure that +the animations in your watch face run smoothly and that your service does not perform unnecessary +computations. Watch faces in Android Wear run continuously on the device, so it is critical +that your watch face uses power efficiently.</p> + +<p>This lesson provides some tips to speed up your animations and to measure and conserve +power on the device.</p> + + + +<h2 id="ReduceSize">Reduce the Size of Your Bitmap Assets</h2> + +<p>Many watch faces consist of a background image and other graphic assets that are transformed +and overlapped on top of the background image, such as clock hands and other elements of the design +that move over time. Typically these graphic elements are rotated (and sometimes scaled) inside the +<code>Engine.onDraw()</code> method every time the system redraws the watch face, as described in +<a href="{@docRoot}training/wearables/watch-faces/drawing.html#Drawing">Draw Your Watch +Face</a>.</p> + +<p>The larger these graphic assets are, the more computationally expensive it is to transform them. +Transforming large graphic assets in the <code>Engine.onDraw()</code> method drastically reduces +the frame rate at which the system can run your animations.</p> + +<div id="fig1" style="float:right;width:250px;margin-left:25px"> +<img src="{@docRoot}training/wearables/watch-faces/images/ClockHandFull.png" alt="" + width="180" height="180"/> +<img src="{@docRoot}training/wearables/watch-faces/images/ClockHandCropped.png" alt="" + width="15" height="65" style="margin-left:25px"/> +<p class="img-caption"> +<strong>Figure 1.</strong> Clock hands can be cropped to remove extra pixels.</p> +</div> + +<p>To improve the performance of your watch face:</p> + +<ul> +<li>Do not use graphic elements that are larger than you need.</li> +<li>Remove extra transparent pixels around the edges.</li> +</ul> + +<p>The example clock hand on the left side of <a href="#fig1">Figure 1</a> can be reduced in size +by 97%.</p> + +<p>Reducing the size of your bitmap assets as described in this section not only improves +the performance of your animations, but it also saves power.</p> + + + +<h2 id="CombineBitmaps">Combine Bitmap Assets</h2> + +<p>If you have bitmaps that are often drawn together, consider combining them into the same +graphic asset. You can often combine the background image in interactive mode with the tick +marks to avoid drawing two full-screen bitmaps every time the system redraws the watch face.</p> + + + +<h2 id="AntiAlias">Disable Anti-Aliasing when Drawing Scaled Bitmaps</h2> + +<p>When you draw a scaled bitmap on the {@link android.graphics.Canvas} object using the {@link +android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint) +Canvas.drawBitmap()} method, you can provide a {@link android.graphics.Paint} instance to configure +several options. To improve performance, disable anti-aliasing using the {@link +android.graphics.Paint#setAntiAlias setAntiAlias()} method, since this option does not have any +effect on bitmaps.</p> + +<div id="fig2" style="float:right;width:250px;margin-left:40px;margin-top:12px"> +<img src="{@docRoot}training/wearables/watch-faces/images/BitmapFilterDisabled.png" alt="" + width="70" height="70" style="margin-left:20px"/> +<img src="{@docRoot}training/wearables/watch-faces/images/BitmapFilterEnabled.png" alt="" + width="70" height="70" style="margin-left:20px"/> +<p class="img-caption"><strong>Figure 2.</strong> Example of bitmap filtering disabled (left) and +enabled (right).</p> +</div> + +<h3 id="BitmapFiltering">Use bitmap filtering</h3> + +<p>For bitmap assets that you draw on top of other elements, enable bitmap filtering on the same +{@link android.graphics.Paint} instance using the {@link android.graphics.Paint#setFilterBitmap +setFilterBitmap()} method. <a href="#fig2">Figure 2</a> shows a magnified view of a clock hand with +and without bitmap filtering.</p> + + + +<h2 id="OutDrawing">Move Expensive Operations Outside the Drawing Method</h2> + +<p>The system calls the <code>Engine.onDraw()</code> method every time it redraws your watch +face, so you should only include operations that are strictly required to update the watch +face inside this method to improve performance.<p> + +<p>When possible, avoid performing these operations inside the <code>onDraw()</code> method:</p> + +<ul> +<li>Loading images and other resources.</li> +<li>Resizing images.</li> +<li>Allocating objects.</li> +<li>Performing computations whose result does not change between frames.</li> +</ul> + +<p>You can usually perform these operations in the <code>Engine.onCreate()</code> method instead. +You can resize images ahead of time in the {@link +android.service.wallpaper.WallpaperService.Engine#onSurfaceChanged(android.view.SurfaceHolder, int, int, int) +Engine.onSurfaceChanged()} method, which provides you with the size of the canvas.</p> + +<p>To analyze the performance of your watch face, use the Android Device Monitor. In particular, +ensure that the execution time for your <code>Engine.onDraw()</code> implementation is short and +consistent across invocations. For more information, see +<a href="{@docRoot}tools/debugging/ddms.html">Using DDMS</a>.</p> + + + +<h2 id="SavePower">Follow Best Practices to Save Power</h2> + +<p>In addition to the techniques described in the previous sections, follow the best +practices in this section to reduce the power consumption of your watch face.</p> + +<h3>Reduce the frame rate of animations</h3> + +<p>Animations are often computationally expensive and consume a significant amount of power. Most +animations look fluid at 30 frames per second, so you should avoid running your animations +at a higher frame rate.</p> + +<h3>Let the CPU sleep</h3> + +<p>Animations and small changes to the contents of the watch face wake up the CPU. Your watch +face should let the CPU sleep in between animations. For example, you can use short bursts of +animation every second in interactive mode and then let the CPU sleep until the next second. +Letting the CPU sleep often, even briefly, can significantly reduce power consumption.</p> + +<p>To maximize battery life, use animations sparingly. Even a blinking colon wakes up the CPU with +every blink and hurts battery life.</p> + +<h3>Monitor power consumption</h3> + +<p>The <a href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en"> +Android Wear companion app</a> lets developers and users see how much battery different processes +on the wearable device are consuming under <strong>Settings</strong> > <strong>Watch +battery</strong>.</p> + +<p>For more information about new features in Android 5.0 that help you improve battery life, +see <a href="{@docRoot}about/versions/android-5.0.html#Power">Project Volta</a>.</p> diff --git a/docs/html/training/wearables/watch-faces/service.jd b/docs/html/training/wearables/watch-faces/service.jd new file mode 100644 index 000000000000..0cb628c7a873 --- /dev/null +++ b/docs/html/training/wearables/watch-faces/service.jd @@ -0,0 +1,280 @@ +page.title=Building a Watch Face Service + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#CreateProject">Create and Configure Your Project</a></li> + <li><a href="#CallbackMethods">Implement the Service Callback Methods</a></li> + <li><a href="#RegisterService">Register the Service Implementation</a></li> +</ol> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}design/wear/watchfaces.html">Watch Faces for Android Wear</a></li> +</ul> +</div> +</div> + +<p>Watch faces in Android Wear are implemented as <a +href="{@docRoot}guide/components/services.html">services</a> and packaged inside a <a +href="{@docRoot}training/wearables/apps/index.html">wearable app</a>. When users install a +handheld app that contains a wearable app with watch faces, these watch faces become available +in the <a +href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android +Wear companion app</a> on the handheld device and in the watch face picker on the wearable. When +users select one of the available watch faces, the wearable device shows the watch face and +invokes its service callback methods as required.</p> + +<p>This lesson shows you how to configure an Android project to include watch faces and how +to implement the watch face service.</p> + + + +<h2 id="CreateProject">Create and Configure Your Project</h2> + +<p>To create an Android project for your watch face in Android Studio:</p> + +<ol> +<li>Start Android Studio.</li> +<li>Create a new project: + <ul> + <li>If you don't have a project opened, in the <strong>Welcome</strong> screen, click + <strong>New Project</strong>.</li> + <li>If you have a project opened, from the <strong>File</strong> menu, select <strong>New + Project</strong>.</li> + </ul> +</li> +<li>Provide an application name and click <strong>Next</strong>.</li> +<li>Select the <strong>Phone and Tablet</strong> form factor.</li> +<li>Under <strong>Minimum SDK</strong>, choose API 18.</li> +<li>Select the <strong>Wear</strong> form factor.</li> +<li>Under <strong>Minimum SDK</strong>, choose API 21 and click <strong>Next</strong>.</li> +<li>Select <strong>Add No Activity</strong> and click <strong>Next</strong> in the two following + screens.</li> +<li>Click <strong>Finish</strong>.</li> +<li>Click <strong>View</strong> > <strong>Tool Windows</strong> > <strong>Project</strong> in the + IDE window.</li> +</ol> + +<p>Android Studio creates a project with the <code>wear</code> and <code>mobile</code> modules. +For more information, see <a href="{@docRoot}sdk/installing/create-project.html">Creating a +Project</a>.</p> + +<h3 id="Dependencies">Dependencies</h3> + +<p>For the handheld app, edit the <code>build.gradle</code> file in the <code>mobile</code> module +to add these dependencies:</p> + +<pre> +apply plugin: 'com.android.application' + +android { ... } + +dependencies { + ... + wearApp project(':wear') + compile 'com.google.android.gms:play-services:6.1.+' +} +</pre> + +<p>For the wearable app, edit the <code>build.gradle</code> file in the <code>wear</code> module +to add these dependencies:</p> + +<pre> +apply plugin: 'com.android.application' + +android { ... } + +dependencies { + ... + compile 'com.google.android.support:wearable:1.1.+' + compile 'com.google.android.gms:play-services-wearable:6.1.+' +} +</pre> + +<p>The Wearable Support Library provides the necessary classes that you extend to create watch +face implementations. The Google Play services client libraries (<code>play-services</code> and +<code>play-services-wearable</code>) are required to sync data items between the companion device +and the wearable with the <a href="{@docRoot}training/wearables/data-layer/index.html">Wearable +Data Layer API</a>.</p> + +<h3 id="Reference">Wearable Support Library API Reference</h3> + +<p>The reference documentation provides detailed information about the classes you use to +implement watch faces. Download the +<a href="{@docRoot}shareables/training/wearable-support-docs.zip">API reference +documentation</a> for the Wearable Support Library.</p> + +<h3 id="LibEclipse">Download the Wearable Support Library for Eclipse ADT</h3> + +<p>If you are using the ADT plugin for Eclipse, download the +<a href="{@docRoot}shareables/training/wearable-support-lib.zip">Wearable Support Library</a> and +include it as a dependency in your project.</p> + +<h3 id="Permissions">Declare Permissions</h3> + +<p>Watch faces require the <code>PROVIDE_BACKGROUND</code> and <code>WAKE_LOCK</code> permissions. +Add the following permissions to the manifest files of both the wearable app and the mobile +app under the <code>manifest</code> element:</p> + +<pre> +<manifest ...> + <uses-permission + android:name="com.google.android.permission.PROVIDE_BACKGROUND" /> + <uses-permission + android:name="android.permission.WAKE_LOCK" /> + ... +</manifest> +</pre> + +<p class="caution"><strong>Caution:</strong> The handheld app must include all the permissions +declared in the wearable app.</p> + + + +<h2 id="CallbackMethods">Implement the Service and Callback Methods</h2> + +<p>Watch faces in Android Wear are implemented as +<a href="{@docRoot}guide/components/services.html">services</a>. +When a watch face is active, the system invokes the methods in its service when the time changes +or when an important event occurs (like switching to ambient mode or receiving a new +notification). The service implementation then draws the watch face on the screen using the +updated time and any other relevant data.</p> + +<p>To implement a watch face, you extend the <code>CanvasWatchFaceService</code> +and <code>CanvasWatchFaceService.Engine</code> classes, and then you override the callback methods +in the <code>CanvasWatchFaceService.Engine</code> class. These classes are included in the +Wearable Support Library.</p> + +<p>The following snippet outlines the key methods you need to implement:</p> + +<pre> +public class AnalogWatchFaceService extends CanvasWatchFaceService { + + @Override + public Engine onCreateEngine() { + /* provide your watch face implementation */ + return new Engine(); + } + + /* implement service callback methods */ + private class Engine extends CanvasWatchFaceService.Engine { + + @Override + public void onCreate(SurfaceHolder holder) { + /* initialize your watch face */ + } + + @Override + public void onPropertiesChanged(Bundle properties) { + /* get device features (burn-in, low-bit ambient) */ + } + + @Override + public void onTimeTick() { + /* the time changed */ + } + + @Override + public void onAmbientModeChanged(boolean inAmbientMode) { + /* the wearable switched between modes */ + } + + @Override + public void onDraw(Canvas canvas) { + /* draw your watch face */ + } + + @Override + public void onVisibilityChanged(boolean visible) { + /* the watch face became visible or invisible */ + } + } +} +</pre> + +<p class="note"><strong>Note:</strong> The <em>WatchFace</em> sample in the Android SDK +demonstrates how to implement analog and digital watch faces extending the +<code>CanvasWatchFaceService</code> class. This sample is located in the +<code>android-sdk/samples/android-21/wearable/WatchFace</code> directory.</p> + +<p>The <code>CanvasWatchFaceService</code> class provides an invalidate mechanism similar to +the {@link android.view.View#invalidate View.invalidate()} method. You can call the +<code>invalidate()</code> method throughout your implementation when you want the system to +redraw the watch face. You can only use the <code>invalidate()</code> method in the main UI +thread. To invalidate the canvas from another thread, call the <code>postInvalidate()</code> +method.</p> + +<p>For more information about implementing the methods in the +<code>CanvasWatchFaceService.Engine</code> class, see <a +href="{@docRoot}training/wearables/watch-faces/drawing.html">Drawing Watch Faces</a>.</p> + + + +<h2 id="RegisterService">Register the Watch Face Service</h2> + +<p>After you implement the watch face service, you register the implementation in the manifest +file of the wearable app. When users install this app, the system uses the information about +the service to make the watch face available in the <a +href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android +Wear companion app</a> and in the watch face picker on the wearable device.</p> + +</p>The following snippet shows how to register a watch face implementation +under the <a href="{@docRoot}guide/topics/manifest/application-element.html"> +<code>application</code></a> element:</p> + +<pre> +<service + android:name=".AnalogWatchFaceService" + android:label="@string/analog_name" + android:allowEmbedded="true" + android:taskAffinity="" + android:permission="android.permission.BIND_WALLPAPER" > + <meta-data + android:name="android.service.wallpaper" + android:resource="@xml/watch_face" /> + <meta-data + android:name="com.google.android.wearable.watchface.preview" + android:resource="@drawable/preview_analog" /> + <meta-data + android:name="com.google.android.wearable.watchface.preview_circular" + android:resource="@drawable/preview_analog_circular" /> + <intent-filter> + <action android:name="android.service.wallpaper.WallpaperService" /> + <category + android:name= + "com.google.android.wearable.watchface.category.WATCH_FACE" /> + </intent-filter> +</service> +</pre> + +<p>The <a +href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android +Wear companion app</a> and the watch face picker on the wearable device use the preview image +defined by the <code>com.google.android.wearable.watchface.preview</code> metadata entry when +presenting users with all the watch faces installed on the device. To obtain this drawable, +run the watch face on your Android Wear device or in an emulator instance and <a +href="{@docRoot}sdk/installing/studio-debug.html#screenCap">take a screenshot</a>. On Android Wear +devices with hdpi screens, the preview image is typically 320x320 pixels in size.</p> + +<p>Watch faces that look substantially different on round devices can provide both round and +square preview images. To specify a round preview image, use the +<code>com.google.android.wearable.watchface.preview_circular</code> metadata entry. If a watch +face includes both preview images, the companion app and the watch face picker on the wearable +show the appropriate one, depending on the shape of the watch. If a round preview image is not +included, the square preview image is used for both square and round devices. For round devices, +a square preview image is cropped using a circular shape.</p> + +<p>The <code>android.service.wallpaper</code> metadata entry specifies the +<code>watch_face.xml</code> resource file, which contains a <code>wallpaper</code> +element:</p> + +<pre> +<?xml version="1.0" encoding="UTF-8"?> +<wallpaper xmlns:android="http://schemas.android.com/apk/res/android" /> +</pre> + +<p>Your wearable app can contain more than one watch face. You must add a service entry to the +manifest file of the wearable app for each of your watch face implementations.</p> |