1 /* 2 * Copyright (C) 2014 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.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.res.Resources; 24 import android.graphics.Canvas; 25 import android.graphics.Paint; 26 import android.graphics.Rect; 27 import android.graphics.Typeface; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.support.wearable.watchface.CanvasWatchFaceService; 32 import android.support.wearable.watchface.WatchFaceService; 33 import android.support.wearable.watchface.WatchFaceStyle; 34 import android.text.format.Time; 35 import android.util.Log; 36 import android.view.SurfaceHolder; 37 import android.view.WindowInsets; 38 39 import com.google.android.gms.common.ConnectionResult; 40 import com.google.android.gms.common.api.GoogleApiClient; 41 import com.google.android.gms.wearable.DataApi; 42 import com.google.android.gms.wearable.DataEvent; 43 import com.google.android.gms.wearable.DataEventBuffer; 44 import com.google.android.gms.wearable.DataItem; 45 import com.google.android.gms.wearable.DataMap; 46 import com.google.android.gms.wearable.DataMapItem; 47 import com.google.android.gms.wearable.Wearable; 48 49 import java.util.TimeZone; 50 import java.util.concurrent.TimeUnit; 51 52 /** 53 * Sample digital watch face with blinking colons and seconds. In ambient mode, the seconds are 54 * replaced with an AM/PM indicator and the colons don't blink. On devices with low-bit ambient 55 * mode, the text is drawn without anti-aliasing in ambient mode. On devices which require burn-in 56 * protection, the hours are drawn in normal rather than bold. The time is drawn with less contrast 57 * and without seconds in mute mode. 58 */ 59 public class DigitalWatchFaceService extends CanvasWatchFaceService { 60 private static final String TAG = "DigitalWatchFaceService"; 61 62 private static final Typeface BOLD_TYPEFACE = 63 Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD); 64 private static final Typeface NORMAL_TYPEFACE = 65 Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL); 66 67 /** 68 * Update rate in milliseconds for normal (not ambient and not mute) mode. We update twice 69 * a second to blink the colons. 70 */ 71 private static final long NORMAL_UPDATE_RATE_MS = 500; 72 73 /** 74 * Update rate in milliseconds for mute mode. We update every minute, like in ambient mode. 75 */ 76 private static final long MUTE_UPDATE_RATE_MS = TimeUnit.MINUTES.toMillis(1); 77 78 @Override onCreateEngine()79 public Engine onCreateEngine() { 80 return new Engine(); 81 } 82 83 private class Engine extends CanvasWatchFaceService.Engine implements DataApi.DataListener, 84 GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { 85 static final String COLON_STRING = ":"; 86 87 /** Alpha value for drawing time when in mute mode. */ 88 static final int MUTE_ALPHA = 100; 89 90 /** Alpha value for drawing time when not in mute mode. */ 91 static final int NORMAL_ALPHA = 255; 92 93 static final int MSG_UPDATE_TIME = 0; 94 95 /** How often {@link #mUpdateTimeHandler} ticks in milliseconds. */ 96 long mInteractiveUpdateRateMs = NORMAL_UPDATE_RATE_MS; 97 98 /** Handler to update the time periodically in interactive mode. */ 99 final Handler mUpdateTimeHandler = new Handler() { 100 @Override 101 public void handleMessage(Message message) { 102 switch (message.what) { 103 case MSG_UPDATE_TIME: 104 if (Log.isLoggable(TAG, Log.VERBOSE)) { 105 Log.v(TAG, "updating time"); 106 } 107 invalidate(); 108 if (shouldTimerBeRunning()) { 109 long timeMs = System.currentTimeMillis(); 110 long delayMs = 111 mInteractiveUpdateRateMs - (timeMs % mInteractiveUpdateRateMs); 112 mUpdateTimeHandler.sendEmptyMessageDelayed(MSG_UPDATE_TIME, delayMs); 113 } 114 break; 115 } 116 } 117 }; 118 119 GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(DigitalWatchFaceService.this) 120 .addConnectionCallbacks(this) 121 .addOnConnectionFailedListener(this) 122 .addApi(Wearable.API) 123 .build(); 124 125 final BroadcastReceiver mTimeZoneReceiver = new BroadcastReceiver() { 126 @Override 127 public void onReceive(Context context, Intent intent) { 128 mTime.clear(intent.getStringExtra("time-zone")); 129 mTime.setToNow(); 130 } 131 }; 132 boolean mRegisteredTimeZoneReceiver = false; 133 134 Paint mBackgroundPaint; 135 Paint mHourPaint; 136 Paint mMinutePaint; 137 Paint mSecondPaint; 138 Paint mAmPmPaint; 139 Paint mColonPaint; 140 float mColonWidth; 141 boolean mMute; 142 Time mTime; 143 boolean mShouldDrawColons; 144 float mXOffset; 145 float mYOffset; 146 String mAmString; 147 String mPmString; 148 int mInteractiveBackgroundColor = 149 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND; 150 int mInteractiveHourDigitsColor = 151 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS; 152 int mInteractiveMinuteDigitsColor = 153 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS; 154 int mInteractiveSecondDigitsColor = 155 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS; 156 157 /** 158 * Whether the display supports fewer bits for each color in ambient mode. When true, we 159 * disable anti-aliasing in ambient mode. 160 */ 161 boolean mLowBitAmbient; 162 163 @Override onCreate(SurfaceHolder holder)164 public void onCreate(SurfaceHolder holder) { 165 if (Log.isLoggable(TAG, Log.DEBUG)) { 166 Log.d(TAG, "onCreate"); 167 } 168 super.onCreate(holder); 169 170 setWatchFaceStyle(new WatchFaceStyle.Builder(DigitalWatchFaceService.this) 171 .setCardPeekMode(WatchFaceStyle.PEEK_MODE_VARIABLE) 172 .setBackgroundVisibility(WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE) 173 .setShowSystemUiTime(false) 174 .build()); 175 Resources resources = DigitalWatchFaceService.this.getResources(); 176 mYOffset = resources.getDimension(R.dimen.digital_y_offset); 177 mAmString = resources.getString(R.string.digital_am); 178 mPmString = resources.getString(R.string.digital_pm); 179 180 mBackgroundPaint = new Paint(); 181 mBackgroundPaint.setColor(mInteractiveBackgroundColor); 182 mHourPaint = createTextPaint(mInteractiveHourDigitsColor, BOLD_TYPEFACE); 183 mMinutePaint = createTextPaint(mInteractiveMinuteDigitsColor); 184 mSecondPaint = createTextPaint(mInteractiveSecondDigitsColor); 185 mAmPmPaint = createTextPaint(resources.getColor(R.color.digital_am_pm)); 186 mColonPaint = createTextPaint(resources.getColor(R.color.digital_colons)); 187 188 mTime = new Time(); 189 } 190 191 @Override onDestroy()192 public void onDestroy() { 193 mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME); 194 super.onDestroy(); 195 } 196 createTextPaint(int defaultInteractiveColor)197 private Paint createTextPaint(int defaultInteractiveColor) { 198 return createTextPaint(defaultInteractiveColor, NORMAL_TYPEFACE); 199 } 200 createTextPaint(int defaultInteractiveColor, Typeface typeface)201 private Paint createTextPaint(int defaultInteractiveColor, Typeface typeface) { 202 Paint paint = new Paint(); 203 paint.setColor(defaultInteractiveColor); 204 paint.setTypeface(typeface); 205 paint.setAntiAlias(true); 206 return paint; 207 } 208 209 @Override onVisibilityChanged(boolean visible)210 public void onVisibilityChanged(boolean visible) { 211 if (Log.isLoggable(TAG, Log.DEBUG)) { 212 Log.d(TAG, "onVisibilityChanged: " + visible); 213 } 214 super.onVisibilityChanged(visible); 215 216 if (visible) { 217 mGoogleApiClient.connect(); 218 219 registerReceiver(); 220 221 // Update time zone in case it changed while we weren't visible. 222 mTime.clear(TimeZone.getDefault().getID()); 223 mTime.setToNow(); 224 } else { 225 unregisterReceiver(); 226 227 if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { 228 Wearable.DataApi.removeListener(mGoogleApiClient, this); 229 mGoogleApiClient.disconnect(); 230 } 231 } 232 233 // Whether the timer should be running depends on whether we're visible (as well as 234 // whether we're in ambient mode), so we may need to start or stop the timer. 235 updateTimer(); 236 } 237 registerReceiver()238 private void registerReceiver() { 239 if (mRegisteredTimeZoneReceiver) { 240 return; 241 } 242 mRegisteredTimeZoneReceiver = true; 243 IntentFilter filter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED); 244 DigitalWatchFaceService.this.registerReceiver(mTimeZoneReceiver, filter); 245 } 246 unregisterReceiver()247 private void unregisterReceiver() { 248 if (!mRegisteredTimeZoneReceiver) { 249 return; 250 } 251 mRegisteredTimeZoneReceiver = false; 252 DigitalWatchFaceService.this.unregisterReceiver(mTimeZoneReceiver); 253 } 254 255 @Override onApplyWindowInsets(WindowInsets insets)256 public void onApplyWindowInsets(WindowInsets insets) { 257 if (Log.isLoggable(TAG, Log.DEBUG)) { 258 Log.d(TAG, "onApplyWindowInsets: " + (insets.isRound() ? "round" : "square")); 259 } 260 super.onApplyWindowInsets(insets); 261 262 // Load resources that have alternate values for round watches. 263 Resources resources = DigitalWatchFaceService.this.getResources(); 264 boolean isRound = insets.isRound(); 265 mXOffset = resources.getDimension(isRound 266 ? R.dimen.digital_x_offset_round : R.dimen.digital_x_offset); 267 float textSize = resources.getDimension(isRound 268 ? R.dimen.digital_text_size_round : R.dimen.digital_text_size); 269 float amPmSize = resources.getDimension(isRound 270 ? R.dimen.digital_am_pm_size_round : R.dimen.digital_am_pm_size); 271 272 mHourPaint.setTextSize(textSize); 273 mMinutePaint.setTextSize(textSize); 274 mSecondPaint.setTextSize(textSize); 275 mAmPmPaint.setTextSize(amPmSize); 276 mColonPaint.setTextSize(textSize); 277 278 mColonWidth = mColonPaint.measureText(COLON_STRING); 279 } 280 281 @Override onPropertiesChanged(Bundle properties)282 public void onPropertiesChanged(Bundle properties) { 283 super.onPropertiesChanged(properties); 284 285 boolean burnInProtection = properties.getBoolean(PROPERTY_BURN_IN_PROTECTION, false); 286 mHourPaint.setTypeface(burnInProtection ? NORMAL_TYPEFACE : BOLD_TYPEFACE); 287 288 mLowBitAmbient = properties.getBoolean(PROPERTY_LOW_BIT_AMBIENT, false); 289 290 if (Log.isLoggable(TAG, Log.DEBUG)) { 291 Log.d(TAG, "onPropertiesChanged: burn-in protection = " + burnInProtection 292 + ", low-bit ambient = " + mLowBitAmbient); 293 } 294 } 295 296 @Override onTimeTick()297 public void onTimeTick() { 298 super.onTimeTick(); 299 if (Log.isLoggable(TAG, Log.DEBUG)) { 300 Log.d(TAG, "onTimeTick: ambient = " + isInAmbientMode()); 301 } 302 invalidate(); 303 } 304 305 @Override onAmbientModeChanged(boolean inAmbientMode)306 public void onAmbientModeChanged(boolean inAmbientMode) { 307 super.onAmbientModeChanged(inAmbientMode); 308 if (Log.isLoggable(TAG, Log.DEBUG)) { 309 Log.d(TAG, "onAmbientModeChanged: " + inAmbientMode); 310 } 311 adjustPaintColorToCurrentMode(mBackgroundPaint, mInteractiveBackgroundColor, 312 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND); 313 adjustPaintColorToCurrentMode(mHourPaint, mInteractiveHourDigitsColor, 314 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS); 315 adjustPaintColorToCurrentMode(mMinutePaint, mInteractiveMinuteDigitsColor, 316 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS); 317 // Actually, the seconds are not rendered in the ambient mode, so we could pass just any 318 // value as ambientColor here. 319 adjustPaintColorToCurrentMode(mSecondPaint, mInteractiveSecondDigitsColor, 320 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS); 321 322 if (mLowBitAmbient) { 323 boolean antiAlias = !inAmbientMode; 324 mHourPaint.setAntiAlias(antiAlias); 325 mMinutePaint.setAntiAlias(antiAlias); 326 mSecondPaint.setAntiAlias(antiAlias); 327 mAmPmPaint.setAntiAlias(antiAlias); 328 mColonPaint.setAntiAlias(antiAlias); 329 } 330 invalidate(); 331 332 // Whether the timer should be running depends on whether we're in ambient mode (as well 333 // as whether we're visible), so we may need to start or stop the timer. 334 updateTimer(); 335 } 336 adjustPaintColorToCurrentMode(Paint paint, int interactiveColor, int ambientColor)337 private void adjustPaintColorToCurrentMode(Paint paint, int interactiveColor, 338 int ambientColor) { 339 paint.setColor(isInAmbientMode() ? ambientColor : interactiveColor); 340 } 341 342 @Override onInterruptionFilterChanged(int interruptionFilter)343 public void onInterruptionFilterChanged(int interruptionFilter) { 344 if (Log.isLoggable(TAG, Log.DEBUG)) { 345 Log.d(TAG, "onInterruptionFilterChanged: " + interruptionFilter); 346 } 347 super.onInterruptionFilterChanged(interruptionFilter); 348 349 boolean inMuteMode = interruptionFilter == WatchFaceService.INTERRUPTION_FILTER_NONE; 350 // We only need to update once a minute in mute mode. 351 setInteractiveUpdateRateMs(inMuteMode ? MUTE_UPDATE_RATE_MS : NORMAL_UPDATE_RATE_MS); 352 353 if (mMute != inMuteMode) { 354 mMute = inMuteMode; 355 int alpha = inMuteMode ? MUTE_ALPHA : NORMAL_ALPHA; 356 mHourPaint.setAlpha(alpha); 357 mMinutePaint.setAlpha(alpha); 358 mColonPaint.setAlpha(alpha); 359 mAmPmPaint.setAlpha(alpha); 360 invalidate(); 361 } 362 } 363 setInteractiveUpdateRateMs(long updateRateMs)364 public void setInteractiveUpdateRateMs(long updateRateMs) { 365 if (updateRateMs == mInteractiveUpdateRateMs) { 366 return; 367 } 368 mInteractiveUpdateRateMs = updateRateMs; 369 370 // Stop and restart the timer so the new update rate takes effect immediately. 371 if (shouldTimerBeRunning()) { 372 updateTimer(); 373 } 374 } 375 updatePaintIfInteractive(Paint paint, int interactiveColor)376 private void updatePaintIfInteractive(Paint paint, int interactiveColor) { 377 if (!isInAmbientMode() && paint != null) { 378 paint.setColor(interactiveColor); 379 } 380 } 381 setInteractiveBackgroundColor(int color)382 private void setInteractiveBackgroundColor(int color) { 383 mInteractiveBackgroundColor = color; 384 updatePaintIfInteractive(mBackgroundPaint, color); 385 } 386 setInteractiveHourDigitsColor(int color)387 private void setInteractiveHourDigitsColor(int color) { 388 mInteractiveHourDigitsColor = color; 389 updatePaintIfInteractive(mHourPaint, color); 390 } 391 setInteractiveMinuteDigitsColor(int color)392 private void setInteractiveMinuteDigitsColor(int color) { 393 mInteractiveMinuteDigitsColor = color; 394 updatePaintIfInteractive(mMinutePaint, color); 395 } 396 setInteractiveSecondDigitsColor(int color)397 private void setInteractiveSecondDigitsColor(int color) { 398 mInteractiveSecondDigitsColor = color; 399 updatePaintIfInteractive(mSecondPaint, color); 400 } 401 formatTwoDigitNumber(int hour)402 private String formatTwoDigitNumber(int hour) { 403 return String.format("%02d", hour); 404 } 405 convertTo12Hour(int hour)406 private int convertTo12Hour(int hour) { 407 int result = hour % 12; 408 return (result == 0) ? 12 : result; 409 } 410 getAmPmString(int hour)411 private String getAmPmString(int hour) { 412 return (hour < 12) ? mAmString : mPmString; 413 } 414 415 @Override onDraw(Canvas canvas, Rect bounds)416 public void onDraw(Canvas canvas, Rect bounds) { 417 mTime.setToNow(); 418 419 // Show colons for the first half of each second so the colons blink on when the time 420 // updates. 421 mShouldDrawColons = (System.currentTimeMillis() % 1000) < 500; 422 423 // Draw the background. 424 canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint); 425 426 // Draw the hours. 427 float x = mXOffset; 428 String hourString = String.valueOf(convertTo12Hour(mTime.hour)); 429 canvas.drawText(hourString, x, mYOffset, mHourPaint); 430 x += mHourPaint.measureText(hourString); 431 432 // In ambient and mute modes, always draw the first colon. Otherwise, draw the 433 // first colon for the first half of each second. 434 if (isInAmbientMode() || mMute || mShouldDrawColons) { 435 canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint); 436 } 437 x += mColonWidth; 438 439 // Draw the minutes. 440 String minuteString = formatTwoDigitNumber(mTime.minute); 441 canvas.drawText(minuteString, x, mYOffset, mMinutePaint); 442 x += mMinutePaint.measureText(minuteString); 443 444 // In ambient and mute modes, draw AM/PM. Otherwise, draw a second blinking 445 // colon followed by the seconds. 446 if (isInAmbientMode() || mMute) { 447 x += mColonWidth; 448 canvas.drawText(getAmPmString(mTime.hour), x, mYOffset, mAmPmPaint); 449 } else { 450 if (mShouldDrawColons) { 451 canvas.drawText(COLON_STRING, x, mYOffset, mColonPaint); 452 } 453 x += mColonWidth; 454 canvas.drawText(formatTwoDigitNumber(mTime.second), x, mYOffset, 455 mSecondPaint); 456 } 457 } 458 459 /** 460 * Starts the {@link #mUpdateTimeHandler} timer if it should be running and isn't currently 461 * or stops it if it shouldn't be running but currently is. 462 */ 463 private void updateTimer() { 464 if (Log.isLoggable(TAG, Log.DEBUG)) { 465 Log.d(TAG, "updateTimer"); 466 } 467 mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME); 468 if (shouldTimerBeRunning()) { 469 mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME); 470 } 471 } 472 473 /** 474 * Returns whether the {@link #mUpdateTimeHandler} timer should be running. The timer should 475 * only run when we're visible and in interactive mode. 476 */ 477 private boolean shouldTimerBeRunning() { 478 return isVisible() && !isInAmbientMode(); 479 } 480 481 private void updateConfigDataItemAndUiOnStartup() { 482 DigitalWatchFaceUtil.fetchConfigDataMap(mGoogleApiClient, 483 new DigitalWatchFaceUtil.FetchConfigDataMapCallback() { 484 @Override 485 public void onConfigDataMapFetched(DataMap startupConfig) { 486 // If the DataItem hasn't been created yet or some keys are missing, 487 // use the default values. 488 setDefaultValuesForMissingConfigKeys(startupConfig); 489 DigitalWatchFaceUtil.putConfigDataItem(mGoogleApiClient, startupConfig); 490 491 updateUiForConfigDataMap(startupConfig); 492 } 493 } 494 ); 495 } 496 497 private void setDefaultValuesForMissingConfigKeys(DataMap config) { 498 addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_BACKGROUND_COLOR, 499 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_BACKGROUND); 500 addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_HOURS_COLOR, 501 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_HOUR_DIGITS); 502 addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_MINUTES_COLOR, 503 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_MINUTE_DIGITS); 504 addIntKeyIfMissing(config, DigitalWatchFaceUtil.KEY_SECONDS_COLOR, 505 DigitalWatchFaceUtil.COLOR_VALUE_DEFAULT_AND_AMBIENT_SECOND_DIGITS); 506 } 507 508 private void addIntKeyIfMissing(DataMap config, String key, int color) { 509 if (!config.containsKey(key)) { 510 config.putInt(key, color); 511 } 512 } 513 514 @Override // DataApi.DataListener 515 public void onDataChanged(DataEventBuffer dataEvents) { 516 try { 517 for (DataEvent dataEvent : dataEvents) { 518 if (dataEvent.getType() != DataEvent.TYPE_CHANGED) { 519 continue; 520 } 521 522 DataItem dataItem = dataEvent.getDataItem(); 523 if (!dataItem.getUri().getPath().equals( 524 DigitalWatchFaceUtil.PATH_WITH_FEATURE)) { 525 continue; 526 } 527 528 DataMapItem dataMapItem = DataMapItem.fromDataItem(dataItem); 529 DataMap config = dataMapItem.getDataMap(); 530 if (Log.isLoggable(TAG, Log.DEBUG)) { 531 Log.d(TAG, "Config DataItem updated:" + config); 532 } 533 updateUiForConfigDataMap(config); 534 } 535 } finally { 536 dataEvents.close(); 537 } 538 } 539 540 private void updateUiForConfigDataMap(final DataMap config) { 541 boolean uiUpdated = false; 542 for (String configKey : config.keySet()) { 543 if (!config.containsKey(configKey)) { 544 continue; 545 } 546 int color = config.getInt(configKey); 547 if (Log.isLoggable(TAG, Log.DEBUG)) { 548 Log.d(TAG, "Found watch face config key: " + configKey + " -> " 549 + Integer.toHexString(color)); 550 } 551 if (updateUiForKey(configKey, color)) { 552 uiUpdated = true; 553 } 554 } 555 if (uiUpdated) { 556 invalidate(); 557 } 558 } 559 560 /** 561 * Updates the color of a UI item according to the given {@code configKey}. Does nothing if 562 * {@code configKey} isn't recognized. 563 * 564 * @return whether UI has been updated 565 */ 566 private boolean updateUiForKey(String configKey, int color) { 567 if (configKey.equals(DigitalWatchFaceUtil.KEY_BACKGROUND_COLOR)) { 568 setInteractiveBackgroundColor(color); 569 } else if (configKey.equals(DigitalWatchFaceUtil.KEY_HOURS_COLOR)) { 570 setInteractiveHourDigitsColor(color); 571 } else if (configKey.equals(DigitalWatchFaceUtil.KEY_MINUTES_COLOR)) { 572 setInteractiveMinuteDigitsColor(color); 573 } else if (configKey.equals(DigitalWatchFaceUtil.KEY_SECONDS_COLOR)) { 574 setInteractiveSecondDigitsColor(color); 575 } else { 576 Log.w(TAG, "Ignoring unknown config key: " + configKey); 577 return false; 578 } 579 return true; 580 } 581 582 @Override // GoogleApiClient.ConnectionCallbacks 583 public void onConnected(Bundle connectionHint) { 584 if (Log.isLoggable(TAG, Log.DEBUG)) { 585 Log.d(TAG, "onConnected: " + connectionHint); 586 } 587 Wearable.DataApi.addListener(mGoogleApiClient, Engine.this); 588 updateConfigDataItemAndUiOnStartup(); 589 } 590 591 @Override // GoogleApiClient.ConnectionCallbacks 592 public void onConnectionSuspended(int cause) { 593 if (Log.isLoggable(TAG, Log.DEBUG)) { 594 Log.d(TAG, "onConnectionSuspended: " + cause); 595 } 596 } 597 598 @Override // GoogleApiClient.OnConnectionFailedListener 599 public void onConnectionFailed(ConnectionResult result) { 600 if (Log.isLoggable(TAG, Log.DEBUG)) { 601 Log.d(TAG, "onConnectionFailed: " + result); 602 } 603 } 604 } 605 } 606