diff --git a/res/layout/locale_dialog.xml b/res/layout/locale_dialog.xml
new file mode 100644
index 00000000000..cbdb37eb23e
--- /dev/null
+++ b/res/layout/locale_dialog.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b7b98f26038..69d25122649 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -228,6 +228,11 @@
Enables Bluetooth LE audio feature if the device supports LE audio hardware capabilities.
+
+ Enable Bluetooth LE audio Allow List
+
+ Enable Bluetooth LE audio allow list feature.
+
Media devices
@@ -372,6 +377,21 @@
https://support.google.com/android?p=per_language_app_settings
+
+ Change system language to %s ?
+
+
+ Your device settings and regional preferences will change.
+
+
+ Change
+
+
+ %s not available
+
+
+ This language can’t be used as a system language, but you’ve let apps and websites know you prefer this language.
+
Regional preferences
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 7e720c908e0..b93ff41c034 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -345,6 +345,11 @@
android:title="@string/bluetooth_enable_leaudio"
android:summary="@string/bluetooth_enable_leaudio_summary" />
+
+
diff --git a/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceController.java b/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceController.java
index c630267e10c..f19795fa8de 100644
--- a/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceController.java
+++ b/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceController.java
@@ -18,7 +18,6 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_CAMERA_FLASH_NOTIFICATION;
import android.content.Context;
import android.provider.Settings;
@@ -45,13 +44,13 @@ public class CameraFlashNotificationPreferenceController extends TogglePreferenc
@Override
public boolean isChecked() {
return Settings.System.getInt(mContext.getContentResolver(),
- SETTING_KEY_CAMERA_FLASH_NOTIFICATION, OFF) != OFF;
+ Settings.System.CAMERA_FLASH_NOTIFICATION, OFF) != OFF;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.System.putInt(mContext.getContentResolver(),
- SETTING_KEY_CAMERA_FLASH_NOTIFICATION, (isChecked ? ON : OFF));
+ Settings.System.CAMERA_FLASH_NOTIFICATION, (isChecked ? ON : OFF));
}
@Override
diff --git a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java
index 8774043c8d3..5a16a30bc9e 100644
--- a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java
+++ b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java
@@ -19,8 +19,6 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.FlashNotificationsUtil.ACTION_FLASH_NOTIFICATION_START_PREVIEW;
import static com.android.settings.accessibility.FlashNotificationsUtil.EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_CAMERA_FLASH_NOTIFICATION;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION;
import static com.android.settings.accessibility.FlashNotificationsUtil.TYPE_SHORT_PREVIEW;
import android.content.ContentResolver;
@@ -95,10 +93,10 @@ public class FlashNotificationsPreviewPreferenceController extends
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_RESUME) {
mContentResolver.registerContentObserver(
- Settings.System.getUriFor(SETTING_KEY_CAMERA_FLASH_NOTIFICATION),
+ Settings.System.getUriFor(Settings.System.CAMERA_FLASH_NOTIFICATION),
/* notifyForDescendants= */ false, mContentObserver);
mContentResolver.registerContentObserver(
- Settings.System.getUriFor(SETTING_KEY_SCREEN_FLASH_NOTIFICATION),
+ Settings.System.getUriFor(Settings.System.SCREEN_FLASH_NOTIFICATION),
/* notifyForDescendants= */ false, mContentObserver);
} else if (event == Lifecycle.Event.ON_PAUSE) {
mContentResolver.unregisterContentObserver(mContentObserver);
diff --git a/src/com/android/settings/accessibility/FlashNotificationsUtil.java b/src/com/android/settings/accessibility/FlashNotificationsUtil.java
index 429936e41ba..544f835d8b5 100644
--- a/src/com/android/settings/accessibility/FlashNotificationsUtil.java
+++ b/src/com/android/settings/accessibility/FlashNotificationsUtil.java
@@ -16,8 +16,6 @@
package com.android.settings.accessibility;
-import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
-
import android.content.Context;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
@@ -43,19 +41,10 @@ class FlashNotificationsUtil {
static final String EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE =
"com.android.internal.intent.extra.FLASH_NOTIFICATION_PREVIEW_TYPE";
- static final String SETTING_KEY_CAMERA_FLASH_NOTIFICATION =
- "camera_flash_notification";
- static final String SETTING_KEY_SCREEN_FLASH_NOTIFICATION =
- "screen_flash_notification";
- static final String SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR =
- "screen_flash_notification_color_global";
-
static final int TYPE_SHORT_PREVIEW = 0;
static final int TYPE_LONG_PREVIEW = 1;
- static final int DEFAULT_SCREEN_FLASH_COLOR =
- ScreenFlashNotificationColor.YELLOW.mColorInt;
-
+ static final int DEFAULT_SCREEN_FLASH_COLOR = ScreenFlashNotificationColor.YELLOW.mColorInt;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
@@ -128,9 +117,9 @@ class FlashNotificationsUtil {
final boolean isTorchAvailable = FlashNotificationsUtil.isTorchAvailable(context);
final boolean isCameraFlashEnabled = Settings.System.getInt(context.getContentResolver(),
- SETTING_KEY_CAMERA_FLASH_NOTIFICATION, State.OFF) != State.OFF;
+ Settings.System.CAMERA_FLASH_NOTIFICATION, State.OFF) != State.OFF;
final boolean isScreenFlashEnabled = Settings.System.getInt(context.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION, State.OFF) != State.OFF;
+ Settings.System.SCREEN_FLASH_NOTIFICATION, State.OFF) != State.OFF;
return ((isTorchAvailable && isCameraFlashEnabled) ? State.CAMERA : State.OFF)
| (isScreenFlashEnabled ? State.SCREEN : State.OFF);
diff --git a/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceController.java b/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceController.java
index 546ae19eb88..2b96dcffb86 100644
--- a/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceController.java
+++ b/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceController.java
@@ -19,8 +19,6 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.FlashNotificationsUtil.DEFAULT_SCREEN_FLASH_COLOR;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR;
import android.content.Context;
import android.graphics.Color;
@@ -59,7 +57,7 @@ public class ScreenFlashNotificationPreferenceController extends TogglePreferenc
@Override
public boolean isChecked() {
return Settings.System.getInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION, OFF) != OFF;
+ Settings.System.SCREEN_FLASH_NOTIFICATION, OFF) != OFF;
}
@Override
@@ -67,7 +65,7 @@ public class ScreenFlashNotificationPreferenceController extends TogglePreferenc
if (isChecked) checkAndSetInitialColor();
return Settings.System.putInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION, (isChecked ? ON : OFF));
+ Settings.System.SCREEN_FLASH_NOTIFICATION, (isChecked ? ON : OFF));
}
@Override
@@ -79,7 +77,8 @@ public class ScreenFlashNotificationPreferenceController extends TogglePreferenc
public CharSequence getSummary() {
return FlashNotificationsUtil.getColorDescriptionText(mContext,
Settings.System.getInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, DEFAULT_SCREEN_FLASH_COLOR));
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
+ DEFAULT_SCREEN_FLASH_COLOR));
}
@Override
@@ -94,12 +93,12 @@ public class ScreenFlashNotificationPreferenceController extends TogglePreferenc
if (getPreferenceKey().equals(preference.getKey()) && mParentFragment != null) {
final int initialColor = Settings.System.getInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR,
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR,
DEFAULT_SCREEN_FLASH_COLOR);
final Consumer consumer = color -> {
Settings.System.putInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, color);
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, color);
refreshColorSummary();
};
@@ -115,10 +114,10 @@ public class ScreenFlashNotificationPreferenceController extends TogglePreferenc
private void checkAndSetInitialColor() {
if (Settings.System.getInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, Color.TRANSPARENT)
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, Color.TRANSPARENT)
== Color.TRANSPARENT) {
Settings.System.putInt(mContext.getContentResolver(),
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, DEFAULT_SCREEN_FLASH_COLOR);
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, DEFAULT_SCREEN_FLASH_COLOR);
}
}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 0bccfe2da86..f8c1f643a0c 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -25,14 +25,11 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.credentials.CredentialManager;
-import android.credentials.ListEnabledProvidersException;
-import android.credentials.ListEnabledProvidersResponse;
+import android.credentials.CredentialProviderInfo;
import android.credentials.SetEnabledProvidersException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.OutcomeReceiver;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
@@ -71,10 +68,9 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
private final PackageManager mPm;
private final IconDrawableFactory mIconFactory;
- private final List mServices;
+ private final List mServices;
private final Set mEnabledPackageNames;
private final @Nullable CredentialManager mCredentialManager;
- private final CancellationSignal mCancellationSignal = new CancellationSignal();
private final Executor mExecutor;
private final Map mPrefs = new HashMap<>(); // key is package name
@@ -132,42 +128,20 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
lifecycleOwner,
mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY));
-
- mCredentialManager.listEnabledProviders(
- mCancellationSignal,
- mExecutor,
- new OutcomeReceiver() {
- @Override
- public void onResult(ListEnabledProvidersResponse result) {
- Set enabledPackages = new HashSet<>();
- for (String flattenedComponentName : result.getProviderComponentNames()) {
- ComponentName cn =
- ComponentName.unflattenFromString(flattenedComponentName);
- if (cn != null) {
- enabledPackages.add(cn.getPackageName());
- }
- }
-
- setEnabledPackageNames(enabledPackages);
- }
-
- @Override
- public void onError(ListEnabledProvidersException e) {
- Log.e(TAG, "listEnabledProviders error: " + e.toString());
- }
- });
}
@VisibleForTesting
- void setAvailableServices(LifecycleOwner lifecycleOwner, List availableServices) {
+ void setAvailableServices(
+ LifecycleOwner lifecycleOwner, List availableServices) {
mServices.clear();
mServices.addAll(availableServices);
- }
- @VisibleForTesting
- void setEnabledPackageNames(Set enabledPackages) {
mEnabledPackageNames.clear();
- mEnabledPackageNames.addAll(enabledPackages);
+ for (CredentialProviderInfo cpi : availableServices) {
+ if (cpi.isEnabled()) {
+ mEnabledPackageNames.add(cpi.getServiceInfo().packageName);
+ }
+ }
for (String packageName : mPrefs.keySet()) {
mPrefs.get(packageName).setChecked(mEnabledPackageNames.contains(packageName));
@@ -189,22 +163,22 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
PreferenceGroup group = screen.findPreference(getPreferenceKey());
Context context = screen.getContext();
- for (ServiceInfo serviceInfo : mServices) {
- CharSequence title = "";
- if (serviceInfo.nonLocalizedLabel != null) {
- title = serviceInfo.loadLabel(mPm);
- }
-
- group.addPreference(
- addProviderPreference(
- context,
- title,
- mIconFactory.getBadgedIcon(
- serviceInfo, serviceInfo.applicationInfo, getUser()),
- serviceInfo.packageName));
+ for (CredentialProviderInfo service : mServices) {
+ group.addPreference(createPreference(context, service));
}
}
+ /** Creates a preference object based on the provider info. */
+ @VisibleForTesting
+ public SwitchPreference createPreference(Context context, CredentialProviderInfo service) {
+ CharSequence label = service.getLabel(context);
+ return addProviderPreference(
+ context,
+ label == null ? "" : label,
+ service.getServiceIcon(mContext),
+ service.getServiceInfo().packageName);
+ }
+
/**
* Enables the package name as an enabled credential manager provider.
*
@@ -246,9 +220,10 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
public List getEnabledSettings() {
// Get all the component names that match the enabled package names.
List enabledServices = new ArrayList<>();
- for (ServiceInfo service : mServices) {
- if (mEnabledPackageNames.contains(service.packageName)) {
- enabledServices.add(service.getComponentName().flattenToString());
+ for (CredentialProviderInfo service : mServices) {
+ ComponentName cn = service.getServiceInfo().getComponentName();
+ if (mEnabledPackageNames.contains(service.getServiceInfo().packageName)) {
+ enabledServices.add(cn.flattenToString());
}
}
diff --git a/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceController.java b/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceController.java
new file mode 100644
index 00000000000..23506b3ccc3
--- /dev/null
+++ b/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceController.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2022 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.development;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothStatusCodes;
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+/**
+ * Preference controller to control Bluetooth LE audio feature
+ */
+public class BluetoothLeAudioAllowListPreferenceController
+ extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+ private static final String PREFERENCE_KEY = "bluetooth_enable_leaudio_allow_list";
+
+ private static final String LE_AUDIO_ALLOW_LIST_SWITCH_SUPPORT_PROPERTY =
+ "ro.bluetooth.leaudio_allow_list.supported";
+ @VisibleForTesting
+ static final String LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY =
+ "persist.bluetooth.leaudio.enable_allow_list";
+
+ private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY =
+ "ro.bluetooth.leaudio_switcher.supported";
+ @VisibleForTesting
+ static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY =
+ "persist.bluetooth.leaudio_switcher.enabled";
+
+ @VisibleForTesting
+ BluetoothAdapter mBluetoothAdapter;
+
+ private final DevelopmentSettingsDashboardFragment mFragment;
+
+ @VisibleForTesting
+ boolean mChanged = false;
+
+ public BluetoothLeAudioAllowListPreferenceController(Context context,
+ DevelopmentSettingsDashboardFragment fragment) {
+ super(context);
+ mFragment = fragment;
+ mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREFERENCE_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ BluetoothRebootDialog.show(mFragment);
+ mChanged = true;
+ return false;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mBluetoothAdapter == null) {
+ return;
+ }
+
+ int leAudioSupportedState = mBluetoothAdapter.isLeAudioSupported();
+ boolean leAudioEnabled = false;
+
+ if ((leAudioSupportedState == BluetoothStatusCodes.FEATURE_SUPPORTED)
+ || (leAudioSupportedState == BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED
+ && SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false)
+ && SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_ENABLED_PROPERTY, false))) {
+ leAudioEnabled = true;
+ }
+
+ final boolean leAudioAllowListSupport =
+ SystemProperties.getBoolean(LE_AUDIO_ALLOW_LIST_SWITCH_SUPPORT_PROPERTY, false);
+
+ if (leAudioEnabled && leAudioAllowListSupport) {
+ final boolean leAudioAllowListEnabled =
+ SystemProperties.getBoolean(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, false);
+ ((SwitchPreference) mPreference).setChecked(leAudioAllowListEnabled);
+ } else {
+ mPreference.setEnabled(false);
+ ((SwitchPreference) mPreference).setChecked(false);
+ }
+ }
+
+ /**
+ * Called when the RebootDialog confirm is clicked.
+ */
+ public void onRebootDialogConfirmed() {
+ if (!mChanged) {
+ return;
+ }
+
+ final boolean leAudioAllowListEnabled =
+ SystemProperties.getBoolean(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, false);
+ SystemProperties.set(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY,
+ Boolean.toString(!leAudioAllowListEnabled));
+ }
+
+ /**
+ * Called when the RebootDialog cancel is clicked.
+ */
+ public void onRebootDialogCanceled() {
+ mChanged = false;
+ }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index c732c0a5549..0d4ad4a97b9 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -389,6 +389,11 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
getDevelopmentOptionsController(
BluetoothLeAudioPreferenceController.class);
leAudioFeatureController.onRebootDialogConfirmed();
+
+ final BluetoothLeAudioAllowListPreferenceController leAudioAllowListController =
+ getDevelopmentOptionsController(
+ BluetoothLeAudioAllowListPreferenceController.class);
+ leAudioAllowListController.onRebootDialogConfirmed();
}
@Override
@@ -406,6 +411,11 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
getDevelopmentOptionsController(
BluetoothLeAudioPreferenceController.class);
leAudioFeatureController.onRebootDialogCanceled();
+
+ final BluetoothLeAudioAllowListPreferenceController leAudioAllowListController =
+ getDevelopmentOptionsController(
+ BluetoothLeAudioAllowListPreferenceController.class);
+ leAudioAllowListController.onRebootDialogCanceled();
}
@Override
@@ -602,6 +612,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
controllers.add(new BluetoothMapVersionPreferenceController(context));
controllers.add(new BluetoothLeAudioPreferenceController(context, fragment));
+ controllers.add(new BluetoothLeAudioAllowListPreferenceController(context, fragment));
controllers.add(new BluetoothA2dpHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothLeAudioHwOffloadPreferenceController(context, fragment));
controllers.add(new BluetoothMaxConnectedAudioDevicesPreferenceController(context));
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java
index ee3f54f70e0..274817e3819 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceController.java
@@ -118,7 +118,7 @@ public class BatterySaverButtonPreferenceController extends
@Override
public boolean setChecked(boolean stateOn) {
return BatterySaverUtils.setPowerSaveMode(mContext, stateOn,
- true /* needFirstTimeWarning */);
+ false /* needFirstTimeWarning */);
}
@Override
diff --git a/src/com/android/settings/localepicker/LocaleDialogFragment.java b/src/com/android/settings/localepicker/LocaleDialogFragment.java
new file mode 100644
index 00000000000..63fc1792a46
--- /dev/null
+++ b/src/com/android/settings/localepicker/LocaleDialogFragment.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 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.localepicker;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentManager;
+
+import com.android.internal.app.LocaleStore;
+import com.android.settings.R;
+import com.android.settings.RestrictedSettingsFragment;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Create a dialog for system locale events.
+ */
+public class LocaleDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = LocaleDialogFragment.class.getSimpleName();
+
+ static final int DIALOG_CONFIRM_SYSTEM_DEFAULT = 0;
+ static final int DIALOG_NOT_AVAILABLE_LOCALE = 1;
+
+ static final String ARG_DIALOG_TYPE = "arg_dialog_type";
+ static final String ARG_TARGET_LOCALE = "arg_target_locale";
+ static final String ARG_RESULT_RECEIVER = "arg_result_receiver";
+
+ /**
+ * Show dialog
+ */
+ public static void show(
+ @NonNull RestrictedSettingsFragment fragment,
+ int dialogType,
+ LocaleStore.LocaleInfo localeInfo) {
+ show(fragment, dialogType, localeInfo, null);
+ }
+
+ /**
+ * Show dialog
+ */
+ public static void show(
+ @NonNull RestrictedSettingsFragment fragment,
+ int dialogType,
+ LocaleStore.LocaleInfo localeInfo,
+ ResultReceiver resultReceiver) {
+ FragmentManager manager = fragment.getChildFragmentManager();
+ Bundle args = new Bundle();
+ args.putInt(ARG_DIALOG_TYPE, dialogType);
+ args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
+ args.putParcelable(ARG_RESULT_RECEIVER, resultReceiver);
+
+ LocaleDialogFragment localeDialogFragment = new LocaleDialogFragment();
+ localeDialogFragment.setArguments(args);
+ localeDialogFragment.show(manager, TAG);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ int dialogType = getArguments().getInt(ARG_DIALOG_TYPE);
+ switch (dialogType) {
+ case DIALOG_CONFIRM_SYSTEM_DEFAULT:
+ return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
+ case DIALOG_NOT_AVAILABLE_LOCALE:
+ return SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE;
+ default:
+ return SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE;
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ LocaleDialogController controller = new LocaleDialogController(this);
+ LocaleDialogController.DialogContent dialogContent = controller.getDialogContent();
+ ViewGroup viewGroup = (ViewGroup) LayoutInflater.from(getContext()).inflate(
+ R.layout.locale_dialog, null);
+ setDialogTitle(viewGroup, dialogContent.mTitle);
+ setDialogMessage(viewGroup, dialogContent.mMessage);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
+ .setView(viewGroup);
+ if (!dialogContent.mPositiveButton.isEmpty()) {
+ builder.setPositiveButton(dialogContent.mPositiveButton, controller);
+ }
+ if (!dialogContent.mNegativeButton.isEmpty()) {
+ builder.setNegativeButton(dialogContent.mNegativeButton, controller);
+ }
+ return builder.create();
+ }
+
+ private static void setDialogTitle(View root, String content) {
+ TextView titleView = root.findViewById(R.id.dialog_title);
+ if (titleView == null) {
+ return;
+ }
+ titleView.setText(content);
+ }
+
+ private static void setDialogMessage(View root, String content) {
+ TextView textView = root.findViewById(R.id.dialog_msg);
+ if (textView == null) {
+ return;
+ }
+ textView.setText(content);
+ }
+
+ static class LocaleDialogController implements DialogInterface.OnClickListener {
+ private final Context mContext;
+ private final int mDialogType;
+ private final LocaleStore.LocaleInfo mLocaleInfo;
+ private final ResultReceiver mResultReceiver;
+
+ LocaleDialogController(
+ @NonNull Context context, @NonNull LocaleDialogFragment dialogFragment) {
+ mContext = context;
+ Bundle arguments = dialogFragment.getArguments();
+ mDialogType = arguments.getInt(ARG_DIALOG_TYPE);
+ mLocaleInfo = (LocaleStore.LocaleInfo) arguments.getSerializable(
+ ARG_TARGET_LOCALE);
+ mResultReceiver = (ResultReceiver) arguments.getParcelable(ARG_RESULT_RECEIVER);
+ }
+
+ LocaleDialogController(@NonNull LocaleDialogFragment dialogFragment) {
+ this(dialogFragment.getContext(), dialogFragment);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mResultReceiver != null && mDialogType == DIALOG_CONFIRM_SYSTEM_DEFAULT) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(ARG_DIALOG_TYPE, DIALOG_CONFIRM_SYSTEM_DEFAULT);
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ mResultReceiver.send(Activity.RESULT_OK, bundle);
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mResultReceiver.send(Activity.RESULT_CANCELED, bundle);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ DialogContent getDialogContent() {
+ DialogContent
+ dialogContent = new DialogContent();
+ switch (mDialogType) {
+ case DIALOG_CONFIRM_SYSTEM_DEFAULT:
+ dialogContent.mTitle = String.format(mContext.getString(
+ R.string.title_change_system_locale), mLocaleInfo.getFullNameNative());
+ dialogContent.mMessage = mContext.getString(
+ R.string.desc_notice_device_locale_settings_change);
+ dialogContent.mPositiveButton = mContext.getString(
+ R.string.button_label_confirmation_of_system_locale_change);
+ dialogContent.mNegativeButton = mContext.getString(R.string.cancel);
+ break;
+ case DIALOG_NOT_AVAILABLE_LOCALE:
+ dialogContent.mTitle = String.format(mContext.getString(
+ R.string.title_unavailable_locale), mLocaleInfo.getFullNameNative());
+ dialogContent.mMessage = mContext.getString(R.string.desc_unavailable_locale);
+ dialogContent.mPositiveButton = mContext.getString(R.string.okay);
+ break;
+ default:
+ break;
+ }
+ return dialogContent;
+ }
+
+ @VisibleForTesting
+ static class DialogContent {
+ String mTitle = "";
+ String mMessage = "";
+ String mPositiveButton = "";
+ String mNegativeButton = "";
+ }
+ }
+}
diff --git a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
index b3c2e3071af..bece4140153 100644
--- a/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
+++ b/src/com/android/settings/localepicker/LocaleDragAndDropAdapter.java
@@ -16,10 +16,14 @@
package com.android.settings.localepicker;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Bundle;
+import android.os.Handler;
import android.os.LocaleList;
+import android.os.Looper;
+import android.os.ResultReceiver;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -49,9 +53,11 @@ class LocaleDragAndDropAdapter
private static final String TAG = "LocaleDragAndDropAdapter";
private static final String CFGKEY_SELECTED_LOCALES = "selectedLocales";
private final Context mContext;
- private final List mFeedItemList;
+ private List mFeedItemList;
+ private List mCacheItemList;
private final ItemTouchHelper mItemTouchHelper;
private RecyclerView mParentView = null;
+ private LocaleListEditor mParent;
private boolean mRemoveMode = false;
private boolean mDragEnabled = true;
private NumberFormat mNumberFormatter = NumberFormat.getNumberInstance();
@@ -81,12 +87,15 @@ class LocaleDragAndDropAdapter
}
}
- public LocaleDragAndDropAdapter(Context context, List feedItemList) {
+ LocaleDragAndDropAdapter(LocaleListEditor parent,
+ List feedItemList) {
mFeedItemList = feedItemList;
- mContext = context;
+ mParent = parent;
+ mCacheItemList = new ArrayList<>(feedItemList);
+ mContext = parent.getContext();
final float dragElevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
- context.getResources().getDisplayMetrics());
+ mContext.getResources().getDisplayMetrics());
mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0 /* no swipe */) {
@@ -168,13 +177,13 @@ class LocaleDragAndDropAdapter
checkbox.setOnCheckedChangeListener(null);
checkbox.setChecked(mRemoveMode ? feedItem.getChecked() : false);
checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- LocaleStore.LocaleInfo feedItem =
- (LocaleStore.LocaleInfo) dragCell.getTag();
- feedItem.setChecked(isChecked);
- }
- });
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ LocaleStore.LocaleInfo feedItem =
+ (LocaleStore.LocaleInfo) dragCell.getTag();
+ feedItem.setChecked(isChecked);
+ }
+ });
}
@Override
@@ -308,6 +317,42 @@ class LocaleDragAndDropAdapter
});
}
+ public void doTheUpdateWithMovingLocaleItem() {
+ LocaleStore.LocaleInfo localeInfo = mFeedItemList.get(0);
+ if (!localeInfo.getLocale().equals(LocalePicker.getLocales().get(0))) {
+ LocaleDialogFragment.show(mParent,
+ LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT,
+ localeInfo,
+ new ResultReceiver(new Handler(Looper.getMainLooper())) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
+ int type = resultData.getInt(LocaleDialogFragment.ARG_DIALOG_TYPE);
+ if (type == LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT) {
+ if (resultCode == Activity.RESULT_OK) {
+ doTheUpdate();
+ if (!localeInfo.isTranslated()) {
+ LocaleDialogFragment.show(mParent,
+ LocaleDialogFragment
+ .DIALOG_NOT_AVAILABLE_LOCALE,
+ localeInfo);
+ }
+ } else {
+ if (!localeInfo.getLocale()
+ .equals(mCacheItemList.get(0).getLocale())) {
+ mFeedItemList = new ArrayList<>(mCacheItemList);
+ notifyDataSetChanged();
+ }
+ }
+ mCacheItemList = new ArrayList<>(mFeedItemList);
+ }
+ }
+ });
+ } else {
+ doTheUpdate();
+ }
+ }
+
private void setDragEnabled(boolean enabled) {
mDragEnabled = enabled;
}
@@ -315,6 +360,7 @@ class LocaleDragAndDropAdapter
/**
* Saves the list of checked locales to preserve status when the list is destroyed.
* (for instance when the device is rotated)
+ *
* @param outInstanceState Bundle in which to place the saved state
*/
public void saveState(Bundle outInstanceState) {
@@ -332,6 +378,7 @@ class LocaleDragAndDropAdapter
/**
* Restores the list of checked locales to preserve status when the list is recreated.
* (for instance when the device is rotated)
+ *
* @param savedInstanceState Bundle with the data saved by {@link #saveState(Bundle)}
*/
public void restoreState(Bundle savedInstanceState) {
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index 89efe53b4ff..bdb9295807e 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -105,7 +105,7 @@ public class LocaleListEditor extends RestrictedSettingsFragment {
LocaleStore.fillCache(this.getContext());
final List feedsList = getUserLocaleList();
- mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList);
+ mAdapter = new LocaleDragAndDropAdapter(this, feedsList);
}
@Override
diff --git a/src/com/android/settings/localepicker/LocaleRecyclerView.java b/src/com/android/settings/localepicker/LocaleRecyclerView.java
index d32a735d48c..5d469bf7f10 100644
--- a/src/com/android/settings/localepicker/LocaleRecyclerView.java
+++ b/src/com/android/settings/localepicker/LocaleRecyclerView.java
@@ -40,7 +40,7 @@ class LocaleRecyclerView extends RecyclerView {
if (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) {
LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
if (adapter != null) {
- adapter.doTheUpdate();
+ adapter.doTheUpdateWithMovingLocaleItem();
}
}
return super.onTouchEvent(e);
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index a5629901966..13bb51928b6 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -50,6 +50,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
@@ -560,7 +561,8 @@ public class SubscriptionUtil {
if (TextUtils.isEmpty(rawPhoneNumber)) {
return null;
}
- String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString());
+ String countryIso = MccTable.countryCodeForMcc(subscriptionInfo.getMccString())
+ .toUpperCase(Locale.ROOT);
return PhoneNumberUtils.formatNumber(rawPhoneNumber, countryIso);
}
diff --git a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
index 92cd911843a..40bae5d7918 100644
--- a/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
+++ b/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceController.java
@@ -27,6 +27,7 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import androidx.annotation.VisibleForTesting;
import androidx.core.text.BidiFormatter;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
@@ -54,7 +55,7 @@ import java.util.List;
public class AppChannelsBypassingDndPreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, LifecycleObserver {
- private static final String KEY = "zen_mode_bypassing_app_channels_list";
+ @VisibleForTesting static final String KEY = "zen_mode_bypassing_app_channels_list";
private static final String ARG_FROM_SETTINGS = "fromSettings";
private RestrictedSwitchPreference mAllNotificationsToggle;
@@ -74,8 +75,8 @@ public class AppChannelsBypassingDndPreferenceController extends NotificationPre
mAllNotificationsToggle = new RestrictedSwitchPreference(mPreferenceCategory.getContext());
mAllNotificationsToggle.setTitle(R.string.zen_mode_bypassing_app_channels_toggle_all);
mAllNotificationsToggle.setDisabledByAdmin(mAdmin);
- mAllNotificationsToggle.setEnabled(
- (mAdmin == null || !mAllNotificationsToggle.isDisabledByAdmin()));
+ mAllNotificationsToggle.setEnabled(!mAppRow.banned
+ && (mAdmin == null || !mAllNotificationsToggle.isDisabledByAdmin()));
mAllNotificationsToggle.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@Override
@@ -206,6 +207,9 @@ public class AppChannelsBypassingDndPreferenceController extends NotificationPre
}
private boolean areAllChannelsBypassing() {
+ if (mAppRow.banned) {
+ return false;
+ }
boolean allChannelsBypassing = true;
for (NotificationChannel channel : mChannels) {
if (showNotification(channel)) {
@@ -226,7 +230,7 @@ public class AppChannelsBypassingDndPreferenceController extends NotificationPre
* Whether notifications from this channel would show if DND weren't on.
*/
private boolean showNotification(NotificationChannel channel) {
- return channel.getImportance() != IMPORTANCE_NONE;
+ return !mAppRow.banned && channel.getImportance() != IMPORTANCE_NONE;
}
/**
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 87a916a7e43..c2716b6e5ca 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -32,6 +32,7 @@ import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.core.instrumentation.SpaLogProvider
import com.android.settings.spa.development.UsageStatsPageProvider
import com.android.settings.spa.home.HomePageProvider
import com.android.settings.spa.network.NetworkAndInternetPageProvider
@@ -87,4 +88,5 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
),
)
}
+ override val logger = SpaLogProvider
}
diff --git a/src/com/android/settings/spa/SpaActivity.kt b/src/com/android/settings/spa/SpaActivity.kt
index 55883c12264..27f7241b257 100644
--- a/src/com/android/settings/spa/SpaActivity.kt
+++ b/src/com/android/settings/spa/SpaActivity.kt
@@ -16,18 +16,29 @@
package com.android.settings.spa
+import android.app.ActivityManager
import android.content.Context
import android.content.Intent
+import android.os.RemoteException
import android.os.UserHandle
+import android.util.Log
import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.util.SESSION_BROWSE
+import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
import com.android.settingslib.spa.framework.util.appendSpaParams
class SpaActivity : BrowseActivity() {
companion object {
+ private const val TAG = "SpaActivity"
@JvmStatic
fun Context.startSpaActivity(destination: String) {
val intent = Intent(this, SpaActivity::class.java)
.appendSpaParams(destination = destination)
+ if (isLaunchedFromInternal()) {
+ intent.appendSpaParams(sessionName = SESSION_BROWSE)
+ } else {
+ intent.appendSpaParams(sessionName = SESSION_EXTERNAL)
+ }
startActivity(intent)
}
@@ -37,5 +48,15 @@ class SpaActivity : BrowseActivity() {
startSpaActivity("$destinationPrefix/$packageName/${UserHandle.myUserId()}")
return true
}
+
+ fun Context.isLaunchedFromInternal(): Boolean {
+ var pkg: String? = null
+ try {
+ pkg = ActivityManager.getService().getLaunchedFromPackage(getActivityToken())
+ } catch (e: RemoteException) {
+ Log.v(TAG, "Could not talk to activity manager.", e)
+ }
+ return applicationContext.packageName == pkg
+ }
}
}
diff --git a/src/com/android/settings/spa/core/instrumentation/MetricsDataModel.kt b/src/com/android/settings/spa/core/instrumentation/MetricsDataModel.kt
new file mode 100644
index 00000000000..62aa8df871a
--- /dev/null
+++ b/src/com/android/settings/spa/core/instrumentation/MetricsDataModel.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 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.spa.core.instrumentation
+
+import androidx.annotation.VisibleForTesting
+
+/**
+ * This class stores some metrics temporary data. Such as the timestamp of the page enter for
+ * calculating the duration time on page.
+ */
+class MetricsDataModel {
+ @VisibleForTesting
+ val pageTimeStampList = mutableListOf()
+
+ fun addTimeStamp(dataItem: PageTimeStamp){
+ pageTimeStampList.add(dataItem)
+ }
+
+ fun getPageDuration(pageId: String, removed: Boolean = true): String {
+ val lastItem = pageTimeStampList.findLast { it.pageId == pageId }
+ if (removed && lastItem != null) {
+ pageTimeStampList.remove(lastItem)
+ }
+ return if (lastItem == null) "0"
+ else (System.currentTimeMillis() - lastItem.timeStamp).toString()
+ }
+}
diff --git a/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
new file mode 100644
index 00000000000..9b3e2d68ac2
--- /dev/null
+++ b/src/com/android/settings/spa/core/instrumentation/SpaLogProvider.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2023 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.spa.core.instrumentation
+
+import android.app.settings.SettingsEnums
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import com.android.settings.core.instrumentation.ElapsedTimeUtils
+import com.android.settings.core.instrumentation.SettingsStatsLog
+import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME
+import com.android.settingslib.spa.framework.common.LogCategory
+import com.android.settingslib.spa.framework.common.LogEvent
+import com.android.settingslib.spa.framework.common.SpaLogger
+import com.android.settingslib.spa.framework.util.SESSION_BROWSE
+import com.android.settingslib.spa.framework.util.SESSION_EXTERNAL
+import com.android.settingslib.spa.framework.util.SESSION_SEARCH
+import com.android.settingslib.spa.framework.util.SESSION_SLICE
+import com.android.settingslib.spa.framework.util.SESSION_UNKNOWN
+
+/**
+ * To receive the events from spa framework and logging the these events.
+ */
+object SpaLogProvider : SpaLogger {
+ private val dataModel = MetricsDataModel()
+
+ override fun event(id: String, event: LogEvent, category: LogCategory, extraData: Bundle) {
+ when(event) {
+ LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE ->
+ write(SpaLogData(id, event, extraData, dataModel))
+ else -> return //TODO(b/253979024): Will be implemented in subsequent CLs.
+ }
+ }
+
+ private fun write(data: SpaLogData) {
+ with(data) {
+ SettingsStatsLog.write(
+ SettingsStatsLog.SETTINGS_SPA_REPORTED /* atomName */,
+ getSessionType(),
+ getPageId(),
+ getTarget(),
+ getAction(),
+ getKey(),
+ getValue(),
+ getPreValue(),
+ getElapsedTime()
+ )
+ }
+ }
+}
+
+@VisibleForTesting
+class SpaLogData(val id: String, val event: LogEvent,
+ val extraData: Bundle, val dataModel: MetricsDataModel) {
+
+ fun getSessionType(): Int {
+ if (!extraData.containsKey(LOG_DATA_SESSION_NAME)) {
+ return SettingsEnums.SESSION_UNKNOWN
+ }
+ val sessionSource = extraData.getString(LOG_DATA_SESSION_NAME)
+ return when(sessionSource) {
+ SESSION_BROWSE -> SettingsEnums.BROWSE
+ SESSION_SEARCH -> SettingsEnums.SEARCH
+ SESSION_SLICE -> SettingsEnums.SLICE_TYPE
+ SESSION_EXTERNAL -> SettingsEnums.EXTERNAL
+ else -> SettingsEnums.SESSION_UNKNOWN
+ }
+ }
+
+ fun getPageId(): String {
+ return when(event) {
+ LogEvent.PAGE_ENTER, LogEvent.PAGE_LEAVE -> id
+ else -> getPageIdByEntryId(id)
+ }
+ }
+
+ //TODO(b/253979024): Will be implemented in subsequent CLs.
+ fun getTarget(): String? {
+ return null
+ }
+
+ fun getAction(): Int {
+ return event.action
+ }
+
+ //TODO(b/253979024): Will be implemented in subsequent CLs.
+ fun getKey(): String? {
+ return null
+ }
+
+ fun getValue(): String? {
+ when(event) {
+ LogEvent.PAGE_ENTER -> dataModel.addTimeStamp(
+ PageTimeStamp(id, System.currentTimeMillis()))
+ LogEvent.PAGE_LEAVE -> return dataModel.getPageDuration(id)
+ else -> {} //TODO(b/253979024): Will be implemented in subsequent CLs.
+ }
+ return null
+ }
+
+ //TODO(b/253979024): Will be implemented in subsequent CLs.
+ fun getPreValue(): String? {
+ return null
+ }
+
+ fun getElapsedTime(): Long {
+ return ElapsedTimeUtils.getElapsedTime(System.currentTimeMillis())
+ }
+
+ //TODO(b/253979024): Will be implemented in subsequent CLs.
+ private fun getPageIdByEntryId(id: String): String {
+ return ""
+ }
+}
+
+/**
+ * The buffer is keeping the time stamp while spa page entering.
+ */
+data class PageTimeStamp(val pageId: String, val timeStamp: Long)
diff --git a/tests/robotests/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceControllerTest.java
index db4e5210315..02ab32d6576 100644
--- a/tests/robotests/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/CameraFlashNotificationPreferenceControllerTest.java
@@ -16,7 +16,8 @@
package com.android.settings.accessibility;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_CAMERA_FLASH_NOTIFICATION;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
@@ -81,28 +82,30 @@ public class CameraFlashNotificationPreferenceControllerTest {
@Test
public void isChecked_setOff_assertFalse() {
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 0);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, OFF);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isChecked_setOn_assertTrue() {
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 1);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, ON);
assertThat(mController.isChecked()).isTrue();
}
@Test
- public void setChecked_setTrue_assertNotZero() {
+ public void setChecked_setTrue_assertNotOff() {
mController.setChecked(true);
- assertThat(Settings.System.getInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION,
- 0)).isNotEqualTo(0);
+ assertThat(
+ Settings.System.getInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION,
+ OFF)).isNotEqualTo(OFF);
}
@Test
- public void setChecked_setFalse_assertNotOne() {
+ public void setChecked_setFalse_assertNotOn() {
mController.setChecked(false);
- assertThat(Settings.System.getInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION,
- 1)).isNotEqualTo(1);
+ assertThat(
+ Settings.System.getInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION,
+ OFF)).isNotEqualTo(ON);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceControllerTest.java
index 5f1dfbb1e63..98da926b3f2 100644
--- a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceControllerTest.java
@@ -18,8 +18,6 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.FlashNotificationsUtil.ACTION_FLASH_NOTIFICATION_START_PREVIEW;
import static com.android.settings.accessibility.FlashNotificationsUtil.EXTRA_FLASH_NOTIFICATION_PREVIEW_TYPE;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_CAMERA_FLASH_NOTIFICATION;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION;
import static com.android.settings.accessibility.FlashNotificationsUtil.TYPE_LONG_PREVIEW;
import static com.android.settings.accessibility.FlashNotificationsUtil.TYPE_SHORT_PREVIEW;
import static com.android.settings.accessibility.ShadowFlashNotificationsUtils.setFlashNotificationsState;
@@ -48,7 +46,6 @@ import androidx.test.core.app.ApplicationProvider;
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;
@@ -131,7 +128,6 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
verify(mPreference).setEnabled(eq(true));
}
- @Ignore
@Test
public void testHandlePreferenceTreeClick_invalidPreference() {
mController.handlePreferenceTreeClick(mock(Preference.class));
@@ -165,16 +161,16 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
public void onStateChanged_onResume_cameraUri_verifyRegister() {
mController.onStateChanged(mock(LifecycleOwner.class), Lifecycle.Event.ON_RESUME);
verify(mContentResolver).registerContentObserver(
- eq(Settings.System.getUriFor(SETTING_KEY_CAMERA_FLASH_NOTIFICATION)), anyBoolean(),
- eq(mController.mContentObserver));
+ eq(Settings.System.getUriFor(Settings.System.CAMERA_FLASH_NOTIFICATION)),
+ anyBoolean(), eq(mController.mContentObserver));
}
@Test
public void onStateChanged_onResume_screenUri_verifyRegister() {
mController.onStateChanged(mock(LifecycleOwner.class), Lifecycle.Event.ON_RESUME);
verify(mContentResolver).registerContentObserver(
- eq(Settings.System.getUriFor(SETTING_KEY_SCREEN_FLASH_NOTIFICATION)), anyBoolean(),
- eq(mController.mContentObserver));
+ eq(Settings.System.getUriFor(Settings.System.SCREEN_FLASH_NOTIFICATION)),
+ anyBoolean(), eq(mController.mContentObserver));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsUtilTest.java b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsUtilTest.java
index c5fe3a7c792..f943e3a3657 100644
--- a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsUtilTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsUtilTest.java
@@ -21,8 +21,8 @@ import static android.hardware.camera2.CameraCharacteristics.LENS_FACING;
import static android.hardware.camera2.CameraCharacteristics.LENS_FACING_BACK;
import static android.hardware.camera2.CameraMetadata.LENS_FACING_FRONT;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_CAMERA_FLASH_NOTIFICATION;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.FlashNotificationsUtil.getColorDescriptionText;
import static com.android.settings.accessibility.FlashNotificationsUtil.getFlashNotificationsState;
import static com.android.settings.accessibility.FlashNotificationsUtil.getScreenColor;
@@ -156,8 +156,8 @@ public class FlashNotificationsUtilTest {
@Test
public void getFlashNotificationsState_torchPresent_cameraOff_screenOff_assertOff() {
setTorchPresent();
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 0);
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 0);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, OFF);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, OFF);
assertThat(getFlashNotificationsState(mContext))
.isEqualTo(FlashNotificationsUtil.State.OFF);
@@ -166,8 +166,8 @@ public class FlashNotificationsUtilTest {
@Test
public void getFlashNotificationsState_torchNotPresent_cameraOn_screenOff_assertOff() {
setTorchNotPresent();
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 1);
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 0);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, ON);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, OFF);
assertThat(getFlashNotificationsState(mContext))
.isEqualTo(FlashNotificationsUtil.State.OFF);
@@ -176,8 +176,8 @@ public class FlashNotificationsUtilTest {
@Test
public void getFlashNotificationsState_torchPresent_cameraOn_screenOff_assertCamera() {
setTorchPresent();
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 1);
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 0);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, ON);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, OFF);
assertThat(getFlashNotificationsState(mContext))
.isEqualTo(FlashNotificationsUtil.State.CAMERA);
@@ -186,8 +186,8 @@ public class FlashNotificationsUtilTest {
@Test
public void getFlashNotificationsState_torchPresent_cameraOff_screenOn_assertScreen() {
setTorchPresent();
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 0);
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 1);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, OFF);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, ON);
assertThat(getFlashNotificationsState(mContext))
.isEqualTo(FlashNotificationsUtil.State.SCREEN);
@@ -196,8 +196,8 @@ public class FlashNotificationsUtilTest {
@Test
public void testGetFlashNotificationsState_torchPresent_cameraOn_screenOn_assertCameraScreen() {
setTorchPresent();
- Settings.System.putInt(mContentResolver, SETTING_KEY_CAMERA_FLASH_NOTIFICATION, 1);
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 1);
+ Settings.System.putInt(mContentResolver, Settings.System.CAMERA_FLASH_NOTIFICATION, ON);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, ON);
assertThat(getFlashNotificationsState(mContext))
.isEqualTo(FlashNotificationsUtil.State.CAMERA_SCREEN);
diff --git a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceControllerTest.java
index 0662daa6fbb..5b8afe65ab5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationPreferenceControllerTest.java
@@ -16,9 +16,9 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.FlashNotificationsUtil.DEFAULT_SCREEN_FLASH_COLOR;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION;
-import static com.android.settings.accessibility.FlashNotificationsUtil.SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
@@ -114,48 +114,50 @@ public class ScreenFlashNotificationPreferenceControllerTest {
@Test
public void isChecked_setOff_assertFalse() {
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 0);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, OFF);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isChecked_setOn_assertTrue() {
- Settings.System.putInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION, 1);
+ Settings.System.putInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION, ON);
assertThat(mController.isChecked()).isTrue();
}
@Test
public void setChecked_whenTransparentColor_setTrue_assertNotTransparentColor() {
Settings.System.putInt(mContentResolver,
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, Color.TRANSPARENT);
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, Color.TRANSPARENT);
mController.setChecked(true);
assertThat(Settings.System.getInt(mContentResolver,
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, 0))
- .isEqualTo(DEFAULT_SCREEN_FLASH_COLOR);
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, 0)).isEqualTo(
+ DEFAULT_SCREEN_FLASH_COLOR);
}
@Test
public void setChecked_whenNotTransparent_setTrue_assertSameColor() {
Settings.System.putInt(mContentResolver,
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, 0x4D0000FF);
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, 0x4D0000FF);
mController.setChecked(true);
assertThat(Settings.System.getInt(mContentResolver,
- SETTING_KEY_SCREEN_FLASH_NOTIFICATION_COLOR, 0))
+ Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, 0))
.isEqualTo(0x4D0000FF);
}
@Test
public void setChecked_setTrue_assertOn() {
mController.setChecked(true);
- assertThat(Settings.System.getInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION,
- 0)).isEqualTo(1);
+ assertThat(
+ Settings.System.getInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION,
+ OFF)).isEqualTo(ON);
}
@Test
public void setChecked_setFalse_assertOff() {
mController.setChecked(false);
- assertThat(Settings.System.getInt(mContentResolver, SETTING_KEY_SCREEN_FLASH_NOTIFICATION,
- 1)).isEqualTo(0);
+ assertThat(
+ Settings.System.getInt(mContentResolver, Settings.System.SCREEN_FLASH_NOTIFICATION,
+ OFF)).isEqualTo(OFF);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceControllerTest.java
new file mode 100644
index 00000000000..f4e52bada6a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/BluetoothLeAudioAllowListPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 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.development;
+
+import static android.bluetooth.BluetoothStatusCodes.FEATURE_SUPPORTED;
+
+import static com.android.settings.development.BluetoothLeAudioAllowListPreferenceController
+ .LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.os.SystemProperties;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import org.junit.Before;
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothLeAudioAllowListPreferenceControllerTest {
+
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private DevelopmentSettingsDashboardFragment mFragment;
+
+ @Mock
+ private BluetoothAdapter mBluetoothAdapter;
+
+ private Context mContext;
+ private SwitchPreference mPreference;
+ private BluetoothLeAudioPreferenceController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mPreference = new SwitchPreference(mContext);
+ mController = spy(new BluetoothLeAudioPreferenceController(mContext, mFragment));
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreference);
+ mController.mBluetoothAdapter = mBluetoothAdapter;
+ mController.displayPreference(mPreferenceScreen);
+ when(mBluetoothAdapter.isLeAudioSupported())
+ .thenReturn(FEATURE_SUPPORTED);
+ }
+
+ @Test
+ public void onRebootDialogConfirmedAsLeAudioAllowListDisabled_shouldSwitchStatus() {
+ SystemProperties.set(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, Boolean.toString(false));
+ mController.mChanged = true;
+
+ mController.onRebootDialogConfirmed();
+ final boolean mode = SystemProperties.getBoolean(
+ LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, false);
+ assertThat(mode).isFalse();
+ }
+
+
+ @Test
+ public void onRebootDialogConfirmedAsLeAudioAllowListEnabled_shouldSwitchStatus() {
+ SystemProperties.set(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, Boolean.toString(true));
+ mController.mChanged = true;
+
+ mController.onRebootDialogConfirmed();
+ final boolean status = SystemProperties.getBoolean(
+ LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, false);
+ assertThat(status).isTrue();
+ }
+
+ @Test
+ public void onRebootDialogCanceled_shouldNotSwitchStatus() {
+ SystemProperties.set(LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, Boolean.toString(false));
+ mController.mChanged = true;
+
+ mController.onRebootDialogCanceled();
+ final boolean status = SystemProperties.getBoolean(
+ LE_AUDIO_ALLOW_LIST_ENABLED_PROPERTY, false);
+ assertThat(status).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
index 4f26754b212..9a7ef409f95 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverButtonPreferenceControllerTest.java
@@ -18,17 +18,13 @@ package com.android.settings.fuelgauge.batterysaver;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.Intent;
import android.os.PowerManager;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
@@ -85,6 +81,15 @@ public class BatterySaverButtonPreferenceControllerTest {
assertThat(mPreference.isChecked()).isFalse();
}
+ @Test
+ public void onSwitchChanged_isCheckedAndAcked_setPowerSaveMode() {
+ setLowPowerWarningAcked(/* acked= */ 1);
+
+ mController.onSwitchChanged(/* switchView= */ null, /* isChecked= */ true);
+
+ verify(mPowerManager).setPowerSaveModeEnabled(true);
+ }
+
@Test
public void updateState_lowPowerOn_preferenceIsChecked() {
when(mPowerManager.isPowerSaveMode()).thenReturn(true);
@@ -104,11 +109,10 @@ public class BatterySaverButtonPreferenceControllerTest {
}
@Test
- public void setChecked_on_showWarningMessage() {
+ public void setChecked_on_setPowerSaveMode() {
mController.setChecked(true);
- verify(mContext).sendBroadcast(any(Intent.class));
- verify(mPowerManager, never()).setPowerSaveModeEnabled(anyBoolean());
+ verify(mPowerManager).setPowerSaveModeEnabled(true);
}
@Test
@@ -143,5 +147,9 @@ public class BatterySaverButtonPreferenceControllerTest {
mContext.getContentResolver(),
Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
acked);
+ Settings.Secure.putInt(
+ mContext.getContentResolver(),
+ Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED,
+ acked);
}
}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java
index 52d10833ef8..734f610b264 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java
@@ -93,13 +93,13 @@ public class KeyboardLayoutPickerControllerTest {
}
@Test
- public void testLifecycle_onStart_NoInputDevice_shouldFinish() {
+ public void testLifecycle_onStart_NoInputDevice_shouldReturn() {
final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
when(mInputManager.getInputDeviceByDescriptor(anyString())).thenReturn(null);
when(mFragment.getActivity()).thenReturn(activity);
mController.onStart();
- assertThat(activity.isFinishing()).isTrue();
+ verify(mInputManager, never()).getEnabledKeyboardLayoutsForInputDevice(any());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
new file mode 100644
index 00000000000..bcb641d9c23
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/app/AppChannelsBypassingDndPreferenceControllerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 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.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.PrimarySwitchPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppChannelsBypassingDndPreferenceControllerTest {
+
+ @Mock
+ private NotificationBackend mBackend;
+
+ private NotificationBackend.AppRow mAppRow;
+ private AppChannelsBypassingDndPreferenceController mController;
+
+ private PreferenceScreen mPreferenceScreen;
+ private PreferenceCategory mCategory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = ApplicationProvider.getApplicationContext();
+
+ mAppRow = new NotificationBackend.AppRow();
+ mAppRow.uid = 42;
+ mAppRow.pkg = "com.example.exampling";
+
+ mController = new AppChannelsBypassingDndPreferenceController(context, mBackend);
+ mController.onResume(mAppRow, null, null, null, null, null, new ArrayList<>());
+
+ PreferenceManager preferenceManager = new PreferenceManager(context);
+ mPreferenceScreen = preferenceManager.createPreferenceScreen(context);
+ mCategory = new PreferenceCategory(context);
+ mCategory.setKey(AppChannelsBypassingDndPreferenceController.KEY);
+ mPreferenceScreen.addPreference(mCategory);
+ }
+
+ @Test
+ public void displayPreference_showsAllAndChannels() {
+ when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(
+ buildGroupList(true, true, false));
+
+ mController.displayPreference(mPreferenceScreen);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(mCategory.getPreferenceCount()).isEqualTo(4); // "All" + 3 channels
+ assertThat(mCategory.getPreference(0).getTitle().toString()).isEqualTo(
+ "Allow all notifications");
+ assertThat(mCategory.getPreference(1).getTitle().toString()).isEqualTo("Channel 1");
+ assertThat(mCategory.getPreference(2).getTitle().toString()).isEqualTo("Channel 2");
+ assertThat(mCategory.getPreference(3).getTitle().toString()).isEqualTo("Channel 3");
+ }
+
+ @Test
+ public void displayPreference_canToggleAllInterrupt() {
+ when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(
+ buildGroupList(true, true, false));
+
+ mController.displayPreference(mPreferenceScreen);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(mCategory.getPreference(0).isEnabled()).isTrue();
+ }
+
+ @Test
+ public void displayPreference_canToggleInterruptIfChannelEnabled() {
+ when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(
+ buildGroupList(true, false, true));
+
+ mController.displayPreference(mPreferenceScreen);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 1)).isSwitchEnabled()).isTrue();
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 2)).isSwitchEnabled()).isFalse();
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 3)).isSwitchEnabled()).isTrue();
+ }
+
+ @Test
+ public void displayPreference_appBlocked_cannotToggleAllOrChannelInterrupts() {
+ mAppRow.banned = true;
+ when(mBackend.getGroups(eq(mAppRow.pkg), eq(mAppRow.uid))).thenReturn(
+ buildGroupList(true, false, true));
+
+ mController.displayPreference(mPreferenceScreen);
+ ShadowApplication.runBackgroundTasks();
+
+ assertThat(mCategory.getPreference(0).isEnabled()).isFalse();
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 1)).isSwitchEnabled()).isFalse();
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 2)).isSwitchEnabled()).isFalse();
+ assertThat(((PrimarySwitchPreference) mCategory.getPreference(
+ 3)).isSwitchEnabled()).isFalse();
+ }
+
+ private static ParceledListSlice buildGroupList(
+ boolean... enabledByChannel) {
+ NotificationChannelGroup group = new NotificationChannelGroup("group", "The Group");
+ for (int i = 0; i < enabledByChannel.length; i++) {
+ group.addChannel(new NotificationChannel("channel-" + (i + 1), "Channel " + (i + 1),
+ enabledByChannel[i] ? NotificationManager.IMPORTANCE_DEFAULT
+ : NotificationManager.IMPORTANCE_NONE));
+ }
+ return new ParceledListSlice<>(Collections.singletonList(group));
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt b/tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt
index 8a894d5b4a8..46b956e6ccc 100644
--- a/tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/SpaActivityTest.kt
@@ -25,12 +25,15 @@ import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivityForApp
import com.android.settingslib.spa.framework.util.KEY_DESTINATION
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
@@ -39,9 +42,14 @@ class SpaActivityTest {
@get:Rule
val mockito: MockitoRule = MockitoJUnit.rule()
- @Mock
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var context: Context
+ @Before
+ fun setUp() {
+ `when`(context.applicationContext.packageName).thenReturn("com.android.settings")
+ }
+
@Test
fun startSpaActivity() {
context.startSpaActivity(DESTINATION)
diff --git a/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/MetricsDataModelTest.kt b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/MetricsDataModelTest.kt
new file mode 100644
index 00000000000..2ba03029b5d
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/MetricsDataModelTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.spa.core.instrumentation
+
+import android.os.SystemClock
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for {@link MetricsDataModel}. */
+@RunWith(AndroidJUnit4::class)
+class MetricsDataModelTest {
+ private val TEST_PID = "pseudo_page_id"
+
+ private lateinit var metricsDataModel: MetricsDataModel
+
+ @Before
+ fun setUp() {
+ metricsDataModel = MetricsDataModel()
+ }
+
+ @Test
+ fun initMetricsDataModel() {
+ assertThat(metricsDataModel.pageTimeStampList.size).isEqualTo(0)
+ }
+
+ @Test
+ fun addTimeStamp_addOnePageTimeStamp_sizeShouldBeOne() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+
+ assertThat(metricsDataModel.pageTimeStampList.size).isEqualTo(1)
+ }
+
+ @Test
+ fun addTimeStamp_addTwoSamePageTimeStamp_sizeShouldBeTwo() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+
+ assertThat(metricsDataModel.pageTimeStampList.size).isEqualTo(2)
+ }
+
+ @Test
+ fun getPageDuration_getExistPageId_mustFoundValue() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+ SystemClock.sleep(5)
+
+ assertThat(metricsDataModel.getPageDuration(TEST_PID).toInt()).isGreaterThan(0)
+ assertThat(metricsDataModel.pageTimeStampList.size).isEqualTo(0)
+ }
+
+ @Test
+ fun getPageDuration_getNonExistPageId_valueShouldBeZero() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+
+ assertThat(metricsDataModel.getPageDuration("WRONG_ID").toLong()).isEqualTo(0L)
+ }
+
+ @Test
+ fun getPageDuration_getExistPageIdAndDonotRemoved_sizeShouldBeOne() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, System.currentTimeMillis()))
+ SystemClock.sleep(5)
+
+ assertThat(metricsDataModel.getPageDuration(TEST_PID, false).toLong()).isGreaterThan(0L)
+ assertThat(metricsDataModel.pageTimeStampList.size).isEqualTo(1)
+ }
+
+ @Test
+ fun getPageDuration_getTwoExistPageId_theOrderIsLIFO() {
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, 10000L))
+ metricsDataModel.addTimeStamp(PageTimeStamp(TEST_PID, 20000L))
+
+ // The formula is d1 = t1 - 20000, d2 = t2 - 10000
+ // d2 - d1 = t2 - t1 + 10000, because t2 > t1 the result of d2 - d1 is greater 10000
+ val duration1 = metricsDataModel.getPageDuration(TEST_PID).toLong()
+ SystemClock.sleep(5)
+ val duration2 = metricsDataModel.getPageDuration(TEST_PID).toLong()
+
+ assertThat(duration2 - duration1).isGreaterThan(10000L)
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt
new file mode 100644
index 00000000000..19be10ec892
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/core/instrumentation/SpaLogDataTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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.spa.core.instrumentation
+
+import android.app.settings.SettingsEnums
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME
+import com.android.settingslib.spa.framework.common.LogEvent
+import com.android.settingslib.spa.framework.util.SESSION_BROWSE
+import com.android.settingslib.spa.framework.util.SESSION_SEARCH
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Tests for {@link SpaLogData}. */
+@RunWith(AndroidJUnit4::class)
+class SpaLogDataTest {
+ private val TEST_PID = "pseudo_page_id"
+
+ private lateinit var bundle: Bundle
+ private lateinit var dataModel: MetricsDataModel
+
+ @Before
+ fun setUp() {
+ bundle = Bundle()
+ dataModel = MetricsDataModel()
+ }
+
+ @Test
+ fun getSessionType_withoutSessionExtraData_returnSessionUnknow() {
+ val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
+
+ assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SESSION_UNKNOWN)
+ }
+
+ @Test
+ fun getSessionType_hasSessionBrowseExtraData_returnSessionBrowse() {
+ bundle.putString(LOG_DATA_SESSION_NAME, SESSION_BROWSE)
+ val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
+
+ assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.BROWSE)
+ }
+
+ @Test
+ fun getSessionType_hasSessionSearchExtraData_returnSessionSearch() {
+ bundle.putString(LOG_DATA_SESSION_NAME, SESSION_SEARCH)
+ val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
+
+ assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SEARCH)
+ }
+
+ @Test
+ fun getSessionType_hasSessionUnknownExtraData_returnSessionUnknow() {
+ bundle.putString(LOG_DATA_SESSION_NAME, "SESSION_OTHER")
+ val spaLogData = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
+
+ assertThat(spaLogData.getSessionType()).isEqualTo(SettingsEnums.SESSION_UNKNOWN)
+ }
+
+ @Test
+ fun getPageId_withPageEvent_returnInputId() {
+ val spaLogData1 = SpaLogData(TEST_PID, LogEvent.PAGE_ENTER, bundle, dataModel)
+ assertThat(spaLogData1.getPageId()).isEqualTo(TEST_PID)
+
+ val spaLogData2 = SpaLogData(TEST_PID, LogEvent.PAGE_LEAVE, bundle, dataModel)
+ assertThat(spaLogData2.getPageId()).isEqualTo(TEST_PID)
+ }
+}
diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
index ff7e71a948f..2633ea7ad40 100644
--- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java
@@ -27,12 +27,14 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
+import android.credentials.CredentialProviderInfo;
import android.os.Looper;
import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -87,7 +89,7 @@ public class CredentialManagerPreferenceControllerTest {
@Test
public void getAvailabilityStatus_withServices_returnsAvailable() {
CredentialManagerPreferenceController controller =
- createControllerWithServices(Lists.newArrayList(createServiceInfo()));
+ createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
assertThat(controller.isConnected()).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@@ -103,24 +105,59 @@ public class CredentialManagerPreferenceControllerTest {
@Test
public void displayPreference_withServices_preferencesAdded() {
CredentialManagerPreferenceController controller =
- createControllerWithServices(Lists.newArrayList(createServiceInfo()));
+ createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
controller.displayPreference(mScreen);
assertThat(controller.isConnected()).isFalse();
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
}
+ @Test
+ public void buildSwitchPreference() {
+ CredentialProviderInfo providerInfo1 =
+ createCredentialProviderInfo(
+ "com.android.provider1", "ClassA", "Service Title", false);
+ CredentialProviderInfo providerInfo2 =
+ createCredentialProviderInfo(
+ "com.android.provider2", "ClassA", "Service Title", false);
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
+ assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ assertThat(controller.isConnected()).isFalse();
+
+ // Test the data is correct.
+ assertThat(providerInfo1.isEnabled()).isFalse();
+ assertThat(providerInfo2.isEnabled()).isFalse();
+ assertThat(controller.getEnabledProviders().size()).isEqualTo(0);
+
+ // Toggle one provider and make sure it worked.
+ assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
+ Set enabledProviders = controller.getEnabledProviders();
+ assertThat(enabledProviders.size()).isEqualTo(1);
+ assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
+
+ // Create the pref (checked).
+ SwitchPreference pref = controller.createPreference(mContext, providerInfo1);
+ assertThat(pref.getTitle().toString()).isEqualTo("Service Title");
+ assertThat(pref.isChecked()).isTrue();
+
+ // Create the pref (not checked).
+ SwitchPreference pref2 = controller.createPreference(mContext, providerInfo2);
+ assertThat(pref2.getTitle().toString()).isEqualTo("Service Title");
+ assertThat(pref2.isChecked()).isFalse();
+ }
+
@Test
public void getAvailabilityStatus_handlesToggleAndSave() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(
- createServiceInfo("com.android.provider1", "ClassA"),
- createServiceInfo("com.android.provider1", "ClassB"),
- createServiceInfo("com.android.provider2", "ClassA"),
- createServiceInfo("com.android.provider3", "ClassA"),
- createServiceInfo("com.android.provider4", "ClassA"),
- createServiceInfo("com.android.provider5", "ClassA"),
- createServiceInfo("com.android.provider6", "ClassA")));
+ createCredentialProviderInfo("com.android.provider1", "ClassA"),
+ createCredentialProviderInfo("com.android.provider1", "ClassB"),
+ createCredentialProviderInfo("com.android.provider2", "ClassA"),
+ createCredentialProviderInfo("com.android.provider3", "ClassA"),
+ createCredentialProviderInfo("com.android.provider4", "ClassA"),
+ createCredentialProviderInfo("com.android.provider5", "ClassA"),
+ createCredentialProviderInfo("com.android.provider6", "ClassA")));
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
assertThat(controller.isConnected()).isFalse();
@@ -177,8 +214,38 @@ public class CredentialManagerPreferenceControllerTest {
assertThat(currentlyEnabledServices.contains("com.android.provider6/ClassA")).isFalse();
}
+ @Test
+ public void handlesCredentialProviderInfoEnabledDisabled() {
+ CredentialProviderInfo providerInfo1 =
+ createCredentialProviderInfo(
+ "com.android.provider1", "ClassA", "Service Title", false);
+ CredentialProviderInfo providerInfo2 =
+ createCredentialProviderInfo(
+ "com.android.provider2", "ClassA", "Service Title", true);
+ CredentialManagerPreferenceController controller =
+ createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
+ assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ assertThat(controller.isConnected()).isFalse();
+
+ // Test the data is correct.
+ assertThat(providerInfo1.isEnabled()).isFalse();
+ assertThat(providerInfo2.isEnabled()).isTrue();
+
+ // Check that they are all actually registered.
+ Set enabledProviders = controller.getEnabledProviders();
+ assertThat(enabledProviders.size()).isEqualTo(1);
+ assertThat(enabledProviders.contains("com.android.provider1")).isFalse();
+ assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
+
+ // Check that the settings string has the right component names.
+ List enabledServices = controller.getEnabledSettings();
+ assertThat(enabledServices.size()).isEqualTo(1);
+ assertThat(enabledServices.contains("com.android.provider1/ClassA")).isFalse();
+ assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
+ }
+
private CredentialManagerPreferenceController createControllerWithServices(
- List availableServices) {
+ List availableServices) {
CredentialManagerPreferenceController controller =
new CredentialManagerPreferenceController(
mContext, mCredentialsPreferenceCategory.getKey());
@@ -186,11 +253,17 @@ public class CredentialManagerPreferenceControllerTest {
return controller;
}
- private ServiceInfo createServiceInfo() {
- return createServiceInfo("com.android.provider", "CredManProvider");
+ private CredentialProviderInfo createCredentialProviderInfo() {
+ return createCredentialProviderInfo("com.android.provider", "CredManProvider");
}
- private ServiceInfo createServiceInfo(String packageName, String className) {
+ private CredentialProviderInfo createCredentialProviderInfo(
+ String packageName, String className) {
+ return createCredentialProviderInfo(packageName, className, null, false);
+ }
+
+ private CredentialProviderInfo createCredentialProviderInfo(
+ String packageName, String className, CharSequence label, boolean isEnabled) {
ServiceInfo si = new ServiceInfo();
si.packageName = packageName;
si.name = className;
@@ -200,6 +273,9 @@ public class CredentialManagerPreferenceControllerTest {
si.applicationInfo.packageName = packageName;
si.applicationInfo.nonLocalizedLabel = "test";
- return si;
+ return new CredentialProviderInfo.Builder(si)
+ .setOverrideLabel(label)
+ .setEnabled(isEnabled)
+ .build();
}
}
diff --git a/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java b/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
new file mode 100644
index 00000000000..5b10adf0ff6
--- /dev/null
+++ b/tests/unit/src/com/android/settings/localepicker/LocaleDialogFragmentTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 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.localepicker;
+
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_DIALOG_TYPE;
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_RESULT_RECEIVER;
+import static com.android.settings.localepicker.LocaleDialogFragment.ARG_TARGET_LOCALE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.app.LocaleStore;
+import com.android.settings.testutils.ResourcesUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.Locale;
+
+@UiThreadTest
+public class LocaleDialogFragmentTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private Context mContext;
+ private LocaleDialogFragment mDialogFragment;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
+ mDialogFragment = new LocaleDialogFragment();
+ }
+
+ private void setArgument(
+ int type, ResultReceiver receiver) {
+ LocaleStore.LocaleInfo localeInfo = LocaleStore.getLocaleInfo(Locale.ENGLISH);
+ Bundle args = new Bundle();
+ args.putInt(ARG_DIALOG_TYPE, type);
+ args.putSerializable(ARG_TARGET_LOCALE, localeInfo);
+ args.putParcelable(ARG_RESULT_RECEIVER, receiver);
+ mDialogFragment.setArguments(args);
+ }
+
+ @Test
+ public void getDialogContent_confirmSystemDefault_has2ButtonText() {
+ setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, null);
+ LocaleDialogFragment.LocaleDialogController controller =
+ new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
+
+ LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
+ controller.getDialogContent();
+
+ assertEquals(ResourcesUtils.getResourcesString(
+ mContext, "button_label_confirmation_of_system_locale_change"),
+ dialogContent.mPositiveButton);
+ assertEquals(ResourcesUtils.getResourcesString(mContext, "cancel"),
+ dialogContent.mNegativeButton);
+ }
+
+ @Test
+ public void getDialogContent_unavailableLocale_has1ButtonText() {
+ setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE, null);
+ LocaleDialogFragment.LocaleDialogController controller =
+ new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
+
+ LocaleDialogFragment.LocaleDialogController.DialogContent dialogContent =
+ controller.getDialogContent();
+
+ assertEquals(ResourcesUtils.getResourcesString(mContext, "okay"),
+ dialogContent.mPositiveButton);
+ assertTrue(dialogContent.mNegativeButton.isEmpty());
+ }
+
+ @Test
+ public void onClick_clickPositiveButton_sendOK() {
+ ResultReceiver resultReceiver = spy(new ResultReceiver(null));
+ setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, resultReceiver);
+ LocaleDialogFragment.LocaleDialogController controller =
+ new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
+
+ controller.onClick(null, DialogInterface.BUTTON_POSITIVE);
+
+ verify(resultReceiver).send(eq(Activity.RESULT_OK), any());
+ }
+
+ @Test
+ public void onClick_clickNegativeButton_sendCancel() {
+ ResultReceiver resultReceiver = spy(new ResultReceiver(null));
+ setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, resultReceiver);
+ LocaleDialogFragment.LocaleDialogController controller =
+ new LocaleDialogFragment.LocaleDialogController(mContext, mDialogFragment);
+
+ controller.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+
+ verify(resultReceiver).send(eq(Activity.RESULT_CANCELED), any());
+ }
+
+ @Test
+ public void getMetricsCategory_systemLocaleChange() {
+ setArgument(LocaleDialogFragment.DIALOG_CONFIRM_SYSTEM_DEFAULT, null);
+
+ int result = mDialogFragment.getMetricsCategory();
+
+ assertEquals(SettingsEnums.DIALOG_SYSTEM_LOCALE_CHANGE, result);
+ }
+
+ @Test
+ public void getMetricsCategory_unavailableLocale() {
+ setArgument(LocaleDialogFragment.DIALOG_NOT_AVAILABLE_LOCALE, null);
+
+ int result = mDialogFragment.getMetricsCategory();
+
+ assertEquals(SettingsEnums.DIALOG_SYSTEM_LOCALE_UNAVAILABLE, result);
+ }
+}