diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 321eb160c7a..0f39c6c2c47 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -148,6 +148,7 @@ + - - - - - - - - - + mPackages = new ArraySet<>(); - private Preference mTotalUsage; - private Preference mForegroundUsage; - private Preference mBackgroundUsage; private RestrictedSwitchPreference mRestrictBackground; private Drawable mIcon; @@ -139,10 +132,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC } } - mTotalUsage = findPreference(KEY_TOTAL_USAGE); - mForegroundUsage = findPreference(KEY_FOREGROUND_USAGE); - mBackgroundUsage = findPreference(KEY_BACKGROUND_USAGE); - final List uidList = getAppUidList(mAppItem.uids); initCycle(uidList); @@ -255,15 +244,17 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC @VisibleForTesting void initCycle(List uidList) { - var controller = use(AppDataUsageCycleController.class); + var cycleController = use(AppDataUsageCycleController.class); + var summaryController = use(AppDataUsageSummaryController.class); var repository = new AppDataUsageDetailsRepository(mContext, mTemplate, mCycles, uidList); - controller.init(repository, data -> { - bindData(data); + cycleController.init(repository, data -> { + mIsLoading = false; + summaryController.update(data); return Unit.INSTANCE; }); if (mCycles != null) { Log.d(TAG, "setInitialCycles: " + mCycles + " " + mSelectedCycle); - controller.setInitialCycles(mCycles, mSelectedCycle); + cycleController.setInitialCycles(mCycles, mSelectedCycle); } } @@ -314,16 +305,6 @@ public class AppDataUsage extends DataUsageBaseFragment implements OnPreferenceC } } - @VisibleForTesting - void bindData(@NonNull NetworkUsageDetailsData data) { - mIsLoading = false; - mTotalUsage.setSummary(DataUsageUtils.formatDataUsage(mContext, data.getTotalUsage())); - mForegroundUsage.setSummary( - DataUsageUtils.formatDataUsage(mContext, data.getForegroundUsage())); - mBackgroundUsage.setSummary( - DataUsageUtils.formatDataUsage(mContext, data.getBackgroundUsage())); - } - private boolean getAppRestrictBackground() { final int uid = mAppItem.key; final int uidPolicy = services.mPolicyManager.getUidPolicy(uid); diff --git a/src/com/android/settings/datausage/AppDataUsageSummaryController.kt b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt new file mode 100644 index 00000000000..a764c1d6cfe --- /dev/null +++ b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt @@ -0,0 +1,77 @@ +/* + * 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.datausage + +import android.content.Context +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.settings.R +import com.android.settings.datausage.lib.NetworkUsageDetailsData +import com.android.settings.spa.preference.ComposePreferenceController +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import com.android.settingslib.spaprivileged.framework.compose.placeholder +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.map + +class AppDataUsageSummaryController(context: Context, preferenceKey: String) : + ComposePreferenceController(context, preferenceKey) { + + private val dataFlow = MutableStateFlow(NetworkUsageDetailsData.AllZero) + + private val totalUsageFlow = dataFlow.map { + DataUsageUtils.formatDataUsage(mContext, it.totalUsage).toString() + } + + private val foregroundUsageFlow = dataFlow.map { + DataUsageUtils.formatDataUsage(mContext, it.foregroundUsage).toString() + } + + private val backgroundUsageFlow = dataFlow.map { + DataUsageUtils.formatDataUsage(mContext, it.backgroundUsage).toString() + } + + override fun getAvailabilityStatus() = AVAILABLE + + fun update(data: NetworkUsageDetailsData) { + dataFlow.value = data + } + + @Composable + override fun Content() { + Column { + val totalUsage by totalUsageFlow.collectAsStateWithLifecycle(placeholder()) + val foregroundUsage by foregroundUsageFlow.collectAsStateWithLifecycle(placeholder()) + val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(placeholder()) + Preference(object : PreferenceModel { + override val title = stringResource(R.string.total_size_label) + override val summary = { totalUsage } + }) + Preference(object : PreferenceModel { + override val title = stringResource(R.string.data_usage_label_foreground) + override val summary = { foregroundUsage } + }) + Preference(object : PreferenceModel { + override val title = stringResource(R.string.data_usage_label_background) + override val summary = { backgroundUsage } + }) + } + } +} diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java index 1ab3ed56e6b..682d1541f1f 100644 --- a/src/com/android/settings/network/SubscriptionUtil.java +++ b/src/com/android/settings/network/SubscriptionUtil.java @@ -76,8 +76,9 @@ public class SubscriptionUtil { 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 final String REGEX_DISPLAY_NAME_SUFFIX = "\\s[0-9]+"; + private static final Pattern REGEX_DISPLAY_NAME_SUFFIX_PATTERN = + Pattern.compile(REGEX_DISPLAY_NAME_SUFFIX); private static List sAvailableResultsForTesting; private static List sActiveResultsForTesting; @@ -461,12 +462,12 @@ public class SubscriptionUtil { @VisibleForTesting static boolean isValidCachedDisplayName(String cachedDisplayName, String originalName) { - if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName)) { + if (TextUtils.isEmpty(cachedDisplayName) || TextUtils.isEmpty(originalName) + || !cachedDisplayName.startsWith(originalName)) { return false; } - String regex = REGEX_DISPLAY_NAME_PREFIXES + originalName + REGEX_DISPLAY_NAME_SUFFIXES; - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(cachedDisplayName); + String displayNameSuffix = cachedDisplayName.substring(originalName.length()); + Matcher matcher = REGEX_DISPLAY_NAME_SUFFIX_PATTERN.matcher(displayNameSuffix); return matcher.matches(); } @@ -918,15 +919,22 @@ public class SubscriptionUtil { SubscriptionManager.class); List allSubInofs = subscriptionManager.getAllSubscriptionInfoList(); for (SubscriptionInfo subInfo : allSubInofs) { - if (subInfo != null) { - if (com.android.internal.telephony.flags.Flags.supportPsimToEsimConversion() - && subInfo.getSubscriptionId() == subId - && !subInfo.isEmbedded() - && subInfo.getTransferStatus() == TRANSFER_STATUS_CONVERTED) { - return true; - } + if (subInfo != null && subInfo.getSubscriptionId() == subId + && isConvertedPsimSubscription(subInfo)) { + return true; } } return false; } + + /** + * Checks if the subscription is converted pSIM. + */ + public static boolean isConvertedPsimSubscription(@NonNull SubscriptionInfo subInfo) { + Log.d(TAG, "isConvertedPsimSubscription: isEmbedded " + subInfo.isEmbedded()); + Log.d(TAG, "isConvertedPsimSubscription: getTransferStatus " + subInfo.getTransferStatus()); + return com.android.internal.telephony.flags.Flags.supportPsimToEsimConversion() + && !subInfo.isEmbedded() + && subInfo.getTransferStatus() == TRANSFER_STATUS_CONVERTED; + } } diff --git a/src/com/android/settings/notification/modes/ZenMode.java b/src/com/android/settings/notification/modes/ZenMode.java index b0036788ecd..2e615008ff5 100644 --- a/src/com/android/settings/notification/modes/ZenMode.java +++ b/src/com/android/settings/notification/modes/ZenMode.java @@ -75,20 +75,22 @@ class ZenMode { private final String mId; private final AutomaticZenRule mRule; + private boolean mIsActive; private final boolean mIsManualDnd; - ZenMode(String id, AutomaticZenRule rule) { - this(id, rule, false); + ZenMode(String id, AutomaticZenRule rule, boolean isActive) { + this(id, rule, isActive, false); } - private ZenMode(String id, AutomaticZenRule rule, boolean isManualDnd) { + private ZenMode(String id, AutomaticZenRule rule, boolean isActive, boolean isManualDnd) { mId = id; mRule = rule; + mIsActive = isActive; mIsManualDnd = isManualDnd; } - static ZenMode manualDndMode(AutomaticZenRule dndPolicyAsRule) { - return new ZenMode(MANUAL_DND_MODE_ID, dndPolicyAsRule, true); + static ZenMode manualDndMode(AutomaticZenRule dndPolicyAsRule, boolean isActive) { + return new ZenMode(MANUAL_DND_MODE_ID, dndPolicyAsRule, isActive, true); } @NonNull @@ -190,11 +192,16 @@ class ZenMode { return mIsManualDnd; } + public boolean isActive() { + return mIsActive; + } + @Override public boolean equals(@Nullable Object obj) { return obj instanceof ZenMode other && mId.equals(other.mId) - && mRule.equals(other.mRule); + && mRule.equals(other.mRule) + && mIsActive == other.mIsActive; } @Override @@ -204,6 +211,6 @@ class ZenMode { @Override public String toString() { - return mId + " -> " + mRule; + return mId + "(" + (mIsActive ? "active" : "inactive") + ") -> " + mRule; } } diff --git a/src/com/android/settings/notification/modes/ZenModesBackend.java b/src/com/android/settings/notification/modes/ZenModesBackend.java index d6a2ef53ad3..373d6540acc 100644 --- a/src/com/android/settings/notification/modes/ZenModesBackend.java +++ b/src/com/android/settings/notification/modes/ZenModesBackend.java @@ -67,11 +67,14 @@ class ZenModesBackend { List getModes() { ArrayList modes = new ArrayList<>(); - modes.add(getManualDndMode()); + ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig(); + modes.add(getManualDndMode(currentConfig)); Map zenRules = mNotificationManager.getAutomaticZenRules(); for (Map.Entry zenRuleEntry : zenRules.entrySet()) { - modes.add(new ZenMode(zenRuleEntry.getKey(), zenRuleEntry.getValue())); + String ruleId = zenRuleEntry.getKey(); + modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(), + isRuleActive(ruleId, currentConfig))); } // TODO: b/331429435 - Sort modes. @@ -80,15 +83,20 @@ class ZenModesBackend { @Nullable ZenMode getMode(String id) { + ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig(); if (ZenMode.MANUAL_DND_MODE_ID.equals(id)) { - return getManualDndMode(); + // Regardless of its contents, non-null manualRule means that manual rule is active. + return getManualDndMode(currentConfig); } else { AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id); - return rule != null ? new ZenMode(id, rule) : null; + if (rule == null) { + return null; + } + return new ZenMode(id, rule, isRuleActive(id, currentConfig)); } } - private ZenMode getManualDndMode() { + private ZenMode getManualDndMode(ZenModeConfig config) { // TODO: b/333530553 - Read ZenDeviceEffects of manual DND. // TODO: b/333682392 - Replace with final strings for name & trigger description AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder( @@ -103,7 +111,17 @@ class ZenModesBackend { .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .build(); - return ZenMode.manualDndMode(manualDndRule); + return ZenMode.manualDndMode(manualDndRule, + config != null && config.manualRule != null); // isActive + } + + private static boolean isRuleActive(String id, ZenModeConfig config) { + if (config == null) { + // shouldn't happen if the config is coming from NM, but be safe + return false; + } + ZenModeConfig.ZenRule configRule = config.automaticRules.get(id); + return configRule != null && configRule.isAutomaticActive(); } void updateMode(ZenMode mode) { diff --git a/src/com/android/settings/spa/network/SimsSection.kt b/src/com/android/settings/spa/network/SimsSection.kt index df92b37e0cb..5f6810d9f5d 100644 --- a/src/com/android/settings/spa/network/SimsSection.kt +++ b/src/com/android/settings/spa/network/SimsSection.kt @@ -28,6 +28,7 @@ import androidx.compose.material.icons.outlined.SimCard import androidx.compose.material.icons.outlined.SimCardDownload import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -44,6 +45,7 @@ import com.android.settingslib.spa.widget.ui.SettingsIcon import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference import com.android.settingslib.spaprivileged.template.preference.RestrictedTwoTargetSwitchPreference +import kotlinx.coroutines.flow.flow @Composable fun SimsSection(subscriptionInfoList: List) { @@ -63,11 +65,23 @@ private fun SimPreference(subInfo: SubscriptionInfo) { context.isSubscriptionEnabledFlow(subInfo.subscriptionId) }.collectAsStateWithLifecycle(initialValue = false) val phoneNumber = phoneNumber(subInfo) + val isConvertedPsim by remember(subInfo) { + flow { + emit(SubscriptionUtil.isConvertedPsimSubscription(subInfo)) + } + }.collectAsStateWithLifecycle(initialValue = false) RestrictedTwoTargetSwitchPreference( model = object : SwitchPreferenceModel { override val title = subInfo.displayName.toString() - override val summary = { phoneNumber.value ?: "" } + override val summary = { + if (isConvertedPsim) { + context.getString(R.string.sim_category_converted_sim) + } else { + phoneNumber.value ?: "" + } + } override val icon = @Composable { SimIcon(subInfo.isEmbedded) } + override val changeable = { !isConvertedPsim } override val checked = { checked.value } override val onCheckedChange = { newChecked: Boolean -> SubscriptionUtil.startToggleSubscriptionDialogActivity( @@ -78,6 +92,7 @@ private fun SimPreference(subInfo: SubscriptionInfo) { } }, restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)), + primaryEnabled = { !isConvertedPsim }, ) { MobileNetworkUtils.launchMobileNetworkSettings(context, subInfo) } diff --git a/src/com/android/settings/wifi/WifiAPITest.java b/src/com/android/settings/wifi/WifiAPITest.java index c8bcf7fe173..89538751685 100644 --- a/src/com/android/settings/wifi/WifiAPITest.java +++ b/src/com/android/settings/wifi/WifiAPITest.java @@ -18,7 +18,10 @@ package com.android.settings.wifi; import static android.content.Context.WIFI_SERVICE; +import static com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed; + import android.app.settings.SettingsEnums; +import android.content.Context; import android.content.DialogInterface; import android.net.wifi.WifiManager; import android.os.Bundle; @@ -38,7 +41,7 @@ import com.android.settings.SettingsPreferenceFragment; public class WifiAPITest extends SettingsPreferenceFragment implements Preference.OnPreferenceClickListener { - private static final String TAG = "WifiAPITest"; + private static final String TAG = "WifiAPITest+++"; private int netid; //============================ @@ -69,18 +72,38 @@ public class WifiAPITest extends SettingsPreferenceFragment implements @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + Context context = getContext(); + if (context == null) { + return; + } addPreferencesFromResource(R.xml.wifi_api_test); + boolean isChangeWifiStateAllowed = isChangeWifiStateAllowed(context); final PreferenceScreen preferenceScreen = getPreferenceScreen(); mWifiDisconnect = (Preference) preferenceScreen.findPreference(KEY_DISCONNECT); - mWifiDisconnect.setOnPreferenceClickListener(this); + if (mWifiDisconnect != null) { + mWifiDisconnect.setEnabled(isChangeWifiStateAllowed); + if (isChangeWifiStateAllowed) { + mWifiDisconnect.setOnPreferenceClickListener(this); + } + } mWifiDisableNetwork = (Preference) preferenceScreen.findPreference(KEY_DISABLE_NETWORK); - mWifiDisableNetwork.setOnPreferenceClickListener(this); + if (mWifiDisableNetwork != null) { + mWifiDisableNetwork.setEnabled(isChangeWifiStateAllowed); + if (isChangeWifiStateAllowed) { + mWifiDisableNetwork.setOnPreferenceClickListener(this); + } + } mWifiEnableNetwork = (Preference) preferenceScreen.findPreference(KEY_ENABLE_NETWORK); - mWifiEnableNetwork.setOnPreferenceClickListener(this); + if (mWifiEnableNetwork != null) { + mWifiEnableNetwork.setEnabled(isChangeWifiStateAllowed); + if (isChangeWifiStateAllowed) { + mWifiEnableNetwork.setOnPreferenceClickListener(this); + } + } } diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java index 1d841fa2505..4b8c9de5349 100644 --- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java +++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java @@ -42,16 +42,13 @@ import android.os.Bundle; import android.os.Process; import android.telephony.SubscriptionManager; import android.util.ArraySet; -import android.util.Range; import androidx.fragment.app.FragmentActivity; -import androidx.preference.Preference; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import androidx.recyclerview.widget.RecyclerView; import com.android.settings.applications.AppInfoBase; -import com.android.settings.datausage.lib.NetworkUsageDetailsData; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; @@ -252,34 +249,6 @@ public class AppDataUsageTest { verify(unrestrictedDataPref).setDisabledByAdmin(any(EnforcedAdmin.class)); } - @Test - public void bindData_shouldUpdateUsageSummary() { - mFragment = spy(new TestFragment()); - final Context context = RuntimeEnvironment.application; - ReflectionHelpers.setField(mFragment, "mContext", context); - final long backgroundBytes = 1234L; - final long foregroundBytes = 5678L; - final NetworkUsageDetailsData appUsage = new NetworkUsageDetailsData( - new Range<>(1L, 2L), - backgroundBytes + foregroundBytes, - foregroundBytes, - backgroundBytes - ); - final Preference backgroundPref = mock(Preference.class); - ReflectionHelpers.setField(mFragment, "mBackgroundUsage", backgroundPref); - final Preference foregroundPref = mock(Preference.class); - ReflectionHelpers.setField(mFragment, "mForegroundUsage", foregroundPref); - final Preference totalPref = mock(Preference.class); - ReflectionHelpers.setField(mFragment, "mTotalUsage", totalPref); - - mFragment.bindData(appUsage); - - verify(totalPref).setSummary( - DataUsageUtils.formatDataUsage(context, backgroundBytes + foregroundBytes)); - verify(backgroundPref).setSummary(DataUsageUtils.formatDataUsage(context, backgroundBytes)); - verify(foregroundPref).setSummary(DataUsageUtils.formatDataUsage(context, foregroundBytes)); - } - @Test @Config(shadows = {ShadowDataUsageUtils.class, ShadowSubscriptionManager.class, ShadowFragment.class}) diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java index 52680ca639c..761f10758c8 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java @@ -45,12 +45,19 @@ public class ZenModeTest { @Test public void testBasicMethods() { - ZenMode zenMode = new ZenMode("id", ZEN_RULE); + ZenMode zenMode = new ZenMode("id", ZEN_RULE, true); assertThat(zenMode.getId()).isEqualTo("id"); assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE); assertThat(zenMode.isManualDnd()).isFalse(); assertThat(zenMode.canBeDeleted()).isTrue(); + assertThat(zenMode.isActive()).isTrue(); + + ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false); + assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID); + assertThat(manualMode.isManualDnd()).isTrue(); + assertThat(manualMode.canBeDeleted()).isFalse(); + assertThat(manualMode.isActive()).isFalse(); } @Test @@ -58,7 +65,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(ZEN_POLICY) - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); } @@ -68,7 +75,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_ALL) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder().allowAllSounds().showAllVisualEffects().build()); @@ -79,7 +86,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder() @@ -95,7 +102,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_NONE) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder() diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java index 06ce80b24d2..a0f6f022fa3 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java @@ -56,6 +56,7 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class ZenModesBackendTest { + private static final String ZEN_RULE_ID = "rule"; private static final AutomaticZenRule ZEN_RULE = new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) .setType(AutomaticZenRule.TYPE_DRIVING) @@ -63,13 +64,11 @@ public class ZenModesBackendTest { .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) .build(); - private static final ZenMode MANUAL_DND_MODE = ZenMode.manualDndMode( + private static final AutomaticZenRule MANUAL_DND_RULE = new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build()); - - private static final ZenMode ZEN_RULE_MODE = new ZenMode("rule", ZEN_RULE); + .build(); @Mock private NotificationManager mNm; @@ -77,6 +76,35 @@ public class ZenModesBackendTest { private Context mContext; private ZenModesBackend mBackend; + // Helper methods to add active/inactive rule state to a config. Returns a copy. + private ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) { + ZenModeConfig out = base.copy(); + if (!active) { + out.manualRule = null; + } else { + out.manualRule = new ZenModeConfig.ZenRule(); + } + return out; + } + + private ZenModeConfig configWithRule(ZenModeConfig base, String ruleId, AutomaticZenRule rule, + boolean active) { + ZenModeConfig out = base.copy(); + + // Note that there are many other fields of zenRule, but here we only set the ones + // relevant to determining whether or not it is active. + ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule(); + zenRule.pkg = "package"; + zenRule.enabled = active; + zenRule.snoozing = false; + zenRule.condition = new Condition(rule.getConditionId(), "", + active ? Condition.STATE_TRUE : Condition.STATE_FALSE, + Condition.SOURCE_USER_ACTION); + out.automaticRules.put(ruleId, zenRule); + + return out; + } + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -85,6 +113,10 @@ public class ZenModesBackendTest { mContext = RuntimeEnvironment.application; mBackend = new ZenModesBackend(mContext); + + // Default catch-all case with no data. This isn't realistic, but tests below that rely + // on the config to get data on rules active will create those individually. + when(mNm.getZenModeConfig()).thenReturn(new ZenModeConfig()); } @Test @@ -102,6 +134,7 @@ public class ZenModesBackendTest { List modes = mBackend.getModes(); + // all modes exist, but none of them are currently active assertThat(modes).containsExactly( ZenMode.manualDndMode( new AutomaticZenRule.Builder( @@ -112,9 +145,10 @@ public class ZenModesBackendTest { .setTriggerDescription( mContext.getString(R.string.zen_mode_settings_summary)) .setManualInvocationAllowed(true) - .build()), - new ZenMode("rule1", ZEN_RULE), - new ZenMode("rule2", rule2)) + .build(), + false), + new ZenMode("rule1", ZEN_RULE, false), + new ZenMode("rule2", rule2, false)) .inOrder(); } @@ -136,26 +170,70 @@ public class ZenModesBackendTest { .setTriggerDescription( mContext.getString(R.string.zen_mode_settings_summary)) .setManualInvocationAllowed(true) - .build())); + .build(), false)); } @Test public void getMode_zenRule_returnsMode() { - when(mNm.getAutomaticZenRule(eq("rule"))).thenReturn(ZEN_RULE); + when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE); - ZenMode mode = mBackend.getMode("rule"); + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); - assertThat(mode).isEqualTo(new ZenMode("rule", ZEN_RULE)); + assertThat(mode).isEqualTo(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); } @Test public void getMode_missingRule_returnsNull() { when(mNm.getAutomaticZenRule(any())).thenReturn(null); - ZenMode mode = mBackend.getMode("rule"); + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); assertThat(mode).isNull(); - verify(mNm).getAutomaticZenRule(eq("rule")); + verify(mNm).getAutomaticZenRule(eq(ZEN_RULE_ID)); + } + + @Test + public void getMode_manualDnd_returnsCorrectActiveState() { + // Set up a base config with an active rule to make sure we're looking at the correct info + ZenModeConfig configWithActiveRule = configWithRule(new ZenModeConfig(), ZEN_RULE_ID, + ZEN_RULE, true); + when(mNm.getZenModeConfig()).thenReturn(configWithActiveRule); + + // Equivalent to disallowAllSounds() + Policy dndPolicy = new Policy(0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(dndPolicy); + + ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID); + + // By default, manual rule is inactive + assertThat(mode.isActive()).isFalse(); + + // Now the returned config will represent the manual rule being active + when(mNm.getZenModeConfig()).thenReturn(configWithManualRule(configWithActiveRule, true)); + ZenMode activeMode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID); + assertThat(activeMode.isActive()).isTrue(); + } + + @Test + public void getMode_zenRule_returnsCorrectActiveState() { + // Set up a base config that has an active manual rule and "rule2", to make sure we're + // looking at the correct rule's info. + ZenModeConfig configWithActiveRules = configWithRule( + configWithManualRule(new ZenModeConfig(), true), // active manual rule + "rule2", ZEN_RULE, true); // active rule 2 + + when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE); + when(mNm.getZenModeConfig()).thenReturn( + configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, false)); + + // Round 1: the current config should indicate that the rule is not active + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); + assertThat(mode.isActive()).isFalse(); + + when(mNm.getZenModeConfig()).thenReturn( + configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, true)); + ZenMode activeMode = mBackend.getMode(ZEN_RULE_ID); + assertThat(activeMode.isActive()).isTrue(); } @Test @@ -163,7 +241,7 @@ public class ZenModesBackendTest { ZenMode manualDnd = ZenMode.manualDndMode( new AutomaticZenRule.Builder("DND", Uri.EMPTY) .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build()); + .build(), false); mBackend.updateMode(manualDnd); @@ -173,7 +251,7 @@ public class ZenModesBackendTest { @Test public void updateMode_zenRule_updatesRule() { - ZenMode ruleMode = new ZenMode("rule", ZEN_RULE); + ZenMode ruleMode = new ZenMode("rule", ZEN_RULE, false); mBackend.updateMode(ruleMode); @@ -182,7 +260,7 @@ public class ZenModesBackendTest { @Test public void activateMode_manualDnd_setsZenModeImportant() { - mBackend.activateMode(MANUAL_DND_MODE, null); + mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), null); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), any(), eq(true)); @@ -190,7 +268,8 @@ public class ZenModesBackendTest { @Test public void activateMode_manualDndWithDuration_setsZenModeImportantWithCondition() { - mBackend.activateMode(MANUAL_DND_MODE, Duration.ofMinutes(30)); + mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), + Duration.ofMinutes(30)); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(ZenModeConfig.toTimeCondition(mContext, 30, 0, true).id), @@ -200,9 +279,9 @@ public class ZenModesBackendTest { @Test public void activateMode_zenRule_setsRuleStateActive() { - mBackend.activateMode(ZEN_RULE_MODE, null); + mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), null); - verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_MODE.getId()), + verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID), eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION))); } @@ -210,34 +289,36 @@ public class ZenModesBackendTest { @Test public void activateMode_zenRuleWithDuration_fails() { assertThrows(IllegalArgumentException.class, - () -> mBackend.activateMode(ZEN_RULE_MODE, Duration.ofMinutes(30))); + () -> mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), + Duration.ofMinutes(30))); } @Test public void deactivateMode_manualDnd_setsZenModeOff() { - mBackend.deactivateMode(MANUAL_DND_MODE); + mBackend.deactivateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, true)); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_OFF), eq(null), any(), eq(true)); } @Test public void deactivateMode_zenRule_setsRuleStateInactive() { - mBackend.deactivateMode(ZEN_RULE_MODE); + mBackend.deactivateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); - verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_MODE.getId()), + verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID), eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_FALSE, Condition.SOURCE_USER_ACTION))); } @Test public void removeMode_zenRule_deletesRule() { - mBackend.removeMode(ZEN_RULE_MODE); + mBackend.removeMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); - verify(mNm).removeAutomaticZenRule(ZEN_RULE_MODE.getId(), true); + verify(mNm).removeAutomaticZenRule(ZEN_RULE_ID, true); } @Test public void removeMode_manualDnd_fails() { - assertThrows(IllegalArgumentException.class, () -> mBackend.removeMode(MANUAL_DND_MODE)); + assertThrows(IllegalArgumentException.class, + () -> mBackend.removeMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false))); } } diff --git a/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt new file mode 100644 index 00000000000..584295649aa --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageSummaryControllerTest.kt @@ -0,0 +1,65 @@ +/* + * 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.datausage + +import android.content.Context +import android.util.Range +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.datausage.lib.NetworkUsageDetailsData +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class AppDataUsageSummaryControllerTest { + + @get:Rule + val composeTestRule = createComposeRule() + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val controller = AppDataUsageSummaryController(context, TEST_KEY) + + @Test + fun summary() { + val appUsage = NetworkUsageDetailsData( + range = Range(1L, 2L), + totalUsage = BACKGROUND_BYTES + FOREGROUND_BYTES, + foregroundUsage = FOREGROUND_BYTES, + backgroundUsage = BACKGROUND_BYTES, + ) + + controller.update(appUsage) + composeTestRule.setContent { + controller.Content() + } + + composeTestRule.onNodeWithText("6.75 kB").assertIsDisplayed() + composeTestRule.onNodeWithText("5.54 kB").assertIsDisplayed() + composeTestRule.onNodeWithText("1.21 kB").assertIsDisplayed() + } + + private companion object { + const val TEST_KEY = "test_key" + const val BACKGROUND_BYTES = 1234L + const val FOREGROUND_BYTES = 5678L + } +} diff --git a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java index 6df281a5db2..46c281f1267 100644 --- a/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java +++ b/tests/unit/src/com/android/settings/network/SubscriptionUtilTest.java @@ -635,6 +635,14 @@ public class SubscriptionUtilTest { assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isFalse(); } + @Test + public void isValidCachedDisplayName_withBrackets_noCrash() { + String originalName = "originalName("; + String cacheString = "originalName( 1234"; + + assertThat(SubscriptionUtil.isValidCachedDisplayName(cacheString, originalName)).isTrue(); + } + @Test public void isConnectedToWifi_hasWiFi_returnTrue() { addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI); diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java index 432a5d67e98..35057a6ba6e 100644 --- a/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java +++ b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java @@ -40,6 +40,7 @@ public class SafetyCenterManagerWrapperTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + SafetyCenterManagerWrapper.sInstance = null; } @Test