ZenModePanel.java revision b71e68f9aa44b220cabaace70493751538d94dc0
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 17package com.android.systemui.volume; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.content.Context; 22import android.content.Intent; 23import android.content.SharedPreferences; 24import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 25import android.content.res.Resources; 26import android.net.Uri; 27import android.os.Handler; 28import android.os.Looper; 29import android.os.Message; 30import android.provider.Settings; 31import android.provider.Settings.Global; 32import android.service.notification.Condition; 33import android.service.notification.ZenModeConfig; 34import android.text.format.DateFormat; 35import android.text.format.Time; 36import android.util.AttributeSet; 37import android.util.Log; 38import android.view.LayoutInflater; 39import android.view.View; 40import android.view.animation.AnimationUtils; 41import android.view.animation.Interpolator; 42import android.widget.CompoundButton; 43import android.widget.CompoundButton.OnCheckedChangeListener; 44import android.widget.ImageView; 45import android.widget.LinearLayout; 46import android.widget.RadioButton; 47import android.widget.TextView; 48 49import com.android.systemui.R; 50import com.android.systemui.statusbar.policy.ZenModeController; 51 52import java.text.SimpleDateFormat; 53import java.util.Arrays; 54import java.util.Date; 55import java.util.Locale; 56import java.util.Objects; 57 58public class ZenModePanel extends LinearLayout { 59 private static final String TAG = "ZenModePanel"; 60 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61 62 private static final int SECONDS_MS = 1000; 63 private static final int MINUTES_MS = 60 * SECONDS_MS; 64 65 private static final int[] MINUTE_BUCKETS = DEBUG 66 ? new int[] { 0, 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 } 67 : new int[] { 15, 30, 45, 60, 120, 180, 240, 480 }; 68 private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0]; 69 private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1]; 70 private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); 71 private static final int FOREVER_CONDITION_INDEX = 0; 72 private static final int TIME_CONDITION_INDEX = 1; 73 private static final int FIRST_CONDITION_INDEX = 2; 74 private static final float SILENT_HINT_PULSE_SCALE = 1.1f; 75 private static final int ZERO_VALUE_MS = 20 * SECONDS_MS; 76 77 public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); 78 79 private final Context mContext; 80 private final LayoutInflater mInflater; 81 private final H mHandler = new H(); 82 private final Favorites mFavorites; 83 private final Interpolator mFastOutSlowInInterpolator; 84 private final int mHardWarningColor; 85 private final int mSoftWarningColor; 86 87 private char mLogTag = '?'; 88 private String mTag; 89 90 private SegmentedButtons mZenButtons; 91 private View mZenSubhead; 92 private TextView mZenSubheadCollapsed; 93 private TextView mZenSubheadExpanded; 94 private View mMoreSettings; 95 private LinearLayout mZenConditions; 96 private TextView mAlarmWarning; 97 98 private int mBottomPadding; 99 private Callback mCallback; 100 private ZenModeController mController; 101 private boolean mRequestingConditions; 102 private Uri mExitConditionId; 103 private int mBucketIndex = -1; 104 private boolean mExpanded; 105 private int mSessionZen; 106 private Uri mSessionExitConditionId; 107 private String mExitConditionText; 108 private long mNextAlarm; 109 110 public ZenModePanel(Context context, AttributeSet attrs) { 111 super(context, attrs); 112 mContext = context; 113 mFavorites = new Favorites(); 114 mInflater = LayoutInflater.from(mContext.getApplicationContext()); 115 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext, 116 android.R.interpolator.fast_out_slow_in); 117 final Resources res = mContext.getResources(); 118 mHardWarningColor = res.getColor(R.color.qs_text); 119 mSoftWarningColor = res.getColor(R.color.zen_alarm_soft_warning_text); 120 updateTag(); 121 if (DEBUG) Log.d(mTag, "new ZenModePanel"); 122 } 123 124 private void updateTag() { 125 mTag = TAG + "/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this)); 126 } 127 128 @Override 129 protected void onFinishInflate() { 130 super.onFinishInflate(); 131 132 mBottomPadding = getPaddingBottom(); 133 134 mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons); 135 mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS); 136 mZenButtons.addButton(R.string.interruption_level_priority, 137 Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); 138 mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF); 139 mZenButtons.setCallback(mZenButtonsCallback); 140 141 mZenSubhead = findViewById(R.id.zen_subhead); 142 143 mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed); 144 mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() { 145 @Override 146 public void onClick(View v) { 147 setExpanded(true); 148 fireInteraction(); 149 } 150 }); 151 152 mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded); 153 154 mMoreSettings = findViewById(R.id.zen_more_settings); 155 mMoreSettings.setOnClickListener(new View.OnClickListener() { 156 @Override 157 public void onClick(View v) { 158 fireMoreSettings(); 159 fireInteraction(); 160 } 161 }); 162 163 mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions); 164 mAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning); 165 } 166 167 @Override 168 protected void onAttachedToWindow() { 169 super.onAttachedToWindow(); 170 if (DEBUG) Log.d(mTag, "onAttachedToWindow"); 171 mSessionZen = getSelectedZen(-1); 172 mSessionExitConditionId = mExitConditionId; 173 refreshExitConditionText(); 174 refreshNextAlarm(); 175 updateWidgets(); 176 } 177 178 @Override 179 protected void onDetachedFromWindow() { 180 super.onDetachedFromWindow(); 181 if (DEBUG) Log.d(mTag, "onDetachedFromWindow"); 182 mSessionZen = -1; 183 mSessionExitConditionId = null; 184 setExpanded(false); 185 } 186 187 private void setExpanded(boolean expanded) { 188 if (expanded == mExpanded) return; 189 mExpanded = expanded; 190 updateWidgets(); 191 setRequestingConditions(mExpanded); 192 fireExpanded(); 193 } 194 195 /** Start or stop requesting relevant zen mode exit conditions */ 196 private void setRequestingConditions(boolean requesting) { 197 if (mRequestingConditions == requesting) return; 198 if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting); 199 mRequestingConditions = requesting; 200 if (mController != null) { 201 mController.requestConditions(mRequestingConditions); 202 } 203 if (mRequestingConditions) { 204 Condition timeCondition = parseExistingTimeCondition(mExitConditionId); 205 if (timeCondition != null) { 206 mBucketIndex = -1; 207 } else { 208 mBucketIndex = DEFAULT_BUCKET_INDEX; 209 timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]); 210 } 211 if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex); 212 handleUpdateConditions(new Condition[0]); // ensures forever exists 213 bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); 214 checkForDefault(); 215 } else { 216 mZenConditions.removeAllViews(); 217 } 218 } 219 220 public void init(ZenModeController controller, char logTag) { 221 mController = controller; 222 mLogTag = logTag; 223 updateTag(); 224 setExitConditionId(mController.getExitConditionId()); 225 refreshExitConditionText(); 226 mSessionZen = getSelectedZen(-1); 227 handleUpdateZen(mController.getZen()); 228 if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId); 229 mZenConditions.removeAllViews(); 230 mController.addCallback(mZenCallback); 231 } 232 233 private void setExitConditionId(Uri exitConditionId) { 234 if (Objects.equals(mExitConditionId, exitConditionId)) return; 235 mExitConditionId = exitConditionId; 236 refreshExitConditionText(); 237 updateWidgets(); 238 } 239 240 private void refreshExitConditionText() { 241 final String forever = mContext.getString(R.string.zen_mode_forever); 242 if (mExitConditionId == null) { 243 mExitConditionText = forever; 244 } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) { 245 final Condition condition = parseExistingTimeCondition(mExitConditionId); 246 mExitConditionText = condition != null ? condition.summary : forever; 247 } else { 248 mExitConditionText = "(until condition ends)"; // TODO persist current description 249 } 250 } 251 252 public void setCallback(Callback callback) { 253 mCallback = callback; 254 } 255 256 public void showSilentHint() { 257 if (DEBUG) Log.d(mTag, "showSilentHint"); 258 if (mZenButtons == null || mZenButtons.getChildCount() == 0) return; 259 final View noneButton = mZenButtons.getChildAt(0); 260 if (noneButton.getScaleX() != 1) return; // already running 261 noneButton.animate().cancel(); 262 noneButton.animate().scaleX(SILENT_HINT_PULSE_SCALE).scaleY(SILENT_HINT_PULSE_SCALE) 263 .setInterpolator(mFastOutSlowInInterpolator) 264 .setListener(new AnimatorListenerAdapter() { 265 @Override 266 public void onAnimationEnd(Animator animation) { 267 noneButton.animate().scaleX(1).scaleY(1).setListener(null); 268 } 269 }); 270 } 271 272 private void handleUpdateZen(int zen) { 273 if (mSessionZen != -1 && mSessionZen != zen) { 274 setExpanded(zen != Global.ZEN_MODE_OFF); 275 mSessionZen = zen; 276 } 277 mZenButtons.setSelectedValue(zen); 278 updateWidgets(); 279 } 280 281 private int getSelectedZen(int defValue) { 282 final Object zen = mZenButtons.getSelectedValue(); 283 return zen != null ? (Integer) zen : defValue; 284 } 285 286 private void updateWidgets() { 287 final int zen = getSelectedZen(Global.ZEN_MODE_OFF); 288 final boolean zenOff = zen == Global.ZEN_MODE_OFF; 289 final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 290 final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS; 291 final boolean foreverSelected = mExitConditionId == null; 292 final boolean hasNextAlarm = mNextAlarm != 0; 293 final boolean showAlarmWarning = zenNone && mExpanded && hasNextAlarm; 294 295 mZenSubhead.setVisibility(!zenOff && (mExpanded || !foreverSelected) ? VISIBLE : GONE); 296 mZenSubheadExpanded.setVisibility(mExpanded ? VISIBLE : GONE); 297 mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE); 298 mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE); 299 mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE); 300 mAlarmWarning.setVisibility(zenNone && mExpanded && hasNextAlarm ? VISIBLE : GONE); 301 setPadding(0, 0, 0, showAlarmWarning ? 0 : mBottomPadding); 302 if (showAlarmWarning) { 303 final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId); 304 final long now = System.currentTimeMillis(); 305 final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay; 306 final String skeleton = (alarmToday ? "" : "E") 307 + (DateFormat.is24HourFormat(mContext) ? "Hm" : "hma"); 308 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 309 final String alarm = new SimpleDateFormat(pattern).format(new Date(mNextAlarm)); 310 final boolean isWarning = exitTime > 0 && mNextAlarm > now && mNextAlarm < exitTime; 311 if (isWarning) { 312 mAlarmWarning.setText(mContext.getString(R.string.zen_alarm_warning, alarm)); 313 mAlarmWarning.setTextColor(mHardWarningColor); 314 mAlarmWarning.setBackgroundResource(R.drawable.zen_alarm_hard_background); 315 } else { 316 mAlarmWarning.setText(mContext.getString(alarmToday 317 ? R.string.zen_alarm_information_time 318 : R.string.zen_alarm_information_day_time, alarm)); 319 mAlarmWarning.setTextColor(mSoftWarningColor); 320 mAlarmWarning.setBackgroundResource(R.drawable.zen_alarm_soft_background); 321 } 322 } 323 324 if (zenNone) { 325 mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning); 326 mZenSubheadCollapsed.setText(mExitConditionText); 327 } else if (zenImportant) { 328 mZenSubheadExpanded.setText(R.string.zen_important_interruptions); 329 mZenSubheadCollapsed.setText(mExitConditionText); 330 } 331 } 332 333 private static Time time(long millis) { 334 final Time t = new Time(); 335 t.set(millis); 336 return t; 337 } 338 339 private Condition parseExistingTimeCondition(Uri conditionId) { 340 final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); 341 if (time == 0) return null; 342 final long span = time - System.currentTimeMillis(); 343 if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null; 344 return timeCondition(time, Math.round(span / (float)MINUTES_MS)); 345 } 346 347 private Condition newTimeCondition(int minutesFromNow) { 348 final long now = System.currentTimeMillis(); 349 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; 350 return timeCondition(now + millis, minutesFromNow); 351 } 352 353 private Condition timeCondition(long time, int minutes) { 354 final int num = minutes < 60 ? minutes : Math.round(minutes / 60f); 355 final int resId = minutes < 60 356 ? R.plurals.zen_mode_duration_minutes 357 : R.plurals.zen_mode_duration_hours; 358 final String caption = mContext.getResources().getQuantityString(resId, num, num); 359 final Uri id = ZenModeConfig.toCountdownConditionId(time); 360 return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE, 361 Condition.FLAG_RELEVANT_NOW); 362 } 363 364 private void refreshNextAlarm() { 365 mNextAlarm = mController.getNextAlarm(); 366 } 367 368 private void handleNextAlarmChanged() { 369 refreshNextAlarm(); 370 updateWidgets(); 371 } 372 373 private void handleUpdateConditions(Condition[] conditions) { 374 final int newCount = conditions == null ? 0 : conditions.length; 375 if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount); 376 for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) { 377 mZenConditions.removeViewAt(i); 378 } 379 bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX)); 380 for (int i = 0; i < newCount; i++) { 381 bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i)); 382 } 383 } 384 385 private ConditionTag getConditionTagAt(int index) { 386 return (ConditionTag) mZenConditions.getChildAt(index).getTag(); 387 } 388 389 private void checkForDefault() { 390 // are we left without anything selected? if so, set a default 391 for (int i = 0; i < mZenConditions.getChildCount(); i++) { 392 if (getConditionTagAt(i).rb.isChecked()) { 393 if (DEBUG) Log.d(mTag, "Not selecting a default, checked=" 394 + getConditionTagAt(i).conditionId); 395 return; 396 } 397 } 398 if (DEBUG) Log.d(mTag, "Selecting a default"); 399 final int favoriteIndex = mFavorites.getMinuteIndex(); 400 if (favoriteIndex == -1) { 401 getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true); 402 } else { 403 final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]); 404 mBucketIndex = favoriteIndex; 405 bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); 406 getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true); 407 } 408 } 409 410 private void handleExitConditionChanged(Uri exitCondition) { 411 setExitConditionId(exitCondition); 412 if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId); 413 final int N = mZenConditions.getChildCount(); 414 for (int i = 0; i < N; i++) { 415 final ConditionTag tag = getConditionTagAt(i); 416 tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition)); 417 } 418 } 419 420 private void bind(final Condition condition, View convertView) { 421 final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE; 422 final View row; 423 if (convertView == null) { 424 row = mInflater.inflate(R.layout.zen_mode_condition, this, false); 425 if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition); 426 mZenConditions.addView(row); 427 } else { 428 row = convertView; 429 } 430 final ConditionTag tag = 431 row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag(); 432 row.setTag(tag); 433 if (tag.rb == null) { 434 tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox); 435 } 436 tag.conditionId = condition != null ? condition.id : null; 437 tag.rb.setEnabled(enabled); 438 if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) { 439 tag.rb.setChecked(true); 440 } 441 tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() { 442 @Override 443 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 444 if (mExpanded && isChecked) { 445 if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId); 446 final int N = mZenConditions.getChildCount(); 447 for (int i = 0; i < N; i++) { 448 ConditionTag childTag = getConditionTagAt(i); 449 if (childTag == tag) continue; 450 childTag.rb.setChecked(false); 451 } 452 select(tag.conditionId); 453 fireInteraction(); 454 } 455 } 456 }); 457 final TextView title = (TextView) row.findViewById(android.R.id.title); 458 if (condition == null) { 459 title.setText(R.string.zen_mode_forever); 460 } else { 461 title.setText(condition.summary); 462 } 463 title.setEnabled(enabled); 464 title.setAlpha(enabled ? 1 : .4f); 465 final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1); 466 button1.setOnClickListener(new OnClickListener() { 467 @Override 468 public void onClick(View v) { 469 onClickTimeButton(row, tag, false /*down*/); 470 } 471 }); 472 473 final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2); 474 button2.setOnClickListener(new OnClickListener() { 475 @Override 476 public void onClick(View v) { 477 onClickTimeButton(row, tag, true /*up*/); 478 } 479 }); 480 title.setOnClickListener(new OnClickListener() { 481 @Override 482 public void onClick(View v) { 483 tag.rb.setChecked(true); 484 fireInteraction(); 485 } 486 }); 487 488 final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId); 489 if (time > 0) { 490 if (mBucketIndex > -1) { 491 button1.setEnabled(mBucketIndex > 0); 492 button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); 493 } else { 494 final long span = time - System.currentTimeMillis(); 495 button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS); 496 final Condition maxCondition = newTimeCondition(MAX_BUCKET_MINUTES); 497 button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary)); 498 } 499 500 button1.setAlpha(button1.isEnabled() ? 1f : .5f); 501 button2.setAlpha(button2.isEnabled() ? 1f : .5f); 502 } else { 503 button1.setVisibility(View.GONE); 504 button2.setVisibility(View.GONE); 505 } 506 } 507 508 private void onClickTimeButton(View row, ConditionTag tag, boolean up) { 509 Condition newCondition = null; 510 final int N = MINUTE_BUCKETS.length; 511 if (mBucketIndex == -1) { 512 // not on a known index, search for the next or prev bucket by time 513 final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId); 514 final long now = System.currentTimeMillis(); 515 for (int i = 0; i < N; i++) { 516 int j = up ? i : N - 1 - i; 517 final int bucketMinutes = MINUTE_BUCKETS[j]; 518 final long bucketTime = now + bucketMinutes * MINUTES_MS; 519 if (up && bucketTime > time || !up && bucketTime < time) { 520 mBucketIndex = j; 521 newCondition = timeCondition(bucketTime, bucketMinutes); 522 break; 523 } 524 } 525 if (newCondition == null) { 526 mBucketIndex = DEFAULT_BUCKET_INDEX; 527 newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]); 528 } 529 } else { 530 // on a known index, simply increment or decrement 531 mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1))); 532 newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]); 533 } 534 bind(newCondition, row); 535 tag.rb.setChecked(true); 536 select(newCondition.id); 537 fireInteraction(); 538 } 539 540 private void select(Uri conditionId) { 541 if (DEBUG) Log.d(mTag, "select " + conditionId); 542 if (mController != null) { 543 mController.setExitConditionId(conditionId); 544 } 545 setExitConditionId(conditionId); 546 if (conditionId == null) { 547 mFavorites.setMinuteIndex(-1); 548 } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) { 549 mFavorites.setMinuteIndex(mBucketIndex); 550 } 551 mSessionExitConditionId = conditionId; 552 } 553 554 private void fireMoreSettings() { 555 if (mCallback != null) { 556 mCallback.onMoreSettings(); 557 } 558 } 559 560 private void fireInteraction() { 561 if (mCallback != null) { 562 mCallback.onInteraction(); 563 } 564 } 565 566 private void fireExpanded() { 567 if (mCallback != null) { 568 mCallback.onExpanded(mExpanded); 569 } 570 } 571 572 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() { 573 @Override 574 public void onZenChanged(int zen) { 575 mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget(); 576 } 577 @Override 578 public void onConditionsChanged(Condition[] conditions) { 579 mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget(); 580 } 581 582 @Override 583 public void onExitConditionChanged(Uri exitConditionId) { 584 mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget(); 585 } 586 587 @Override 588 public void onNextAlarmChanged() { 589 mHandler.sendEmptyMessage(H.NEXT_ALARM_CHANGED); 590 } 591 }; 592 593 private final class H extends Handler { 594 private static final int UPDATE_CONDITIONS = 1; 595 private static final int EXIT_CONDITION_CHANGED = 2; 596 private static final int UPDATE_ZEN = 3; 597 private static final int NEXT_ALARM_CHANGED = 4; 598 599 private H() { 600 super(Looper.getMainLooper()); 601 } 602 603 @Override 604 public void handleMessage(Message msg) { 605 if (msg.what == UPDATE_CONDITIONS) { 606 handleUpdateConditions((Condition[]) msg.obj); 607 checkForDefault(); 608 } else if (msg.what == EXIT_CONDITION_CHANGED) { 609 handleExitConditionChanged((Uri) msg.obj); 610 } else if (msg.what == UPDATE_ZEN) { 611 handleUpdateZen(msg.arg1); 612 } else if (msg.what == NEXT_ALARM_CHANGED) { 613 handleNextAlarmChanged(); 614 } 615 } 616 } 617 618 public interface Callback { 619 void onMoreSettings(); 620 void onInteraction(); 621 void onExpanded(boolean expanded); 622 } 623 624 // used as the view tag on condition rows 625 private static class ConditionTag { 626 RadioButton rb; 627 Uri conditionId; 628 } 629 630 private final class Favorites implements OnSharedPreferenceChangeListener { 631 private static final String KEY_MINUTE_INDEX = "minuteIndex"; 632 633 private int mMinuteIndex; 634 635 private Favorites() { 636 prefs().registerOnSharedPreferenceChangeListener(this); 637 updateMinuteIndex(); 638 } 639 640 public int getMinuteIndex() { 641 return mMinuteIndex; 642 } 643 644 public void setMinuteIndex(int minuteIndex) { 645 minuteIndex = clamp(minuteIndex); 646 if (minuteIndex == mMinuteIndex) return; 647 mMinuteIndex = clamp(minuteIndex); 648 if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex); 649 prefs().edit().putInt(KEY_MINUTE_INDEX, mMinuteIndex).apply(); 650 } 651 652 @Override 653 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 654 updateMinuteIndex(); 655 } 656 657 private SharedPreferences prefs() { 658 return mContext.getSharedPreferences(ZenModePanel.class.getSimpleName(), 0); 659 } 660 661 private void updateMinuteIndex() { 662 mMinuteIndex = clamp(prefs().getInt(KEY_MINUTE_INDEX, DEFAULT_BUCKET_INDEX)); 663 if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex); 664 } 665 666 private int clamp(int index) { 667 return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index)); 668 } 669 } 670 671 private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() { 672 @Override 673 public void onSelected(Object value) { 674 if (value != null && mZenButtons.isShown()) { 675 if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value); 676 mController.setZen((Integer) value); 677 } 678 } 679 }; 680} 681