Merge "Redirect to new modes page when modes_ui is on." into main

This commit is contained in:
Yuri Lin
2024-05-15 21:14:45 +00:00
committed by Android (Google) Code Review
9 changed files with 438 additions and 1 deletions

View File

@@ -7893,6 +7893,9 @@
<!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]--> <!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
<string name="zen_mode_settings_title">Do Not Disturb</string> <string name="zen_mode_settings_title">Do Not Disturb</string>
<!-- Sound: Title for the Modes option and associated settings page. [CHAR LIMIT=50]-->
<string name="zen_modes_list_title">Priority Modes</string>
<!-- Sound: Summary for the Do not Disturb option and associated settings page. [CHAR LIMIT=240]--> <!-- Sound: Summary for the Do not Disturb option and associated settings page. [CHAR LIMIT=240]-->
<string name="zen_mode_settings_summary">Only get notified by important people and apps</string> <string name="zen_mode_settings_summary">Only get notified by important people and apps</string>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/zen_modes_list_title" >
<!-- TODO: b/333682392 - add strings for summary as appropriate -->
<PreferenceCategory
android:key="zen_modes_list">
<!-- Preferences leading to rules are added in this PreferenceCategory. -->
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/zen_modes_list_title" >
<!-- TODO: b/308819292 - implement page, delete this test preference -->
<Preference
android:key="zen_mode_test" />
</PreferenceScreen>

View File

@@ -0,0 +1,78 @@
/*
* 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 android.app.AutomaticZenRule;
import android.app.settings.SettingsEnums;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
public class ZenModeFragment extends ZenModeFragmentBase {
@Override
protected int getPreferenceScreenResId() {
return R.xml.modes_rule_settings;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
// TODO: fill in with all the elements of this page. Each should be an instance of
// {@link AbstractZenModePreferenceController}.
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
return prefControllers;
}
@Override
public void onStart() {
super.onStart();
// Set title for the entire screen
ZenMode mode = getMode();
AutomaticZenRule azr = getAZR();
if (mode == null || azr == null) {
return;
}
getActivity().setTitle(azr.getName());
// TODO: b/308819292 - implement the real screen!
final PreferenceScreen screen = getPreferenceScreen();
if (screen == null) {
return;
}
Preference tmpPref = screen.findPreference("zen_mode_test");
if (tmpPref == null) {
return;
}
tmpPref.setTitle(azr.getTriggerDescription());
tmpPref.setSummary("active?: " + mode.isActive());
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - make this the correct metrics category
return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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 com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.zen.ZenModeSettings;
import com.android.settingslib.RestrictedPreference;
/**
* Preference representing a single mode item on the modes aggregator page. Clicking on this
* preference leads to an individual mode's configuration page.
*/
public class ZenModeListPreference extends RestrictedPreference {
final Context mContext;
ZenMode mZenMode;
ZenModeListPreference(Context context, ZenMode zenMode) {
super(context);
mContext = context;
mZenMode = zenMode;
setTitle(mZenMode.getRule().getName());
setSummary((mZenMode.isActive() ? "ACTIVE" : "inactive") + ": "
+ mZenMode.getRule().getTriggerDescription());
}
@Override
public void onClick() {
// TODO: b/322373473 - This implementation is a hack that just leads to the old DND page
// for manual only; remove this in favor of the real implementation.
if (mZenMode.isManualDnd()) {
new SubSettingLauncher(mContext)
.setDestination(ZenModeSettings.class.getName())
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE)
.launch();
} else {
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, mZenMode.getId());
new SubSettingLauncher(mContext)
.setDestination(ZenModeFragment.class.getName())
.setArguments(bundle)
.setSourceMetricsCategory(SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION)
.launch();
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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 android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.service.notification.ConditionProviderService;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.utils.ManagedServiceSettings;
import com.android.settings.utils.ZenServiceListing;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
@SearchIndexable
public class ZenModesListFragment extends ZenModesFragmentBase {
protected final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig();
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG);
serviceListing.reloadApprovedServices();
return buildPreferenceControllers(context, this, serviceListing);
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
@Nullable Fragment parent, @Nullable ZenServiceListing serviceListing) {
// We need to redefine ZenModesBackend here even though mBackend exists so that this method
// can be static; it must be static to be able to be used in SEARCH_INDEX_DATA_PROVIDER.
ZenModesBackend backend = ZenModesBackend.getInstance(context);
List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModesListPreferenceController(
context, parent, backend));
// TODO: b/326442408 - Add controller for "Add Mode" preference/flow, which is what uses
// the ZenServiceListing.
return controllers;
}
@Override
protected void updateZenModeState() {
// TODO: b/322373473 -- update any overall description of modes state here if necessary.
// Note the preferences linking to individual rules do not need to be updated, as
// updateState() is called on all preference controllers whenever the page is resumed.
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.modes_list_settings;
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - add new & set metrics categories correctly
return SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION;
}
protected static ManagedServiceSettings.Config getConditionProviderConfig() {
return new ManagedServiceSettings.Config.Builder()
.setTag(TAG)
.setIntentAction(ConditionProviderService.SERVICE_INTERFACE)
.setConfigurationIntentAction(NotificationManager.ACTION_AUTOMATIC_ZEN_RULE)
.setPermission(android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE)
.setNoun("condition provider")
.build();
}
// TODO: b/322373473 - Add 3-dot options menu with capability to delete modes.
/**
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.modes_list_settings) {
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
// TODO: b/332937523 - determine if this should be removed once the preference
// controller adds dynamic data to index
keys.add(ZenModesListPreferenceController.KEY);
return keys;
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildPreferenceControllers(context, null, null);
}
};
}

View File

@@ -0,0 +1,80 @@
/*
* 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 android.app.Flags;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settingslib.core.AbstractPreferenceController;
/**
* Controller for the PreferenceCategory on the modes aggregator page ({@link ZenModesListFragment})
* containing links to each individual mode. This is a central controller that populates and updates
* all the preferences that then lead to a mode configuration page.
*/
public class ZenModesListPreferenceController extends AbstractPreferenceController {
protected static final String KEY = "zen_modes_list";
@Nullable
protected Fragment mParent;
protected ZenModesBackend mBackend;
public ZenModesListPreferenceController(Context context, @Nullable Fragment parent,
@NonNull ZenModesBackend backend) {
super(context);
mParent = parent;
mBackend = backend;
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
return Flags.modesUi();
}
@Override
public void updateState(Preference preference) {
if (mBackend == null) {
return;
}
// The preference given us is a PreferenceCategory; create one preference inside the
// category for each rule that exists.
PreferenceCategory category = (PreferenceCategory) preference;
// TODO: b/322373473 - This is not the right way to replace these preferences; we should
// follow something similar to what
// ZenModeAutomaticRulesPreferenceController does to change rules
// only as necessary and update them.
category.removeAll();
for (ZenMode mode : mBackend.getModes()) {
Preference pref = new ZenModeListPreference(mContext, mode);
category.addPreference(pref);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification.zen; package com.android.settings.notification.zen;
import android.app.Flags;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver; import android.database.ContentObserver;
@@ -27,7 +28,9 @@ import android.provider.Settings;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController; import com.android.settings.core.BasePreferenceController;
import com.android.settings.notification.modes.ZenModesListFragment;
import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume; import com.android.settingslib.core.lifecycle.events.OnResume;
@@ -46,7 +49,9 @@ public class ZenModePreferenceController extends BasePreferenceController
@Override @Override
public void displayPreference(PreferenceScreen screen) { public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen); super.displayPreference(screen);
mSettingObserver = new SettingObserver(screen.findPreference(getPreferenceKey())); Preference preference = screen.findPreference(getPreferenceKey());
mSettingObserver = new SettingObserver(preference);
maybeSetTitleAndDestination(preference);
} }
@Override @Override
@@ -71,11 +76,22 @@ public class ZenModePreferenceController extends BasePreferenceController
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
super.updateState(preference); super.updateState(preference);
maybeSetTitleAndDestination(preference);
if (preference.isEnabled()) { if (preference.isEnabled()) {
preference.setSummary(mSummaryBuilder.getSoundSummary()); preference.setSummary(mSummaryBuilder.getSoundSummary());
} }
} }
// Only when modes_ui is active: change title & target fragment.
private void maybeSetTitleAndDestination(Preference preference) {
if (!Flags.modesUi()) {
return;
}
preference.setTitle(R.string.zen_modes_list_title);
preference.setFragment(ZenModesListFragment.class.getCanonicalName());
}
class SettingObserver extends ContentObserver { class SettingObserver extends ContentObserver {
private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE); private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor( private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor(

View File

@@ -16,10 +16,13 @@
package com.android.settings.notification.zen; package com.android.settings.notification.zen;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -27,13 +30,20 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.Flags;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.NotificationManager.Policy; import android.app.NotificationManager.Policy;
import android.content.Context; import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.notification.modes.ZenModesListFragment;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -46,6 +56,9 @@ import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class ZenModePreferenceControllerTest { public class ZenModePreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@Mock @Mock
private Preference mPreference; private Preference mPreference;
@Mock @Mock
@@ -96,4 +109,20 @@ public class ZenModePreferenceControllerTest {
verify(mPreference, never()).setSummary(anyString()); verify(mPreference, never()).setSummary(anyString());
} }
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
public void updateState_modesUi_resetsTitleAndFragment() {
mController.updateState(mPreference);
verify(mPreference).setTitle(anyInt()); // Resource IDs are ints
verify(mPreference).setFragment(ZenModesListFragment.class.getCanonicalName());
}
@Test
@DisableFlags(Flags.FLAG_MODES_UI)
public void updateState_noModesUi_doesNotSetTitleAndFragment() {
mController.updateState(mPreference);
verify(mPreference, never()).setTitle(anyInt());
verify(mPreference, never()).setFragment(anyString());
}
} }