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