ZenModePanel.java revision 661f2cf45860d2e10924e6b69966a9afe255f28b
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.app.ActivityManager; 20import android.content.Context; 21import android.content.Intent; 22import android.content.SharedPreferences; 23import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 24import android.content.res.Resources; 25import android.net.Uri; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.Message; 29import android.provider.Settings; 30import android.provider.Settings.Global; 31import android.service.notification.Condition; 32import android.service.notification.ZenModeConfig; 33import android.text.TextUtils; 34import android.util.AttributeSet; 35import android.util.Log; 36import android.util.MathUtils; 37import android.view.LayoutInflater; 38import android.view.View; 39import android.widget.CompoundButton; 40import android.widget.CompoundButton.OnCheckedChangeListener; 41import android.widget.ImageView; 42import android.widget.LinearLayout; 43import android.widget.RadioButton; 44import android.widget.TextView; 45 46import com.android.systemui.R; 47import com.android.systemui.statusbar.policy.ZenModeController; 48 49import java.util.Arrays; 50import java.util.Objects; 51 52public class ZenModePanel extends LinearLayout { 53 private static final String TAG = "ZenModePanel"; 54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 55 56 private static final int SECONDS_MS = 1000; 57 private static final int MINUTES_MS = 60 * SECONDS_MS; 58 59 private static final int[] MINUTE_BUCKETS = DEBUG 60 ? new int[] { 0, 1, 2, 5, 15, 30, 45, 60, 120, 180, 240, 480 } 61 : ZenModeConfig.MINUTE_BUCKETS; 62 private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0]; 63 private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1]; 64 private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); 65 private static final int FOREVER_CONDITION_INDEX = 0; 66 private static final int TIME_CONDITION_INDEX = 1; 67 private static final int FIRST_CONDITION_INDEX = 2; 68 private static final long SELECT_DEFAULT_DELAY = 300; 69 70 public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); 71 72 private final Context mContext; 73 private final LayoutInflater mInflater; 74 private final H mHandler = new H(); 75 private final Prefs mPrefs; 76 private final IconPulser mIconPulser; 77 private final int mSubheadWarningColor; 78 private final int mSubheadColor; 79 80 private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this)); 81 82 private SegmentedButtons mZenButtons; 83 private View mZenSubhead; 84 private TextView mZenSubheadCollapsed; 85 private TextView mZenSubheadExpanded; 86 private View mMoreSettings; 87 private LinearLayout mZenConditions; 88 89 private Callback mCallback; 90 private ZenModeController mController; 91 private boolean mRequestingConditions; 92 private Condition mExitCondition; 93 private String mExitConditionText; 94 private int mBucketIndex = -1; 95 private boolean mExpanded; 96 private boolean mHidden = false; 97 private int mSessionZen; 98 private int mAttachedZen; 99 private Condition mSessionExitCondition; 100 private Condition[] mConditions; 101 private Condition mTimeCondition; 102 103 public ZenModePanel(Context context, AttributeSet attrs) { 104 super(context, attrs); 105 mContext = context; 106 mPrefs = new Prefs(); 107 mInflater = LayoutInflater.from(mContext.getApplicationContext()); 108 mIconPulser = new IconPulser(mContext); 109 final Resources res = mContext.getResources(); 110 mSubheadWarningColor = res.getColor(R.color.system_warning_color); 111 mSubheadColor = res.getColor(R.color.qs_subhead); 112 if (DEBUG) Log.d(mTag, "new ZenModePanel"); 113 } 114 115 @Override 116 protected void onFinishInflate() { 117 super.onFinishInflate(); 118 119 mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons); 120 mZenButtons.addButton(R.string.interruption_level_none, R.drawable.ic_zen_none, 121 Global.ZEN_MODE_NO_INTERRUPTIONS); 122 mZenButtons.addButton(R.string.interruption_level_priority, R.drawable.ic_zen_important, 123 Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); 124 mZenButtons.addButton(R.string.interruption_level_all, R.drawable.ic_zen_all, 125 Global.ZEN_MODE_OFF); 126 mZenButtons.setCallback(mZenButtonsCallback); 127 128 mZenSubhead = findViewById(R.id.zen_subhead); 129 130 mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed); 131 mZenSubheadCollapsed.setOnClickListener(new View.OnClickListener() { 132 @Override 133 public void onClick(View v) { 134 setExpanded(true); 135 } 136 }); 137 Interaction.register(mZenSubheadCollapsed, mInteractionCallback); 138 139 mZenSubheadExpanded = (TextView) findViewById(R.id.zen_subhead_expanded); 140 Interaction.register(mZenSubheadExpanded, mInteractionCallback); 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 } 148 }); 149 Interaction.register(mMoreSettings, mInteractionCallback); 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(mContext, 216 MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); 217 } 218 if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex); 219 mConditions = null; // reset conditions 220 handleUpdateConditions(); 221 } else { 222 mZenConditions.removeAllViews(); 223 } 224 } 225 226 public void init(ZenModeController controller) { 227 mController = controller; 228 setExitCondition(mController.getExitCondition()); 229 refreshExitConditionText(); 230 mSessionZen = getSelectedZen(-1); 231 handleUpdateZen(mController.getZen()); 232 if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition); 233 mZenConditions.removeAllViews(); 234 mController.addCallback(mZenCallback); 235 } 236 237 public void updateLocale() { 238 mZenButtons.updateLocale(); 239 } 240 241 private void setExitCondition(Condition exitCondition) { 242 if (Objects.equals(mExitCondition, exitCondition)) return; 243 mExitCondition = exitCondition; 244 refreshExitConditionText(); 245 updateWidgets(); 246 } 247 248 private static Uri getConditionId(Condition condition) { 249 return condition != null ? condition.id : null; 250 } 251 252 private static boolean sameConditionId(Condition lhs, Condition rhs) { 253 return lhs == null ? rhs == null : rhs != null && lhs.id.equals(rhs.id); 254 } 255 256 private static Condition copy(Condition condition) { 257 return condition == null ? null : condition.copy(); 258 } 259 260 private void refreshExitConditionText() { 261 final String forever = mContext.getString(com.android.internal.R.string.zen_mode_forever); 262 if (mExitCondition == null) { 263 mExitConditionText = forever; 264 } else if (ZenModeConfig.isValidCountdownConditionId(mExitCondition.id)) { 265 final Condition condition = parseExistingTimeCondition(mExitCondition); 266 mExitConditionText = condition != null ? condition.summary : forever; 267 } else { 268 mExitConditionText = mExitCondition.summary; 269 } 270 } 271 272 public void setCallback(Callback callback) { 273 mCallback = callback; 274 } 275 276 public void showSilentHint() { 277 if (DEBUG) Log.d(mTag, "showSilentHint"); 278 if (mZenButtons == null || mZenButtons.getChildCount() == 0) return; 279 final View noneButton = mZenButtons.getChildAt(0); 280 mIconPulser.start(noneButton); 281 } 282 283 private void handleUpdateZen(int zen) { 284 if (mSessionZen != -1 && mSessionZen != zen) { 285 setExpanded(zen != Global.ZEN_MODE_OFF); 286 mSessionZen = zen; 287 } 288 mZenButtons.setSelectedValue(zen); 289 updateWidgets(); 290 } 291 292 private int getSelectedZen(int defValue) { 293 final Object zen = mZenButtons.getSelectedValue(); 294 return zen != null ? (Integer) zen : defValue; 295 } 296 297 private void updateWidgets() { 298 final int zen = getSelectedZen(Global.ZEN_MODE_OFF); 299 final boolean zenOff = zen == Global.ZEN_MODE_OFF; 300 final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 301 final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS; 302 final boolean expanded = !mHidden && mExpanded; 303 304 mZenButtons.setVisibility(mHidden ? GONE : VISIBLE); 305 mZenSubhead.setVisibility(!mHidden && !zenOff ? VISIBLE : GONE); 306 mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE); 307 mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE); 308 mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE); 309 mZenConditions.setVisibility(!zenOff && expanded ? VISIBLE : GONE); 310 311 if (zenNone) { 312 mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning); 313 mZenSubheadCollapsed.setText(mExitConditionText); 314 } else if (zenImportant) { 315 mZenSubheadExpanded.setText(R.string.zen_important_interruptions); 316 mZenSubheadCollapsed.setText(mExitConditionText); 317 } 318 mZenSubheadExpanded.setTextColor(zenNone && mPrefs.isNoneDangerous() 319 ? mSubheadWarningColor : mSubheadColor); 320 } 321 322 private Condition parseExistingTimeCondition(Condition condition) { 323 if (condition == null) return null; 324 final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id); 325 if (time == 0) return null; 326 final long now = System.currentTimeMillis(); 327 final long span = time - now; 328 if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null; 329 return ZenModeConfig.toTimeCondition(mContext, 330 time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser()); 331 } 332 333 private void handleUpdateConditions(Condition[] conditions) { 334 mConditions = conditions; 335 handleUpdateConditions(); 336 } 337 338 private void handleUpdateConditions() { 339 final int conditionCount = mConditions == null ? 0 : mConditions.length; 340 if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount); 341 for (int i = mZenConditions.getChildCount() - 1; i >= FIRST_CONDITION_INDEX; i--) { 342 mZenConditions.removeViewAt(i); 343 } 344 // forever 345 bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX)); 346 // countdown 347 bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); 348 // provider conditions 349 boolean foundDowntime = false; 350 for (int i = 0; i < conditionCount; i++) { 351 bind(mConditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i)); 352 foundDowntime |= isDowntime(mConditions[i]); 353 } 354 // ensure downtime exists, if active 355 if (isDowntime(mSessionExitCondition) && !foundDowntime) { 356 bind(mSessionExitCondition, null); 357 } 358 // ensure something is selected, after waiting for providers to respond 359 mHandler.removeMessages(H.SELECT_DEFAULT); 360 mHandler.sendEmptyMessageDelayed(H.SELECT_DEFAULT, SELECT_DEFAULT_DELAY); 361 } 362 363 private static boolean isDowntime(Condition c) { 364 return ZenModeConfig.isValidDowntimeConditionId(getConditionId(c)); 365 } 366 367 private ConditionTag getConditionTagAt(int index) { 368 return (ConditionTag) mZenConditions.getChildAt(index).getTag(); 369 } 370 371 private void handleSelectDefault() { 372 if (!mExpanded) return; 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(mContext, 387 MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser()); 388 mBucketIndex = favoriteIndex; 389 bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX)); 390 getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true); 391 } 392 } 393 394 private void handleExitConditionChanged(Condition exitCondition) { 395 setExitCondition(exitCondition); 396 if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition); 397 final int N = mZenConditions.getChildCount(); 398 for (int i = 0; i < N; i++) { 399 final ConditionTag tag = getConditionTagAt(i); 400 tag.rb.setChecked(sameConditionId(tag.condition, mExitCondition)); 401 } 402 } 403 404 private void bind(final Condition condition, View convertView) { 405 final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE; 406 final View row; 407 if (convertView == null) { 408 row = mInflater.inflate(R.layout.zen_mode_condition, this, false); 409 if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition); 410 mZenConditions.addView(row); 411 } else { 412 row = convertView; 413 } 414 final ConditionTag tag = 415 row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag(); 416 row.setTag(tag); 417 if (tag.rb == null) { 418 tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox); 419 } 420 tag.condition = condition; 421 tag.rb.setEnabled(enabled); 422 if (mSessionExitCondition != null 423 && sameConditionId(mSessionExitCondition, tag.condition)) { 424 tag.rb.setChecked(true); 425 } 426 tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() { 427 @Override 428 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 429 if (mExpanded && isChecked) { 430 if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.condition); 431 final int N = mZenConditions.getChildCount(); 432 for (int i = 0; i < N; i++) { 433 ConditionTag childTag = getConditionTagAt(i); 434 if (childTag == tag) continue; 435 childTag.rb.setChecked(false); 436 } 437 select(tag.condition); 438 announceConditionSelection(tag); 439 } 440 } 441 }); 442 443 if (tag.lines == null) { 444 tag.lines = row.findViewById(android.R.id.content); 445 } 446 if (tag.line1 == null) { 447 tag.line1 = (TextView) row.findViewById(android.R.id.text1); 448 } 449 if (tag.line2 == null) { 450 tag.line2 = (TextView) row.findViewById(android.R.id.text2); 451 } 452 final String line1, line2; 453 if (condition == null) { 454 line1 = mContext.getString(com.android.internal.R.string.zen_mode_forever); 455 line2 = null; 456 } else { 457 line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1 : condition.summary; 458 line2 = condition.line2; 459 } 460 tag.line1.setText(line1); 461 if (TextUtils.isEmpty(line2)) { 462 tag.line2.setVisibility(GONE); 463 } else { 464 tag.line2.setVisibility(VISIBLE); 465 tag.line2.setText(line2); 466 } 467 tag.lines.setEnabled(enabled); 468 tag.lines.setAlpha(enabled ? 1 : .4f); 469 470 final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1); 471 button1.setOnClickListener(new OnClickListener() { 472 @Override 473 public void onClick(View v) { 474 onClickTimeButton(row, tag, false /*down*/); 475 } 476 }); 477 478 final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2); 479 button2.setOnClickListener(new OnClickListener() { 480 @Override 481 public void onClick(View v) { 482 onClickTimeButton(row, tag, true /*up*/); 483 } 484 }); 485 tag.lines.setOnClickListener(new OnClickListener() { 486 @Override 487 public void onClick(View v) { 488 tag.rb.setChecked(true); 489 } 490 }); 491 492 final long time = ZenModeConfig.tryParseCountdownConditionId(getConditionId(tag.condition)); 493 if (time > 0) { 494 if (mBucketIndex > -1) { 495 button1.setEnabled(mBucketIndex > 0); 496 button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); 497 } else { 498 final long span = time - System.currentTimeMillis(); 499 button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS); 500 final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext, 501 MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser()); 502 button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary)); 503 } 504 505 button1.setAlpha(button1.isEnabled() ? 1f : .5f); 506 button2.setAlpha(button2.isEnabled() ? 1f : .5f); 507 } else { 508 button1.setVisibility(View.GONE); 509 button2.setVisibility(View.GONE); 510 } 511 // wire up interaction callbacks for newly-added condition rows 512 if (convertView == null) { 513 Interaction.register(tag.rb, mInteractionCallback); 514 Interaction.register(tag.lines, mInteractionCallback); 515 Interaction.register(button1, mInteractionCallback); 516 Interaction.register(button2, mInteractionCallback); 517 } 518 } 519 520 private void announceConditionSelection(ConditionTag tag) { 521 final int zen = getSelectedZen(Global.ZEN_MODE_OFF); 522 String modeText; 523 switch(zen) { 524 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 525 modeText = mContext.getString(R.string.zen_important_interruptions); 526 break; 527 case Global.ZEN_MODE_NO_INTERRUPTIONS: 528 modeText = mContext.getString(R.string.zen_no_interruptions); 529 break; 530 default: 531 return; 532 } 533 announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText, 534 tag.line1.getText())); 535 } 536 537 private void onClickTimeButton(View row, ConditionTag tag, boolean up) { 538 Condition newCondition = null; 539 final int N = MINUTE_BUCKETS.length; 540 if (mBucketIndex == -1) { 541 // not on a known index, search for the next or prev bucket by time 542 final Uri conditionId = getConditionId(tag.condition); 543 final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); 544 final long now = System.currentTimeMillis(); 545 for (int i = 0; i < N; i++) { 546 int j = up ? i : N - 1 - i; 547 final int bucketMinutes = MINUTE_BUCKETS[j]; 548 final long bucketTime = now + bucketMinutes * MINUTES_MS; 549 if (up && bucketTime > time || !up && bucketTime < time) { 550 mBucketIndex = j; 551 newCondition = ZenModeConfig.toTimeCondition(mContext, 552 bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser()); 553 break; 554 } 555 } 556 if (newCondition == null) { 557 mBucketIndex = DEFAULT_BUCKET_INDEX; 558 newCondition = ZenModeConfig.toTimeCondition(mContext, 559 MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); 560 } 561 } else { 562 // on a known index, simply increment or decrement 563 mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1))); 564 newCondition = ZenModeConfig.toTimeCondition(mContext, 565 MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); 566 } 567 mTimeCondition = newCondition; 568 bind(mTimeCondition, row); 569 tag.rb.setChecked(true); 570 select(mTimeCondition); 571 announceConditionSelection(tag); 572 } 573 574 private void select(Condition condition) { 575 if (DEBUG) Log.d(mTag, "select " + condition); 576 if (mController != null) { 577 mController.setExitCondition(condition); 578 } 579 setExitCondition(condition); 580 if (condition == null) { 581 mPrefs.setMinuteIndex(-1); 582 } else if (ZenModeConfig.isValidCountdownConditionId(condition.id) && mBucketIndex != -1) { 583 mPrefs.setMinuteIndex(mBucketIndex); 584 } 585 mSessionExitCondition = copy(condition); 586 } 587 588 private void fireMoreSettings() { 589 if (mCallback != null) { 590 mCallback.onMoreSettings(); 591 } 592 } 593 594 private void fireInteraction() { 595 if (mCallback != null) { 596 mCallback.onInteraction(); 597 } 598 } 599 600 private void fireExpanded() { 601 if (mCallback != null) { 602 mCallback.onExpanded(mExpanded); 603 } 604 } 605 606 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() { 607 @Override 608 public void onZenChanged(int zen) { 609 mHandler.obtainMessage(H.UPDATE_ZEN, zen, 0).sendToTarget(); 610 } 611 @Override 612 public void onConditionsChanged(Condition[] conditions) { 613 mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget(); 614 } 615 616 @Override 617 public void onExitConditionChanged(Condition exitCondition) { 618 mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitCondition).sendToTarget(); 619 } 620 }; 621 622 private final class H extends Handler { 623 private static final int UPDATE_CONDITIONS = 1; 624 private static final int EXIT_CONDITION_CHANGED = 2; 625 private static final int UPDATE_ZEN = 3; 626 private static final int SELECT_DEFAULT = 4; 627 628 private H() { 629 super(Looper.getMainLooper()); 630 } 631 632 @Override 633 public void handleMessage(Message msg) { 634 if (msg.what == UPDATE_CONDITIONS) { 635 handleUpdateConditions((Condition[]) msg.obj); 636 } else if (msg.what == EXIT_CONDITION_CHANGED) { 637 handleExitConditionChanged((Condition) msg.obj); 638 } else if (msg.what == UPDATE_ZEN) { 639 handleUpdateZen(msg.arg1); 640 } else if (msg.what == SELECT_DEFAULT) { 641 handleSelectDefault(); 642 } 643 } 644 } 645 646 public interface Callback { 647 void onMoreSettings(); 648 void onInteraction(); 649 void onExpanded(boolean expanded); 650 } 651 652 // used as the view tag on condition rows 653 private static class ConditionTag { 654 RadioButton rb; 655 View lines; 656 TextView line1; 657 TextView line2; 658 Condition condition; 659 } 660 661 private final class Prefs implements OnSharedPreferenceChangeListener { 662 private static final String KEY_MINUTE_INDEX = "minuteIndex"; 663 private static final String KEY_NONE_SELECTED = "noneSelected"; 664 665 private final int mNoneDangerousThreshold; 666 667 private int mMinuteIndex; 668 private int mNoneSelected; 669 670 private Prefs() { 671 mNoneDangerousThreshold = mContext.getResources() 672 .getInteger(R.integer.zen_mode_alarm_warning_threshold); 673 prefs().registerOnSharedPreferenceChangeListener(this); 674 updateMinuteIndex(); 675 updateNoneSelected(); 676 } 677 678 public boolean isNoneDangerous() { 679 return mNoneSelected < mNoneDangerousThreshold; 680 } 681 682 public void trackNoneSelected() { 683 mNoneSelected = clampNoneSelected(mNoneSelected + 1); 684 if (DEBUG) Log.d(mTag, "Setting none selected: " + mNoneSelected + " threshold=" 685 + mNoneDangerousThreshold); 686 prefs().edit().putInt(KEY_NONE_SELECTED, mNoneSelected).apply(); 687 } 688 689 public int getMinuteIndex() { 690 return mMinuteIndex; 691 } 692 693 public void setMinuteIndex(int minuteIndex) { 694 minuteIndex = clampIndex(minuteIndex); 695 if (minuteIndex == mMinuteIndex) return; 696 mMinuteIndex = clampIndex(minuteIndex); 697 if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex); 698 prefs().edit().putInt(KEY_MINUTE_INDEX, mMinuteIndex).apply(); 699 } 700 701 @Override 702 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 703 updateMinuteIndex(); 704 updateNoneSelected(); 705 } 706 707 private SharedPreferences prefs() { 708 return mContext.getSharedPreferences(mContext.getPackageName(), 0); 709 } 710 711 private void updateMinuteIndex() { 712 mMinuteIndex = clampIndex(prefs().getInt(KEY_MINUTE_INDEX, DEFAULT_BUCKET_INDEX)); 713 if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex); 714 } 715 716 private int clampIndex(int index) { 717 return MathUtils.constrain(index, -1, MINUTE_BUCKETS.length - 1); 718 } 719 720 private void updateNoneSelected() { 721 mNoneSelected = clampNoneSelected(prefs().getInt(KEY_NONE_SELECTED, 0)); 722 if (DEBUG) Log.d(mTag, "None selected: " + mNoneSelected); 723 } 724 725 private int clampNoneSelected(int noneSelected) { 726 return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE); 727 } 728 } 729 730 private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() { 731 @Override 732 public void onSelected(Object value) { 733 if (value != null && mZenButtons.isShown()) { 734 if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value); 735 mController.setZen((Integer) value); 736 } 737 } 738 739 @Override 740 public void onInteraction() { 741 fireInteraction(); 742 } 743 }; 744 745 private final Interaction.Callback mInteractionCallback = new Interaction.Callback() { 746 @Override 747 public void onInteraction() { 748 fireInteraction(); 749 } 750 }; 751} 752