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