diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index ed0bce40041..febdb0412a4 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1567,7 +1567,7 @@
- @*android:drawable/ic_zen_mode_icon_child
- @*android:drawable/ic_zen_mode_icon_animal_paw
- - @*android:drawable/ic_zen_mode_type_unknown
+ - @*android:drawable/ic_zen_mode_icon_star_badge
- @*android:drawable/ic_zen_mode_type_managed
- @*android:drawable/ic_zen_mode_type_other
- @*android:drawable/ic_zen_mode_icon_heart
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b4092f63fbe..208073d05fb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9041,6 +9041,13 @@
Apply to work profiles
Apply to work profile apps
+
+ Bundled notifications
+ On
+ Off
+ Use notification bundling
+ Notifications with similar themes will be silenced and grouped together for a quieter experience. Bundling will override an app\'s own notification settings.
+
VR helper services
@@ -13781,9 +13788,12 @@
No default set
Device only
+
+ Device and %1$s
Add an account to get started
-
+
+ Where to save contacts
diff --git a/res/xml/accessibility_hearing_aids.xml b/res/xml/accessibility_hearing_aids.xml
index 9c6e661235a..16128281182 100644
--- a/res/xml/accessibility_hearing_aids.xml
+++ b/res/xml/accessibility_hearing_aids.xml
@@ -20,7 +20,8 @@
android:title="@string/accessibility_hearingaid_title">
+ android:title="@string/accessibility_hearingaid_intro"
+ settings:searchable="false" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index c88b7bace8a..aedf0b413aa 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -43,6 +43,14 @@
android:targetPackage="com.android.settings"
android:targetClass="com.android.settings.notification.history.NotificationHistoryActivity" />
+
+
+
+
+
-
-
+
+
mRestrictedItems = new ArrayList<>();
private boolean mRequiresActiveUnlockedProfile = false;
@@ -61,6 +65,11 @@ public class RestrictedListPreference extends CustomListPreference {
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
diff --git a/src/com/android/settings/SettingsApplication.java b/src/com/android/settings/SettingsApplication.java
index c908855ee49..99d3d922a0c 100644
--- a/src/com/android/settings/SettingsApplication.java
+++ b/src/com/android/settings/SettingsApplication.java
@@ -46,6 +46,7 @@ import com.android.settingslib.datastore.BackupRestoreStorageManager;
import com.android.settingslib.metadata.PreferenceScreenMetadata;
import com.android.settingslib.metadata.PreferenceScreenRegistry;
import com.android.settingslib.metadata.ProvidePreferenceScreenOptions;
+import com.android.settingslib.preference.PreferenceBindingFactory;
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -76,6 +77,7 @@ public class SettingsApplication extends Application {
if (Flags.catalyst()) {
PreferenceScreenRegistry.INSTANCE.setPreferenceScreensSupplier(
this::getPreferenceScreens);
+ PreferenceBindingFactory.setDefaultFactory(new SettingsPreferenceBindingFactory());
}
BackupRestoreStorageManager.getInstance(this)
diff --git a/src/com/android/settings/SettingsPreferenceBindingFactory.kt b/src/com/android/settings/SettingsPreferenceBindingFactory.kt
new file mode 100644
index 00000000000..53e5d0f45b5
--- /dev/null
+++ b/src/com/android/settings/SettingsPreferenceBindingFactory.kt
@@ -0,0 +1,49 @@
+/*
+ * 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
+
+import android.os.UserHandle
+import androidx.preference.Preference
+import com.android.settingslib.RestrictedPreferenceHelperProvider
+import com.android.settingslib.metadata.PreferenceHierarchyNode
+import com.android.settingslib.preference.DefaultPreferenceBindingFactory
+import com.android.settingslib.preference.PreferenceBinding
+
+/** Preference binding factory for settings app. */
+class SettingsPreferenceBindingFactory : DefaultPreferenceBindingFactory() {
+ override fun bind(
+ preference: Preference,
+ node: PreferenceHierarchyNode,
+ preferenceBinding: PreferenceBinding?,
+ ) {
+ super.bind(preference, node, preferenceBinding)
+
+ // handle restriction consistently
+ val metadata = node.metadata
+ if (metadata is PreferenceRestrictionMixin) {
+ if (preference is RestrictedPreferenceHelperProvider) {
+ preference.getRestrictedPreferenceHelper().apply {
+ val restrictionKey = metadata.restrictionKey
+ if (!preference.context.hasBaseUserRestriction(restrictionKey)) {
+ useAdminDisabledSummary(metadata.useAdminDisabledSummary)
+ checkRestrictionAndSetDisabled(restrictionKey, UserHandle.myUserId())
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/contacts/ContactsStoragePreferenceController.java b/src/com/android/settings/applications/contacts/ContactsStoragePreferenceController.java
index e4343e542bc..340a666d88a 100644
--- a/src/com/android/settings/applications/contacts/ContactsStoragePreferenceController.java
+++ b/src/com/android/settings/applications/contacts/ContactsStoragePreferenceController.java
@@ -61,6 +61,9 @@ public class ContactsStoragePreferenceController extends BasePreferenceControlle
@Override
public CharSequence getSummary() {
if (mCurrentDefaultAccountAndState != null) {
+ // Re-fetch account in controller to refresh the latest set default account.
+ mCurrentDefaultAccountAndState =
+ DefaultAccount.getDefaultAccountForNewContacts(mContext.getContentResolver());
int currentDefaultAccountState = mCurrentDefaultAccountAndState.getState();
Account currentDefaultAccount = mCurrentDefaultAccountAndState.getAccount();
if (currentDefaultAccountState
diff --git a/src/com/android/settings/applications/contacts/ContactsStorageSettings.java b/src/com/android/settings/applications/contacts/ContactsStorageSettings.java
index 8e71d08f1a3..5e48dd98ec6 100644
--- a/src/com/android/settings/applications/contacts/ContactsStorageSettings.java
+++ b/src/com/android/settings/applications/contacts/ContactsStorageSettings.java
@@ -25,6 +25,7 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState;
@@ -36,7 +37,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
-import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceGroup;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
@@ -63,6 +64,7 @@ public class ContactsStorageSettings extends DashboardFragment
private static final String TAG = "ContactsStorageSettings";
private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
private static final String PREF_KEY_DEVICE_ONLY = "device_only_account_preference";
+ private static final String PREF_KEY_ACCOUNT_CATEGORY = "account_category";
private final Map mAccountMap = new HashMap<>();
private AuthenticatorHelper mAuthenticatorHelper;
@@ -71,6 +73,12 @@ public class ContactsStorageSettings extends DashboardFragment
super.onAttach(context);
mAuthenticatorHelper = new AuthenticatorHelper(context,
new UserHandle(UserHandle.myUserId()), null);
+ String[] accountTypes = getEligibleAccountTypes();
+ for (String accountType : accountTypes) {
+ // Preload the drawable for the account type to avoid the latency when rendering the
+ // account preference.
+ mAuthenticatorHelper.preloadDrawableForType(context, accountType);
+ }
}
@UiThread
@@ -130,23 +138,24 @@ public class ContactsStorageSettings extends DashboardFragment
// Clear all the accounts stored in the map and later on re-fetch the eligible accounts
// when creating eligible account preferences.
mAccountMap.clear();
- final PreferenceScreen screen = getPreferenceScreen();
+ final PreferenceGroup preferenceGroup = findPreference(PREF_KEY_ACCOUNT_CATEGORY);
// If the default account is SIM, we should show in the page, otherwise don't show.
SelectorWithWidgetPreference simAccountPreference = buildSimAccountPreference();
if (simAccountPreference != null) {
- getPreferenceScreen().addPreference(simAccountPreference);
+ preferenceGroup.addPreference(simAccountPreference);
}
List accounts = DefaultAccount.getEligibleCloudAccounts(getContentResolver());
for (int i = 0; i < accounts.size(); i++) {
- screen.addPreference(buildCloudAccountPreference(accounts.get(i), /*order=*/i));
+ preferenceGroup.addPreference(
+ buildCloudAccountPreference(accounts.get(i), /*order=*/i));
}
// If there's no eligible account types, the "Add Account" preference should
// not be shown to the users.
if (getEligibleAccountTypes().length > 0) {
- screen.addPreference(buildAddAccountPreference(accounts.isEmpty()));
+ getPreferenceScreen().addPreference(buildAddAccountPreference(accounts.isEmpty()));
}
setupDeviceOnlyPreference();
- setDefaultAccountPreference();
+ setDefaultAccountPreference(preferenceGroup);
}
private void setupDeviceOnlyPreference() {
@@ -157,7 +166,7 @@ public class ContactsStorageSettings extends DashboardFragment
}
}
- private void setDefaultAccountPreference() {
+ private void setDefaultAccountPreference(PreferenceGroup preferenceGroup) {
DefaultAccountAndState currentDefaultAccountAndState =
DefaultAccount.getDefaultAccountForNewContacts(getContentResolver());
String preferenceKey = getAccountHashCode(currentDefaultAccountAndState);
@@ -170,20 +179,21 @@ public class ContactsStorageSettings extends DashboardFragment
preference = getPreferenceScreen().findPreference(preferenceKey);
} else if (preferenceKey != null && currentDefaultAccount != null) {
preference = buildCloudAccountPreference(currentDefaultAccount, mAccountMap.size());
- getPreferenceScreen().addPreference(preference);
+ preferenceGroup.addPreference(preference);
}
if (preference != null) {
preference.setChecked(true);
}
}
- //TODO: Add preference category on account preferences.
private SelectorWithWidgetPreference buildCloudAccountPreference(Account account, int order) {
SelectorWithWidgetPreference preference = new SelectorWithWidgetPreference(
getPrefContext());
DefaultAccountAndState accountAndState = DefaultAccountAndState.ofCloud(account);
String preferenceKey = getAccountHashCode(accountAndState);
- preference.setTitle(mAuthenticatorHelper.getLabelForType(getPrefContext(), account.type));
+ String accountPreferenceTitle = getString(R.string.contacts_storage_account_title,
+ mAuthenticatorHelper.getLabelForType(getPrefContext(), account.type));
+ preference.setTitle(accountPreferenceTitle);
preference.setIcon(mAuthenticatorHelper.getDrawableForType(getPrefContext(), account.type));
preference.setSummary(account.name);
preference.setKey(preferenceKey);
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt
index 9f62ed03572..bf0084d14a8 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/AccessibilityInteractor.kt
@@ -16,6 +16,7 @@
package com.android.settings.biometrics.fingerprint2.domain.interactor
+import android.util.Log
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityEvent.TYPE_ANNOUNCEMENT
import android.view.accessibility.AccessibilityManager
@@ -30,23 +31,26 @@ import kotlinx.coroutines.flow.stateIn
interface AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
fun isEnabledFlow(scope: CoroutineScope): Flow
+
val isEnabled: Boolean
+
fun announce(clazz: Class<*>, announcement: CharSequence?)
+
+ fun interrupt()
}
-class AccessibilityInteractorImpl(
- private val accessibilityManager: AccessibilityManager,
-) : AccessibilityInteractor {
+class AccessibilityInteractorImpl(private val accessibilityManager: AccessibilityManager) :
+ AccessibilityInteractor {
/** A flow that contains whether or not accessibility is enabled */
override fun isEnabledFlow(scope: CoroutineScope): Flow =
callbackFlow {
- val listener =
- AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
- accessibilityManager.addAccessibilityStateChangeListener(listener)
+ val listener =
+ AccessibilityManager.AccessibilityStateChangeListener { enabled -> trySend(enabled) }
+ accessibilityManager.addAccessibilityStateChangeListener(listener)
- // This clause will be called when no one is listening to the flow
- awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) }
- }
+ // This clause will be called when no one is listening to the flow
+ awaitClose { accessibilityManager.removeAccessibilityStateChangeListener(listener) }
+ }
.stateIn(
scope,
SharingStarted.WhileSubscribed(), // When no longer subscribed, we removeTheListener
@@ -63,4 +67,17 @@ class AccessibilityInteractorImpl(
event.text.add(announcement)
accessibilityManager.sendAccessibilityEvent(event)
}
+
+ /** Interrupts the current accessibility manager from announcing a phrase. */
+ override fun interrupt() {
+ try {
+ accessibilityManager.interrupt()
+ } catch (e: IllegalStateException) {
+ Log.e(TAG, "Error trying to interrupt when accessibility isn't enabled $e")
+ }
+ }
+
+ companion object {
+ const val TAG = "AccessibilityInteractor"
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
index e55d6b899ca..e273bb76c8f 100644
--- a/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
+++ b/src/com/android/settings/biometrics/fingerprint2/domain/interactor/OrientationInteractor.kt
@@ -19,10 +19,13 @@ package com.android.settings.biometrics.fingerprint2.domain.interactor
import android.content.Context
import android.view.OrientationEventListener
import com.android.internal.R
+import com.android.settings.biometrics.fingerprint2.lib.model.Orientation
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.transform
/** Interactor which provides information about orientation */
@@ -45,6 +48,9 @@ interface OrientationInteractor {
* [R.bool.config_reverseDefaultConfigRotation]
*/
fun getRotationFromDefault(rotation: Int): Int
+
+ /** Indicates an orientation changed event has occurred */
+ val orientationChanged: Flow
}
class OrientationInteractorImpl(private val context: Context) : OrientationInteractor {
@@ -60,7 +66,10 @@ class OrientationInteractorImpl(private val context: Context) : OrientationInter
awaitClose { orientationEventListener.disable() }
}
- override val rotation: Flow = orientation.transform { emit(context.display.rotation) }
+ override val rotation: Flow =
+ orientation
+ .transform { emit(context.display.rotation) }
+ .onStart { emit(context.display.rotation) }
override val rotationFromDefault: Flow = rotation.map { getRotationFromDefault(it) }
@@ -73,4 +82,24 @@ class OrientationInteractorImpl(private val context: Context) : OrientationInter
rotation
}
}
+
+ override val orientationChanged: Flow =
+ rotationFromDefault
+ .map {
+ when (it) {
+ 1 -> {
+ Orientation.Portrait
+ }
+ 2 -> {
+ Orientation.ReverseLandscape
+ }
+ 3 -> {
+ Orientation.UpsideDownPortrait
+ }
+ else -> {
+ Orientation.Landscape
+ }
+ }
+ }
+ .distinctUntilChanged()
}
diff --git a/src/com/android/settings/biometrics/fingerprint2/lib/model/Orientation.kt b/src/com/android/settings/biometrics/fingerprint2/lib/model/Orientation.kt
new file mode 100644
index 00000000000..c88067772b8
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint2/lib/model/Orientation.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.biometrics.fingerprint2.lib.model
+
+/** The orientation events correspond to androids internal orientation events. */
+sealed class Orientation {
+ /** Indicates the device is in landscape orientation */
+ data object Landscape : Orientation()
+
+ /** Indicates the device is in reverse landscape orientation */
+ data object ReverseLandscape : Orientation()
+
+ /** Indicates the device is in portrait orientation */
+ data object Portrait : Orientation()
+
+ /** Indicates the device is in the upside down portrait orientation */
+ data object UpsideDownPortrait : Orientation()
+}
diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
index 1c8aafdbce1..e3ed7f597b5 100644
--- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
+++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt
@@ -294,6 +294,7 @@ class DeviceDetailsFragmentFormatterImpl(
TwoTargetSwitchPreference(
switchPrefModel,
primaryOnClick = { triggerAction(model.action) },
+ primaryEnabled = { !model.disabled }
)
} else {
SwitchPreference(switchPrefModel)
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
index b3e66a9ce06..bb84c1804e9 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
@@ -30,6 +30,7 @@ import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -37,7 +38,7 @@ import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AppSwitchPreference;
public class UnrestrictedDataAccessPreference extends AppSwitchPreference implements
- DataSaverBackend.Listener {
+ DataSaverBackend.Listener, RestrictedPreferenceHelperProvider {
private static final String ECM_SETTING_IDENTIFIER = "android:unrestricted_data_access";
private final ApplicationsState mApplicationsState;
@@ -78,6 +79,11 @@ public class UnrestrictedDataAccessPreference extends AppSwitchPreference implem
return entry.info.packageName + "|" + entry.info.uid;
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
@Override
public void onAttached() {
super.onAttached();
diff --git a/src/com/android/settings/display/AutoBrightnessScreen.kt b/src/com/android/settings/display/AutoBrightnessScreen.kt
index 0e682ff92df..5d08b1dfed1 100644
--- a/src/com/android/settings/display/AutoBrightnessScreen.kt
+++ b/src/com/android/settings/display/AutoBrightnessScreen.kt
@@ -16,17 +16,15 @@
package com.android.settings.display
import android.content.Context
-import android.os.Process
-import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings
import android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
import android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL
import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.flags.Flags
import com.android.settingslib.PrimarySwitchPreference
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObservableDelegate
import com.android.settingslib.datastore.SettingsStore
@@ -35,7 +33,6 @@ import com.android.settingslib.metadata.BooleanValue
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.ProvidePreferenceScreen
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.metadata.preferenceHierarchy
@@ -47,7 +44,7 @@ class AutoBrightnessScreen :
PreferenceScreenCreator,
PreferenceScreenBinding,
PreferenceAvailabilityProvider,
- PreferenceRestrictionProvider,
+ PreferenceRestrictionMixin,
PersistentPreference,
BooleanValue {
override val key: String
@@ -75,23 +72,19 @@ class AutoBrightnessScreen :
com.android.internal.R.bool.config_automatic_brightness_available
)
- override fun isEnabled(context: Context) =
- !UserManager.get(context)
- .hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, Process.myUserHandle())
+ override fun isEnabled(context: Context) = super.isEnabled(context)
- override fun isRestricted(context: Context) =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- UserManager.DISALLOW_CONFIG_BRIGHTNESS,
- UserHandle.myUserId(),
- ) != null
+ override val restrictionKey: String
+ get() = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+
+ override val useAdminDisabledSummary: Boolean
+ get() = true
override fun createWidget(context: Context) = PrimarySwitchPreference(context)
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
super.bind(preference, metadata)
(preference as PrimarySwitchPreference).apply {
- useAdminDisabledSummary(true)
isSwitchEnabled = isEnabled
// "true" is not the real default value (it is provided by AutoBrightnessDataStore)
isChecked = preferenceDataStore!!.getBoolean(key, true)
diff --git a/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
index a412b8cb310..09d92ebeee8 100644
--- a/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
+++ b/src/com/android/settings/display/BrightnessLevelRestrictedPreference.kt
@@ -23,15 +23,13 @@ import android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH
import android.hardware.display.BrightnessInfo
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManager.DisplayListener
-import android.os.Process
-import android.os.UserHandle
import android.os.UserManager
import android.provider.Settings.System
import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
import com.android.settings.Utils
import com.android.settings.core.SettingsBaseActivity
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.RestrictedPreference
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyedObserver
@@ -42,7 +40,6 @@ import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.PreferenceSummaryProvider
import com.android.settingslib.preference.PreferenceBinding
import com.android.settingslib.transition.SettingsTransitionHelper
@@ -52,7 +49,7 @@ import java.text.NumberFormat
class BrightnessLevelRestrictedPreference :
PreferenceMetadata,
PreferenceBinding,
- PreferenceRestrictionProvider,
+ PreferenceRestrictionMixin,
PreferenceSummaryProvider,
PreferenceLifecycleProvider,
Preference.OnPreferenceClickListener {
@@ -69,34 +66,28 @@ class BrightnessLevelRestrictedPreference :
override val keywords: Int
get() = R.string.keywords_display_brightness_level
- override fun getSummary(context: Context) =
+ override fun getSummary(context: Context): CharSequence? =
NumberFormat.getPercentInstance().format(getCurrentBrightness(context))
- override fun isEnabled(context: Context) =
- !UserManager.get(context)
- .hasBaseUserRestriction(UserManager.DISALLOW_CONFIG_BRIGHTNESS, Process.myUserHandle())
+ override fun isEnabled(context: Context) = super.isEnabled(context)
- override fun isRestricted(context: Context) =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- UserManager.DISALLOW_CONFIG_BRIGHTNESS,
- UserHandle.myUserId(),
- ) != null
+ override val restrictionKey: String
+ get() = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+
+ override val useAdminDisabledSummary: Boolean
+ get() = true
override fun createWidget(context: Context) = RestrictedPreference(context)
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
super.bind(preference, metadata)
- if (preference is RestrictedPreference) preference.useAdminDisabledSummary(true)
preference.onPreferenceClickListener = this
}
override fun onStart(context: PreferenceLifecycleContext) {
val observer =
- object : KeyedObserver {
- override fun onKeyChanged(key: String, reason: Int) {
- context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
- }
+ KeyedObserver { _, _ ->
+ context.notifyPreferenceChange(this@BrightnessLevelRestrictedPreference)
}
brightnessObserver = observer
SettingsSystemStore.get(context)
@@ -113,13 +104,11 @@ class BrightnessLevelRestrictedPreference :
}
}
displayListener = listener
- context
- .getSystemService(DisplayManager::class.java)
- .registerDisplayListener(
- listener,
- HandlerExecutor.main,
- DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS,
- )
+ context.displayManager.registerDisplayListener(
+ listener,
+ HandlerExecutor.main,
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS,
+ )
}
override fun onStop(context: PreferenceLifecycleContext) {
@@ -129,11 +118,14 @@ class BrightnessLevelRestrictedPreference :
}
displayListener?.let {
- context.getSystemService(DisplayManager::class.java).unregisterDisplayListener(it)
+ context.displayManager.unregisterDisplayListener(it)
displayListener = null
}
}
+ private val Context.displayManager: DisplayManager
+ get() = getSystemService(DisplayManager::class.java)!!
+
override fun onPreferenceClick(preference: Preference): Boolean {
val context = preference.context
val intent =
diff --git a/src/com/android/settings/notification/BundleGlobalPreferenceController.java b/src/com/android/settings/notification/BundleGlobalPreferenceController.java
new file mode 100644
index 00000000000..e41e7dd7c63
--- /dev/null
+++ b/src/com/android/settings/notification/BundleGlobalPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+import android.app.Flags;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.widget.SettingsMainSwitchPreferenceController;
+
+public class BundleGlobalPreferenceController extends
+ SettingsMainSwitchPreferenceController {
+
+ NotificationBackend mBackend;
+
+ public BundleGlobalPreferenceController(@NonNull Context context,
+ @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ mBackend = new NotificationBackend();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mBackend.isNotificationBundlingEnabled(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ mBackend.setNotificationBundlingEnabled(isChecked);
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ // not needed since it's not sliceable
+ return NO_RES;
+ }
+}
diff --git a/src/com/android/settings/notification/BundlePreferenceController.java b/src/com/android/settings/notification/BundlePreferenceController.java
new file mode 100644
index 00000000000..39d28ceee07
--- /dev/null
+++ b/src/com/android/settings/notification/BundlePreferenceController.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import android.app.Flags;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller for the bundled notifications settings page.
+ */
+public class BundlePreferenceController extends BasePreferenceController {
+
+ NotificationBackend mBackend;
+
+ public BundlePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mBackend = new NotificationBackend();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mBackend.isNotificationBundlingEnabled(mContext)
+ ? mContext.getString(R.string.notification_bundle_on)
+ : mContext.getString(R.string.notification_bundle_off);
+ }
+}
diff --git a/src/com/android/settings/notification/BundlePreferenceFragment.java b/src/com/android/settings/notification/BundlePreferenceFragment.java
new file mode 100644
index 00000000000..14de2c26d1c
--- /dev/null
+++ b/src/com/android/settings/notification/BundlePreferenceFragment.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.app.Flags;
+
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Fragment for bundled notifications.
+ */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class BundlePreferenceFragment extends DashboardFragment {
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.BUNDLED_NOTIFICATIONS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.bundle_notifications_settings;
+ }
+ @Override
+ protected String getLogTag() {
+ return "BundlePreferenceFragment";
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.bundle_notifications_settings) {
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ return Flags.notificationClassificationUi();
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/BundleTypePreferenceController.java b/src/com/android/settings/notification/BundleTypePreferenceController.java
new file mode 100644
index 00000000000..b9fb2b2d78f
--- /dev/null
+++ b/src/com/android/settings/notification/BundleTypePreferenceController.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import android.app.Flags;
+import android.content.Context;
+import android.service.notification.Adjustment;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.widget.SettingsMainSwitchPreferenceController;
+
+public class BundleTypePreferenceController extends
+ SettingsMainSwitchPreferenceController {
+
+ static final String PROMO_KEY = "promotions";
+ static final String NEWS_KEY = "news";
+ static final String SOCIAL_KEY = "social";
+ static final String RECS_KEY = "recs";
+
+ NotificationBackend mBackend;
+ int mType;
+
+ public BundleTypePreferenceController(@NonNull Context context,
+ @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ mBackend = new NotificationBackend();
+ mType = getBundleTypeForKey();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (Flags.notificationClassificationUi() && mBackend.isNotificationBundlingSupported()
+ && mBackend.isNotificationBundlingEnabled(mContext)) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mBackend.isBundleTypeApproved(mType);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ mBackend.setBundleTypeState(mType, isChecked);
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ // not needed since it's not sliceable
+ return NO_RES;
+ }
+
+ private @Adjustment.Types int getBundleTypeForKey() {
+ if (PROMO_KEY.equals(mPreferenceKey)) {
+ return Adjustment.TYPE_PROMOTION;
+ } else if (NEWS_KEY.equals(mPreferenceKey)) {
+ return Adjustment.TYPE_NEWS;
+ } else if (SOCIAL_KEY.equals(mPreferenceKey)) {
+ return Adjustment.TYPE_SOCIAL_MEDIA;
+ } else if (RECS_KEY.equals(mPreferenceKey)) {
+ return Adjustment.TYPE_CONTENT_RECOMMENDATION;
+ }
+ return Adjustment.TYPE_OTHER;
+ }
+}
diff --git a/src/com/android/settings/notification/CallVolumePreference.kt b/src/com/android/settings/notification/CallVolumePreference.kt
index 3c14ae4fafb..df6c4275c94 100644
--- a/src/com/android/settings/notification/CallVolumePreference.kt
+++ b/src/com/android/settings/notification/CallVolumePreference.kt
@@ -20,18 +20,16 @@ import android.content.Context
import android.media.AudioManager
import android.media.AudioManager.STREAM_BLUETOOTH_SCO
import android.media.AudioManager.STREAM_VOICE_CALL
-import android.os.UserHandle
-import android.os.UserManager.DISALLOW_ADJUST_VOLUME
+import android.os.UserManager
import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceIconProvider
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.PreferenceBinding
@@ -44,7 +42,7 @@ open class CallVolumePreference :
RangeValue,
PreferenceAvailabilityProvider,
PreferenceIconProvider,
- PreferenceRestrictionProvider {
+ PreferenceRestrictionMixin {
override val key: String
get() = KEY
@@ -55,18 +53,12 @@ open class CallVolumePreference :
override fun isAvailable(context: Context) =
context.resources.getBoolean(R.bool.config_show_call_volume) &&
- !createAudioHelper(context).isSingleVolume()
+ !createAudioHelper(context).isSingleVolume
- override fun isRestricted(context: Context) =
- RestrictedLockUtilsInternal.hasBaseUserRestriction(
- context,
- DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId()
- ) || RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId()
- ) != null
+ override fun isEnabled(context: Context) = super.isEnabled(context)
+
+ override val restrictionKey: String
+ get() = UserManager.DISALLOW_ADJUST_VOLUME
override fun storage(context: Context): KeyValueStore {
val helper = createAudioHelper(context)
diff --git a/src/com/android/settings/notification/MediaVolumePreference.kt b/src/com/android/settings/notification/MediaVolumePreference.kt
index acb8f8d3af1..a39023361f4 100644
--- a/src/com/android/settings/notification/MediaVolumePreference.kt
+++ b/src/com/android/settings/notification/MediaVolumePreference.kt
@@ -18,18 +18,16 @@ package com.android.settings.notification
import android.content.Context
import android.media.AudioManager.STREAM_MUSIC
-import android.os.UserHandle
import android.os.UserManager
import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceIconProvider
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.PreferenceBinding
@@ -42,7 +40,7 @@ open class MediaVolumePreference :
RangeValue,
PreferenceAvailabilityProvider,
PreferenceIconProvider,
- PreferenceRestrictionProvider {
+ PreferenceRestrictionMixin {
override val key: String
get() = KEY
@@ -58,17 +56,10 @@ open class MediaVolumePreference :
override fun isAvailable(context: Context) =
context.resources.getBoolean(R.bool.config_show_media_volume)
- override fun isRestricted(context: Context) =
- RestrictedLockUtilsInternal.hasBaseUserRestriction(
- context,
- UserManager.DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId(),
- ) ||
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- UserManager.DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId(),
- ) != null
+ override fun isEnabled(context: Context) = super.isEnabled(context)
+
+ override val restrictionKey: String
+ get() = UserManager.DISALLOW_ADJUST_VOLUME
override fun storage(context: Context): KeyValueStore {
val helper = createAudioHelper(context)
@@ -107,9 +98,9 @@ open class MediaVolumePreference :
open fun createAudioHelper(context: Context) = AudioHelper(context)
- fun updateContentDescription(preference: VolumeSeekBarPreference) {
+ private fun updateContentDescription(preference: VolumeSeekBarPreference) {
when {
- preference.isMuted() ->
+ preference.isMuted ->
preference.updateContentDescription(
preference.context.getString(
R.string.volume_content_description_silent_mode,
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index d6f810c99d1..388e0d7ffaf 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -46,6 +46,7 @@ import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.text.format.DateUtils;
@@ -65,9 +66,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
public class NotificationBackend {
private static final String TAG = "NotificationBackend";
@@ -651,6 +654,59 @@ public class NotificationBackend {
return false;
}
+ public boolean isNotificationBundlingSupported() {
+ try {
+ return !sINM.getUnsupportedAdjustmentTypes().contains(Adjustment.KEY_TYPE);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ return false;
+ }
+
+ public boolean isNotificationBundlingEnabled(Context context) {
+ try {
+ return sINM.getAllowedAssistantAdjustments(context.getPackageName())
+ .contains(Adjustment.KEY_TYPE);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ return false;
+ }
+
+ public void setNotificationBundlingEnabled(boolean enabled) {
+ try {
+ if (enabled) {
+ sINM.allowAssistantAdjustment(Adjustment.KEY_TYPE);
+ } else {
+ sINM.disallowAssistantAdjustment(Adjustment.KEY_TYPE);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
+ public boolean isBundleTypeApproved(@Adjustment.Types int type) {
+ try {
+ int[] approved = sINM.getAllowedAdjustmentKeyTypes();
+ for (int approvedType : approved) {
+ if (type == approvedType) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ return false;
+ }
+
+ public void setBundleTypeState(@Adjustment.Types int type, boolean enabled) {
+ try {
+ sINM.setAssistantAdjustmentKeyTypeState(type, enabled);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ }
+
@VisibleForTesting
void setNm(INotificationManager inm) {
sINM = inm;
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
index 6831daad683..c6485686f52 100644
--- a/src/com/android/settings/notification/SeparateRingVolumePreference.kt
+++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
@@ -24,21 +24,19 @@ import android.media.AudioManager.RINGER_MODE_SILENT
import android.media.AudioManager.RINGER_MODE_VIBRATE
import android.media.AudioManager.STREAM_RING
import android.os.ServiceManager
-import android.os.UserHandle
-import android.os.UserManager.DISALLOW_ADJUST_VOLUME
+import android.os.UserManager
import android.os.Vibrator
import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS
import android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS
import androidx.preference.Preference
+import com.android.settings.PreferenceRestrictionMixin
import com.android.settings.R
-import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.NoOpKeyedObservable
import com.android.settingslib.metadata.PersistentPreference
import com.android.settingslib.metadata.PreferenceAvailabilityProvider
import com.android.settingslib.metadata.PreferenceIconProvider
import com.android.settingslib.metadata.PreferenceMetadata
-import com.android.settingslib.metadata.PreferenceRestrictionProvider
import com.android.settingslib.metadata.RangeValue
import com.android.settingslib.metadata.ReadWritePermit
import com.android.settingslib.preference.PreferenceBinding
@@ -51,7 +49,8 @@ open class SeparateRingVolumePreference :
RangeValue,
PreferenceAvailabilityProvider,
PreferenceIconProvider,
- PreferenceRestrictionProvider {
+ PreferenceRestrictionMixin {
+
override val key: String
get() = KEY
@@ -64,21 +63,12 @@ open class SeparateRingVolumePreference :
else -> R.drawable.ic_ring_volume
}
- override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume()
+ override fun isAvailable(context: Context) = !createAudioHelper(context).isSingleVolume
- override fun isEnabled(context: Context) =
- !RestrictedLockUtilsInternal.hasBaseUserRestriction(
- context,
- DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId(),
- )
+ override fun isEnabled(context: Context) = super.isEnabled(context)
- override fun isRestricted(context: Context) =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- DISALLOW_ADJUST_VOLUME,
- UserHandle.myUserId(),
- ) != null
+ override val restrictionKey: String
+ get() = UserManager.DISALLOW_ADJUST_VOLUME
override fun storage(context: Context): KeyValueStore {
val helper = createAudioHelper(context)
@@ -118,7 +108,7 @@ open class SeparateRingVolumePreference :
open fun createAudioHelper(context: Context) = AudioHelper(context)
- fun updateContentDescription(preference: VolumeSeekBarPreference) {
+ private fun updateContentDescription(preference: VolumeSeekBarPreference) {
val context = preference.context
val ringerMode = getEffectiveRingerMode(context)
when (ringerMode) {
@@ -152,13 +142,13 @@ open class SeparateRingVolumePreference :
}
}
- fun getSuppressionText(context: Context): String? {
+ private fun getSuppressionText(context: Context): String? {
val suppressor = NotificationManager.from(context).getEffectsSuppressor()
val notificationManager =
INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE)
)
- val hints = notificationManager.getHintsFromListenerNoToken()
+ val hints = notificationManager.hintsFromListenerNoToken
return when {
hintsMatch(hints) -> SuppressorHelper.getSuppressionText(context, suppressor)
else -> null
@@ -167,7 +157,7 @@ open class SeparateRingVolumePreference :
private fun hintsMatch(hints: Int) =
(hints and HINT_HOST_DISABLE_CALL_EFFECTS) != 0 ||
- (hints and HINT_HOST_DISABLE_EFFECTS) != 0
+ (hints and HINT_HOST_DISABLE_EFFECTS) != 0
companion object {
const val KEY = "separate_ring_volume"
diff --git a/src/com/android/settings/notification/app/BubblePreference.java b/src/com/android/settings/notification/app/BubblePreference.java
index 17deef9e6f0..73f4582034b 100644
--- a/src/com/android/settings/notification/app/BubblePreference.java
+++ b/src/com/android/settings/notification/app/BubblePreference.java
@@ -33,11 +33,13 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
/**
* A tri-state preference allowing a user to specify what gets to bubble.
*/
-public class BubblePreference extends Preference implements RadioGroup.OnCheckedChangeListener {
+public class BubblePreference extends Preference implements RadioGroup.OnCheckedChangeListener,
+ RestrictedPreferenceHelperProvider {
RestrictedPreferenceHelper mHelper;
private int mSelectedPreference;
@@ -64,6 +66,11 @@ public class BubblePreference extends Preference implements RadioGroup.OnChecked
setLayoutResource(R.layout.bubble_preference);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
public void setSelectedPreference(int preference) {
mSelectedPreference = preference;
notifyChanged();
diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java
index c76a5de4535..86552422467 100644
--- a/src/com/android/settings/widget/RestrictedAppPreference.java
+++ b/src/com/android/settings/widget/RestrictedAppPreference.java
@@ -27,6 +27,7 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
import com.android.settingslib.widget.AppPreference;
/**
@@ -34,7 +35,8 @@ import com.android.settingslib.widget.AppPreference;
* {@link com.android.settingslib.RestrictedPreferenceHelper}.
* Used to show policy transparency on {@link AppPreference}.
*/
-public class RestrictedAppPreference extends AppPreference {
+public class RestrictedAppPreference extends AppPreference implements
+ RestrictedPreferenceHelperProvider {
private RestrictedPreferenceHelper mHelper;
private String userRestriction;
@@ -58,6 +60,11 @@ public class RestrictedAppPreference extends AppPreference {
this.userRestriction = userRestriction;
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mHelper;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
index 9f6d787e841..17d5fc88915 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
@@ -23,12 +23,14 @@ import android.util.AttributeSet;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import androidx.annotation.NonNull;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.TwoStatePreference;
import com.android.settings.R;
import com.android.settings.widget.SettingsMainSwitchBar.OnBeforeCheckedChangeListener;
import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
import java.util.ArrayList;
@@ -40,7 +42,7 @@ import java.util.List;
* to enable or disable the preferences on the page.
*/
public class SettingsMainSwitchPreference extends TwoStatePreference implements
- OnCheckedChangeListener {
+ OnCheckedChangeListener, RestrictedPreferenceHelperProvider {
private final List mBeforeCheckedChangeListeners =
new ArrayList<>();
@@ -71,6 +73,11 @@ public class SettingsMainSwitchPreference extends TwoStatePreference implements
init(context, attrs);
}
+ @Override
+ public @NonNull RestrictedPreferenceHelper getRestrictedPreferenceHelper() {
+ return mRestrictedHelper;
+ }
+
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
diff --git a/tests/robotests/src/com/android/settings/MainClearTest.java b/tests/robotests/src/com/android/settings/MainClearTest.java
index 4c2b2669dcf..c938e5f17e1 100644
--- a/tests/robotests/src/com/android/settings/MainClearTest.java
+++ b/tests/robotests/src/com/android/settings/MainClearTest.java
@@ -66,7 +66,6 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -263,7 +262,6 @@ public class MainClearTest {
assertThat(mMainClear.showWipeEuicc()).isTrue();
}
- @Ignore("b/313566998")
@Test
public void testShowWipeEuicc_developerMode_unprovisioned() {
prepareEuiccState(
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidsFragmentTest.java
index 25a2cc16b4e..80cc5719e38 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidsFragmentTest.java
@@ -51,6 +51,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.List;
+import java.util.Objects;
/** Tests for {@link AccessibilityHearingAidsFragment}. */
@RunWith(RobolectricTestRunner.class)
@@ -88,7 +89,9 @@ public class AccessibilityHearingAidsFragmentTest {
mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
final List niks = AccessibilityHearingAidsFragment.SEARCH_INDEX_DATA_PROVIDER
- .getNonIndexableKeys(mContext);
+ .getNonIndexableKeys(mContext).stream()
+ .filter(Objects::nonNull)
+ .toList();
final List keys =
XmlTestUtils.getKeysFromPreferenceXml(mContext, R.xml.accessibility_hearing_aids);
diff --git a/tests/robotests/src/com/android/settings/applications/contacts/ContactsStorageSettingsTest.java b/tests/robotests/src/com/android/settings/applications/contacts/ContactsStorageSettingsTest.java
index 4e873fb20ee..a2debd24ee2 100644
--- a/tests/robotests/src/com/android/settings/applications/contacts/ContactsStorageSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/applications/contacts/ContactsStorageSettingsTest.java
@@ -44,6 +44,7 @@ import android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccou
import android.provider.SearchIndexableResource;
import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
@@ -73,7 +74,7 @@ import java.util.List;
@Config(shadows = ShadowAuthenticationHelper.class)
public class ContactsStorageSettingsTest {
private static final String PREF_KEY_DEVICE_ONLY = "device_only_account_preference";
-
+ private static final String PREF_KEY_ACCOUNT_CATEGORY = "account_category";
private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
private static final Account TEST_ACCOUNT1 = new Account("test@gmail.com", "type1");
@@ -95,6 +96,7 @@ public class ContactsStorageSettingsTest {
private PreferenceManager mPreferenceManager;
private TestContactsStorageSettings mContactsStorageSettings;
private PreferenceScreen mScreen;
+ private PreferenceGroup accountCategory;
@Before
public void setUp() throws Exception {
@@ -103,8 +105,16 @@ public class ContactsStorageSettingsTest {
eq(ContactsContract.AUTHORITY_URI))).thenReturn(mContentProviderClient);
mPreferenceManager = new PreferenceManager(mContext);
when(mContactsStorageSettings.getPreferenceManager()).thenReturn(mPreferenceManager);
- mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
+ mScreen = spy(mPreferenceManager.inflateFromResource(mContext,
+ R.xml.contacts_storage_settings, mScreen));
when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
+ accountCategory = mScreen.findPreference(PREF_KEY_ACCOUNT_CATEGORY);
+ SelectorWithWidgetPreference deviceOnlyPreference = mScreen.findPreference(
+ PREF_KEY_DEVICE_ONLY);
+ when(mContactsStorageSettings.findPreference(eq(PREF_KEY_DEVICE_ONLY))).thenReturn(
+ deviceOnlyPreference);
+ when(mContactsStorageSettings.findPreference(eq(PREF_KEY_ACCOUNT_CATEGORY))).thenReturn(
+ accountCategory);
when(mContactsStorageSettings.getPreferenceScreen()).thenReturn(mScreen);
mContactsStorageSettings.onAttach(mContext);
}
@@ -134,17 +144,15 @@ public class ContactsStorageSettingsTest {
when(mContentProviderClient.call(eq(QUERY_ELIGIBLE_DEFAULT_ACCOUNTS_METHOD), any(),
any())).thenReturn(eligibleAccountBundle);
- PreferenceScreen settingScreen = mPreferenceManager.inflateFromResource(mContext,
- R.xml.contacts_storage_settings, mScreen);
- SelectorWithWidgetPreference deviceOnlyPreference = settingScreen.findPreference(
+ SelectorWithWidgetPreference deviceOnlyPreference = mContactsStorageSettings.findPreference(
PREF_KEY_DEVICE_ONLY);
- when(mContactsStorageSettings.findPreference(eq(PREF_KEY_DEVICE_ONLY))).thenReturn(
- deviceOnlyPreference);
assertThat(deviceOnlyPreference.getTitle()).isEqualTo("Device only");
assertThat(deviceOnlyPreference.getSummary()).isEqualTo(
"New contacts won't be synced with an account");
assertThat(deviceOnlyPreference.getOrder()).isEqualTo(999);
+ assertThat(mContactsStorageSettings.findPreference(
+ PREF_KEY_ACCOUNT_CATEGORY).getTitle()).isEqualTo("Where to save contacts");
mContactsStorageSettings.refreshUI();
mContactsStorageSettings.onRadioButtonClicked(deviceOnlyPreference);
@@ -175,6 +183,8 @@ public class ContactsStorageSettingsTest {
mContactsStorageSettings.refreshUI();
+ assertThat(mContactsStorageSettings.findPreference(
+ PREF_KEY_ACCOUNT_CATEGORY).getTitle()).isEqualTo("Where to save contacts");
assertThat(mScreen.findPreference(PREF_KEY_ADD_ACCOUNT).getTitle()).isEqualTo(
"Add an account to get started");
assertThat(mScreen.findPreference(PREF_KEY_ADD_ACCOUNT).getOrder()).isEqualTo(998);
@@ -232,15 +242,15 @@ public class ContactsStorageSettingsTest {
mContactsStorageSettings.refreshUI();
- SelectorWithWidgetPreference account1Preference = mScreen.findPreference(
+ SelectorWithWidgetPreference account1Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT1.hashCode()));
- assertThat(account1Preference.getTitle()).isEqualTo("LABEL1");
+ assertThat(account1Preference.getTitle()).isEqualTo("Device and LABEL1");
assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com");
assertThat(account1Preference.getIcon()).isNotNull();
- SelectorWithWidgetPreference account2Preference = mScreen.findPreference(
+ SelectorWithWidgetPreference account2Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT2.hashCode()));
- assertThat(account2Preference.getTitle()).isEqualTo("LABEL2");
+ assertThat(account2Preference.getTitle()).isEqualTo("Device and LABEL2");
assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com");
assertThat(account2Preference.getIcon()).isNotNull();
@@ -286,21 +296,21 @@ public class ContactsStorageSettingsTest {
mContactsStorageSettings.refreshUI();
- SelectorWithWidgetPreference account1Preference = mScreen.findPreference(
+ SelectorWithWidgetPreference account1Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT1.hashCode()));
- assertThat(account1Preference.getTitle()).isEqualTo("LABEL1");
+ assertThat(account1Preference.getTitle()).isEqualTo("Device and LABEL1");
assertThat(account1Preference.getSummary()).isEqualTo("test@gmail.com");
assertThat(account1Preference.getIcon()).isNotNull();
- SelectorWithWidgetPreference account2Preference = mScreen.findPreference(
+ SelectorWithWidgetPreference account2Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT2.hashCode()));
- assertThat(account2Preference.getTitle()).isEqualTo("LABEL2");
+ assertThat(account2Preference.getTitle()).isEqualTo("Device and LABEL2");
assertThat(account2Preference.getSummary()).isEqualTo("test@samsung.com");
assertThat(account2Preference.getIcon()).isNotNull();
- SelectorWithWidgetPreference account3Preference = mScreen.findPreference(
+ SelectorWithWidgetPreference account3Preference = accountCategory.findPreference(
String.valueOf(TEST_ACCOUNT3.hashCode()));
- assertThat(account3Preference.getTitle()).isEqualTo("LABEL3");
+ assertThat(account3Preference.getTitle()).isEqualTo("Device and LABEL3");
assertThat(account3Preference.getSummary()).isEqualTo("test@outlook.com");
assertThat(account3Preference.getIcon()).isNotNull();
@@ -327,7 +337,7 @@ public class ContactsStorageSettingsTest {
mContactsStorageSettings.refreshUI();
- SelectorWithWidgetPreference simPreference = mScreen.findPreference(
+ SelectorWithWidgetPreference simPreference = accountCategory.findPreference(
String.valueOf(SIM_ACCOUNT.hashCode()));
assertThat(simPreference.getTitle()).isEqualTo("SIM");
assertThat(simPreference.getSummary()).isEqualTo("SIM");
diff --git a/tests/robotests/src/com/android/settings/display/AutoBrightnessScreenTest.kt b/tests/robotests/src/com/android/settings/display/AutoBrightnessScreenTest.kt
index d05b258574d..ab2d9bd29d9 100644
--- a/tests/robotests/src/com/android/settings/display/AutoBrightnessScreenTest.kt
+++ b/tests/robotests/src/com/android/settings/display/AutoBrightnessScreenTest.kt
@@ -25,7 +25,7 @@ import androidx.preference.PreferenceViewHolder
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.PrimarySwitchPreference
-import com.android.settingslib.preference.PreferenceDataStoreAdapter
+import com.android.settingslib.preference.createAndBindWidget
import com.android.settingslib.widget.SettingsThemeHelper.isExpressiveTheme
import com.android.settingslib.widget.theme.R
import com.google.common.truth.Truth.assertThat
@@ -117,17 +117,14 @@ class AutoBrightnessScreenTest {
assertThat(preferenceScreenCreator.isAvailable(context)).isFalse()
}
- private fun getPrimarySwitchPreference(): PrimarySwitchPreference =
- preferenceScreenCreator.run {
- val preference = createWidget(context)
- preference.preferenceDataStore = PreferenceDataStoreAdapter(storage(context))
- bind(preference, this)
+ private fun getPrimarySwitchPreference() =
+ preferenceScreenCreator.createAndBindWidget(context).also {
val holder =
PreferenceViewHolder.createInstanceForTests(
LayoutInflater.from(context).inflate(getResId(), /* root= */ null)
)
.apply { findViewById(androidx.preference.R.id.switchWidget) }
- preference.apply { onBindViewHolder(holder) }
+ it.onBindViewHolder(holder)
}
private fun setScreenBrightnessMode(value: Int) =
diff --git a/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java
new file mode 100644
index 00000000000..c389247389f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BundleGlobalPreferenceControllerTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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;
+
+import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_TYPE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.app.INotificationManager;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class BundleGlobalPreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ BundleGlobalPreferenceController mController;
+ @Mock
+ INotificationManager mInm;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mSetFlagsRule.enableFlags(
+ android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ mController = new BundleGlobalPreferenceController(mContext, PREFERENCE_KEY);
+ mController.mBackend.setNm(mInm);
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isChecked() throws Exception {
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isChecked()).isTrue();
+
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_IMPORTANCE));
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked() throws Exception {
+ mController.setChecked(false);
+ verify(mInm).disallowAssistantAdjustment(KEY_TYPE);
+
+ mController.setChecked(true);
+ verify(mInm).allowAssistantAdjustment(KEY_TYPE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BundlePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundlePreferenceControllerTest.java
new file mode 100644
index 00000000000..75da895c88c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BundlePreferenceControllerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_TYPE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.app.INotificationManager;
+import android.content.Context;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class BundlePreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ BundlePreferenceController mController;
+ @Mock
+ INotificationManager mInm;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mSetFlagsRule.enableFlags(
+ android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ mController = new BundlePreferenceController(mContext, PREFERENCE_KEY);
+ mController.mBackend.setNm(mInm);
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasSupports_shouldReturnTrue() {
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse() throws Exception {
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getSummary() throws Exception {
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.getSummary()).isEqualTo("On");
+
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_IMPORTANCE));
+ assertThat(mController.getSummary()).isEqualTo("Off");
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java
new file mode 100644
index 00000000000..68bb1bb276e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/BundleTypePreferenceControllerTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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;
+
+import static android.service.notification.Adjustment.KEY_TYPE;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.app.INotificationManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class BundleTypePreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ BundleTypePreferenceController mController;
+ @Mock
+ INotificationManager mInm;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
+ mSetFlagsRule.enableFlags(
+ android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION,
+ Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ mContext = RuntimeEnvironment.application;
+ mController = new BundleTypePreferenceController(mContext, PREFERENCE_KEY);
+ mController.mBackend.setNm(mInm);
+ }
+
+ @Test
+ public void isAvailable() throws RemoteException {
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasDoesNotSupport_shouldReturnFalse()
+ throws RemoteException {
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of(KEY_TYPE));
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_flagDisabledNasSupports_shouldReturnFalse() throws RemoteException {
+ mSetFlagsRule.disableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI);
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of(KEY_TYPE));
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_flagEnabledNasDisabled_shouldReturnFalse() throws RemoteException {
+ when(mInm.getUnsupportedAdjustmentTypes()).thenReturn(List.of());
+ when(mInm.getAllowedAssistantAdjustments(any())).thenReturn(List.of());
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isChecked_promotions() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.PROMO_KEY);
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_PROMOTION});
+ assertThat(mController.isChecked()).isTrue();
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_news() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.NEWS_KEY);
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_NEWS});
+ assertThat(mController.isChecked()).isTrue();
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_social() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.SOCIAL_KEY);
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{TYPE_SOCIAL_MEDIA});
+ assertThat(mController.isChecked()).isTrue();
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_recs() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.RECS_KEY);
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(
+ new int[]{TYPE_CONTENT_RECOMMENDATION});
+ assertThat(mController.isChecked()).isTrue();
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(new int[]{});
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_mixed() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.RECS_KEY);
+
+ when(mInm.getAllowedAdjustmentKeyTypes()).thenReturn(
+ new int[]{TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION});
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void setChecked() throws RemoteException {
+ mController = new BundleTypePreferenceController(mContext,
+ BundleTypePreferenceController.PROMO_KEY);
+ mController.setChecked(false);
+ verify(mInm).setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+
+ mController.setChecked(true);
+ verify(mInm).setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, true);
+ }
+}
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
index a5d0461e4a3..2f68521f3a1 100644
--- a/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/biometrics/fingerprint/Injector.kt
@@ -29,6 +29,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Accessibil
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Default
+import com.android.settings.biometrics.fingerprint2.lib.model.Orientation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSIconTouchViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.modules.enrolling.rfps.ui.viewmodel.RFPSViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
@@ -78,6 +79,7 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
override val isEnabled: Boolean
get() = true
override fun announce(clazz: Class<*>, announcement: CharSequence?) {}
+ override fun interrupt() {}
}
var foldStateInteractor =
@@ -97,6 +99,7 @@ class Injector(step: FingerprintNavigationStep.UiStep) {
override val rotationFromDefault: Flow = rotation
override fun getRotationFromDefault(rotation: Int): Int = rotation
+ override val orientationChanged: Flow = flowOf(Orientation.Portrait)
}
var gatekeeperViewModel = FingerprintGatekeeperViewModel(fingerprintManagerInteractor)
diff --git a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
index a8c5e684d33..70e6de10a07 100644
--- a/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
+++ b/tests/unit/src/com/android/settings/fingerprint2/enrollment/viewmodel/FingerprintEnrollFindSensorViewModelV2Test.kt
@@ -30,6 +30,7 @@ import com.android.settings.biometrics.fingerprint2.domain.interactor.Accessibil
import com.android.settings.biometrics.fingerprint2.domain.interactor.FoldStateInteractor
import com.android.settings.biometrics.fingerprint2.domain.interactor.OrientationInteractor
import com.android.settings.biometrics.fingerprint2.lib.model.Default
+import com.android.settings.biometrics.fingerprint2.lib.model.Orientation
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.BackgroundViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollFindSensorViewModel
import com.android.settings.biometrics.fingerprint2.ui.enrollment.viewmodel.FingerprintEnrollViewModel
@@ -111,6 +112,8 @@ class FingerprintEnrollFindSensorViewModelV2Test {
override val isEnabled: Boolean
get() = true
override fun announce(clazz: Class<*>, announcement: CharSequence?) {}
+ override fun interrupt() {
+ }
}
foldStateInteractor =
object : FoldStateInteractor {
@@ -128,6 +131,7 @@ class FingerprintEnrollFindSensorViewModelV2Test {
override val rotationFromDefault: Flow = flowOf(Surface.ROTATION_0)
override fun getRotationFromDefault(rotation: Int): Int = rotation
+ override val orientationChanged: Flow = flowOf(Orientation.Portrait)
}
underTest =
FingerprintEnrollFindSensorViewModel(