Aurimas Liutikas | 88c7ff1 | 2023-08-10 12:42:26 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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.android.settingslib.notification; |
| 18 | |
| 19 | import android.annotation.Nullable; |
| 20 | import android.app.ActivityManager; |
| 21 | import android.app.AlarmManager; |
| 22 | import android.app.AlertDialog; |
| 23 | import android.app.NotificationManager; |
| 24 | import android.content.Context; |
| 25 | import android.content.DialogInterface; |
| 26 | import android.net.Uri; |
| 27 | import android.provider.Settings; |
| 28 | import android.service.notification.Condition; |
| 29 | import android.service.notification.ZenModeConfig; |
| 30 | import android.text.TextUtils; |
| 31 | import android.text.format.DateFormat; |
| 32 | import android.util.Log; |
| 33 | import android.util.Slog; |
| 34 | import android.view.LayoutInflater; |
| 35 | import android.view.View; |
| 36 | import android.view.ViewGroup; |
| 37 | import android.widget.CompoundButton; |
| 38 | import android.widget.ImageView; |
| 39 | import android.widget.LinearLayout; |
| 40 | import android.widget.RadioButton; |
| 41 | import android.widget.RadioGroup; |
| 42 | import android.widget.ScrollView; |
| 43 | import android.widget.TextView; |
| 44 | |
| 45 | import com.android.internal.annotations.VisibleForTesting; |
| 46 | import com.android.internal.policy.PhoneWindow; |
| 47 | import com.android.settingslib.R; |
| 48 | |
| 49 | import java.util.Arrays; |
| 50 | import java.util.Calendar; |
| 51 | import java.util.GregorianCalendar; |
| 52 | import java.util.Locale; |
| 53 | import java.util.Objects; |
| 54 | |
| 55 | public class EnableZenModeDialog { |
| 56 | private static final String TAG = "EnableZenModeDialog"; |
| 57 | private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); |
| 58 | |
| 59 | private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS; |
| 60 | private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0]; |
| 61 | private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1]; |
| 62 | private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); |
| 63 | |
| 64 | @VisibleForTesting |
| 65 | protected static final int FOREVER_CONDITION_INDEX = 0; |
| 66 | @VisibleForTesting |
| 67 | protected static final int COUNTDOWN_CONDITION_INDEX = 1; |
| 68 | @VisibleForTesting |
| 69 | protected static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2; |
| 70 | |
| 71 | private static final int SECONDS_MS = 1000; |
| 72 | private static final int MINUTES_MS = 60 * SECONDS_MS; |
| 73 | |
| 74 | @Nullable |
| 75 | private final ZenModeDialogMetricsLogger mMetricsLogger; |
| 76 | |
| 77 | @VisibleForTesting |
| 78 | protected Uri mForeverId; |
| 79 | private int mBucketIndex = -1; |
| 80 | |
| 81 | @VisibleForTesting |
| 82 | protected NotificationManager mNotificationManager; |
| 83 | private AlarmManager mAlarmManager; |
| 84 | private int mUserId; |
| 85 | private boolean mAttached; |
| 86 | |
| 87 | @VisibleForTesting |
| 88 | protected Context mContext; |
| 89 | private final int mThemeResId; |
| 90 | private final boolean mCancelIsNeutral; |
| 91 | @VisibleForTesting |
| 92 | protected TextView mZenAlarmWarning; |
| 93 | @VisibleForTesting |
| 94 | protected LinearLayout mZenRadioGroupContent; |
| 95 | |
| 96 | private RadioGroup mZenRadioGroup; |
| 97 | private int MAX_MANUAL_DND_OPTIONS = 3; |
| 98 | |
| 99 | @VisibleForTesting |
| 100 | protected LayoutInflater mLayoutInflater; |
| 101 | |
| 102 | public EnableZenModeDialog(Context context) { |
| 103 | this(context, 0); |
| 104 | } |
| 105 | |
| 106 | public EnableZenModeDialog(Context context, int themeResId) { |
| 107 | this(context, themeResId, false /* cancelIsNeutral */, |
| 108 | new ZenModeDialogMetricsLogger(context)); |
| 109 | } |
| 110 | |
| 111 | public EnableZenModeDialog(Context context, int themeResId, boolean cancelIsNeutral, |
| 112 | ZenModeDialogMetricsLogger metricsLogger) { |
| 113 | mContext = context; |
| 114 | mThemeResId = themeResId; |
| 115 | mCancelIsNeutral = cancelIsNeutral; |
| 116 | mMetricsLogger = metricsLogger; |
| 117 | } |
| 118 | |
| 119 | public AlertDialog createDialog() { |
| 120 | mNotificationManager = (NotificationManager) mContext. |
| 121 | getSystemService(Context.NOTIFICATION_SERVICE); |
| 122 | mForeverId = Condition.newId(mContext).appendPath("forever").build(); |
| 123 | mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); |
| 124 | mUserId = mContext.getUserId(); |
| 125 | mAttached = false; |
| 126 | |
| 127 | final AlertDialog.Builder builder = new AlertDialog.Builder(mContext, mThemeResId) |
| 128 | .setTitle(R.string.zen_mode_settings_turn_on_dialog_title) |
| 129 | .setPositiveButton(R.string.zen_mode_enable_dialog_turn_on, |
| 130 | new DialogInterface.OnClickListener() { |
| 131 | @Override |
| 132 | public void onClick(DialogInterface dialog, int which) { |
| 133 | int checkedId = mZenRadioGroup.getCheckedRadioButtonId(); |
| 134 | ConditionTag tag = getConditionTagAt(checkedId); |
| 135 | |
| 136 | if (isForever(tag.condition)) { |
| 137 | mMetricsLogger.logOnEnableZenModeForever(); |
| 138 | } else if (isAlarm(tag.condition)) { |
| 139 | mMetricsLogger.logOnEnableZenModeUntilAlarm(); |
| 140 | } else if (isCountdown(tag.condition)) { |
| 141 | mMetricsLogger.logOnEnableZenModeUntilCountdown(); |
| 142 | } else { |
| 143 | Slog.d(TAG, "Invalid manual condition: " + tag.condition); |
| 144 | } |
| 145 | // always triggers priority-only dnd with chosen condition |
| 146 | mNotificationManager.setZenMode( |
| 147 | Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, |
| 148 | getRealConditionId(tag.condition), TAG); |
| 149 | } |
| 150 | }); |
| 151 | |
| 152 | if (mCancelIsNeutral) { |
| 153 | builder.setNeutralButton(R.string.cancel, null); |
| 154 | } else { |
| 155 | builder.setNegativeButton(R.string.cancel, null); |
| 156 | } |
| 157 | |
| 158 | View contentView = getContentView(); |
| 159 | bindConditions(forever()); |
| 160 | builder.setView(contentView); |
| 161 | return builder.create(); |
| 162 | } |
| 163 | |
| 164 | private void hideAllConditions() { |
| 165 | final int N = mZenRadioGroupContent.getChildCount(); |
| 166 | for (int i = 0; i < N; i++) { |
| 167 | mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE); |
| 168 | } |
| 169 | |
| 170 | mZenAlarmWarning.setVisibility(View.GONE); |
| 171 | } |
| 172 | |
| 173 | protected View getContentView() { |
| 174 | if (mLayoutInflater == null) { |
| 175 | mLayoutInflater = new PhoneWindow(mContext).getLayoutInflater(); |
| 176 | } |
| 177 | View contentView = mLayoutInflater.inflate(R.layout.zen_mode_turn_on_dialog_container, |
| 178 | null); |
| 179 | ScrollView container = (ScrollView) contentView.findViewById(R.id.container); |
| 180 | |
| 181 | mZenRadioGroup = container.findViewById(R.id.zen_radio_buttons); |
| 182 | mZenRadioGroupContent = container.findViewById(R.id.zen_radio_buttons_content); |
| 183 | mZenAlarmWarning = container.findViewById(R.id.zen_alarm_warning); |
| 184 | |
| 185 | for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) { |
| 186 | final View radioButton = mLayoutInflater.inflate(R.layout.zen_mode_radio_button, |
| 187 | mZenRadioGroup, false); |
| 188 | mZenRadioGroup.addView(radioButton); |
| 189 | radioButton.setId(i); |
| 190 | |
| 191 | final View radioButtonContent = mLayoutInflater.inflate(R.layout.zen_mode_condition, |
| 192 | mZenRadioGroupContent, false); |
| 193 | radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS); |
| 194 | mZenRadioGroupContent.addView(radioButtonContent); |
| 195 | } |
| 196 | |
| 197 | hideAllConditions(); |
| 198 | return contentView; |
| 199 | } |
| 200 | |
| 201 | @VisibleForTesting |
| 202 | protected void bind(final Condition condition, final View row, final int rowId) { |
| 203 | if (condition == null) throw new IllegalArgumentException("condition must not be null"); |
| 204 | |
| 205 | final boolean enabled = condition.state == Condition.STATE_TRUE; |
| 206 | final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() : |
| 207 | new ConditionTag(); |
| 208 | row.setTag(tag); |
| 209 | final boolean first = tag.rb == null; |
| 210 | if (tag.rb == null) { |
| 211 | tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId); |
| 212 | } |
| 213 | tag.condition = condition; |
| 214 | final Uri conditionId = getConditionId(tag.condition); |
| 215 | if (DEBUG) Log.d(TAG, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first=" |
| 216 | + first + " condition=" + conditionId); |
| 217 | tag.rb.setEnabled(enabled); |
| 218 | tag.rb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { |
| 219 | @Override |
| 220 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| 221 | if (isChecked) { |
| 222 | tag.rb.setChecked(true); |
| 223 | if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId); |
| 224 | mMetricsLogger.logOnConditionSelected(); |
| 225 | updateAlarmWarningText(tag.condition); |
| 226 | } |
| 227 | tag.line1.setStateDescription( |
| 228 | isChecked ? buttonView.getContext().getString( |
| 229 | com.android.internal.R.string.selected) : null); |
| 230 | } |
| 231 | }); |
| 232 | |
| 233 | updateUi(tag, row, condition, enabled, rowId, conditionId); |
| 234 | row.setVisibility(View.VISIBLE); |
| 235 | } |
| 236 | |
| 237 | @VisibleForTesting |
| 238 | protected ConditionTag getConditionTagAt(int index) { |
| 239 | return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag(); |
| 240 | } |
| 241 | |
| 242 | @VisibleForTesting |
| 243 | protected void bindConditions(Condition c) { |
| 244 | // forever |
| 245 | bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX), |
| 246 | FOREVER_CONDITION_INDEX); |
| 247 | if (c == null) { |
| 248 | bindGenericCountdown(); |
| 249 | bindNextAlarm(getTimeUntilNextAlarmCondition()); |
| 250 | } else if (isForever(c)) { |
| 251 | getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true); |
| 252 | bindGenericCountdown(); |
| 253 | bindNextAlarm(getTimeUntilNextAlarmCondition()); |
| 254 | } else { |
| 255 | if (isAlarm(c)) { |
| 256 | bindGenericCountdown(); |
| 257 | bindNextAlarm(c); |
| 258 | getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true); |
| 259 | } else if (isCountdown(c)) { |
| 260 | bindNextAlarm(getTimeUntilNextAlarmCondition()); |
| 261 | bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX), |
| 262 | COUNTDOWN_CONDITION_INDEX); |
| 263 | getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true); |
| 264 | } else { |
| 265 | Slog.d(TAG, "Invalid manual condition: " + c); |
| 266 | } |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | public static Uri getConditionId(Condition condition) { |
| 271 | return condition != null ? condition.id : null; |
| 272 | } |
| 273 | |
| 274 | public Condition forever() { |
| 275 | Uri foreverId = Condition.newId(mContext).appendPath("forever").build(); |
| 276 | return new Condition(foreverId, foreverSummary(mContext), "", "", 0 /*icon*/, |
| 277 | Condition.STATE_TRUE, 0 /*flags*/); |
| 278 | } |
| 279 | |
| 280 | public long getNextAlarm() { |
| 281 | final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId); |
| 282 | return info != null ? info.getTriggerTime() : 0; |
| 283 | } |
| 284 | |
| 285 | @VisibleForTesting |
| 286 | protected boolean isAlarm(Condition c) { |
| 287 | return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id); |
| 288 | } |
| 289 | |
| 290 | @VisibleForTesting |
| 291 | protected boolean isCountdown(Condition c) { |
| 292 | return c != null && ZenModeConfig.isValidCountdownConditionId(c.id); |
| 293 | } |
| 294 | |
| 295 | private boolean isForever(Condition c) { |
| 296 | return c != null && mForeverId.equals(c.id); |
| 297 | } |
| 298 | |
| 299 | private Uri getRealConditionId(Condition condition) { |
| 300 | return isForever(condition) ? null : getConditionId(condition); |
| 301 | } |
| 302 | |
| 303 | private String foreverSummary(Context context) { |
| 304 | return context.getString(com.android.internal.R.string.zen_mode_forever); |
| 305 | } |
| 306 | |
| 307 | private static void setToMidnight(Calendar calendar) { |
| 308 | calendar.set(Calendar.HOUR_OF_DAY, 0); |
| 309 | calendar.set(Calendar.MINUTE, 0); |
| 310 | calendar.set(Calendar.SECOND, 0); |
| 311 | calendar.set(Calendar.MILLISECOND, 0); |
| 312 | } |
| 313 | |
| 314 | // Returns a time condition if the next alarm is within the next week. |
| 315 | @VisibleForTesting |
| 316 | protected Condition getTimeUntilNextAlarmCondition() { |
| 317 | GregorianCalendar weekRange = new GregorianCalendar(); |
| 318 | setToMidnight(weekRange); |
| 319 | weekRange.add(Calendar.DATE, 6); |
| 320 | final long nextAlarmMs = getNextAlarm(); |
| 321 | if (nextAlarmMs > 0) { |
| 322 | GregorianCalendar nextAlarm = new GregorianCalendar(); |
| 323 | nextAlarm.setTimeInMillis(nextAlarmMs); |
| 324 | setToMidnight(nextAlarm); |
| 325 | |
| 326 | if (weekRange.compareTo(nextAlarm) >= 0) { |
| 327 | return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs, |
| 328 | ActivityManager.getCurrentUser()); |
| 329 | } |
| 330 | } |
| 331 | return null; |
| 332 | } |
| 333 | |
| 334 | @VisibleForTesting |
| 335 | protected void bindGenericCountdown() { |
| 336 | mBucketIndex = DEFAULT_BUCKET_INDEX; |
| 337 | Condition countdown = ZenModeConfig.toTimeCondition(mContext, |
| 338 | MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); |
| 339 | if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) { |
| 340 | bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX), |
| 341 | COUNTDOWN_CONDITION_INDEX); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | private void updateUi(ConditionTag tag, View row, Condition condition, |
| 346 | boolean enabled, int rowId, Uri conditionId) { |
| 347 | if (tag.lines == null) { |
| 348 | tag.lines = row.findViewById(android.R.id.content); |
| 349 | } |
| 350 | if (tag.line1 == null) { |
| 351 | tag.line1 = (TextView) row.findViewById(android.R.id.text1); |
| 352 | } |
| 353 | |
| 354 | if (tag.line2 == null) { |
| 355 | tag.line2 = (TextView) row.findViewById(android.R.id.text2); |
| 356 | } |
| 357 | |
| 358 | final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1 |
| 359 | : condition.summary; |
| 360 | final String line2 = condition.line2; |
| 361 | tag.line1.setText(line1); |
| 362 | if (TextUtils.isEmpty(line2)) { |
| 363 | tag.line2.setVisibility(View.GONE); |
| 364 | } else { |
| 365 | tag.line2.setVisibility(View.VISIBLE); |
| 366 | tag.line2.setText(line2); |
| 367 | } |
| 368 | tag.lines.setEnabled(enabled); |
| 369 | tag.lines.setAlpha(enabled ? 1 : .4f); |
| 370 | |
| 371 | tag.lines.setOnClickListener(new View.OnClickListener() { |
| 372 | @Override |
| 373 | public void onClick(View v) { |
| 374 | tag.rb.setChecked(true); |
| 375 | } |
| 376 | }); |
| 377 | |
| 378 | final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); |
| 379 | final ImageView minusButton = (ImageView) row.findViewById(android.R.id.button1); |
| 380 | final ImageView plusButton = (ImageView) row.findViewById(android.R.id.button2); |
| 381 | if (rowId == COUNTDOWN_CONDITION_INDEX && time > 0) { |
| 382 | minusButton.setOnClickListener(new View.OnClickListener() { |
| 383 | @Override |
| 384 | public void onClick(View v) { |
| 385 | onClickTimeButton(row, tag, false /*down*/, rowId); |
| 386 | tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); |
| 387 | } |
| 388 | }); |
| 389 | |
| 390 | plusButton.setOnClickListener(new View.OnClickListener() { |
| 391 | @Override |
| 392 | public void onClick(View v) { |
| 393 | onClickTimeButton(row, tag, true /*up*/, rowId); |
| 394 | tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); |
| 395 | } |
| 396 | }); |
| 397 | if (mBucketIndex > -1) { |
| 398 | minusButton.setEnabled(mBucketIndex > 0); |
| 399 | plusButton.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); |
| 400 | } else { |
| 401 | final long span = time - System.currentTimeMillis(); |
| 402 | minusButton.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS); |
| 403 | final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext, |
| 404 | MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser()); |
| 405 | plusButton.setEnabled(!Objects.equals(condition.summary, maxCondition.summary)); |
| 406 | } |
| 407 | |
| 408 | minusButton.setAlpha(minusButton.isEnabled() ? 1f : .5f); |
| 409 | plusButton.setAlpha(plusButton.isEnabled() ? 1f : .5f); |
| 410 | } else { |
| 411 | if (minusButton != null) { |
| 412 | ((ViewGroup) row).removeView(minusButton); |
| 413 | } |
| 414 | |
| 415 | if (plusButton != null) { |
| 416 | ((ViewGroup) row).removeView(plusButton); |
| 417 | } |
| 418 | } |
| 419 | } |
| 420 | |
| 421 | @VisibleForTesting |
| 422 | protected void bindNextAlarm(Condition c) { |
| 423 | View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX); |
| 424 | ConditionTag tag = (ConditionTag) alarmContent.getTag(); |
| 425 | |
| 426 | if (c != null && (!mAttached || tag == null || tag.condition == null)) { |
| 427 | bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX); |
| 428 | } |
| 429 | |
| 430 | // hide the alarm radio button if there isn't a "next alarm condition" |
| 431 | tag = (ConditionTag) alarmContent.getTag(); |
| 432 | boolean showAlarm = tag != null && tag.condition != null; |
| 433 | mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility( |
| 434 | showAlarm ? View.VISIBLE : View.GONE); |
| 435 | alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE); |
| 436 | } |
| 437 | |
| 438 | private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) { |
| 439 | mMetricsLogger.logOnClickTimeButton(up); |
| 440 | Condition newCondition = null; |
| 441 | final int N = MINUTE_BUCKETS.length; |
| 442 | if (mBucketIndex == -1) { |
| 443 | // not on a known index, search for the next or prev bucket by time |
| 444 | final Uri conditionId = getConditionId(tag.condition); |
| 445 | final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); |
| 446 | final long now = System.currentTimeMillis(); |
| 447 | for (int i = 0; i < N; i++) { |
| 448 | int j = up ? i : N - 1 - i; |
| 449 | final int bucketMinutes = MINUTE_BUCKETS[j]; |
| 450 | final long bucketTime = now + bucketMinutes * MINUTES_MS; |
| 451 | if (up && bucketTime > time || !up && bucketTime < time) { |
| 452 | mBucketIndex = j; |
| 453 | newCondition = ZenModeConfig.toTimeCondition(mContext, |
| 454 | bucketTime, bucketMinutes, ActivityManager.getCurrentUser(), |
| 455 | false /*shortVersion*/); |
| 456 | break; |
| 457 | } |
| 458 | } |
| 459 | if (newCondition == null) { |
| 460 | mBucketIndex = DEFAULT_BUCKET_INDEX; |
| 461 | newCondition = ZenModeConfig.toTimeCondition(mContext, |
| 462 | MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); |
| 463 | } |
| 464 | } else { |
| 465 | // on a known index, simply increment or decrement |
| 466 | mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1))); |
| 467 | newCondition = ZenModeConfig.toTimeCondition(mContext, |
| 468 | MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); |
| 469 | } |
| 470 | bind(newCondition, row, rowId); |
| 471 | updateAlarmWarningText(tag.condition); |
| 472 | tag.rb.setChecked(true); |
| 473 | } |
| 474 | |
| 475 | private void updateAlarmWarningText(Condition condition) { |
| 476 | String warningText = computeAlarmWarningText(condition); |
| 477 | mZenAlarmWarning.setText(warningText); |
| 478 | mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE); |
| 479 | } |
| 480 | |
| 481 | @VisibleForTesting |
| 482 | protected String computeAlarmWarningText(Condition condition) { |
| 483 | boolean allowAlarms = (mNotificationManager.getNotificationPolicy().priorityCategories |
| 484 | & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0; |
| 485 | |
| 486 | // don't show alarm warning if alarms are allowed to bypass dnd |
| 487 | if (allowAlarms) { |
| 488 | return null; |
| 489 | } |
| 490 | |
| 491 | final long now = System.currentTimeMillis(); |
| 492 | final long nextAlarm = getNextAlarm(); |
| 493 | if (nextAlarm < now) { |
| 494 | return null; |
| 495 | } |
| 496 | int warningRes = 0; |
| 497 | if (condition == null || isForever(condition)) { |
| 498 | warningRes = R.string.zen_alarm_warning_indef; |
| 499 | } else { |
| 500 | final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id); |
| 501 | if (time > now && nextAlarm < time) { |
| 502 | warningRes = R.string.zen_alarm_warning; |
| 503 | } |
| 504 | } |
| 505 | if (warningRes == 0) { |
| 506 | return null; |
| 507 | } |
| 508 | |
| 509 | return mContext.getResources().getString(warningRes, getTime(nextAlarm, now)); |
| 510 | } |
| 511 | |
| 512 | @VisibleForTesting |
| 513 | protected String getTime(long nextAlarm, long now) { |
| 514 | final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000; |
| 515 | final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser()); |
| 516 | final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma"); |
| 517 | final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); |
| 518 | final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm); |
| 519 | final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far; |
| 520 | return mContext.getResources().getString(templateRes, formattedTime); |
| 521 | } |
| 522 | |
| 523 | // used as the view tag on condition rows |
| 524 | @VisibleForTesting |
| 525 | protected static class ConditionTag { |
| 526 | public RadioButton rb; |
| 527 | public View lines; |
| 528 | public TextView line1; |
| 529 | public TextView line2; |
| 530 | public Condition condition; |
| 531 | } |
| 532 | } |