Merge "Fix failure in BiometricsSafetySourceTest" into main
diff --git a/res/drawable/ic_android_satellite_24px.xml b/res/drawable/ic_android_satellite_24px.xml
new file mode 100644
index 0000000..15f2884
--- /dev/null
+++ b/res/drawable/ic_android_satellite_24px.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M487,600L392,505L346,551L376,580Q399,603 399,637Q399,671 376,694L333,737Q310,760 276.5,760Q243,760 220,737L103,620Q80,597 80,563.5Q80,530 103,507L146,464Q169,441 203,441Q237,441 260,464L289,494L335,448L160,273L273,160L448,335L493,290L464,260Q441,237 441,203Q441,169 464,146L507,103Q530,80 563.5,80Q597,80 620,103L737,220Q760,243 760,276.5Q760,310 737,333L694,376Q671,399 637,399Q603,399 580,376L550,347L505,392L600,487L487,600ZM520,880L520,800Q637,800 718.5,718.5Q800,637 800,520L880,520Q880,595 851.5,660.5Q823,726 774.5,774.5Q726,823 660.5,851.5Q595,880 520,880ZM520,720L520,640Q570,640 605,605Q640,570 640,520L720,520Q720,603 661.5,661.5Q603,720 520,720ZM520,203L550,233L593,190L563,160Q563,160 563,160Q563,160 563,160L520,203Q520,203 520,203Q520,203 520,203ZM160,563L190,593L233,550L203,520Q203,520 203,520Q203,520 203,520L160,563Q160,563 160,563Q160,563 160,563ZM637,320L680,277Q680,277 680,277Q680,277 680,277L650,247L607,290L637,320Q637,320 637,320Q637,320 637,320ZM277,680L320,637Q320,637 320,637Q320,637 320,637L290,607L247,650L277,680Q277,680 277,680Q277,680 277,680Z"/>
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 33f1864..710ee5d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13182,4 +13182,7 @@
<!-- Provider Model: summary of converted in SIM category. [CHAR LIMIT=50] -->
<string name="sim_category_converted_sim">Converted to eSIM. Remove and discard.</string>
+
+ <!--Title for Sync Across Devices category-->
+ <string name="sync_across_devices_title">Sync across devices</string>
</resources>
diff --git a/res/xml/configure_notification_settings.xml b/res/xml/configure_notification_settings.xml
index d9fccc4..7fcf85e 100644
--- a/res/xml/configure_notification_settings.xml
+++ b/res/xml/configure_notification_settings.xml
@@ -109,6 +109,11 @@
</PreferenceCategory>
<PreferenceCategory
+ android:key="sync_across_devices"
+ android:title="@string/sync_across_devices_title"
+ settings:controller="com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesPreferenceController"/>
+
+ <PreferenceCategory
android:key="advanced_section_header"
android:title="@string/advanced_section_header">
<com.android.settingslib.RestrictedPreference
diff --git a/res/xml/satellite_setting.xml b/res/xml/satellite_setting.xml
index 9eb1763..09fbbd6 100644
--- a/res/xml/satellite_setting.xml
+++ b/res/xml/satellite_setting.xml
@@ -53,7 +53,7 @@
android:key="key_supported_service"
android:title="@string/title_supported_service"
android:summary="@string/summary_supported_service"
- android:icon="@drawable/ic_satellite_alt_24px"/>
+ android:icon="@drawable/ic_android_satellite_24px"/>
</PreferenceCategory>
<com.android.settingslib.widget.FooterPreference
diff --git a/src/com/android/settings/accessibility/OWNERS b/src/com/android/settings/accessibility/OWNERS
index 3bd156b..633e9c7 100644
--- a/src/com/android/settings/accessibility/OWNERS
+++ b/src/com/android/settings/accessibility/OWNERS
@@ -1,3 +1,6 @@
+# The Android Accessibility team should approve all changes to Settings > Accessibility content.
+set noparent
+
# Default reviewers for this and subdirectories.
chunkulin@google.com
danielnorman@google.com
@@ -8,5 +11,9 @@
# Legacy owner(s).
menghanli@google.com #{LAST_RESORT_SUGGESTION}
+# Core Settings owner for emergency changes.
+cipson@google.com #{LAST_RESORT_SUGGESTION}
+
+# Partner-team files
per-file HapticFeedbackIntensityPreferenceController.java = michaelwr@google.com
per-file *Vibration* = michaelwr@google.com
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
index 5e395b2..564e138 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHearingAidsPresetsController.java
@@ -199,10 +199,8 @@
@Override
public void onPresetSelectionFailed(@NonNull BluetoothDevice device, int reason) {
if (device.equals(mCachedDevice.getDevice())) {
- if (DEBUG) {
- Log.d(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
- + ", reason: " + reason);
- }
+ Log.w(TAG, "onPresetSelectionFailed, device: " + device.getAddress()
+ + ", reason: " + reason);
mContext.getMainExecutor().execute(() -> {
refresh();
showErrorToast();
@@ -213,10 +211,8 @@
@Override
public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
if (hapGroupId == mHapClientProfile.getHapGroup(mCachedDevice.getDevice())) {
- if (DEBUG) {
- Log.d(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
- + ", reason: " + reason);
- }
+ Log.w(TAG, "onPresetSelectionForGroupFailed, group: " + hapGroupId
+ + ", reason: " + reason);
// Try to set the preset independently if group operation failed
if (mPreference != null) {
selectPresetIndependently(Integer.parseInt(mPreference.getValue()));
@@ -242,11 +238,8 @@
@Override
public void onSetPresetNameFailed(@NonNull BluetoothDevice device, int reason) {
if (device.equals(mCachedDevice.getDevice())) {
- if (DEBUG) {
- Log.d(TAG,
- "onSetPresetNameFailed, device: " + device.getAddress()
- + ", reason: " + reason);
- }
+ Log.w(TAG, "onSetPresetNameFailed, device: " + device.getAddress()
+ + ", reason: " + reason);
mContext.getMainExecutor().execute(() -> {
refresh();
showErrorToast();
@@ -257,10 +250,8 @@
@Override
public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
if (hapGroupId == mHapClientProfile.getHapGroup(mCachedDevice.getDevice())) {
- if (DEBUG) {
- Log.d(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
- + ", reason: " + reason);
- }
+ Log.w(TAG, "onSetPresetNameForGroupFailed, group: " + hapGroupId
+ + ", reason: " + reason);
mContext.getMainExecutor().execute(() -> {
refresh();
showErrorToast();
@@ -288,9 +279,6 @@
for (int i = 0; i < infoList.size(); i++) {
presetNames[i] = infoList.get(i).getName();
presetIndexes[i] = Integer.toString(infoList.get(i).getIndex());
- if (DEBUG) {
- Log.d(TAG, "loadAllPresetInfo, preset " + presetIndexes[i] + ": " + presetNames[i]);
- }
}
mPreference.setEntries(presetNames);
mPreference.setEntryValues(presetIndexes);
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 6df80f1..9abc6c2 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -545,9 +545,13 @@
getActivity(), this, forceRoundedIcons, pref, tile, key,
mPlaceholderPreferenceController.getOrder());
if (Flags.dynamicInjectionCategory()) {
- Preference group = screen.findPreference(tile.getGroupKey());
- if (tile.hasGroupKey() && group instanceof PreferenceCategory) {
- ((PreferenceCategory) group).addPreference(pref);
+ if (tile.hasGroupKey()) {
+ Preference group = screen.findPreference(tile.getGroupKey());
+ if (group instanceof PreferenceCategory) {
+ ((PreferenceCategory) group).addPreference(pref);
+ } else {
+ screen.addPreference(pref);
+ }
} else {
screen.addPreference(pref);
}
diff --git a/src/com/android/settings/development/OemUnlockPreferenceController.java b/src/com/android/settings/development/OemUnlockPreferenceController.java
index 9076f9a..2542a11 100644
--- a/src/com/android/settings/development/OemUnlockPreferenceController.java
+++ b/src/com/android/settings/development/OemUnlockPreferenceController.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -176,6 +177,13 @@
/** Returns {@code true} if the device is SIM-locked. Otherwise, returns {@code false}. */
private boolean isSimLockedDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
+ Log.w(TAG,
+ "getAllowedCarriers is unsupported without "
+ + "PackageManager#FEATURE_TELEPHONY_CARRIERLOCK");
+ return false;
+ }
int phoneCount = mTelephonyManager.getPhoneCount();
for (int i = 0; i < phoneCount; i++) {
if (mTelephonyManager.getAllowedCarriers(i).size() > 0) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 904923a..66ce8b8 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -396,7 +396,11 @@
chargeTimeMs <= 0
? null
: getPowerRemainingChargingLabel(
- context, chargeTimeMs, info.isFastCharging, currentTimeMs);
+ context,
+ chargeTimeMs,
+ info.isFastCharging,
+ info.pluggedStatus,
+ currentTimeMs);
info.chargeLabel =
chargeTimeMs <= 0
@@ -428,20 +432,35 @@
}
private static CharSequence getPowerRemainingChargingLabel(
- Context context, long remainingTimeMs, boolean isFastCharging, long currentTimeMs) {
+ Context context,
+ long chargeRemainingTimeMs,
+ boolean isFastCharging,
+ int pluggedStatus,
+ long currentTimeMs) {
+ if (pluggedStatus == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+ BatterySettingsFeatureProvider featureProvider =
+ FeatureFactory.getFeatureFactory().getBatterySettingsFeatureProvider();
+ final CharSequence wirelessChargingRemainingLabel =
+ featureProvider.getWirelessChargingRemainingLabel(
+ context, chargeRemainingTimeMs, currentTimeMs);
+ if (wirelessChargingRemainingLabel != null) {
+ return wirelessChargingRemainingLabel;
+ }
+ }
if (com.android.settingslib.fuelgauge.BatteryUtils.isChargingStringV2Enabled()) {
int chargeLabelResId =
isFastCharging
? R.string.power_remaining_fast_charging_duration_only_v2
: R.string.power_remaining_charging_duration_only_v2;
String timeString =
- PowerUtil.getTargetTimeShortString(context, remainingTimeMs, currentTimeMs);
+ PowerUtil.getTargetTimeShortString(
+ context, chargeRemainingTimeMs, currentTimeMs);
return context.getString(chargeLabelResId, timeString);
}
final CharSequence timeString =
StringUtil.formatElapsedTime(
context,
- remainingTimeMs,
+ chargeRemainingTimeMs,
/* withSeconds= */ false,
/* collapseTimeUnit= */ true);
return context.getString(R.string.power_remaining_charging_duration_only, timeString);
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
index c6fd6d4..a1a220d 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -48,4 +48,9 @@
/** Return a label for the bottom summary during wireless charging. */
@Nullable
CharSequence getWirelessChargingLabel(@NonNull Context context, @NonNull BatteryInfo info);
+
+ /** Return a charging remaining time label for wireless charging. */
+ @Nullable
+ CharSequence getWirelessChargingRemainingLabel(
+ @NonNull Context context, long remainingTimeMs, long currentTimeMs);
}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
index a4900fb..49db450 100644
--- a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -60,4 +60,11 @@
@NonNull Context context, @NonNull BatteryInfo info) {
return null;
}
+
+ @Nullable
+ @Override
+ public CharSequence getWirelessChargingRemainingLabel(
+ @NonNull Context context, long remainingTimeMs, long currentTimeMs) {
+ return null;
+ }
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index 1cbe6da..1c6ff54 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -321,8 +321,17 @@
final List<BatteryEvent> batteryEventList = new ArrayList<>();
final List<BatteryLevelData.PeriodBatteryLevelData> levelDataList =
batteryLevelData.getHourlyBatteryLevelsPerDay();
- for (BatteryLevelData.PeriodBatteryLevelData oneDayData : levelDataList) {
- for (int hourIndex = 0; hourIndex < oneDayData.getLevels().size() - 1; hourIndex++) {
+ final int dailyDataSize = levelDataList.size();
+ for (int dailyIndex = 0; dailyIndex < dailyDataSize; dailyIndex++) {
+ final BatteryLevelData.PeriodBatteryLevelData oneDayData =
+ levelDataList.get(dailyIndex);
+ final int hourDataSize = oneDayData.getLevels().size();
+ for (int hourIndex = 0; hourIndex < hourDataSize; hourIndex++) {
+ // For timestamp data on adjacent days, the last data (24:00) of the previous day is
+ // equal to the first data (00:00) of the next day, so skip sending EVEN_HOUR event.
+ if (dailyIndex < dailyDataSize - 1 && hourIndex == hourDataSize - 1) {
+ continue;
+ }
batteryEventList.add(
convertToBatteryEvent(
oneDayData.getTimestamps().get(hourIndex),
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index afb385d..719d3bd 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -649,6 +650,22 @@
// Process raw history map data into hourly timestamps.
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap =
DataProcessor.getHistoryMapWithExpectedTimestamps(context, batteryHistoryMap);
+ if (isFromPeriodJob && !processedBatteryHistoryMap.isEmpty()) {
+ // For periodic job, only generate battery usage data between even-hour timestamps.
+ // Remove the timestamps:
+ // 1) earlier than the latest completed period job (startTimestamp)
+ // 2) later than current scheduled even-hour job (lastEvenHourTimestamp).
+ final long lastEvenHourTimestamp = TimestampUtils.getLastEvenHourTimestamp(currentTime);
+ final Set<Long> batteryHistMapKeySet = processedBatteryHistoryMap.keySet();
+ final long minTimestamp = Collections.min(batteryHistMapKeySet);
+ final long maxTimestamp = Collections.max(batteryHistMapKeySet);
+ if (minTimestamp < startTimestamp) {
+ processedBatteryHistoryMap.remove(minTimestamp);
+ }
+ if (maxTimestamp > lastEvenHourTimestamp) {
+ processedBatteryHistoryMap.remove(maxTimestamp);
+ }
+ }
// Wrap and processed history map into easy-to-use format for UI rendering.
final BatteryLevelData batteryLevelData =
DataProcessor.getLevelDataThroughProcessedHistoryMap(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
index 0ac8cca..88bd4ad 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java
@@ -37,6 +37,7 @@
import java.time.Clock;
import java.time.Duration;
import java.util.List;
+import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
@@ -97,6 +98,7 @@
static void dumpBatteryUsageSlotDatabaseHist(Context context, PrintWriter writer) {
final BatteryUsageSlotDao dao =
BatteryStateDatabase.getInstance(context).batteryUsageSlotDao();
+ writer.println("\n\tBattery Usage Slot TimeZone ID: " + TimeZone.getDefault().getID());
writer.println("\n\tBattery Usage Slot DatabaseHistory:");
final List<BatteryUsageSlotEntity> entities =
dao.getAllAfterForLog(getLastFullChargeTimestamp(context));
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 48d918c..a2ca9ae 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -204,7 +204,7 @@
if (mIsEmbeddingActivityEnabled) {
final UserManager um = getSystemService(UserManager.class);
final UserInfo userInfo = um.getUserInfo(getUserId());
- if (userInfo.isManagedProfile()) {
+ if (!userInfo.isMain()) {
final Intent intent = new Intent(getIntent())
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
.putExtra(EXTRA_USER_HANDLE, getUser())
diff --git a/src/com/android/settings/network/WepNetworkDialogActivity.kt b/src/com/android/settings/network/WepNetworkDialogActivity.kt
index d6fa795..3233916 100644
--- a/src/com/android/settings/network/WepNetworkDialogActivity.kt
+++ b/src/com/android/settings/network/WepNetworkDialogActivity.kt
@@ -18,6 +18,7 @@
import android.app.settings.SettingsEnums
import android.net.wifi.WifiManager
+import android.view.WindowManager
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -30,13 +31,11 @@
import com.android.settingslib.spa.SpaDialogWindowTypeActivity
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogContent
+import com.android.settingslib.wifi.WifiUtils.Companion.DIALOG_WINDOW_TYPE
import com.android.settingslib.wifi.WifiUtils.Companion.SSID
class WepNetworkDialogActivity : SpaDialogWindowTypeActivity() {
- // TODO: Set different window type when called from Quick Settings.
- override val dialogWindowType = null
-
@Composable
override fun Content() {
val context = LocalContext.current
@@ -70,4 +69,11 @@
)
})
}
+
+ override fun getDialogWindowType(): Int {
+ return intent.getIntExtra(
+ DIALOG_WINDOW_TYPE,
+ WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+ )
+ }
}
diff --git a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
index cf1350c..1acfaf8 100644
--- a/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ConvertToEsimPreferenceController.java
@@ -125,16 +125,21 @@
}
EuiccManager euiccManager = (EuiccManager)
mContext.getSystemService(Context.EUICC_SERVICE);
- if (!euiccManager.isPsimConversionSupported(subInfo.getCarrierId())) {
- Log.i(TAG, "subId is not matched with pSIM conversion"
- + " supported carriers:" + subInfo.getCarrierId());
- return CONDITIONALLY_UNAVAILABLE;
- }
- if (findConversionSupportComponent()) {
- return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
- && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
- ? AVAILABLE
- : CONDITIONALLY_UNAVAILABLE;
+ try {
+ if (!euiccManager.isPsimConversionSupported(subInfo.getCarrierId())) {
+ Log.i(TAG, "subId is not matched with pSIM conversion"
+ + " supported carriers:" + subInfo.getCarrierId());
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ if (findConversionSupportComponent()) {
+ return mSubscriptionInfoEntity != null
+ && mSubscriptionInfoEntity.isActiveSubscriptionId
+ && !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
+ ? AVAILABLE
+ : CONDITIONALLY_UNAVAILABLE;
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Fail to check pSIM conversion supported carrier: " + e.getMessage());
}
return CONDITIONALLY_UNAVAILABLE;
}
diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt b/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
index c696ef1..7f42400 100644
--- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
+++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.kt
@@ -19,6 +19,12 @@
import android.content.Context
import android.telephony.AccessNetworkConstants.AccessNetworkType
import android.telephony.CellIdentity
+import android.telephony.CellIdentityCdma
+import android.telephony.CellIdentityGsm
+import android.telephony.CellIdentityLte
+import android.telephony.CellIdentityNr
+import android.telephony.CellIdentityTdscdma
+import android.telephony.CellIdentityWcdma
import android.telephony.CellInfo
import android.telephony.CellInfoCdma
import android.telephony.CellInfoGsm
@@ -120,17 +126,17 @@
getAccessNetworkTypeFromCellInfo(),
)
- private fun getIconIdForCell(): Int = when (cellInfo) {
- is CellInfoGsm -> R.drawable.signal_strength_g
- is CellInfoCdma -> R.drawable.signal_strength_1x
- is CellInfoWcdma, is CellInfoTdscdma -> R.drawable.signal_strength_3g
+ private fun getIconIdForCell(): Int = when (cellId) {
+ is CellIdentityGsm -> R.drawable.signal_strength_g
+ is CellIdentityCdma -> R.drawable.signal_strength_1x
+ is CellIdentityWcdma, is CellIdentityTdscdma -> R.drawable.signal_strength_3g
- is CellInfoLte -> {
+ is CellIdentityLte -> {
if (show4GForLTE) R.drawable.ic_signal_strength_4g
else R.drawable.signal_strength_lte
}
- is CellInfoNr -> R.drawable.signal_strength_5g
+ is CellIdentityNr -> R.drawable.signal_strength_5g
else -> MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON
}
diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
index 510bfd6..f29a5ef 100644
--- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java
+++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java
@@ -490,6 +490,7 @@
*/
private void forceUpdateConnectedPreferenceCategory(
NetworkSelectRepository.NetworkRegistrationAndForbiddenInfo info) {
+ mPreferenceCategory.removeAll();
for (NetworkRegistrationInfo regInfo : info.getNetworkList()) {
final CellIdentity cellIdentity = regInfo.getCellIdentity();
if (cellIdentity == null) {
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java
new file mode 100644
index 0000000..6dfe183
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+
+/** Callback to add or remove {@link Preference} in Sync Across Devices feature. */
+public interface SyncAcrossDevicesFeatureCallback {
+
+ /**
+ * Called when a sync across devices feature is added
+ *
+ * @param preference present the feature
+ */
+ void onFeatureAdded(@Nullable Preference preference);
+
+ /**
+ * Called when a sync across devices feature is removed
+ *
+ * @param preference present the feature
+ */
+ void onFeatureRemoved(@Nullable Preference preference);
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java
new file mode 100644
index 0000000..d575d59
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/** Feature provider for the Sync Across Devices. */
+public interface SyncAcrossDevicesFeatureProvider {
+
+ /** Returns the SyncAcrossDevicesFeatureUpdater of the Sync Across Devices feature */
+ @Nullable
+ SyncAcrossDevicesFeatureUpdater getSyncAcrossDevicesFeatureUpdater(
+ @NonNull Context context,
+ @NonNull SyncAcrossDevicesFeatureCallback featurePreferenceCallback);
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java
new file mode 100644
index 0000000..090bf63
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureProviderImpl.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/** Default implementation for {@link SyncAcrossDevicesFeatureProvider} */
+public class SyncAcrossDevicesFeatureProviderImpl implements SyncAcrossDevicesFeatureProvider {
+
+ @Override
+ @Nullable
+ public SyncAcrossDevicesFeatureUpdater getSyncAcrossDevicesFeatureUpdater(
+ @NonNull Context context,
+ @NonNull SyncAcrossDevicesFeatureCallback featurePreferenceCallback) {
+ return new SyncAcrossDevicesFeatureUpdater() {};
+ }
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java
new file mode 100644
index 0000000..f9407b0
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesFeatureUpdater.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import android.content.Context;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Updates the sync across devices feature state. It notifies the upper level whether to add/remove
+ * the preference through {@link SyncAcrossDevicesFeatureCallback}
+ */
+public interface SyncAcrossDevicesFeatureUpdater {
+
+ /** Forces to update the list of the Sync Across Devices feature. */
+ default void forceUpdate() {}
+
+ /** Sets the context to generate the {@link Preference}, so it could get the correct theme. */
+ default void setPreferenceContext(@Nullable Context preferenceContext) {}
+}
diff --git a/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java
new file mode 100644
index 0000000..ccea671
--- /dev/null
+++ b/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.overlay.FeatureFactory;
+
+public class SyncAcrossDevicesPreferenceController extends BasePreferenceController
+ implements PreferenceControllerMixin, SyncAcrossDevicesFeatureCallback {
+
+ private static final String TAG = "SyncXDevicesPrefCtr";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private PreferenceGroup mPreferenceGroup;
+ private SyncAcrossDevicesFeatureUpdater mSyncAcrossDevicesFeatureUpdater;
+
+ public SyncAcrossDevicesPreferenceController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ SyncAcrossDevicesFeatureProvider syncAcrossDevicesFeatureProvider =
+ FeatureFactory.getFeatureFactory().getSyncAcrossDevicesFeatureProvider();
+ mSyncAcrossDevicesFeatureUpdater =
+ syncAcrossDevicesFeatureProvider.getSyncAcrossDevicesFeatureUpdater(context, this);
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceGroup = screen.findPreference(getPreferenceKey());
+ mPreferenceGroup.setVisible(false);
+ if (isAvailable()) {
+ final Context context = screen.getContext();
+ mSyncAcrossDevicesFeatureUpdater.setPreferenceContext(context);
+ mSyncAcrossDevicesFeatureUpdater.forceUpdate();
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mSyncAcrossDevicesFeatureUpdater != null ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void onFeatureAdded(@Nullable Preference preference) {
+ if (preference == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onFeatureAdded receives null preference. Ignore.");
+ }
+ return;
+ }
+ mPreferenceGroup.addPreference(preference);
+ updatePreferenceVisibility();
+ }
+
+ @Override
+ public void onFeatureRemoved(@Nullable Preference preference) {
+ if (preference == null) {
+ if (DEBUG) {
+ Log.d(TAG, "onFeatureRemoved receives null preference. Ignore.");
+ }
+ return;
+ }
+ mPreferenceGroup.removePreference(preference);
+ updatePreferenceVisibility();
+ }
+
+ private void updatePreferenceVisibility() {
+ mPreferenceGroup.setVisible(mPreferenceGroup.getPreferenceCount() > 0);
+ }
+
+ @VisibleForTesting
+ public void setPreferenceGroup(@NonNull PreferenceGroup preferenceGroup) {
+ mPreferenceGroup = preferenceGroup;
+ }
+}
diff --git a/src/com/android/settings/overlay/FeatureFactory.kt b/src/com/android/settings/overlay/FeatureFactory.kt
index 53ad8ba..ef63f19 100644
--- a/src/com/android/settings/overlay/FeatureFactory.kt
+++ b/src/com/android/settings/overlay/FeatureFactory.kt
@@ -38,6 +38,7 @@
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
import com.android.settings.localepicker.LocaleFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
import com.android.settings.onboarding.OnboardingFeatureProvider
import com.android.settings.overlay.FeatureFactory.Companion.setFactory
import com.android.settings.panel.PanelFeatureProvider
@@ -188,6 +189,11 @@
*/
abstract val audioSharingFeatureProvider: AudioSharingFeatureProvider
+ /**
+ * Gets implementation for sync across devices related feature.
+ */
+ abstract val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider
+
companion object {
private var _factory: FeatureFactory? = null
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.kt b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
index 1770209..c74260c 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.kt
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.kt
@@ -55,6 +55,8 @@
import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
import com.android.settings.inputmethod.KeyboardSettingsFeatureProviderImpl
import com.android.settings.localepicker.LocaleFeatureProviderImpl
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProviderImpl
import com.android.settings.panel.PanelFeatureProviderImpl
import com.android.settings.search.SearchFeatureProvider
import com.android.settings.search.SearchFeatureProviderImpl
@@ -197,4 +199,8 @@
override val audioSharingFeatureProvider: AudioSharingFeatureProvider by lazy {
AudioSharingFeatureProviderImpl()
}
+
+ override val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider by lazy {
+ SyncAcrossDevicesFeatureProviderImpl()
+ }
}
diff --git a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
index f61f7bb..623816a 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceAuthenticationActivity.java
@@ -36,11 +36,11 @@
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentActivity;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -52,7 +52,7 @@
* user to set a device lock if not set with an alert dialog. This can be launched using the intent
* com.android.settings.action.OPEN_PRIVATE_SPACE_SETTINGS.
*/
-public class PrivateSpaceAuthenticationActivity extends FragmentActivity {
+public class PrivateSpaceAuthenticationActivity extends SettingsActivity {
private static final String TAG = "PrivateSpaceAuthCheck";
public static final String EXTRA_SHOW_PRIVATE_SPACE_UNLOCKED =
"extra_show_private_space_unlocked";
@@ -76,6 +76,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (isFinishing()) {
+ return;
+ }
if (Flags.allowPrivateProfile()
&& android.multiuser.Flags.enablePrivateSpaceFeatures()) {
diff --git a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
index 4e904d3..ed70030 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceDashboardFragment.java
@@ -58,7 +58,8 @@
public void onStart() {
super.onStart();
if (PrivateSpaceMaintainer.getInstance(getContext()).isPrivateSpaceLocked()) {
- finish();
+ // To make sure the task is removed if it is the last activity in that stack.
+ getActivity().finishAndRemoveTask();
}
}
diff --git a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
index b0b65f6..6f3c824 100644
--- a/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
+++ b/src/com/android/settings/sim/SelectSpecificDataSimDialogFragment.java
@@ -114,7 +114,7 @@
return getSubscriptionManager().getDefaultDataSubscriptionInfo();
}
- private void updateDialog(AlertDialog dialog) {
+ private void updateDialog(@Nullable AlertDialog dialog) {
Log.d(TAG, "Dialog updated, dismiss status: " + mWasDismissed);
if (mWasDismissed) {
return;
@@ -123,6 +123,7 @@
if (dialog == null) {
Log.d(TAG, "Dialog is null.");
dismiss();
+ return;
}
SubscriptionInfo currentDataSubInfo = getDefaultDataSubInfo();
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 2b40a91..03cd743 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -111,7 +111,7 @@
placeholder = {Text(text = originalSimCarrierName)},
modifier = Modifier.fillMaxWidth()
) {
- titleSimName = it
+ titleSimName = if (it.isEmpty()) originalSimCarrierName else it
}
},
)
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
index d5887e6..ff5c6c5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryInfoTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -634,6 +635,79 @@
expectedChargeLabel);
}
+ @Test
+ public void getBatteryInfo_customizedWLCLabel_updateRemainingLabelAndStatusLabel() {
+ prepareTestGetBatteryInfoEnvironment(
+ /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+ /* chargingStringV2Enabled= */ true);
+ Intent batteryIntent =
+ createIntentForGetBatteryInfoTest(
+ ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
+ var expectedLabel = "Full by 8:00 AM";
+ when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+ eq(mContext), anyLong(), anyLong()))
+ .thenReturn(expectedLabel);
+ var currentTimeMillis = Instant.parse("2021-02-09T13:00:00.00Z").toEpochMilli();
+ var info =
+ BatteryInfo.getBatteryInfo(
+ mContext,
+ batteryIntent,
+ mBatteryUsageStats,
+ MOCK_ESTIMATE,
+ /* elapsedRealtimeUs= */ UNUSED_TIME_MS,
+ /* shortString= */ false,
+ /* currentTimeMillis= */ currentTimeMillis);
+
+ assertThat(info.remainingLabel).isEqualTo(expectedLabel);
+ }
+
+ @Test
+ public void getBatteryInfo_noCustomizedWLCLabel_updateRemainingLabelAndStatusLabel() {
+ prepareTestGetBatteryInfoEnvironment(
+ /* remainingTimeMs= */ Duration.ofHours(1).toMillis(),
+ /* chargingStringV2Enabled= */ true);
+ Intent batteryIntent =
+ createIntentForGetBatteryInfoTest(
+ ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 45);
+ when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+ eq(mContext), anyLong(), anyLong()))
+ .thenReturn(null);
+ var expectedStatusLabel = "Charging";
+ var expectedRemainingLabel = "Fully charged by";
+ var expectedChargeLabel = "45% - " + expectedRemainingLabel;
+ var currentTimeMillis = Instant.parse("2024-04-01T15:00:00Z").toEpochMilli();
+
+ assertGetBatteryInfo(
+ batteryIntent,
+ currentTimeMillis,
+ expectedStatusLabel,
+ expectedRemainingLabel,
+ expectedChargeLabel);
+ }
+
+ @Test
+ public void getBatteryInfo_noCustomWirelessChargingLabelWithV1_updateRemainingAndStatusLabel() {
+ prepareTestGetBatteryInfoEnvironment(
+ /* remainingTimeMs= */ Duration.ofMinutes(130).toMillis(),
+ /* chargingStringV2Enabled= */ false);
+ Intent batteryIntent =
+ createIntentForGetBatteryInfoTest(
+ ChargingType.WIRELESS, ChargingSpeed.REGULAR, /* batteryLevel= */ 10);
+ when(mFeatureFactory.batterySettingsFeatureProvider.getWirelessChargingRemainingLabel(
+ eq(mContext), anyLong(), anyLong()))
+ .thenReturn(null);
+ var expectedStatusLabel = "Charging wirelessly";
+ var expectedRemainingLabel = "2 hr, 10 min left until full";
+ var expectedChargeLabel = "10% - " + expectedRemainingLabel;
+
+ assertGetBatteryInfo(
+ batteryIntent,
+ /* currentTimeMillis= */ UNUSED_TIME_MS,
+ expectedStatusLabel,
+ expectedRemainingLabel,
+ expectedChargeLabel);
+ }
+
private enum ChargingSpeed {
FAST,
REGULAR,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
index 158756a..c6241ab 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImplTest.java
@@ -70,7 +70,13 @@
assertThat(expectedResult).isTrue();
}
- @Test void getWirelessChargingLabel_returnNull() {
+ @Test
+ public void getWirelessChargingLabel_returnNull() {
assertThat(mImpl.getWirelessChargingLabel(mContext, new BatteryInfo())).isNull();
}
+
+ @Test
+ public void getWirelessChargingRemainingLabel_returnNull() {
+ assertThat(mImpl.getWirelessChargingRemainingLabel(mContext, 1000L, 1000L)).isNull();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index 3ed2dd1..b5cb446 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -565,7 +565,9 @@
public void convertToBatteryEventList_normalCase_returnsExpectedResult() {
final BatteryLevelData batteryLevelData =
new BatteryLevelData(
- Map.of(1691589600000L, 98, 1691596800000L, 90, 1691596812345L, 80));
+ // 2023-08-09 14:00:00 UTC
+ // 2023-08-09 16:00:00 UTC
+ Map.of(1691589600000L, 98, 1691596800000L, 90));
final List<BatteryEvent> batteryEventList =
ConvertUtils.convertToBatteryEventList(batteryLevelData);
@@ -580,6 +582,31 @@
}
@Test
+ public void convertToBatteryEventList_multipleDays_returnsExpectedResult() {
+ final BatteryLevelData batteryLevelData =
+ new BatteryLevelData(
+ // 2024-04-23 22:00:00 UTC
+ // 2024-04-24 00:00:00 UTC
+ // 2024-04-24 02:00:00 UTC
+ Map.of(1713909600000L, 98, 1713916800000L, 90, 1713924000000L, 83));
+
+ final List<BatteryEvent> batteryEventList =
+ ConvertUtils.convertToBatteryEventList(batteryLevelData);
+
+ assertThat(batteryEventList).hasSize(3);
+ assertThat(batteryEventList.get(0).getTimestamp()).isEqualTo(1713909600000L);
+ assertThat(batteryEventList.get(0).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+ assertThat(batteryEventList.get(0).getBatteryLevel()).isEqualTo(98);
+ assertThat(batteryEventList.get(1).getTimestamp()).isEqualTo(1713916800000L);
+ assertThat(batteryEventList.get(1).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+ assertThat(batteryEventList.get(1).getBatteryLevel()).isEqualTo(90);
+ assertThat(batteryEventList.get(2).getTimestamp()).isEqualTo(1713924000000L);
+ assertThat(batteryEventList.get(2).getType()).isEqualTo(BatteryEventType.EVEN_HOUR);
+ assertThat(batteryEventList.get(2).getBatteryLevel()).isEqualTo(83);
+ }
+
+
+ @Test
public void convertToBatteryUsageSlotList_normalCase_returnsExpectedResult() {
BatteryDiffData batteryDiffData1 =
new BatteryDiffData(
diff --git a/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java
new file mode 100644
index 0000000..7bfde1b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/syncacrossdevices/SyncAcrossDevicesPreferenceControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification.syncacrossdevices;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class SyncAcrossDevicesPreferenceControllerTest {
+
+ @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ @Mock private SyncAcrossDevicesFeatureUpdater mSyncAcrossDevicesFeatureUpdater;
+ @Mock private PreferenceManager mPreferenceManager;
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ private SyncAcrossDevicesPreferenceController mSyncAcrossDevicesPreferenceController;
+ private PreferenceGroup mPreferenceGroup;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ SyncAcrossDevicesFeatureProvider provider =
+ FakeFeatureFactory.setupForTest().getSyncAcrossDevicesFeatureProvider();
+ doReturn(mSyncAcrossDevicesFeatureUpdater)
+ .when(provider)
+ .getSyncAcrossDevicesFeatureUpdater(any(), any());
+
+ mSyncAcrossDevicesPreferenceController =
+ new SyncAcrossDevicesPreferenceController(mContext, PREFERENCE_KEY);
+
+ mPreferenceGroup = spy(new PreferenceCategory(mContext));
+ doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager();
+ mPreferenceGroup.setVisible(false);
+
+ mPreferenceGroup.setKey(mSyncAcrossDevicesPreferenceController.getPreferenceKey());
+ mSyncAcrossDevicesPreferenceController.setPreferenceGroup(mPreferenceGroup);
+ }
+
+ @Test
+ public void testGetAvailabilityStatus_noFeatureUpdater_returnUnSupported() {
+ SyncAcrossDevicesFeatureProvider provider =
+ FakeFeatureFactory.setupForTest().getSyncAcrossDevicesFeatureProvider();
+ doReturn(null).when(provider).getSyncAcrossDevicesFeatureUpdater(any(), any());
+
+ SyncAcrossDevicesPreferenceController syncAcrossDevicesPreferenceController =
+ new SyncAcrossDevicesPreferenceController(mContext, PREFERENCE_KEY);
+
+ assertThat(syncAcrossDevicesPreferenceController.getAvailabilityStatus())
+ .isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void testGetAvailabilityStatus_withFeatureUpdater_returnSupported() {
+ assertThat(mSyncAcrossDevicesPreferenceController.getAvailabilityStatus())
+ .isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void testUpdatePreferenceVisibility_addFeaturePreference_shouldShowPreference() {
+ Preference preference = new Preference(mContext);
+
+ mSyncAcrossDevicesPreferenceController.onFeatureAdded(preference);
+
+ assertThat(mPreferenceGroup.isVisible()).isTrue();
+ }
+
+ @Test
+ public void testUpdatePreferenceVisibility_removeFeaturePreference_shouldHidePreference() {
+ Preference preference = new Preference(mContext);
+
+ mSyncAcrossDevicesPreferenceController.onFeatureAdded(preference);
+ mSyncAcrossDevicesPreferenceController.onFeatureRemoved(preference);
+
+ assertThat(mPreferenceGroup.isVisible()).isFalse();
+ }
+
+ @Test
+ public void testDisplayPreference_availabilityStatusIsAvailable_shouldForceUpdated() {
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen preferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+ preferenceScreen.addPreference(mPreferenceGroup);
+
+ mSyncAcrossDevicesPreferenceController.displayPreference(preferenceScreen);
+
+ verify(mSyncAcrossDevicesFeatureUpdater, times(1)).setPreferenceContext(any());
+ verify(mSyncAcrossDevicesFeatureUpdater, times(1)).forceUpdate();
+ }
+}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
index 38683d0..71f8e58 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/FakeFeatureFactory.java
@@ -40,6 +40,7 @@
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider;
import com.android.settings.onboarding.OnboardingFeatureProvider;
import com.android.settings.overlay.DockUpdaterFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -103,6 +104,7 @@
public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
public DisplayFeatureProvider mDisplayFeatureProvider;
public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
+ public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -150,9 +152,10 @@
mStylusFeatureProvider = mock(StylusFeatureProvider.class);
mOnboardingFeatureProvider = mock(OnboardingFeatureProvider.class);
mFastPairFeatureProvider = mock(FastPairFeatureProvider.class);
- mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
+ mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
+ mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
}
@Override
@@ -340,5 +343,10 @@
public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
return mAudioSharingFeatureProvider;
}
+
+ @Override
+ public SyncAcrossDevicesFeatureProvider getSyncAcrossDevicesFeatureProvider() {
+ return mSyncAcrossDevicesFeatureProvider;
+ }
}
diff --git a/tests/spa_unit/AndroidManifest.xml b/tests/spa_unit/AndroidManifest.xml
index 118060a..1950f20 100644
--- a/tests/spa_unit/AndroidManifest.xml
+++ b/tests/spa_unit/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="com.android.settings.BATTERY_DATA" />
<application android:debuggable="true">
<provider android:name="com.android.settings.slices.SettingsSliceProvider"
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
index b7b96b0..a724a8c 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
@@ -20,13 +20,14 @@
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.hasTextExactly
+import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
@@ -36,18 +37,23 @@
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
+import com.android.settingslib.spa.testutils.delay
import com.android.settingslib.spaprivileged.model.app.userId
+import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
import org.mockito.MockitoSession
-import org.mockito.Spy
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
-import org.mockito.Mockito.`when` as whenever
+@OptIn(ExperimentalTestApi::class)
@RunWith(AndroidJUnit4::class)
class AppBatteryPreferenceTest {
@get:Rule
@@ -55,29 +61,28 @@
private lateinit var mockSession: MockitoSession
- @Spy
- private val context: Context = ApplicationProvider.getApplicationContext()
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
- @Spy
- private val resources = context.resources
+ private val resources = spy(context.resources) {
+ on { getBoolean(R.bool.config_show_app_info_settings_battery) } doReturn true
+ }
@Before
fun setUp() {
mockSession = ExtendedMockito.mockitoSession()
- .initMocks(this)
.mockStatic(BatteryChartPreferenceController::class.java)
.mockStatic(AdvancedPowerUsageDetail::class.java)
.strictness(Strictness.LENIENT)
.startMocking()
whenever(context.resources).thenReturn(resources)
- whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
- .thenReturn(true)
}
private fun mockBatteryDiffEntry(batteryDiffEntry: BatteryDiffEntry?) {
- whenever(BatteryChartPreferenceController.getAppBatteryUsageData(
- context, PACKAGE_NAME, APP.userId
- )).thenReturn(batteryDiffEntry)
+ whenever(
+ BatteryChartPreferenceController.getAppBatteryUsageData(
+ context, PACKAGE_NAME, APP.userId
+ )
+ ).thenReturn(batteryDiffEntry)
}
@After
@@ -87,8 +92,9 @@
@Test
fun whenConfigIsFalse_notDisplayed() {
- whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
- .thenReturn(false)
+ resources.stub {
+ on { getBoolean(R.bool.config_show_app_info_settings_battery) } doReturn false
+ }
setContent()
@@ -112,47 +118,47 @@
setContent()
- composeTestRule.onNode(
+ composeTestRule.waitUntilExactlyOneExists(
hasTextExactly(
context.getString(R.string.battery_details_title),
context.getString(R.string.no_battery_summary),
- ),
- ).assertIsDisplayed().assertIsEnabled()
+ ) and isEnabled(),
+ )
}
@Test
fun noConsumePower() {
- val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
- mConsumePower = 0.0
- }
+ val batteryDiffEntry = mock<BatteryDiffEntry>().apply { mConsumePower = 0.0 }
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
- composeTestRule.onNodeWithText(context.getString(R.string.no_battery_summary))
- .assertIsDisplayed()
+ composeTestRule.waitUntilExactlyOneExists(
+ hasText(context.getString(R.string.no_battery_summary))
+ )
}
@Test
fun hasConsumePower() {
- val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
- mConsumePower = 12.3
- }
- whenever(batteryDiffEntry.percentage).thenReturn(45.6)
+ val batteryDiffEntry = mock<BatteryDiffEntry> {
+ on { percentage } doReturn 45.6
+ }.apply { mConsumePower = 12.3 }
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
- composeTestRule.onNodeWithText("46% use since last full charge").assertIsDisplayed()
+ composeTestRule.waitUntilExactlyOneExists(hasText("46% use since last full charge"))
}
@Test
fun whenClick_openDetailsPage() {
- val batteryDiffEntry = mock(BatteryDiffEntry::class.java)
- whenever(batteryDiffEntry.percentage).thenReturn(10.0)
+ val batteryDiffEntry = mock<BatteryDiffEntry> {
+ on { percentage } doReturn 10.0
+ }.apply { mConsumePower = 12.3 }
mockBatteryDiffEntry(batteryDiffEntry)
setContent()
+ composeTestRule.waitUntilExactlyOneExists(hasText("10% use since last full charge"))
composeTestRule.onRoot().performClick()
ExtendedMockito.verify {
@@ -178,7 +184,7 @@
}
private companion object {
- const val PACKAGE_NAME = "packageName"
+ const val PACKAGE_NAME = "package.name"
const val UID = 123
val APP = ApplicationInfo().apply {
packageName = PACKAGE_NAME
diff --git a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
index 606db8e..e1dcda2 100644
--- a/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
+++ b/tests/spa_unit/src/com/android/settings/testutils/FakeFeatureFactory.kt
@@ -39,6 +39,7 @@
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider
import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider
import com.android.settings.localepicker.LocaleFeatureProvider
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider
import com.android.settings.overlay.DockUpdaterFeatureProvider
import com.android.settings.overlay.FeatureFactory
import com.android.settings.overlay.SurveyFeatureProvider
@@ -152,4 +153,6 @@
get() = TODO("Not yet implemented")
override val audioSharingFeatureProvider: AudioSharingFeatureProvider
get() = TODO("Not yet implemented")
+ override val syncAcrossDevicesFeatureProvider: SyncAcrossDevicesFeatureProvider
+ get() = TODO("Not yet implemented")
}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 29758de..cc129fd 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -40,6 +40,7 @@
import com.android.settings.homepage.contextualcards.ContextualCardFeatureProvider;
import com.android.settings.inputmethod.KeyboardSettingsFeatureProvider;
import com.android.settings.localepicker.LocaleFeatureProvider;
+import com.android.settings.notification.syncacrossdevices.SyncAcrossDevicesFeatureProvider;
import com.android.settings.onboarding.OnboardingFeatureProvider;
import com.android.settings.overlay.DockUpdaterFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -102,6 +103,7 @@
public PrivateSpaceLoginFeatureProvider mPrivateSpaceLoginFeatureProvider;
public DisplayFeatureProvider mDisplayFeatureProvider;
public AudioSharingFeatureProvider mAudioSharingFeatureProvider;
+ public SyncAcrossDevicesFeatureProvider mSyncAcrossDevicesFeatureProvider;
/** Call this in {@code @Before} method of the test class to use fake factory. */
public static FakeFeatureFactory setupForTest() {
@@ -154,6 +156,7 @@
mPrivateSpaceLoginFeatureProvider = mock(PrivateSpaceLoginFeatureProvider.class);
mDisplayFeatureProvider = mock(DisplayFeatureProvider.class);
mAudioSharingFeatureProvider = mock(AudioSharingFeatureProvider.class);
+ mSyncAcrossDevicesFeatureProvider = mock(SyncAcrossDevicesFeatureProvider.class);
}
@Override
@@ -341,4 +344,9 @@
public AudioSharingFeatureProvider getAudioSharingFeatureProvider() {
return mAudioSharingFeatureProvider;
}
+
+ @Override
+ public SyncAcrossDevicesFeatureProvider getSyncAcrossDevicesFeatureProvider() {
+ return mSyncAcrossDevicesFeatureProvider;
+ }
}