From 0a43ecdf2b900a9b7e393aade1a0b8ab3c60af08 Mon Sep 17 00:00:00 2001 From: Alexander Roederer Date: Tue, 20 Aug 2024 15:33:11 +0000 Subject: [PATCH 1/7] Add metrics for driving and bedtime interstitials Adds metrics categories for driving and bedtime interstitial pages, which we want to distinguish from other types of interstitial pages. Bug: 332937635 Flag: android.app.modes_ui Test: Build and flash Change-Id: I49f2fd9f86f7d2737aebd9ef9d8675787421cc09 --- .../modes/SetupInterstitialActivity.java | 13 ++++++++++--- .../modes/SetupInterstitialActivityTest.java | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java index 830baaf7bfa..b44220a7a58 100644 --- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java +++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java @@ -25,6 +25,7 @@ import static android.app.AutomaticZenRule.TYPE_THEATER; import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; import android.app.ActionBar; +import android.app.AutomaticZenRule; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; @@ -151,22 +152,28 @@ public class SetupInterstitialActivity extends FragmentActivity { private void setupButton(Button button, @NonNull ZenMode mode) { button.setText(getString(R.string.zen_mode_setup_button_label, mode.getName())); - button.setOnClickListener(enableButtonListener(mode.getId())); + button.setOnClickListener(enableButtonListener(mode.getId(), mode.getType())); } @VisibleForTesting - View.OnClickListener enableButtonListener(String modeId) { + View.OnClickListener enableButtonListener(String modeId, @AutomaticZenRule.Type int modeType) { return unused -> { // When clicked, we first reload mode info in case it has changed in the interim, // then enable the mode and then send the user to the mode's configuration page. boolean updated = enableMode(modeId); + int metricsCategory = switch (modeType) { + case TYPE_BEDTIME -> SettingsEnums.ZEN_MODE_INTERSTITIAL_BEDTIME; + case TYPE_DRIVING -> SettingsEnums.ZEN_MODE_INTERSTITIAL_DRIVING; + default -> SettingsEnums.ZEN_MODE_INTERSTITIAL; + }; + // Don't come back to this activity after sending the user to the modes page, if // they happen to go back. Forward the activity result in case we got here (indirectly) // from some app that is waiting for the result. if (updated) { ZenSubSettingLauncher.forModeFragment(this, ZenModeFragment.class, modeId, - SettingsEnums.ZEN_MODE_INTERSTITIAL) + metricsCategory) .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT).launch(); } finish(); diff --git a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java index 40bf1c725e9..d60f0736b11 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java @@ -128,7 +128,7 @@ public class SetupInterstitialActivityTest { SetupInterstitialActivity.class) .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID)); scenario.onActivity(activity -> { - View.OnClickListener listener = activity.enableButtonListener(MODE_ID); + View.OnClickListener listener = activity.enableButtonListener(MODE_ID, mode.getType()); // simulate button press even though we don't actually have a button listener.onClick(null); From 6d879999ea46b8cce080d1e12ee2c137d86d1874 Mon Sep 17 00:00:00 2001 From: Alexander Roederer Date: Wed, 21 Aug 2024 18:55:42 +0000 Subject: [PATCH 2/7] Add log for enable/disable zen mode Adds logging for button on confirmation dialog after enable/disable zen mode toggle is clicked. Bug: 356154473 Flag: android.app.modes_ui Test: build and flash Change-Id: Ie1dc1c14e1519541da641a69a217cf8a5dfa54f2 --- .../modes/AbstractZenModePreferenceController.java | 12 +++++++++++- .../ZenModeTriggerUpdatePreferenceController.java | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java index c47345659fb..b27292a1bbd 100644 --- a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java +++ b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java @@ -28,7 +28,9 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; @@ -43,13 +45,14 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon @Nullable protected final ZenModesBackend mBackend; - @Nullable // only until setZenMode() is called private ZenMode mZenMode; @NonNull private final String mKey; + @NonNull private final MetricsFeatureProvider mMetricsFeatureProvider; + /** * Constructor suitable for "read-only" controllers (e.g. link to a different sub-screen. * Controllers that call this constructor to initialize themselves cannot call @@ -59,6 +62,7 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon super(context); mKey = key; mBackend = null; + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } /** @@ -71,6 +75,7 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon super(context); mKey = key; mBackend = backend; + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } @Override @@ -79,6 +84,11 @@ abstract class AbstractZenModePreferenceController extends AbstractPreferenceCon return mKey; } + @NonNull + public MetricsFeatureProvider getMetricsFeatureProvider() { + return mMetricsFeatureProvider; + } + @Override public boolean isAvailable() { if (mZenMode != null) { diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java index 1add4889c66..f2302c0b335 100644 --- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java @@ -218,6 +218,8 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference } return zenMode; }); + getMetricsFeatureProvider().action(mContext, SettingsEnums.ACTION_ZEN_MODE_ENABLE_TOGGLE, + enabled); } private void undoToggleSwitch(Preference preference, boolean wasSwitchedTo) { From 38dd287e01cf6ea6b41b1f603087ce2fbfa647ae Mon Sep 17 00:00:00 2001 From: Michal Brzezinski Date: Thu, 22 Aug 2024 15:41:39 +0000 Subject: [PATCH 3/7] Launching touchpad tutorial as user 0 It's required to properly intercept touchpad gestures which is happening in process of user 0. By default activity was launched in the current user process. Also targeting sysui package directly with intent. Bug: 361518125 Test: launch touchpad tutorial with HSUM and see touchpad gestures intercepted Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial Change-Id: I904df93758e49a7b10cf6d7a5398550405daafbb --- .../TouchGesturesButtonPreferenceController.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java index 12dc076a706..71fa28bf5ca 100644 --- a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java +++ b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java @@ -21,11 +21,13 @@ import static com.android.systemui.shared.Flags.newTouchpadGesturesTutorial; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; +import android.os.UserHandle; import android.util.FeatureFlagUtils; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceScreen; +import com.android.settings.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; @@ -79,10 +81,12 @@ public class TouchGesturesButtonPreferenceController extends BasePreferenceContr private void showTouchpadGestureEducation() { mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_LEARN_TOUCHPAD_GESTURE_CLICK); if (newTouchpadGesturesTutorial()) { - Intent intent = new Intent(TUTORIAL_ACTION); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + Intent intent = new Intent(TUTORIAL_ACTION) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .setPackage(Utils.SYSTEMUI_PACKAGE_NAME); + // touchpad tutorial must be started as system user as it needs to have access to state + // of user 0 sysui instance + mContext.startActivityAsUser(intent, UserHandle.SYSTEM); } else { TrackpadGestureDialogFragment fragment = new TrackpadGestureDialogFragment(); fragment.setTargetFragment(mParent, 0); From 8c50cdc72a1216a4eed0fe4f432bd830222e3d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Thu, 22 Aug 2024 19:31:22 +0200 Subject: [PATCH 4/7] Update Modes List if modes change in the background Fixes: 361518126 Test: manual Flag: android.app.modes_ui Change-Id: Ia00025adbb89ddc9163a9c50582cbcbd524231bb --- .../notification/modes/ZenModesListFragment.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/notification/modes/ZenModesListFragment.java b/src/com/android/settings/notification/modes/ZenModesListFragment.java index 2b58f8e1c3b..588b3202bb6 100644 --- a/src/com/android/settings/notification/modes/ZenModesListFragment.java +++ b/src/com/android/settings/notification/modes/ZenModesListFragment.java @@ -23,6 +23,7 @@ import android.content.Intent; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; @@ -62,9 +63,12 @@ public class ZenModesListFragment extends ZenModesFragmentBase { @Override protected void onUpdatedZenModeState() { - // 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. + // Preferences linking to individual rules do not need to be updated as part of onStart(), + // because DashboardFragment does that in onResume(). However, we force the update if we + // detect Modes changes in the background with the page open. + if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) { + forceUpdatePreferences(); + } } @Override From d8603124d1453ba2fe0bda26423d4cecbb263892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Thu, 22 Aug 2024 17:49:30 +0200 Subject: [PATCH 5/7] Don't observe Modes changes if MODES_UI is disabled The ZenModesLinkPreferenceController is present in the Notifications and Sound screens, and although isAvailable() is false when MODES_UI is not enabled, it was still registering the listener for Settings changes, and calling updateState(). Thus, Settings would (likely) crash if Zen settings change while the user is in one of those screens, due to not finding the manual rule. Fixes: 360498255 Test: atest ZenSettingsObserverTest Flag: android.app.modes_ui Change-Id: I7741809fd0028aace6ac58992be965701e64a2e3 --- .../modes/ZenSettingsObserver.java | 13 ++- .../modes/ZenSettingsObserverTest.java | 97 +++++++++++++++++++ 2 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java diff --git a/src/com/android/settings/notification/modes/ZenSettingsObserver.java b/src/com/android/settings/notification/modes/ZenSettingsObserver.java index a853646fd94..0f22d7de341 100644 --- a/src/com/android/settings/notification/modes/ZenSettingsObserver.java +++ b/src/com/android/settings/notification/modes/ZenSettingsObserver.java @@ -16,6 +16,7 @@ package com.android.settings.notification.modes; +import android.app.Flags; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; @@ -42,13 +43,17 @@ class ZenSettingsObserver extends ContentObserver { } void register() { - mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); - mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, - this); + if (Flags.modesApi() && Flags.modesUi()) { + mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); + mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, + this); + } } void unregister() { - mContext.getContentResolver().unregisterContentObserver(this); + if (Flags.modesApi() && Flags.modesUi()) { + mContext.getContentResolver().unregisterContentObserver(this); + } } void setOnChangeListener(@Nullable Runnable callback) { diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java new file mode 100644 index 00000000000..7fd47d9d9c1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java @@ -0,0 +1,97 @@ +/* + * 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.provider.Settings.Global.ZEN_MODE_CONFIG_ETAG; + +import static com.google.common.truth.Truth.assertThat; + +import static org.robolectric.Shadows.shadowOf; + +import android.app.Flags; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; + +import androidx.test.core.app.ApplicationProvider; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowLooper; + +import java.util.concurrent.atomic.AtomicInteger; + +@RunWith(RobolectricTestRunner.class) +public class ZenSettingsObserverTest { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private static final Uri SETTINGS_URI = Settings.Global.getUriFor( + ZEN_MODE_CONFIG_ETAG); + + private Context mContext; + private ZenSettingsObserver mObserver; + + @Before + public void setUp() { + mContext = ApplicationProvider.getApplicationContext(); + mObserver = new ZenSettingsObserver(mContext); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void register_withFlagEnabled_registersAndCallsBack() { + AtomicInteger someValue = new AtomicInteger(); + mObserver.setOnChangeListener(someValue::incrementAndGet); + assertThat(getSettingsContentObservers()).isEmpty(); + + mObserver.register(); + assertThat(getSettingsContentObservers()).hasSize(1); + + getSettingsContentObservers().forEach(o -> o.dispatchChange(false, SETTINGS_URI)); + ShadowLooper.idleMainLooper(); + assertThat(someValue.get()).isEqualTo(1); + + mObserver.unregister(); + assertThat(getSettingsContentObservers()).isEmpty(); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void register_withFlagDisabled_doesNotRegister() { + mObserver.register(); + assertThat(getSettingsContentObservers()).isEmpty(); + mObserver.unregister(); + assertThat(getSettingsContentObservers()).isEmpty(); + } + + private ImmutableList getSettingsContentObservers() { + return ImmutableList.copyOf( + shadowOf(mContext.getContentResolver()) + .getContentObservers(SETTINGS_URI)); + } +} From 64987b0bea79e793b3d05627907024d97ce4d2f5 Mon Sep 17 00:00:00 2001 From: Weng Su Date: Fri, 23 Aug 2024 06:16:33 +0800 Subject: [PATCH 6/7] Avoid launching Wi-Fi details settings if WifiEntry key is lost - When restarting Wi-Fi details settings from the recent apps, the extra data of the sent Intent may be lost, resulting in the loss of the necessary WifiEntry key - Don't launch Wi-Fi details settings and remove it from recent apps in the above case Fix: 349180072 Flag: EXEMPT bugfix Test: Manual testing Change-Id: Ifa9718b0197ab27b77c24b706f7974a664980fba --- src/com/android/settings/Settings.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 24d9525e87c..8321588fd12 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -27,6 +27,8 @@ import android.telephony.ims.ImsRcsManager; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.settings.biometrics.face.FaceSettings; import com.android.settings.communal.CommunalPreferenceController; @@ -35,6 +37,7 @@ import com.android.settings.network.MobileNetworkIntentConverter; import com.android.settings.overlay.FeatureFactory; import com.android.settings.safetycenter.SafetyCenterManagerWrapper; import com.android.settings.security.SecuritySettingsFeatureProvider; +import com.android.settings.wifi.WifiUtils; import com.google.android.setupdesign.util.ThemeHelper; @@ -73,7 +76,18 @@ public class Settings extends SettingsActivity { public static class NetworkProviderSettingsActivity extends SettingsActivity { /* empty */ } public static class NetworkSelectActivity extends SettingsActivity { /* empty */ } /** Activity for the Wi-Fi network details settings. */ - public static class WifiDetailsSettingsActivity extends SettingsActivity { /* empty */ } + public static class WifiDetailsSettingsActivity extends SettingsActivity { + @Override + protected void createUiFromIntent(@Nullable Bundle savedState, Intent intent) { + Bundle bundle = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); + if (TextUtils.isEmpty(bundle.getString(WifiUtils.KEY_CHOSEN_WIFIENTRY_KEY))) { + Log.e(getLocalClassName(), "The key of WifiEntry is empty!"); + finishAndRemoveTask(); + return; + } + super.createUiFromIntent(savedState, intent); + } + } public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ } public static class AvailableVirtualKeyboardActivity extends SettingsActivity { /* empty */ } public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ } From dd7d18ec8ac12156de11d20b1179216680cd4073 Mon Sep 17 00:00:00 2001 From: Rambo Wang Date: Fri, 23 Aug 2024 14:43:07 +0000 Subject: [PATCH 7/7] Turn off RILD reset in Reset Network Settings Flow The CL temporarily removes RILD reset in the flow and plans to bring it back when a more reliable solution is available. Resetting RID in the flow may cause device failure to camp on the default cellular network. The interface used to reset RILD in the flow was designed for telephony process silent restart feature. The implementation doesn't work well together with other reset options (e.g. nvResetConfig). Bug: 356272264 Test: Execute feature test plan, including regression test Flag: com.android.internal.telephony.flags.reset_mobile_network_settings Change-Id: I955bcb0b151f27dc73c03a580c0144bea9e3bfa3 --- src/com/android/settings/ResetNetwork.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java index c1e3494d621..ad9f35e889b 100644 --- a/src/com/android/settings/ResetNetwork.java +++ b/src/com/android/settings/ResetNetwork.java @@ -132,7 +132,6 @@ public class ResetNetwork extends InstrumentedFragment { if (Flags.resetMobileNetworkSettings()) { resetOptions |= ResetNetworkRequest.RESET_IMS_STACK; resetOptions |= ResetNetworkRequest.RESET_PHONE_PROCESS; - resetOptions |= ResetNetworkRequest.RESET_RILD; } ResetNetworkRequest request = new ResetNetworkRequest(resetOptions); if (mSubscriptions != null && mSubscriptions.size() > 0) {