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) { 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 */ } 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); 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/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java index 84086245b53..c5beb36c11c 100644 --- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java +++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java @@ -29,6 +29,7 @@ import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; import android.annotation.SuppressLint; import android.app.ActionBar; +import android.app.AutomaticZenRule; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; @@ -184,22 +185,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/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) { 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 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/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); 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)); + } +}