1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.wearable.watchface;
18 
19 import android.content.res.Resources;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Paint;
23 import android.graphics.Rect;
24 import android.graphics.Typeface;
25 import android.os.Bundle;
26 import android.support.wearable.watchface.CanvasWatchFaceService;
27 import android.support.wearable.watchface.WatchFaceStyle;
28 import android.util.Log;
29 import android.view.SurfaceHolder;
30 import android.view.WindowInsets;
31 
32 /**
33  * Demonstrates interactive watch face capabilities, i.e., touching the display and registering
34  * three different events: touch, touch-cancel and tap. The watch face UI will show the count of
35  * these events as they occur. See the {@code onTapCommand} below.
36  */
37 public class InteractiveWatchFaceService extends CanvasWatchFaceService {
38 
39     private static final String TAG = "InteractiveWatchFace";
40 
41     private static final Typeface BOLD_TYPEFACE =
42             Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
43     private static final Typeface NORMAL_TYPEFACE =
44             Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
45 
46     @Override
onCreateEngine()47     public Engine onCreateEngine() {
48         return new Engine();
49     }
50 
51     private class Engine extends CanvasWatchFaceService.Engine {
52 
53         private Paint mTextPaint;
54         private final Paint mPeekCardBackgroundPaint = new Paint();
55 
56         private float mXOffset;
57         private float mYOffset;
58         private float mTextSpacingHeight;
59         private int mScreenTextColor = Color.WHITE;
60 
61         private int mTouchCommandTotal;
62         private int mTouchCancelCommandTotal;
63         private int mTapCommandTotal;
64 
65         private int mTouchCoordinateX;
66         private int mTouchCoordinateY;
67 
68         private final Rect mCardBounds = new Rect();
69 
70         /**
71          * Whether the display supports fewer bits for each color in ambient mode. When true, we
72          * disable anti-aliasing in ambient mode.
73          */
74         private boolean mLowBitAmbient;
75 
76         @Override
onCreate(SurfaceHolder holder)77         public void onCreate(SurfaceHolder holder) {
78             if (Log.isLoggable(TAG, Log.DEBUG)) {
79                 Log.d(TAG, "onCreate");
80             }
81             super.onCreate(holder);
82 
83             /** Accepts tap events via WatchFaceStyle (setAcceptsTapEvents(true)). */
84             setWatchFaceStyle(new WatchFaceStyle.Builder(InteractiveWatchFaceService.this)
85                     .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE)
86                     .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE)
87                     .setShowSystemUiTime(false)
88                     .setAcceptsTapEvents(true)
89                     .build());
90 
91             Resources resources = InteractiveWatchFaceService.this.getResources();
92             mTextSpacingHeight = resources.getDimension(R.dimen.interactive_text_size);
93 
94             mTextPaint = new Paint();
95             mTextPaint.setColor(mScreenTextColor);
96             mTextPaint.setTypeface(BOLD_TYPEFACE);
97             mTextPaint.setAntiAlias(true);
98 
99             mTouchCommandTotal = 0;
100             mTouchCancelCommandTotal = 0;
101             mTapCommandTotal = 0;
102 
103             mTouchCoordinateX = 0;
104             mTouchCoordinateX = 0;
105         }
106 
107         @Override
onApplyWindowInsets(WindowInsets insets)108         public void onApplyWindowInsets(WindowInsets insets) {
109             if (Log.isLoggable(TAG, Log.DEBUG)) {
110                 Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square"));
111             }
112             super.onApplyWindowInsets(insets);
113 
114             /** Loads offsets / text size based on device type (square vs. round). */
115             Resources resources = InteractiveWatchFaceService.this.getResources();
116             boolean isRound = insets.isRound();
117             mXOffset = resources.getDimension(
118                     isRound ? R.dimen.interactive_x_offset_round : R.dimen.interactive_x_offset);
119             mYOffset = resources.getDimension(
120                     isRound ? R.dimen.interactive_y_offset_round : R.dimen.interactive_y_offset);
121 
122             float textSize = resources.getDimension(
123                     isRound ? R.dimen.interactive_text_size_round : R.dimen.interactive_text_size);
124 
125             mTextPaint.setTextSize(textSize);
126         }
127 
128         @Override
onPeekCardPositionUpdate(Rect bounds)129         public void onPeekCardPositionUpdate(Rect bounds) {
130             super.onPeekCardPositionUpdate(bounds);
131             if (Log.isLoggable(TAG, Log.DEBUG)) {
132                 Log.d(TAG, "onPeekCardPositionUpdate: " + bounds);
133             }
134             super.onPeekCardPositionUpdate(bounds);
135             if (!bounds.equals(mCardBounds)) {
136                 mCardBounds.set(bounds);
137                 invalidate();
138             }
139         }
140 
141         @Override
onPropertiesChanged(Bundle properties)142         public void onPropertiesChanged(Bundle properties) {
143             super.onPropertiesChanged(properties);
144 
145             boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false);
146             mTextPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE);
147 
148             mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false);
149 
150             if (Log.isLoggable(TAG, Log.DEBUG)) {
151                 Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection
152                         + ", low-bit ambient = " + mLowBitAmbient);
153             }
154         }
155 
156         @Override
onAmbientModeChanged(boolean inAmbientMode)157         public void onAmbientModeChanged(boolean inAmbientMode) {
158             super.onAmbientModeChanged(inAmbientMode);
159             if (Log.isLoggable(TAG, Log.DEBUG)) {
160                 Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode);
161             }
162 
163             if (mLowBitAmbient) {
164                 boolean antiAlias = !inAmbientMode;
165                 mTextPaint.setAntiAlias(antiAlias);
166             }
167             invalidate();
168         }
169 
170         /*
171          * Captures tap event (and tap type) and increments correct tap type total.
172          */
173         @Override
onTapCommand(int tapType, int x, int y, long eventTime)174         public void onTapCommand(int tapType, int x, int y, long eventTime) {
175             if (Log.isLoggable(TAG, Log.DEBUG)) {
176                 Log.d(TAG, "Tap Command: " + tapType);
177             }
178 
179             mTouchCoordinateX = x;
180             mTouchCoordinateY = y;
181 
182             switch(tapType) {
183                 case TAP_TYPE_TOUCH:
184                     mTouchCommandTotal++;
185                     break;
186                 case TAP_TYPE_TOUCH_CANCEL:
187                     mTouchCancelCommandTotal++;
188                     break;
189                 case TAP_TYPE_TAP:
190                     mTapCommandTotal++;
191                     break;
192             }
193 
194             invalidate();
195         }
196 
197         @Override
onDraw(Canvas canvas, Rect bounds)198         public void onDraw(Canvas canvas, Rect bounds) {
199             /** Draws background */
200             canvas.drawColor(Color.BLACK);
201 
202             canvas.drawText(
203                     "TAP: " + String.valueOf(mTapCommandTotal),
204                     mXOffset,
205                     mYOffset,
206                     mTextPaint);
207 
208             canvas.drawText(
209                     "CANCEL: " + String.valueOf(mTouchCancelCommandTotal),
210                     mXOffset,
211                     mYOffset + mTextSpacingHeight,
212                     mTextPaint);
213 
214             canvas.drawText(
215                     "TOUCH: " + String.valueOf(mTouchCommandTotal),
216                     mXOffset,
217                     mYOffset + (mTextSpacingHeight * 2),
218                     mTextPaint);
219 
220             canvas.drawText(
221                     "X, Y: " + mTouchCoordinateX + ", " + mTouchCoordinateY,
222                     mXOffset,
223                     mYOffset + (mTextSpacingHeight * 3),
224                     mTextPaint
225             );
226 
227             /** Covers area under peek card */
228             if (isInAmbientMode()) {
229                 canvas.drawRect(mCardBounds, mPeekCardBackgroundPaint);
230             }
231         }
232     }
233 }
234