Whenever setInterruptionFilter() is called with NONE or ALARMS, the Do Not Disturb mode now shows the current policy (nothing allowed or alarms/media allowed), instead of the normal PRIORITY policy. This policy is read-only in the UI since it cannot be customized. This should be, or at least become, pretty rare (with small exceptions, apps targeting V that call setInterruptionFilter() will use an implicit mode instead of changing the global zen mode, plus using these filters is quite nonstandard in itself). Fixes: 361586248 Test: atest & manual (toggling DND via cmd shell) Flag: android.app.modes_ui Change-Id: If2439480235d30aa310ad8925341183b9761784c
230 lines
9.8 KiB
Java
230 lines
9.8 KiB
Java
/*
|
|
* Copyright (C) 2024 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.settings.notification.modes;
|
|
|
|
import static android.app.AutomaticZenRule.TYPE_BEDTIME;
|
|
import static android.app.AutomaticZenRule.TYPE_DRIVING;
|
|
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
|
|
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
|
|
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.app.AlertDialog;
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.service.notification.SystemZenRules;
|
|
import android.service.notification.ZenModeConfig;
|
|
import android.util.Log;
|
|
|
|
import androidx.annotation.DrawableRes;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.StringRes;
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settingslib.PrimarySwitchPreference;
|
|
import com.android.settingslib.notification.modes.ZenMode;
|
|
import com.android.settingslib.notification.modes.ZenModesBackend;
|
|
|
|
import com.google.common.base.Strings;
|
|
|
|
class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreferenceController {
|
|
|
|
private static final String TAG = "ZenModeTriggerUpdate";
|
|
|
|
private final PackageManager mPackageManager;
|
|
private final ConfigurationActivityHelper mConfigurationActivityHelper;
|
|
private final ZenServiceListing mServiceListing;
|
|
|
|
private String mModeName;
|
|
|
|
ZenModeTriggerUpdatePreferenceController(Context context, String key,
|
|
ZenModesBackend backend) {
|
|
this(context, key, backend, context.getPackageManager(),
|
|
new ConfigurationActivityHelper(context.getPackageManager()),
|
|
new ZenServiceListing(context));
|
|
}
|
|
|
|
@VisibleForTesting
|
|
ZenModeTriggerUpdatePreferenceController(Context context, String key,
|
|
ZenModesBackend backend, PackageManager packageManager,
|
|
ConfigurationActivityHelper configurationActivityHelper,
|
|
ZenServiceListing serviceListing) {
|
|
super(context, key, backend);
|
|
mPackageManager = packageManager;
|
|
mConfigurationActivityHelper = configurationActivityHelper;
|
|
mServiceListing = serviceListing;
|
|
}
|
|
|
|
@Override
|
|
public boolean isAvailable(@NonNull ZenMode zenMode) {
|
|
return !zenMode.isCustomManual() && !zenMode.isManualDnd();
|
|
}
|
|
|
|
@Override
|
|
void updateState(Preference preference, @NonNull ZenMode zenMode) {
|
|
if (!isAvailable(zenMode)) {
|
|
return;
|
|
}
|
|
|
|
mModeName = zenMode.getName();
|
|
PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference;
|
|
triggerPref.setChecked(zenMode.isEnabled());
|
|
triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
|
|
if (zenMode.isSystemOwned()) {
|
|
setUpForSystemOwnedTrigger(triggerPref, zenMode);
|
|
} else {
|
|
setUpForAppTrigger(triggerPref, zenMode);
|
|
}
|
|
}
|
|
|
|
private void setUpForSystemOwnedTrigger(Preference preference, ZenMode mode) {
|
|
if (mode.getType() == TYPE_SCHEDULE_TIME) {
|
|
preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
|
ZenModeSetScheduleFragment.class, mode.getId(),
|
|
SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
|
|
|
|
// [Clock Icon] 9:00 - 17:00 / Sun-Mon
|
|
preference.setIcon(com.android.internal.R.drawable.ic_zen_mode_type_schedule_time);
|
|
ZenModeConfig.ScheduleInfo schedule =
|
|
tryParseScheduleConditionId(mode.getRule().getConditionId());
|
|
if (schedule != null) {
|
|
preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule));
|
|
preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule));
|
|
} else {
|
|
// Fallback, but shouldn't happen.
|
|
Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode);
|
|
preference.setTitle(R.string.zen_mode_set_schedule_link);
|
|
preference.setSummary(null);
|
|
}
|
|
} else if (mode.getType() == TYPE_SCHEDULE_CALENDAR) {
|
|
preference.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
|
|
ZenModeSetCalendarFragment.class, mode.getId(),
|
|
SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
|
|
|
|
// [Event Icon] Calendar Events / <Calendar name>
|
|
preference.setIcon(
|
|
com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar);
|
|
preference.setTitle(R.string.zen_mode_trigger_title_schedule_calendar);
|
|
preference.setSummary(mode.getTriggerDescription());
|
|
} else {
|
|
Log.wtf(TAG, "Unexpected type for system-owned mode: " + mode);
|
|
}
|
|
}
|
|
|
|
@SuppressLint("SwitchIntDef")
|
|
private void setUpForAppTrigger(Preference preference, ZenMode mode) {
|
|
// App-owned mode may have triggerDescription, configurationActivity, or both/neither.
|
|
mServiceListing.loadApprovedComponents(mode.getRule().getPackageName());
|
|
Intent configurationIntent =
|
|
mConfigurationActivityHelper.getConfigurationActivityIntentForMode(
|
|
mode, mServiceListing::findService);
|
|
|
|
@StringRes int title = switch (mode.getType()) {
|
|
case TYPE_BEDTIME -> R.string.zen_mode_trigger_title_bedtime;
|
|
case TYPE_DRIVING -> R.string.zen_mode_trigger_title_driving;
|
|
default -> R.string.zen_mode_trigger_title_generic;
|
|
};
|
|
|
|
String summary;
|
|
if (!Strings.isNullOrEmpty(mode.getTriggerDescription())) {
|
|
summary = mode.getTriggerDescription();
|
|
} else if (!Strings.isNullOrEmpty(mode.getRule().getPackageName())) {
|
|
String appName = null;
|
|
try {
|
|
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
|
|
mode.getRule().getPackageName(), 0);
|
|
appName = appInfo.loadLabel(mPackageManager).toString();
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
Log.e(TAG, "Couldn't resolve owner for mode: " + mode);
|
|
}
|
|
|
|
if (appName != null) {
|
|
summary = mContext.getString(
|
|
configurationIntent != null
|
|
? R.string.zen_mode_trigger_summary_settings_in_app
|
|
: R.string.zen_mode_trigger_summary_managed_by_app,
|
|
appName);
|
|
} else {
|
|
summary = null;
|
|
}
|
|
} else {
|
|
Log.e(TAG, "Mode without package! " + mode);
|
|
summary = null;
|
|
}
|
|
|
|
@DrawableRes int icon;
|
|
if (mode.getType() == TYPE_BEDTIME) {
|
|
icon = com.android.internal.R.drawable.ic_zen_mode_type_schedule_time; // Clock
|
|
} else if (mode.getType() == TYPE_DRIVING) {
|
|
icon = com.android.internal.R.drawable.ic_zen_mode_type_driving; // Car
|
|
} else {
|
|
icon = configurationIntent != null ? R.drawable.ic_zen_mode_trigger_with_activity
|
|
: R.drawable.ic_zen_mode_trigger_without_activity;
|
|
}
|
|
|
|
preference.setTitle(title);
|
|
preference.setSummary(summary);
|
|
preference.setIcon(icon);
|
|
preference.setIntent(configurationIntent);
|
|
}
|
|
|
|
private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
|
|
confirmChangeEnabled(p, (boolean) newValue);
|
|
return true;
|
|
};
|
|
|
|
private void confirmChangeEnabled(Preference preference, boolean enabled) {
|
|
@StringRes int titleFormat = enabled ? R.string.zen_mode_confirm_enable_mode_title
|
|
: R.string.zen_mode_confirm_disable_mode_title;
|
|
@StringRes int message = enabled ? R.string.zen_mode_confirm_enable_message
|
|
: R.string.zen_mode_confirm_disable_message;
|
|
@StringRes int confirmButton = enabled ? R.string.zen_mode_action_enable
|
|
: R.string.zen_mode_action_disable;
|
|
|
|
new AlertDialog.Builder(mContext)
|
|
.setTitle(mContext.getString(titleFormat, mModeName))
|
|
.setMessage(message)
|
|
.setPositiveButton(confirmButton,
|
|
(dialog, which) -> setModeEnabled(enabled))
|
|
.setNegativeButton(R.string.cancel,
|
|
(dialog, which) -> undoToggleSwitch(preference, enabled))
|
|
.setOnCancelListener(dialog -> undoToggleSwitch(preference, enabled))
|
|
.show();
|
|
}
|
|
|
|
private void setModeEnabled(boolean enabled) {
|
|
saveMode((zenMode) -> {
|
|
if (enabled != zenMode.isEnabled()) {
|
|
zenMode.getRule().setEnabled(enabled);
|
|
}
|
|
return zenMode;
|
|
});
|
|
getMetricsFeatureProvider().action(mContext, SettingsEnums.ACTION_ZEN_MODE_ENABLE_TOGGLE,
|
|
enabled);
|
|
}
|
|
|
|
private void undoToggleSwitch(Preference preference, boolean wasSwitchedTo) {
|
|
PrimarySwitchPreference switchPreference = (PrimarySwitchPreference) preference;
|
|
switchPreference.setChecked(!wasSwitchedTo);
|
|
}
|
|
}
|