Merge "Fix Notifications SettingsRoboTests" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c64187f..998ecc5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -645,6 +645,7 @@
<activity android:name="Settings$FaceSettingsActivity"
android:label="@string/security_settings_face_preference_title"
android:exported="true"
+ android:theme="@style/Theme.Settings.NoActionBar"
android:icon="@drawable/ic_face_header">
<intent-filter>
<action android:name="android.settings.FACE_SETTINGS" />
@@ -659,6 +660,7 @@
<activity android:name="Settings$FaceSettingsInternalActivity"
android:label="@string/security_settings_face_preference_title"
android:exported="false"
+ android:theme="@style/Theme.Settings.NoActionBar"
android:icon="@drawable/ic_face_header"
android:taskAffinity="com.android.settings.root">
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index d4960c2..d0a8afe 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -9,6 +9,7 @@
"settings_connecteddevice_flag_declarations.aconfig",
"settings_globalintl_flag_declarations.aconfig",
"settings_apn_flag_declarations.aconfig",
+ "settings_onboarding_experience_flag_declarations.aconfig"
],
}
diff --git a/aconfig/settings_onboarding_experience_flag_declarations.aconfig b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
new file mode 100644
index 0000000..6fb5377
--- /dev/null
+++ b/aconfig/settings_onboarding_experience_flag_declarations.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.settings.flags"
+
+flag {
+ name: "enable_sound_backup"
+ namespace: "onboarding_experience"
+ description: "Feature flag for B&R sound settings."
+ bug: "278975761"
+}
diff --git a/src/com/android/settings/accessibility/HearingAidUtils.java b/src/com/android/settings/accessibility/HearingAidUtils.java
index 4734c55..edf2d9f 100644
--- a/src/com/android/settings/accessibility/HearingAidUtils.java
+++ b/src/com/android/settings/accessibility/HearingAidUtils.java
@@ -42,9 +42,10 @@
*/
public static void launchHearingAidPairingDialog(FragmentManager fragmentManager,
@NonNull CachedBluetoothDevice device, int launchPage) {
- // No need to show the pair another ear dialog if the device supports and enables CSIP.
+ // No need to show the pair another ear dialog if the device supports CSIP.
// CSIP will pair other devices in the same set automatically.
- if (isCsipSupportedAndEnabled(device)) {
+ if (device.getProfiles().stream().anyMatch(
+ profile -> profile instanceof CsipSetCoordinatorProfile)) {
return;
}
if (device.isConnectedAshaHearingAidDevice()
@@ -63,10 +64,4 @@
HearingAidPairingDialogFragment.newInstance(device.getAddress(), launchPage)
.show(fragmentManager, HearingAidPairingDialogFragment.TAG);
}
-
- private static boolean isCsipSupportedAndEnabled(@NonNull CachedBluetoothDevice device) {
- return device.getProfiles().stream().anyMatch(
- profile -> (profile instanceof CsipSetCoordinatorProfile)
- && (profile.isEnabled(device.getDevice())));
- }
}
diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
index 182e5ae..98d56cc 100644
--- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
+++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java
@@ -159,6 +159,16 @@
return CONDITIONALLY_UNAVAILABLE;
}
+ // If we are in work profile mode and there is no user then we
+ // should hide for now. We use CONDITIONALLY_UNAVAILABLE
+ // because it is possible for the user to be set later.
+ if (mIsWorkProfile) {
+ UserHandle workProfile = getWorkProfileUserHandle();
+ if (workProfile == null) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ }
+
return AVAILABLE;
}
@@ -186,12 +196,17 @@
fragment.getSettingsLifecycle().addObserver(this);
mFragmentManager = fragmentManager;
mIsWorkProfile = isWorkProfile;
+
setDelegate(delegate);
verifyReceivedIntent(launchIntent);
// Recreate the content observers because the user might have changed.
mSettingsContentObserver.unregister();
mSettingsContentObserver.register();
+
+ // When we set the mIsWorkProfile above we should try and force a refresh
+ // so we can get the correct data.
+ delegate.forceDelegateRefresh();
}
/**
@@ -302,10 +317,15 @@
null);
}
- private Set<ComponentName> buildComponentNameSet(List<CredentialProviderInfo> providers) {
+ private Set<ComponentName> buildComponentNameSet(
+ List<CredentialProviderInfo> providers, boolean removeNonPrimary) {
Set<ComponentName> output = new HashSet<>();
for (CredentialProviderInfo cpi : providers) {
+ if (removeNonPrimary && !cpi.isPrimary()) {
+ continue;
+ }
+
output.add(cpi.getComponentName());
}
@@ -321,13 +341,16 @@
List<CredentialProviderInfo> newProviders =
mCredentialManager.getCredentialProviderServices(
getUser(), CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_ONLY);
- Set<ComponentName> newComponents = buildComponentNameSet(newProviders);
+ Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false);
+ Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true);
// Get the list of old components
- Set<ComponentName> oldComponents = buildComponentNameSet(mServices);
+ Set<ComponentName> oldComponents = buildComponentNameSet(mServices, false);
+ Set<ComponentName> oldPrimaryComponents = buildComponentNameSet(mServices, true);
// If the sets are equal then don't update the UI.
- if (oldComponents.equals(newComponents)) {
+ if (oldComponents.equals(newComponents)
+ && oldPrimaryComponents.equals(newPrimaryComponents)) {
return;
}
@@ -698,12 +721,22 @@
protected int getUser() {
if (mIsWorkProfile) {
- UserHandle workProfile = Utils.getManagedProfile(UserManager.get(mContext));
- return workProfile.getIdentifier();
+ UserHandle workProfile = getWorkProfileUserHandle();
+ if (workProfile != null) {
+ return workProfile.getIdentifier();
+ }
}
return UserHandle.myUserId();
}
+ private @Nullable UserHandle getWorkProfileUserHandle() {
+ if (mIsWorkProfile) {
+ return Utils.getManagedProfile(UserManager.get(mContext));
+ }
+
+ return null;
+ }
+
/** Called when the dialog button is clicked. */
private static interface DialogHost {
void onDialogClick(int whichButton);
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
index 188b4ad..562a469 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherController.java
@@ -31,6 +31,8 @@
import com.google.common.annotations.VisibleForTesting;
+import java.util.Set;
+
/**
* This class handles button preference logic to display for hearing aid device.
*/
@@ -91,7 +93,11 @@
}
private boolean getButtonPreferenceVisibility(CachedBluetoothDevice cachedDevice) {
- return isBinauralMode(cachedDevice) && isOnlyOneSideConnected(cachedDevice);
+ // The device is not connected yet. Don't show the button.
+ if (!cachedDevice.isConnectedHearingAidDevice()) {
+ return false;
+ }
+ return isBinauralMode(cachedDevice) && !isOtherSideConnected(cachedDevice);
}
private void launchPairingDetail() {
@@ -106,16 +112,25 @@
return cachedDevice.getDeviceMode() == HearingAidInfo.DeviceMode.MODE_BINAURAL;
}
- private boolean isOnlyOneSideConnected(CachedBluetoothDevice cachedDevice) {
- if (!cachedDevice.isConnectedAshaHearingAidDevice()) {
- return false;
+ private boolean isOtherSideConnected(CachedBluetoothDevice cachedDevice) {
+ // Check sub device for ASHA hearing aid
+ if (cachedDevice.isConnectedAshaHearingAidDevice()) {
+ final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+ if (subDevice != null && subDevice.isConnectedAshaHearingAidDevice()) {
+ return true;
+ }
}
- final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
- if (subDevice != null && subDevice.isConnectedAshaHearingAidDevice()) {
- return false;
+ // Check member device for LE audio hearing aid
+ if (cachedDevice.isConnectedLeAudioHearingAidDevice()) {
+ final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ for (CachedBluetoothDevice memberDevice : memberDevices) {
+ if (memberDevice.isConnectedLeAudioHearingAidDevice()) {
+ return true;
+ }
+ }
}
- return true;
+ return false;
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
index ed8cc62..9e970d2 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -23,8 +23,6 @@
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
-import java.util.concurrent.TimeUnit;
-
/**
* Detect whether the battery is too low
*/
@@ -46,9 +44,7 @@
@Override
public BatteryTip detect() {
- final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel
- || (mBatteryInfo.discharging && mBatteryInfo.remainingTimeUs != 0
- && mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(mPolicy.lowBatteryHour));
+ final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel;
final boolean lowBatteryEnabled = mPolicy.lowBatteryEnabled && !mIsPowerSaveMode;
final boolean dischargingLowBatteryState =
mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index 1cbf2a3..fd0f866 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -116,8 +116,16 @@
final Handler handler = new Handler(Looper.getMainLooper());
final BatteryLevelData batteryLevelData = DataProcessManager.getBatteryLevelData(
context, handler, /*isFromPeriodJob=*/ true,
- batteryDiffDataMap -> DatabaseUtils.sendBatteryUsageSlotData(context,
- ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap)));
+ batteryDiffDataMap -> {
+ DatabaseUtils.sendBatteryUsageSlotData(context,
+ ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap));
+ if (batteryDiffDataMap.values().stream().anyMatch(data ->
+ (!data.getAppDiffEntryList().isEmpty()
+ || !data.getSystemDiffEntryList().isEmpty()))) {
+ FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
+ .detectSettingsAnomaly(context, /* displayDrain= */ 0);
+ }
+ });
if (batteryLevelData == null) {
Log.d(TAG, "preprocessBatteryUsageSlots() no new battery usage data.");
return;
@@ -139,8 +147,6 @@
// No app usage data or battery diff data at this time.
loadAppUsageData(context);
preprocessBatteryUsageSlots(context);
- FeatureFactory.getFeatureFactory().getPowerUsageFeatureProvider()
- .detectSettingsAnomaly(context, /* displayDrain= */ 0);
}
Log.d(TAG, String.format(
"loadUsageDataSafely() in %d/ms", System.currentTimeMillis() - start));
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 0cd12fe..9974ba2 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -18,7 +18,6 @@
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
-
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import android.annotation.Nullable;
@@ -56,6 +55,8 @@
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -66,6 +67,9 @@
static final String SUB_ID = "sub_id";
@VisibleForTesting
static final String KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME = "unique_subscription_displayName";
+ private static final String REGEX_DISPLAY_NAME_PREFIXES = "^";
+ private static final String REGEX_DISPLAY_NAME_SUFFIXES = "\\s[0-9]+";
+
private static List<SubscriptionInfo> sAvailableResultsForTesting;
private static List<SubscriptionInfo> sActiveResultsForTesting;
@@ -281,8 +285,8 @@
String displayName = i.getDisplayName().toString();
info.originalName =
TextUtils.equals(displayName, PROFILE_GENERIC_DISPLAY_NAME)
- ? context.getResources().getString(R.string.sim_card)
- : displayName.trim();
+ ? context.getResources().getString(R.string.sim_card)
+ : displayName.trim();
return info;
});
@@ -298,12 +302,17 @@
// If a display name is duplicate, append the final 4 digits of the phone number.
// Creates a mapping of Subscription id to original display name + phone number display name
final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+ int infoSubId = info.subscriptionInfo.getSubscriptionId();
String cachedDisplayName = getDisplayNameFromSharedPreference(
- context, info.subscriptionInfo.getSubscriptionId());
- if (!TextUtils.isEmpty(cachedDisplayName)) {
- Log.d(TAG, "use cached display name : " + cachedDisplayName);
+ context, infoSubId);
+ if (isValidCachedDisplayName(cachedDisplayName, info.originalName.toString())) {
+ Log.d(TAG, "use cached display name : for subId : " + infoSubId
+ + "cached display name : " + cachedDisplayName);
info.uniqueName = cachedDisplayName;
return info;
+ } else {
+ Log.d(TAG, "remove cached display name : " + infoSubId);
+ removeItemFromDisplayNameSharedPreference(context, infoSubId);
}
if (duplicateOriginalNames.contains(info.originalName)) {
@@ -320,9 +329,8 @@
} else {
info.uniqueName = info.originalName + " " + lastFourDigits;
Log.d(TAG, "Cache display name [" + info.uniqueName + "] for sub id "
- + info.subscriptionInfo.getSubscriptionId());
- saveDisplayNameToSharedPreference(
- context, info.subscriptionInfo.getSubscriptionId(), info.uniqueName);
+ + infoSubId);
+ saveDisplayNameToSharedPreference(context, infoSubId, info.uniqueName);
}
} else {
info.uniqueName = info.originalName;
@@ -404,10 +412,27 @@
.apply();
}
+ private static void removeItemFromDisplayNameSharedPreference(Context context, int subId) {
+ getDisplayNameSharedPreferenceEditor(context)
+ .remove(SUB_ID + subId)
+ .commit();
+ }
+
private static String getDisplayNameFromSharedPreference(Context context, int subid) {
return getDisplayNameSharedPreferences(context).getString(SUB_ID + subid, "");
}
+ @VisibleForTesting
+ static boolean isValidCachedDisplayName(String cachedDisplayName, String originalName) {
+ if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName)) {
+ return false;
+ }
+ String regex = REGEX_DISPLAY_NAME_PREFIXES + originalName + REGEX_DISPLAY_NAME_SUFFIXES;
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(cachedDisplayName);
+ return matcher.matches();
+ }
+
public static String getDisplayName(SubscriptionInfo info) {
final CharSequence name = info.getDisplayName();
if (name != null) {
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 1cca81a..756d90f 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -20,16 +20,23 @@
import android.os.Bundle
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settings.R
import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import java.util.Base64
@@ -56,7 +63,10 @@
@Composable
override fun Page(arguments: Bundle?) {
val apnDataInit = ApnData()
- ApnPage(apnDataInit)
+ val apnDataCur = remember {
+ mutableStateOf(apnDataInit)
+ }
+ ApnPage(apnDataCur)
}
fun getRoute(
@@ -71,8 +81,13 @@
}
@Composable
-fun ApnPage(apnDataInit: ApnData) {
- var apnData by remember { mutableStateOf(apnDataInit) }
+fun ApnPage(apnDataCur: MutableState<ApnData>) {
+ var apnData by apnDataCur
+ val context = LocalContext.current
+ val authTypeOptions = stringArrayResource(R.array.apn_auth_entries).toList()
+ val apnProtocolOptions = stringArrayResource(R.array.apn_protocol_entries).toList()
+ val mvnoTypeOptions = stringArrayResource(R.array.mvno_type_entries).toList()
+
RegularScaffold(
title = stringResource(id = R.string.apn_edit),
) {
@@ -133,11 +148,50 @@
stringResource(R.string.apn_mnc),
enabled = apnData.mncEnabled
) { apnData = apnData.copy(mnc = it) }
+ SettingsExposedDropdownMenuBox(
+ label = stringResource(R.string.apn_auth_type),
+ options = authTypeOptions,
+ selectedOptionText =
+ authTypeOptions.getOrElse(apnData.authType) { "" },
+ enabled = apnData.authTypeEnabled,
+ ) { apnData = apnData.copy(authType = authTypeOptions.indexOf(it)) }
SettingsOutlinedTextField(
apnData.apnType,
stringResource(R.string.apn_type),
enabled = apnData.apnTypeEnabled
) { apnData = apnData.copy(apn = it) } // TODO: updateApnType
+ SettingsExposedDropdownMenuBox(
+ stringResource(R.string.apn_protocol),
+ apnProtocolOptions,
+ apnData.apnProtocol,
+ apnData.apnProtocolEnabled
+ ) { apnData = apnData.copy(apnProtocol = it) }
+ SettingsExposedDropdownMenuBox(
+ stringResource(R.string.apn_roaming_protocol),
+ apnProtocolOptions,
+ apnData.apnRoaming,
+ apnData.apnRoamingEnabled
+ ) { apnData = apnData.copy(apnRoaming = it) }
+ SwitchPreference(
+ object : SwitchPreferenceModel {
+ override val title = context.resources.getString(R.string.carrier_enabled)
+ override val changeable =
+ stateOf(apnData.apnEnableEnabled)
+ override val checked =
+ stateOf(apnData.apnEnable)
+ override val onCheckedChange = { newChecked: Boolean ->
+ apnData = apnData.copy(apnEnable = newChecked)
+ }
+ }
+ )
+ SettingsExposedDropdownMenuBox(
+ stringResource(R.string.mvno_type),
+ mvnoTypeOptions,
+ apnData.mvnoType,
+ apnData.mvnoTypeEnabled
+ ) {
+ apnData = apnData.copy(mvnoType = it)
+ } // TODO: mvnoDescription
SettingsOutlinedTextField(
apnData.mvnoValue,
stringResource(R.string.mvno_match_data),
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index 78734d0..8a2d613 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -36,7 +36,7 @@
val apnType: String = "",
val apnProtocol: String = "",
val apnRoaming: String = "",
- val apnEnable: Int = 1,
+ val apnEnable: Boolean = true,
val bearer: Int = 0,
val mvnoType: String = "",
var mvnoValue: String = "",
diff --git a/src/com/android/settings/system/SystemDashboardFragment.java b/src/com/android/settings/system/SystemDashboardFragment.java
index 79c5b9f..678b675 100644
--- a/src/com/android/settings/system/SystemDashboardFragment.java
+++ b/src/com/android/settings/system/SystemDashboardFragment.java
@@ -16,9 +16,7 @@
package com.android.settings.system;
import android.app.settings.SettingsEnums;
-import android.content.Context;
import android.os.Bundle;
-import android.provider.SearchIndexableResource;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -29,9 +27,6 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
-import java.util.Arrays;
-import java.util.List;
-
@SearchIndexable
public class SystemDashboardFragment extends DashboardFragment {
@@ -85,13 +80,5 @@
* For Search.
*/
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider() {
- @Override
- public List<SearchIndexableResource> getXmlResourcesToIndex(
- Context context, boolean enabled) {
- final SearchIndexableResource sir = new SearchIndexableResource(context);
- sir.xmlResId = R.xml.system_dashboard_fragment;
- return Arrays.asList(sir);
- }
- };
-}
\ No newline at end of file
+ new BaseSearchIndexProvider(R.xml.system_dashboard_fragment);
+}
diff --git a/src/com/android/settings/system/SystemUpdateManagerExt.kt b/src/com/android/settings/system/SystemUpdateManagerExt.kt
new file mode 100644
index 0000000..8ddf174
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdateManagerExt.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private const val TAG = "SystemUpdateManagerExt"
+
+/**
+ * Gets the system update status.
+ *
+ * Note: [SystemUpdateManager.retrieveSystemUpdateInfo] must be called on worker thread to avoid
+ * StrictMode violation.
+ */
+suspend fun Context.getSystemUpdateInfo(): Bundle? = withContext(Dispatchers.Default) {
+ val updateManager = getSystemService(SystemUpdateManager::class.java)!!
+ try {
+ updateManager.retrieveSystemUpdateInfo()
+ } catch (e: Exception) {
+ Log.w(TAG, "Error getting system update info.")
+ null
+ }
+}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.java b/src/com/android/settings/system/SystemUpdatePreferenceController.java
deleted file mode 100644
index b2a22ff..0000000
--- a/src/com/android/settings/system/SystemUpdatePreferenceController.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2016 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.system;
-
-import static android.content.Context.CARRIER_CONFIG_SERVICE;
-import static android.content.Context.SYSTEM_UPDATE_SERVICE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.PersistableBundle;
-import android.os.SystemUpdateManager;
-import android.os.UserManager;
-import android.telephony.CarrierConfigManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.BasePreferenceController;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
-public class SystemUpdatePreferenceController extends BasePreferenceController {
-
- private static final String TAG = "SysUpdatePrefContr";
-
- private static final String KEY_SYSTEM_UPDATE_SETTINGS = "system_update_settings";
-
- private final UserManager mUm;
- private final SystemUpdateManager mUpdateManager;
-
- public SystemUpdatePreferenceController(Context context) {
- super(context, KEY_SYSTEM_UPDATE_SETTINGS);
- mUm = UserManager.get(context);
- mUpdateManager = (SystemUpdateManager) context.getSystemService(SYSTEM_UPDATE_SERVICE);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return mContext.getResources().getBoolean(R.bool.config_show_system_update_settings)
- && mUm.isAdminUser()
- ? AVAILABLE
- : UNSUPPORTED_ON_DEVICE;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- if (isAvailable()) {
- Utils.updatePreferenceToSpecificActivityOrRemove(mContext, screen,
- getPreferenceKey(),
- Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY);
- }
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (TextUtils.equals(getPreferenceKey(), preference.getKey())) {
- CarrierConfigManager configManager =
- (CarrierConfigManager) mContext.getSystemService(CARRIER_CONFIG_SERVICE);
- PersistableBundle b = configManager.getConfig();
- if (b != null && b.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
- ciActionOnSysUpdate(b);
- }
- }
- // always return false here because this handler does not want to block other handlers.
- return false;
- }
-
- @Override
- public CharSequence getSummary() {
- CharSequence summary = mContext.getString(R.string.android_version_summary,
- Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY);
- final FutureTask<Bundle> bundleFutureTask = new FutureTask<>(
- // Put the API call in a future to avoid StrictMode violation.
- () -> mUpdateManager.retrieveSystemUpdateInfo());
- final Bundle updateInfo;
- try {
- bundleFutureTask.run();
- updateInfo = bundleFutureTask.get();
- } catch (InterruptedException | ExecutionException e) {
- Log.w(TAG, "Error getting system update info.");
- return summary;
- }
- switch (updateInfo.getInt(SystemUpdateManager.KEY_STATUS)) {
- case SystemUpdateManager.STATUS_WAITING_DOWNLOAD:
- case SystemUpdateManager.STATUS_IN_PROGRESS:
- case SystemUpdateManager.STATUS_WAITING_INSTALL:
- case SystemUpdateManager.STATUS_WAITING_REBOOT:
- summary = mContext.getText(R.string.android_version_pending_update_summary);
- break;
- case SystemUpdateManager.STATUS_UNKNOWN:
- Log.d(TAG, "Update statue unknown");
- // fall through to next branch
- case SystemUpdateManager.STATUS_IDLE:
- final String version = updateInfo.getString(SystemUpdateManager.KEY_TITLE);
- if (!TextUtils.isEmpty(version)) {
- summary = mContext.getString(R.string.android_version_summary, version);
- }
- break;
- }
- return summary;
- }
-
- /**
- * Trigger client initiated action (send intent) on system update
- */
- private void ciActionOnSysUpdate(PersistableBundle b) {
- String intentStr = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING);
- if (!TextUtils.isEmpty(intentStr)) {
- String extra = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING);
- String extraVal = b.getString(CarrierConfigManager.
- KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING);
-
- Intent intent = new Intent(intentStr);
- if (!TextUtils.isEmpty(extra)) {
- intent.putExtra(extra, extraVal);
- }
- Log.d(TAG, "ciActionOnSysUpdate: broadcasting intent " + intentStr +
- " with extra " + extra + ", " + extraVal);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mContext.getApplicationContext().sendBroadcast(intent);
- }
- }
-}
diff --git a/src/com/android/settings/system/SystemUpdatePreferenceController.kt b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
new file mode 100644
index 0000000..01df065
--- /dev/null
+++ b/src/com/android/settings/system/SystemUpdatePreferenceController.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.PersistableBundle
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import android.telephony.CarrierConfigManager
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.launch
+
+open class SystemUpdatePreferenceController(context: Context, preferenceKey: String) :
+ BasePreferenceController(context, preferenceKey) {
+ private val userManager: UserManager = context.userManager
+ private lateinit var preference: Preference
+
+ override fun getAvailabilityStatus() =
+ if (mContext.resources.getBoolean(R.bool.config_show_system_update_settings) &&
+ userManager.isAdminUser
+ ) AVAILABLE else UNSUPPORTED_ON_DEVICE
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ if (isAvailable) {
+ Utils.updatePreferenceToSpecificActivityOrRemove(
+ mContext,
+ screen,
+ preferenceKey,
+ Utils.UPDATE_PREFERENCE_FLAG_SET_TITLE_TO_MATCHING_ACTIVITY,
+ )
+ }
+ }
+
+ override fun handlePreferenceTreeClick(preference: Preference): Boolean {
+ if (preferenceKey == preference.key) {
+ val configManager = mContext.getSystemService(CarrierConfigManager::class.java)!!
+ configManager.getConfig(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)?.let {
+ if (it.getBoolean(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_BOOL)) {
+ ciActionOnSysUpdate(it)
+ }
+ }
+ }
+ // always return false here because this handler does not want to block other handlers.
+ return false
+ }
+
+ override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ preference.summary = calculateSummary()
+ }
+ }
+ }
+
+ private suspend fun calculateSummary(): String {
+ val updateInfo = mContext.getSystemUpdateInfo() ?: return getReleaseVersionSummary()
+
+ val status = updateInfo.getInt(SystemUpdateManager.KEY_STATUS)
+ if (status == SystemUpdateManager.STATUS_UNKNOWN) {
+ Log.d(TAG, "Update statue unknown")
+ }
+ when (status) {
+ SystemUpdateManager.STATUS_WAITING_DOWNLOAD,
+ SystemUpdateManager.STATUS_IN_PROGRESS,
+ SystemUpdateManager.STATUS_WAITING_INSTALL,
+ SystemUpdateManager.STATUS_WAITING_REBOOT -> {
+ return mContext.getString(R.string.android_version_pending_update_summary)
+ }
+
+ SystemUpdateManager.STATUS_IDLE,
+ SystemUpdateManager.STATUS_UNKNOWN -> {
+ val version = updateInfo.getString(SystemUpdateManager.KEY_TITLE)
+ if (!version.isNullOrEmpty()) {
+ return mContext.getString(R.string.android_version_summary, version)
+ }
+ }
+ }
+ return getReleaseVersionSummary()
+ }
+
+ private fun getReleaseVersionSummary(): String = mContext.getString(
+ R.string.android_version_summary,
+ Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+ )
+
+ /**
+ * Trigger client initiated action (send intent) on system update
+ */
+ private fun ciActionOnSysUpdate(b: PersistableBundle) {
+ val intentStr = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING)
+ if (intentStr.isNullOrEmpty()) return
+ val extra = b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING)
+ val extraVal =
+ b.getString(CarrierConfigManager.KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING)
+ Log.d(
+ TAG,
+ "ciActionOnSysUpdate: broadcasting intent $intentStr with extra $extra, $extraVal"
+ )
+ val intent = Intent(intentStr).apply {
+ if (!extra.isNullOrEmpty()) putExtra(extra, extraVal)
+ addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)
+ }
+ mContext.applicationContext.sendBroadcast(intent)
+ }
+
+ companion object {
+ private const val TAG = "SysUpdatePrefContr"
+ }
+}
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 82edea5..a5767e4 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -53,7 +53,6 @@
<bool name="config_show_pointer_speed">false</bool>
<bool name="config_show_vibrate_input_devices">false</bool>
<bool name="config_show_reset_dashboard">false</bool>
- <bool name="config_show_system_update_settings">false</bool>
<bool name="config_show_device_model">false</bool>
<bool name="config_show_top_level_accessibility">false</bool>
<bool name="config_show_top_level_battery">false</bool>
diff --git a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
index 6d45af2..9368ec8 100644
--- a/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/HearingAidUtilsTest.java
@@ -156,14 +156,13 @@
}
@Test
- public void launchHearingAidPairingDialog_deviceSupportsCsip_csipEnabled_noDialog() {
+ public void launchHearingAidPairingDialog_deviceSupportsCsip_noDialog() {
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
HearingAidInfo.DeviceMode.MODE_BINAURAL);
when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
HearingAidInfo.DeviceSide.SIDE_LEFT);
makeDeviceSupportCsip();
- makeDeviceEnableCsip(true);
HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice,
TEST_LAUNCH_PAGE);
@@ -174,24 +173,6 @@
}
@Test
- public void launchHearingAidPairingDialog_deviceSupportsCsip_csipDisabled_dialogShown() {
- when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
- when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
- HearingAidInfo.DeviceMode.MODE_BINAURAL);
- when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
- HearingAidInfo.DeviceSide.SIDE_LEFT);
- makeDeviceSupportCsip();
- makeDeviceEnableCsip(false);
-
- HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, mCachedBluetoothDevice,
- TEST_LAUNCH_PAGE);
-
- shadowMainLooper().idle();
- final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
- assertThat(dialog.isShowing()).isTrue();
- }
-
- @Test
public void launchHearingAidPairingDialog_dialogShown() {
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDeviceMode()).thenReturn(
@@ -213,11 +194,6 @@
when(mCachedBluetoothDevice.getProfiles()).thenReturn(uuids);
}
- private void makeDeviceEnableCsip(boolean enabled) {
- when(mCsipSetCoordinatorProfile.isEnabled(mCachedBluetoothDevice.getDevice()))
- .thenReturn(enabled);
- }
-
private void setupEnvironment() {
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
index 04b48c0..f3fa69d 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ScreenFlashNotificationColorDialogFragmentTest.java
@@ -39,21 +39,21 @@
import android.graphics.Color;
import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.testing.FragmentScenario;
+import androidx.lifecycle.Lifecycle;
import com.android.settings.R;
import com.android.settings.testutils.FakeTimer;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowLooper;
import org.robolectric.util.ReflectionHelpers;
-import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.function.Consumer;
@@ -61,7 +61,7 @@
@RunWith(RobolectricTestRunner.class)
public class ScreenFlashNotificationColorDialogFragmentTest {
- private ShadowContextWrapper mShadowContextWrapper;
+ private FragmentScenario<TestScreenFlashNotificationColorDialogFragment> mFragmentScenario;
private ScreenFlashNotificationColorDialogFragment mDialogFragment;
private AlertDialog mAlertDialog;
private ColorSelectorLayout mColorSelectorLayout;
@@ -69,35 +69,32 @@
@Before
public void setUp() {
- FragmentActivity fragmentActivity = Robolectric.setupActivity(FragmentActivity.class);
- mShadowContextWrapper = shadowOf(fragmentActivity);
-
mCurrentColor = ROSE.mColorInt;
- mDialogFragment = createFragment();
+ mFragmentScenario = FragmentScenario.launch(
+ TestScreenFlashNotificationColorDialogFragment.class,
+ /* fragmentArgs= */ null,
+ R.style.Theme_AlertDialog_SettingsLib,
+ Lifecycle.State.INITIALIZED);
+ setupFragment();
+ }
- mDialogFragment.show(fragmentActivity.getSupportFragmentManager(), "test");
-
- mAlertDialog = (AlertDialog) mDialogFragment.getDialog();
- if (mAlertDialog != null) {
- mColorSelectorLayout = mAlertDialog.findViewById(R.id.color_selector_preference);
- }
+ @After
+ public void cleanUp() {
+ mFragmentScenario.close();
}
@Test
- @Ignore
public void test_assertShow() {
assertThat(mAlertDialog.isShowing()).isTrue();
}
@Test
- @Ignore
public void clickNeutral_assertShow() {
performClickOnDialog(BUTTON_NEUTRAL);
assertThat(mAlertDialog.isShowing()).isTrue();
}
@Test
- @Ignore
public void clickNeutral_assertStartPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
@@ -106,7 +103,6 @@
}
@Test
- @Ignore
public void clickNeutral_flushAllScheduledTasks_assertStopPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runAllTasks();
@@ -115,31 +111,28 @@
}
@Test
- @Ignore
public void clickNegative_assertNotShow() {
performClickOnDialog(BUTTON_NEGATIVE);
assertThat(mAlertDialog.isShowing()).isFalse();
}
@Test
- @Ignore
public void clickPositive_assertNotShow() {
performClickOnDialog(BUTTON_POSITIVE);
assertThat(mAlertDialog.isShowing()).isFalse();
}
@Test
- @Ignore
public void clickNeutralAndPause_assertStopPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
- mDialogFragment.onPause();
+ // move the state from RESUMED to CREATED to make fragment's onPause() to be called
+ mFragmentScenario.moveToState(Lifecycle.State.CREATED);
assertStopPreview();
}
@Test
- @Ignore
public void clickNeutralAndClickNegative_assertStopPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
@@ -149,7 +142,6 @@
}
@Test
- @Ignore
public void clickNeutralAndClickPositive_assertStopPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
@@ -159,7 +151,6 @@
}
@Test
- @Ignore
public void clickNeutralAndClickColor_assertStartPreview() {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
@@ -177,7 +168,6 @@
}
@Test
- @Ignore
public void clickColorAndClickNegative_assertColor() {
checkColorButton(AZURE);
performClickOnDialog(BUTTON_NEGATIVE);
@@ -187,7 +177,6 @@
}
@Test
- @Ignore
public void clickColorAndClickPositive_assertColor() {
checkColorButton(BLUE);
performClickOnDialog(BUTTON_POSITIVE);
@@ -201,23 +190,32 @@
private void performClickOnDialog(int whichButton) {
mAlertDialog.getButton(whichButton).performClick();
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
}
private Intent getLastCapturedIntent() {
- final List<Intent> capturedIntents = new ArrayList<>(
- mShadowContextWrapper.getBroadcastIntents());
+ final List<Intent> capturedIntents =
+ shadowOf(RuntimeEnvironment.getApplication()).getBroadcastIntents();
final int size = capturedIntents.size();
return capturedIntents.get(size - 1);
}
- private ScreenFlashNotificationColorDialogFragment createFragment() {
- ScreenFlashNotificationColorDialogFragmentWithFakeTimer fragment =
- new ScreenFlashNotificationColorDialogFragmentWithFakeTimer();
- ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
- ReflectionHelpers.setField(fragment, "mConsumer",
- (Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);
+ private void setupFragment() {
+ mFragmentScenario.onFragment(fragment -> {
+ ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
+ ReflectionHelpers.setField(fragment, "mConsumer",
+ (Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);
+ });
+ mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
- return fragment;
+ mFragmentScenario.onFragment(fragment -> {
+ assertThat(fragment.getDialog()).isNotNull();
+ assertThat(fragment.requireDialog().isShowing()).isTrue();
+ assertThat(fragment.requireDialog()).isInstanceOf(AlertDialog.class);
+ mAlertDialog = (AlertDialog) fragment.requireDialog();
+ mDialogFragment = fragment;
+ mColorSelectorLayout = mAlertDialog.findViewById(R.id.color_selector_preference);
+ });
}
private FakeTimer getTimerFromFragment() {
@@ -243,7 +241,7 @@
* A {@link ScreenFlashNotificationColorDialogFragment} that uses a fake timer so that it won't
* create unmanageable timer threads during test.
*/
- public static class ScreenFlashNotificationColorDialogFragmentWithFakeTimer extends
+ public static class TestScreenFlashNotificationColorDialogFragment extends
ScreenFlashNotificationColorDialogFragment {
@Override
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
index 090fb0c..2bd2d74 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsPairOtherControllerTest.java
@@ -34,6 +34,9 @@
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import java.util.HashSet;
+import java.util.Set;
+
/** Tests for {@link BluetoothDetailsPairOtherController}. */
@RunWith(RobolectricTestRunner.class)
public class BluetoothDetailsPairOtherControllerTest extends BluetoothDetailsControllerTestBase {
@@ -60,8 +63,9 @@
mScreen.addPreference(mSpacePreference);
}
+ /** Test the pair other side button title during initialization. */
@Test
- public void init_leftSideDevice_rightSideButtonTitle() {
+ public void init_leftSideDevice_pairRightSideButtonTitle() {
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_LEFT);
mController.init(mScreen);
@@ -70,8 +74,9 @@
mContext.getString(R.string.bluetooth_pair_right_ear_button));
}
+ /** Test the pair other side button title during initialization. */
@Test
- public void init_rightSideDevice_leftSideButtonTitle() {
+ public void init_rightSideDevice_pairLeftSideButtonTitle() {
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
mController.init(mScreen);
@@ -80,9 +85,10 @@
mContext.getString(R.string.bluetooth_pair_left_ear_button));
}
+ /** Test the pair other side button visibility during initialization. */
@Test
- public void init_isNotConnectedAshaHearingAidDevice_notVisiblePreference() {
- when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+ public void init_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
mController.init(mScreen);
@@ -90,23 +96,49 @@
assertThat(mSpacePreference.isVisible()).isFalse();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Hearing aids is not connected
+ * Expected result:
+ * The controller is not available. No need to show pair other side hint for
+ * not connected device.
+ */
@Test
- public void isAvailable_isNotConnectedAshaHearingAidDevice_notAvailable() {
- when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+ public void isAvailable_isNotConnectedHearingAidDevice_notAvailable() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Monaural hearing aids
+ * Expected result:
+ * The controller is not available. No need to show pair other side hint for
+ * monaural device.
+ */
@Test
- public void isAvailable_isConnectedAshaHearingAidDevice_isMonaural_notAvailable() {
- when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
+ public void isAvailable_isConnectedHearingAidDevice_isMonaural_notAvailable() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_MONAURAL);
assertThat(mController.isAvailable()).isFalse();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural ASHA hearing aids
+ * 2. Sub device is added
+ * 3. Sub device is connected
+ * Expected result:
+ * The controller is not available. Both sides are already paired and connected.
+ */
@Test
- public void isAvailable_subDeviceIsConnectedAshaHearingAidDevice_notAvailable() {
+ public void isAvailable_ashaDevice_otherDeviceIsConnected_notAvailable() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -115,8 +147,18 @@
assertThat(mController.isAvailable()).isFalse();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural ASHA hearing aids
+ * 2. Sub device is added
+ * 3. Sub device is not connected
+ * Expected result:
+ * The controller is available. Need to show the hint to pair the other side.
+ */
@Test
- public void isAvailable_subDeviceIsNotConnectedAshaHearingAidDevice_available() {
+ public void isAvailable_ashaDevice_otherDeviceIsNotConnected_available() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
when(mSubCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
@@ -125,8 +167,17 @@
assertThat(mController.isAvailable()).isTrue();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural ASHA hearing aids
+ * 2. No sub device added
+ * Expected result:
+ * The controller is available. Need to show the hint to pair the other side.
+ */
@Test
- public void isAvailable_subDeviceNotExist_available() {
+ public void isAvailable_ashaDevice_otherDeviceIsNotExist_available() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
when(mCachedDevice.getSubDevice()).thenReturn(null);
@@ -134,8 +185,67 @@
assertThat(mController.isAvailable()).isTrue();
}
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural LE Audio hearing aids
+ * 2. Member device is added
+ * 3. Member device is connected
+ * Expected result:
+ * The controller is not available. Both sides are already paired and connected.
+ */
@Test
- public void refresh_leftSideDevice_leftSideButtonTitle() {
+ public void isAvailable_leAudioDevice_otherDeviceIsConnected_notAvailable() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+ when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural LE Audio hearing aids
+ * 2. Member device is added
+ * 3. Member device is not connected
+ * Expected result:
+ * The controller is available. Need to show the hint to pair the other side.
+ */
+ @Test
+ public void isAvailable_leAudioDevice_otherDeviceIsNotConnected_available() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+ when(mSubCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(false);
+ when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ /**
+ * Test if the controller is available.
+ * Conditions:
+ * 1. Binaural LE Audio hearing aids
+ * 2. No member device added
+ * Expected result:
+ * The controller is available. Need to show the hint to pair the other side.
+ */
+ @Test
+ public void isAvailable_leAudioDevice_otherDeviceIsNotExist_available() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.isConnectedLeAudioHearingAidDevice()).thenReturn(true);
+ when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
+ when(mCachedDevice.getMemberDevice()).thenReturn(new HashSet<>());
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ /** Test the pair other side button title after refreshing. */
+ @Test
+ public void refresh_rightSideDevice_pairLeftSideButtonTitle() {
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
mController.init(mScreen);
@@ -145,9 +255,10 @@
mContext.getString(R.string.bluetooth_pair_left_ear_button));
}
+ /** Test the pair other side button visibility after refreshing. */
@Test
- public void refresh_isNotConnectedAshaHearingAidDevice_notVisiblePreference() {
- when(mCachedDevice.isConnectedAshaHearingAidDevice()).thenReturn(false);
+ public void refresh_isNotConnectedHearingAidDevice_preferenceIsNotVisible() {
+ when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
mController.init(mScreen);
mController.refresh();
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index 3ad14e5..19eac82 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -132,23 +132,23 @@
}
@Test
- public void isSliceableCorrectKey_returnsTrue() {
+ public void isPublicSliceCorrectKey_returnsTrue() {
final AmbientDisplayAlwaysOnPreferenceController controller =
new AmbientDisplayAlwaysOnPreferenceController(mContext,
"ambient_display_always_on");
- assertThat(controller.isSliceable()).isTrue();
+ assertThat(controller.isPublicSlice()).isTrue();
}
@Test
- public void isSliceableIncorrectKey_returnsFalse() {
+ public void isPublicSliceIncorrectKey_returnsFalse() {
final AmbientDisplayAlwaysOnPreferenceController controller =
new AmbientDisplayAlwaysOnPreferenceController(mContext, "bad_key");
- assertThat(controller.isSliceable()).isFalse();
+ assertThat(controller.isPublicSlice()).isFalse();
}
@Test
- public void isPublicSlice_returnTrue() {
- assertThat(mController.isPublicSlice()).isTrue();
+ public void isSliceable_returnTrue() {
+ assertThat(mController.isSliceable()).isTrue();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
index 7104206..c9e201b 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -53,7 +53,6 @@
mPolicy = spy(new BatteryTipPolicy(RuntimeEnvironment.application));
mContext = RuntimeEnvironment.application;
ReflectionHelpers.setField(mPolicy, "lowBatteryEnabled", true);
- ReflectionHelpers.setField(mPolicy, "lowBatteryHour", 3);
mBatteryInfo.discharging = true;
mLowBatteryDetector = new LowBatteryDetector(mContext, mPolicy, mBatteryInfo,
@@ -78,13 +77,9 @@
@Test
public void testDetect_lowBattery_tipNew() {
- mBatteryInfo.batteryLevel = 3;
+ mBatteryInfo.batteryLevel = 20;
mBatteryInfo.remainingTimeUs = TimeUnit.DAYS.toMillis(1);
assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
-
- mBatteryInfo.batteryLevel = 50;
- mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
- assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.NEW);
}
@Test
@@ -104,9 +99,9 @@
}
@Test
- public void testDetect_timeEstimationZero_tipInvisible() {
+ public void testDetect_lowTimeEstimation_tipInvisible() {
mBatteryInfo.batteryLevel = 50;
- mBatteryInfo.remainingTimeUs = 0;
+ mBatteryInfo.remainingTimeUs = TimeUnit.MINUTES.toMillis(1);
assertThat(mLowBatteryDetector.detect().isVisible()).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
deleted file mode 100644
index 61aa294..0000000
--- a/tests/robotests/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2016 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.system;
-
-import static android.os.SystemUpdateManager.KEY_STATUS;
-import static android.os.SystemUpdateManager.KEY_TITLE;
-import static android.os.SystemUpdateManager.STATUS_IDLE;
-import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
-import static android.os.SystemUpdateManager.STATUS_WAITING_DOWNLOAD;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemUpdateManager;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-
-import org.junit.After;
-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;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowUserManager.class)
-public class SystemUpdatePreferenceControllerTest {
-
- @Mock
- private PreferenceScreen mScreen;
- @Mock
- private SystemUpdateManager mSystemUpdateManager;
-
- private Context mContext;
- private ShadowUserManager mShadowUserManager;
- private SystemUpdatePreferenceController mController;
- private Preference mPreference;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mShadowUserManager = ShadowUserManager.getShadow();
-
- ShadowApplication.getInstance().setSystemService(Context.SYSTEM_UPDATE_SERVICE,
- mSystemUpdateManager);
- mController = new SystemUpdatePreferenceController(mContext);
- mPreference = new Preference(RuntimeEnvironment.application);
- mPreference.setKey(mController.getPreferenceKey());
- when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
- }
-
- @After
- public void cleanUp() {
- mShadowUserManager.setIsAdminUser(false);
- }
-
- @Test
- public void updateNonIndexable_ifAvailable_shouldNotUpdate() {
- final List<String> keys = new ArrayList<>();
- mShadowUserManager.setIsAdminUser(true);
-
- mController.updateNonIndexableKeys(keys);
-
- assertThat(keys).isEmpty();
- }
-
- @Test
- public void updateNonIndexable_ifNotAvailable_shouldUpdate() {
- mShadowUserManager.setIsAdminUser(false);
- final List<String> keys = new ArrayList<>();
-
- mController.updateNonIndexableKeys(keys);
-
- assertThat(keys).hasSize(1);
- }
-
- @Test
- public void displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
- mShadowUserManager.setIsAdminUser(false);
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- @Config(qualifiers = "mcc999")
- public void displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
- mShadowUserManager.setIsAdminUser(true);
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isFalse();
- }
-
- @Test
- public void displayPrefs_ifAvailable_shouldDisplay() {
- mShadowUserManager.setIsAdminUser(true);
-
- mController.displayPreference(mScreen);
-
- assertThat(mPreference.isVisible()).isTrue();
- }
-
- @Test
- public void updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_summary,
- Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY));
- }
-
- @Test
- public void updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
- final String testReleaseName = "ANDROID TEST VERSION";
-
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_IDLE);
- bundle.putString(KEY_TITLE, testReleaseName);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_summary, testReleaseName));
- }
-
- @Test
- public void updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
- final Bundle bundle = new Bundle();
- bundle.putInt(KEY_STATUS, STATUS_WAITING_DOWNLOAD);
- when(mSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary())
- .isEqualTo(mContext.getString(R.string.android_version_pending_update_summary));
- }
-}
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index bfdb408..c6c37d5 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -17,9 +17,17 @@
package com.android.settings.network.apn
import android.content.Context
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChild
+import androidx.compose.ui.test.onChildAt
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performScrollToNode
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
@@ -34,8 +42,22 @@
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
-
- val apnData = ApnData(name = "apn_name")
+ private val apnName = "apn_name"
+ private val mmsc = "mmsc"
+ private val mmsProxy = "mms_proxy"
+ private val mnc = "mnc"
+ private val apnType = "apn_type"
+ private val apnRoaming = "IPv4"
+ private val apnEnable = context.resources.getString(R.string.carrier_enabled)
+ private val apnData = ApnData(
+ name = apnName,
+ mmsc = mmsc,
+ mmsProxy = mmsProxy,
+ mnc = mnc,
+ apnType = apnType,
+ apnRoaming = apnRoaming,
+ apnEnable = true
+ )
@Test
fun apnEditPageProvider_name() {
@@ -45,7 +67,9 @@
@Test
fun title_displayed() {
composeTestRule.setContent {
- ApnPage(apnData)
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
}
composeTestRule.onNodeWithText(context.getString(R.string.apn_edit)).assertIsDisplayed()
}
@@ -53,8 +77,94 @@
@Test
fun name_displayed() {
composeTestRule.setContent {
- ApnPage(apnData)
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
}
- composeTestRule.onNodeWithText("apn_name", true).assertIsDisplayed()
+ composeTestRule.onNodeWithText(apnName, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun mmsc_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(mmsc, true))
+ composeTestRule.onNodeWithText(mmsc, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun mms_proxy_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(mmsProxy, true))
+ composeTestRule.onNodeWithText(mmsProxy, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun mnc_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(mnc, true))
+ composeTestRule.onNodeWithText(mnc, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun apn_type_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(apnType, true))
+ composeTestRule.onNodeWithText(apnType, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun apn_roaming_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(apnRoaming, true))
+ composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun carrier_enabled_displayed() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(apnEnable, true))
+ composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
+ }
+
+ @Test
+ fun carrier_enabled_isChecked() {
+ composeTestRule.setContent {
+ ApnPage(remember {
+ mutableStateOf(apnData)
+ })
+ }
+ composeTestRule.onRoot().onChild().onChildAt(0)
+ .performScrollToNode(hasText(apnEnable, true))
+ composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
}
}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
new file mode 100644
index 0000000..0ba91df
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdateManagerExtKtTest.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.os.Bundle
+import android.os.SystemUpdateManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdateManagerExtKtTest {
+
+ private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+ }
+
+ @Test
+ fun getSystemUpdateInfo() = runTest {
+ val bundle = Bundle()
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+
+ val info = context.getSystemUpdateInfo()
+
+ assertThat(info).isSameInstanceAs(bundle)
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
new file mode 100644
index 0000000..17cdf04
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/system/SystemUpdatePreferenceControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.system
+
+import android.content.Context
+import android.os.Build
+import android.os.Bundle
+import android.os.SystemClock
+import android.os.SystemUpdateManager
+import android.os.UserManager
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceScreen
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class SystemUpdatePreferenceControllerTest {
+ private val mockUserManager = mock<UserManager>()
+ private val mockSystemUpdateManager = mock<SystemUpdateManager>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { userManager } doReturn mockUserManager
+ on { getSystemService(SystemUpdateManager::class.java) } doReturn mockSystemUpdateManager
+ }
+
+ private val resources = spy(context.resources) {
+ on { getBoolean(R.bool.config_show_system_update_settings) } doReturn true
+ }
+
+ private val preference = Preference(context).apply { key = KEY }
+ private val preferenceScreen = mock<PreferenceScreen> {
+ onGeneric { findPreference(KEY) } doReturn preference
+ }
+ private val controller = SystemUpdatePreferenceController(context, KEY)
+
+ @Before
+ fun setUp() {
+ whenever(context.resources).thenReturn(resources)
+ }
+
+ @Test
+ fun updateNonIndexable_ifAvailable_shouldNotUpdate() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+ val keys = mutableListOf<String>()
+
+ controller.updateNonIndexableKeys(keys)
+
+ assertThat(keys).isEmpty()
+ }
+
+ @Test
+ fun updateNonIndexable_ifNotAvailable_shouldUpdate() {
+ whenever(mockUserManager.isAdminUser).thenReturn(false)
+ val keys = mutableListOf<String>()
+
+ controller.updateNonIndexableKeys(keys)
+
+ assertThat(keys).containsExactly(KEY)
+ }
+
+ @Test
+ fun displayPrefs_ifVisible_butNotAdminUser_shouldNotDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(false)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isFalse()
+ }
+
+ @Test
+ fun displayPrefs_ifAdminUser_butNotVisible_shouldNotDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+ whenever(resources.getBoolean(R.bool.config_show_system_update_settings)).thenReturn(false)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isFalse()
+ }
+
+ @Test
+ fun displayPrefs_ifAvailable_shouldDisplay() {
+ whenever(mockUserManager.isAdminUser).thenReturn(true)
+
+ controller.displayPreference(preferenceScreen)
+
+ assertThat(preference.isVisible).isTrue()
+ }
+
+ @Test
+ fun updateState_systemUpdateStatusUnknown_shouldSetToAndroidVersion() {
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_UNKNOWN)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary).isEqualTo(
+ context.getString(
+ R.string.android_version_summary,
+ Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY,
+ )
+ )
+ }
+
+ @Test
+ fun updateState_systemUpdateStatusIdle_shouldSetToAndroidVersion() {
+ val testReleaseName = "ANDROID TEST VERSION"
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_IDLE)
+ putString(SystemUpdateManager.KEY_TITLE, testReleaseName)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary)
+ .isEqualTo(context.getString(R.string.android_version_summary, testReleaseName))
+ }
+
+ @Test
+ fun updateState_systemUpdateInProgress_shouldSetToUpdatePending() {
+ val bundle = Bundle().apply {
+ putInt(SystemUpdateManager.KEY_STATUS, SystemUpdateManager.STATUS_WAITING_DOWNLOAD)
+ }
+ whenever(mockSystemUpdateManager.retrieveSystemUpdateInfo()).thenReturn(bundle)
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ SystemClock.sleep(100)
+
+ assertThat(preference.summary)
+ .isEqualTo(context.getString(R.string.android_version_pending_update_summary))
+ }
+
+ private companion object {
+ const val KEY = "test_key"
+ }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java b/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java
deleted file mode 100644
index 36beb90..0000000
--- a/tests/uitests/src/com/android/settings/ui/ConnectedDeviceTests.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2018 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.ui;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.content.Intent;
-import android.nfc.NfcAdapter;
-import android.nfc.NfcManager;
-import android.os.RemoteException;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Ignore
-@MediumTest
-@RunWith(AndroidJUnit4.class)
-public class ConnectedDeviceTests {
-
- private static final String SETTINGS_PACKAGE = "com.android.settings";
- private static final int TIMEOUT = 2000;
- private UiDevice mDevice;
-
- @Before
- public void setUp() throws Exception {
- mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- try {
- mDevice.setOrientationNatural();
- } catch (RemoteException e) {
- throw new RuntimeException("failed to freeze device orientation", e);
- }
- }
-
- @After
- public void tearDown() throws Exception {
- mDevice.pressBack();
- mDevice.pressHome();
- }
-
- // This NFC toggle test is set up this way since there's no way to set
- // the NFC flag to enabled or disabled without touching UI.
- // This way, we get coverage for whether or not the toggle button works.
- @Test
- public void testNFCToggle() throws Exception {
- NfcManager manager = (NfcManager) InstrumentationRegistry.getTargetContext()
- .getSystemService(Context.NFC_SERVICE);
- NfcAdapter nfcAdapter = manager.getDefaultAdapter();
- boolean nfcInitiallyEnabled = nfcAdapter.isEnabled();
- InstrumentationRegistry.getContext().startActivity(new Intent()
- .setClassName(
- SETTINGS_PACKAGE,
- "com.android.settings.Settings$ConnectedDeviceDashboardActivity"));
- UiObject2 nfcSetting = mDevice.wait(Until.findObject(By.text("NFC")), TIMEOUT);
- nfcSetting.click();
- Thread.sleep(TIMEOUT * 2);
- if (nfcInitiallyEnabled) {
- assertFalse("NFC wasn't disabled on toggle", nfcAdapter.isEnabled());
- nfcSetting.click();
- Thread.sleep(TIMEOUT * 2);
- assertTrue("NFC wasn't enabled on toggle", nfcAdapter.isEnabled());
- } else {
- assertTrue("NFC wasn't enabled on toggle", nfcAdapter.isEnabled());
- nfcSetting.click();
- Thread.sleep(TIMEOUT * 2);
- assertFalse("NFC wasn't disabled on toggle", nfcAdapter.isEnabled());
- }
- }
-}
diff --git a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt
new file mode 100644
index 0000000..413fae7
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.ui
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageSettingsTest {
+ private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ @Before
+ fun setUp() {
+ device.startMainActivityFromHomeScreen(Settings.ACTION_DATA_USAGE_SETTINGS)
+ }
+
+ @Test
+ fun hasTexts() {
+ device.assertHasTexts(ON_SCREEN_TEXTS)
+ }
+
+ private companion object {
+ val ON_SCREEN_TEXTS = listOf(
+ "0 B",
+ "Data Saver",
+ "Wi‑Fi data usage",
+ )
+ }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java b/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java
deleted file mode 100644
index 3befca3..0000000
--- a/tests/uitests/src/com/android/settings/ui/DataUsageSettingsTests.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2018 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.ui;
-
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.system.helpers.SettingsHelper;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
-
-import org.junit.Ignore;
-
-@Ignore
-public class DataUsageSettingsTests extends InstrumentationTestCase {
-
- private static final String SETTINGS_PACKAGE = "com.android.settings";
- private static final int TIMEOUT = 2000;
- private UiDevice mDevice;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mDevice = UiDevice.getInstance(getInstrumentation());
- try {
- mDevice.setOrientationNatural();
- } catch (RemoteException e) {
- throw new RuntimeException("failed to freeze device orientaion", e);
- }
- }
-
- @Override
- protected void tearDown() throws Exception {
- // Need to finish settings activity
- mDevice.pressBack();
- mDevice.pressHome();
- super.tearDown();
- }
-
- @MediumTest
- public void testElementsOnDataUsageScreen() throws Exception {
- launchDataUsageSettings();
- assertNotNull("Data usage element not found",
- mDevice.wait(Until.findObject(By.text("Usage")),
- TIMEOUT));
- assertNotNull("Data usage bar not found",
- mDevice.wait(Until.findObject(By.res(SETTINGS_PACKAGE,
- "color_bar")), TIMEOUT));
- assertNotNull("WiFi Data usage element not found",
- mDevice.wait(Until.findObject(By.text("Wi-Fi data usage")),
- TIMEOUT));
- assertNotNull("Network restrictions element not found",
- mDevice.wait(Until.findObject(By.text("Network restrictions")),
- TIMEOUT));
- }
-
- public void launchDataUsageSettings() throws Exception {
- SettingsHelper.launchSettingsPage(getInstrumentation().getContext(),
- Settings.ACTION_SETTINGS);
- mDevice.wait(Until
- .findObject(By.text("Network & Internet")), TIMEOUT)
- .click();
- Thread.sleep(TIMEOUT * 2);
- assertNotNull("Network & internet screen not loaded", mDevice.wait(
- Until.findObject(By.text("Data usage")), TIMEOUT));
- mDevice.wait(Until
- .findObject(By.text("Data usage")), TIMEOUT)
- .click();
- }
-}
diff --git a/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt
new file mode 100644
index 0000000..701ba2f
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/NetworkOperatorSettingsTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.ui
+
+import android.provider.Settings
+import android.telephony.SubscriptionManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import com.google.common.truth.TruthJUnit.assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NetworkOperatorSettingsTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val device = UiDevice.getInstance(instrumentation)
+
+ @Before
+ fun setUp() {
+ assume().that(
+ instrumentation.context.getSystemService(SubscriptionManager::class.java)!!
+ .availableSubscriptionInfoList
+ ).isNotEmpty()
+ device.startMainActivityFromHomeScreen(Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ }
+
+ @Test
+ fun hasTexts() {
+ device.assertHasTexts(ON_SCREEN_TEXTS)
+ }
+
+ private companion object {
+ val ON_SCREEN_TEXTS = listOf(
+ "Use SIM",
+ "0 B",
+ "Calls preference",
+ "SMS preference",
+ "Mobile data",
+ "Roaming",
+ "App data usage",
+ "Data warning & limit",
+ "Preferred network type",
+ "Carrier settings version",
+ "Automatically select network",
+ "Choose network",
+ "Access Point Names",
+ )
+ }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.kt b/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.kt
new file mode 100644
index 0000000..2fbbfe5
--- /dev/null
+++ b/tests/uitests/src/com/android/settings/ui/NfcSettingsTest.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.ui
+
+import android.nfc.NfcAdapter
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts
+import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen
+import com.google.common.truth.TruthJUnit.assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NfcSettingsTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val device = UiDevice.getInstance(instrumentation)
+
+ @Before
+ fun setUp() {
+ assume().that(NfcAdapter.getDefaultAdapter(instrumentation.context)).isNotNull()
+ device.startMainActivityFromHomeScreen(Settings.ACTION_NFC_SETTINGS)
+ }
+
+ @Test
+ fun hasTexts() {
+ device.assertHasTexts(ON_SCREEN_TEXTS)
+ }
+
+ private companion object {
+ val ON_SCREEN_TEXTS = listOf(
+ "Use NFC",
+ "Require device unlock for NFC",
+ "Contactless payments",
+ )
+ }
+}
diff --git a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
index f063042..587e734 100644
--- a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
+++ b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java
@@ -18,9 +18,7 @@
import static com.android.settings.network.SubscriptionUtil.KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME;
import static com.android.settings.network.SubscriptionUtil.SUB_ID;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
@@ -185,7 +183,7 @@
@Ignore
@Test
public void getUniqueDisplayNames_identicalCarriers_fourDigitsUsed() {
- // Both subscriptoins have the same display name.
+ // Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -215,7 +213,7 @@
@Ignore
@Test
public void getUniqueDisplayNames_identicalCarriersAfterTrim_fourDigitsUsed() {
- // Both subscriptoins have the same display name.
+ // Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -244,8 +242,8 @@
@Ignore
@Test
- public void getUniqueDisplayNames_phoneNumberBlocked_subscriptoinIdFallback() {
- // Both subscriptoins have the same display name.
+ public void getUniqueDisplayNames_phoneNumberBlocked_subscriptionIdFallback() {
+ // Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
when(info1.getSubscriptionId()).thenReturn(SUBID_1);
@@ -273,9 +271,9 @@
@Ignore
@Test
- public void getUniqueDisplayNames_phoneNumberIdentical_subscriptoinIdFallback() {
+ public void getUniqueDisplayNames_phoneNumberIdentical_subscriptionIdFallback() {
// TODO have three here from the same carrier
- // Both subscriptoins have the same display name.
+ // Both subscriptions have the same display name.
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
final SubscriptionInfo info3 = mock(SubscriptionInfo.class);
@@ -464,8 +462,8 @@
SharedPreferences sp = mock(SharedPreferences.class);
when(mContext.getSharedPreferences(
KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
- when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + "6789");
- when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + "4321");
+ when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
+ when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
final CharSequence nameOfSub1 =
@@ -475,8 +473,41 @@
assertThat(nameOfSub1).isNotNull();
assertThat(nameOfSub2).isNotNull();
- assertEquals(CARRIER_1 + "6789", nameOfSub1.toString());
- assertEquals(CARRIER_1 + "4321", nameOfSub2.toString());
+ assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
+ assertEquals(CARRIER_1 + " 4321", nameOfSub2.toString());
+ }
+
+ @Test
+ public void getUniqueDisplayName_hasRecordAndNameIsChanged_doesNotUseRecordBeTheResult() {
+ final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
+ final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
+ when(info1.getSubscriptionId()).thenReturn(SUBID_1);
+ when(info2.getSubscriptionId()).thenReturn(SUBID_2);
+ when(info1.getDisplayName()).thenReturn(CARRIER_1);
+ when(info2.getDisplayName()).thenReturn(CARRIER_2);
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(
+ Arrays.asList(info1, info2));
+
+ SharedPreferences sp = mock(SharedPreferences.class);
+ SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class);
+ when(mContext.getSharedPreferences(
+ KEY_UNIQUE_SUBSCRIPTION_DISPLAYNAME, Context.MODE_PRIVATE)).thenReturn(sp);
+ when(sp.edit()).thenReturn(editor);
+ when(editor.remove(anyString())).thenReturn(editor);
+
+ when(sp.getString(eq(SUB_ID + SUBID_1), anyString())).thenReturn(CARRIER_1 + " 6789");
+ when(sp.getString(eq(SUB_ID + SUBID_2), anyString())).thenReturn(CARRIER_1 + " 4321");
+
+
+ final CharSequence nameOfSub1 =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(info1, mContext);
+ final CharSequence nameOfSub2 =
+ SubscriptionUtil.getUniqueSubscriptionDisplayName(info2, mContext);
+
+ assertThat(nameOfSub1).isNotNull();
+ assertThat(nameOfSub2).isNotNull();
+ assertEquals(CARRIER_1 + " 6789", nameOfSub1.toString());
+ assertEquals(CARRIER_2.toString(), nameOfSub2.toString());
}
@Test
@@ -501,4 +532,60 @@
assertTrue(SubscriptionUtil.isSimHardwareVisible(mContext));
}
+
+ @Test
+ public void isValidCachedDisplayName_matchesRule1_returnTrue() {
+ String originalName = "originalName";
+ String cacheString = "originalName 1234";
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_matchesRule2_returnTrue() {
+ String originalName = "original Name";
+ String cacheString = originalName + " " + 1234;
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_nameIsEmpty1_returnFalse() {
+ String originalName = "original Name";
+ String cacheString = "";
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_nameIsEmpty2_returnFalse() {
+ String originalName = "";
+ String cacheString = "originalName 1234";
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_nameIsDifferent_returnFalse() {
+ String originalName = "original Name";
+ String cacheString = "originalName 1234";
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_noNumber_returnFalse() {
+ String originalName = "original Name";
+ String cacheString = originalName;
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+ }
+
+ @Test
+ public void isValidCachedDisplayName_noSpace_returnFalse() {
+ String originalName = "original Name";
+ String cacheString = originalName;
+
+ assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse();
+ }
}