diff --git a/res/layout-land/request_manage_credentials.xml b/res/layout-land/request_manage_credentials.xml index fbe0bd0fbad..f6bfa0eb568 100644 --- a/res/layout-land/request_manage_credentials.xml +++ b/res/layout-land/request_manage_credentials.xml @@ -16,6 +16,7 @@ Allow visual signals - Notifications that can reach you + Stay focused Additional actions @@ -8060,7 +8060,10 @@ other {{effect_1}, {effect_2}, and # more} } - + + Filter interruptions + + No interruptions are filtered Display options for filtered diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml index cf090be46d3..0df9f80f851 100644 --- a/res/xml/modes_rule_settings.xml +++ b/res/xml/modes_rule_settings.xml @@ -35,13 +35,18 @@ + + + + android:title="@string/zen_category_apps"/> SA_PROFILES = + ImmutableSet.of( + BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID); + public BluetoothDetailsSpatialAudioController( Context context, PreferenceFragmentCompat fragment, CachedBluetoothDevice device, Lifecycle lifecycle) { super(context, fragment, device, lifecycle); + mAudioManager = context.getSystemService(AudioManager.class); mSpatializer = FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider() .getSpatializer(context); } @@ -142,8 +155,12 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont @Override protected void refresh() { - if (mAudioDevice == null) { - getAvailableDevice(); + if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) { + getAvailableDeviceByProfileState(); + } else { + if (mAudioDevice == null) { + getAvailableDevice(); + } } ThreadUtils.postOnBackgroundThread( () -> { @@ -274,6 +291,77 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont + ", type : " + (mAudioDevice == null ? "no type" : mAudioDevice.getType())); } + private void getAvailableDeviceByProfileState() { + Log.i( + TAG, + "getAvailableDevice() mCachedDevice: " + + mCachedDevice + + " profiles: " + + mCachedDevice.getProfiles()); + + AudioDeviceAttributes saDevice = null; + for (LocalBluetoothProfile profile : mCachedDevice.getProfiles()) { + // pick first enabled profile that is compatible with spatial audio + if (SA_PROFILES.contains(profile.getProfileId()) + && profile.isEnabled(mCachedDevice.getDevice())) { + switch (profile.getProfileId()) { + case BluetoothProfile.A2DP: + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, + mCachedDevice.getAddress()); + break; + case BluetoothProfile.LE_AUDIO: + if (mAudioManager.getBluetoothAudioDeviceCategory( + mCachedDevice.getAddress()) + == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) { + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_SPEAKER, + mCachedDevice.getAddress()); + } else { + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_BLE_HEADSET, + mCachedDevice.getAddress()); + } + + break; + case BluetoothProfile.HEARING_AID: + saDevice = + new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_HEARING_AID, + mCachedDevice.getAddress()); + break; + default: + Log.i( + TAG, + "unrecognized profile for spatial audio: " + + profile.getProfileId()); + break; + } + break; + } + } + mAudioDevice = null; + if (saDevice != null && mSpatializer.isAvailableForDevice(saDevice)) { + mAudioDevice = saDevice; + } + + Log.d( + TAG, + "getAvailableDevice() device : " + + mCachedDevice.getDevice().getAnonymizedAddress() + + ", is available : " + + (mAudioDevice != null) + + ", type : " + + (mAudioDevice == null ? "no type" : mAudioDevice.getType())); + } + @VisibleForTesting void setAvailableDevice(AudioDeviceAttributes audioDevice) { mAudioDevice = audioDevice; diff --git a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt index d324c75d6f9..59c853d3bc2 100644 --- a/src/com/android/settings/datausage/lib/BillingCycleRepository.kt +++ b/src/com/android/settings/datausage/lib/BillingCycleRepository.kt @@ -21,7 +21,7 @@ import android.os.INetworkManagementService import android.os.ServiceManager import android.util.Log import androidx.annotation.OpenForTesting -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settingslib.spaprivileged.framework.common.userManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -36,13 +36,13 @@ open class BillingCycleRepository @JvmOverloads constructor( INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ), - private val telephonyRepository: TelephonyRepository = TelephonyRepository(context), + private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context), ) { private val userManager = context.userManager fun isModifiableFlow(subId: Int): Flow = - telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled -> - isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser + mobileDataRepository.isMobileDataEnabledFlow(subId).map { mobileDataEnabled -> + mobileDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser }.conflate().flowOn(Dispatchers.Default) open fun isBandwidthControlEnabled(): Boolean = try { diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java index 0132273a57e..e7b9a42cac2 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java @@ -23,11 +23,13 @@ import android.os.Bundle; import android.util.ArrayMap; import android.util.SparseIntArray; +import com.android.settings.fuelgauge.batteryusage.BatteryDiffData; import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settingslib.fuelgauge.Estimate; import java.util.List; +import java.util.Map; import java.util.Set; /** Feature Provider used in power usage */ @@ -157,4 +159,8 @@ public interface PowerUsageFeatureProvider { /** Whether the device is under the battery defender mode */ boolean isBatteryDefend(BatteryInfo info); + + /** Collect and process battery reattribute data if needed. */ + boolean processBatteryReattributeData( + Context context, Map batteryDiffDataMap); } diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java index 1675ce6fb41..267c0a3fc60 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java +++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java @@ -28,12 +28,14 @@ import android.util.ArraySet; import android.util.SparseIntArray; import com.android.internal.util.ArrayUtils; +import com.android.settings.fuelgauge.batteryusage.BatteryDiffData; import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settingslib.fuelgauge.Estimate; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; /** Implementation of {@code PowerUsageFeatureProvider} */ @@ -245,4 +247,10 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider public boolean isBatteryDefend(BatteryInfo info) { return info.isBatteryDefender && !isExtraDefend(); } + + @Override + public boolean processBatteryReattributeData( + Context context, Map batteryDiffDataMap) { + return false; + } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java index 7ea7203a332..7b5cd9f9e00 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffData.java @@ -77,11 +77,13 @@ public class BatteryDiffData { processAndSortEntries(mSystemEntries); } - long getStartTimestamp() { + /** Gets the start timestamp. */ + public long getStartTimestamp() { return mStartTimestamp; } - long getEndTimestamp() { + /** Gets the end timestamp. */ + public long getEndTimestamp() { return mEndTimestamp; } diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java index 08369127b6d..7ef4615e744 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java @@ -128,6 +128,9 @@ public final class BatteryUsageDataLoader { final PowerUsageFeatureProvider featureProvider = FeatureFactory.getFeatureFactory() .getPowerUsageFeatureProvider(); + // Collect and process battery reattribute data. + featureProvider.processBatteryReattributeData( + context, batteryDiffDataMap); DatabaseUtils.sendBatteryUsageSlotData( context, ConvertUtils.convertToBatteryUsageSlotList( diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java index baae10959c3..7e759ee3f26 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java +++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java @@ -55,12 +55,12 @@ public final class BugReportContentProvider extends ContentProvider { } writer.println("dump BatteryUsage and AppUsage states:"); LogUtils.dumpAppOptimizationModeEventHist(context, writer); + LogUtils.dumpBatteryReattributeDatabaseHist(context, writer); LogUtils.dumpBatteryUsageDatabaseHist(context, writer); LogUtils.dumpAppUsageDatabaseHist(context, writer); LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer); LogUtils.dumpBatteryEventDatabaseHist(context, writer); LogUtils.dumpBatteryStateDatabaseHist(context, writer); - LogUtils.dumpBatteryReattributeDatabaseHist(context, writer); } @Override diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java index 53678496d16..0ee22602ecd 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryReattributeEntity.java @@ -58,12 +58,16 @@ public class BatteryReattributeEntity { @NonNull @Override public String toString() { + final BatteryReattribute batteryReattribute = + ConvertUtils.decodeBatteryReattribute(reattributeData); final StringBuilder builder = new StringBuilder() .append("\nBatteryReattributeEntity{") .append("\n\t" + utcToLocalTimeForLogging(timestampStart)) .append("\n\t" + utcToLocalTimeForLogging(timestampEnd)) - .append("\n\t" + ConvertUtils.decodeBatteryReattribute(reattributeData)) - .append("\n}"); - return builder.toString(); + .append("\n\t" + batteryReattribute); + if (batteryReattribute != null) { + builder.append("\n\t" + batteryReattribute.getReattributeDataMap()); + } + return builder.append("\n}").toString(); } } diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java index 9a4f164c133..8e3d6e37e82 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java +++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java @@ -33,7 +33,7 @@ import androidx.room.RoomDatabase; BatteryUsageSlotEntity.class, BatteryReattributeEntity.class }, - version = 1) + version = 2) public abstract class BatteryStateDatabase extends RoomDatabase { private static final String TAG = "BatteryStateDatabase"; private static final String DB_FILE_NAME = "battery-usage-db-v10"; diff --git a/src/com/android/settings/network/MobileDataEnabledFlow.kt b/src/com/android/settings/network/MobileDataEnabledFlow.kt deleted file mode 100644 index 1f995a94b0a..00000000000 --- a/src/com/android/settings/network/MobileDataEnabledFlow.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.network - -import android.content.Context -import android.provider.Settings -import android.telephony.SubscriptionManager -import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.merge - -/** - * Flow for mobile data enabled changed event. - * - * Note: This flow can only notify enabled status changes, cannot provide the latest status. - */ -fun Context.mobileDataEnabledFlow(subId: Int, sendInitialValue: Boolean = true): Flow { - val flow = settingsGlobalChangeFlow(Settings.Global.MOBILE_DATA, sendInitialValue) - return when (subId) { - SubscriptionManager.INVALID_SUBSCRIPTION_ID -> flow - else -> { - val subIdFlow = settingsGlobalChangeFlow( - name = Settings.Global.MOBILE_DATA + subId, - sendInitialValue = false, - ) - merge(flow, subIdFlow) - } - } -} diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt index f6c6065e40d..ea0b5acc1f2 100644 --- a/src/com/android/settings/network/SimOnboardingService.kt +++ b/src/com/android/settings/network/SimOnboardingService.kt @@ -27,9 +27,8 @@ import android.telephony.UiccCardInfo import android.telephony.UiccSlotInfo import android.util.Log import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.sim.SimActivationNotifier -import com.android.settings.spa.network.setAutomaticData import com.android.settings.spa.network.setDefaultData import com.android.settings.spa.network.setDefaultSms import com.android.settings.spa.network.setDefaultVoice @@ -366,7 +365,7 @@ class SimOnboardingService { wifiPickerTrackerHelper, targetPrimarySimMobileData ) - TelephonyRepository(context).setAutomaticData( + MobileDataRepository(context).setAutoDataSwitch( targetNonDds, targetPrimarySimAutoDataSwitch.value ) diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt index e14d5f82b4b..445597f22d6 100644 --- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt +++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt @@ -22,7 +22,6 @@ import android.telephony.TelephonyManager import android.telephony.data.ApnSetting import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceScreen -import com.android.settings.network.mobileDataEnabledFlow import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.flow.combine @@ -71,7 +70,7 @@ class MmsMessagePreferenceController @JvmOverloads constructor( override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { combine( - mContext.mobileDataEnabledFlow(mSubId), + MobileDataRepository(mContext).mobileDataEnabledChangedFlow(mSubId), mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { preferenceScreen?.let { super.displayPreference(it) } diff --git a/src/com/android/settings/network/telephony/MobileDataRepository.kt b/src/com/android/settings/network/telephony/MobileDataRepository.kt new file mode 100644 index 00000000000..46f814b60da --- /dev/null +++ b/src/com/android/settings/network/telephony/MobileDataRepository.kt @@ -0,0 +1,124 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.provider.Settings +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.telephony.TelephonyManager.MobileDataPolicy +import android.util.Log +import com.android.settings.wifi.WifiPickerTrackerHelper +import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalChangeFlow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach + +class MobileDataRepository( + private val context: Context, + private val subscriptionsChangedFlow: Flow = context.subscriptionsChangedFlow(), +) { + fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + val telephonyManager = context.telephonyManager(subId) + return subscriptionsChangedFlow + .map { telephonyManager.isMobileDataPolicyEnabled(policy) } + .distinctUntilChanged() + .conflate() + .onEach { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") } + .flowOn(Dispatchers.Default) + } + + fun setMobileDataPolicyEnabled(subId: Int, @MobileDataPolicy policy: Int, enabled: Boolean) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return + Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled") + context.telephonyManager(subId).setMobileDataPolicyEnabled(policy, enabled) + } + + fun setAutoDataSwitch(subId: Int, newEnabled: Boolean) { + setMobileDataPolicyEnabled( + subId = subId, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + enabled = newEnabled, + ) + } + + /** + * Flow for mobile data enabled changed event. + * + * Note: This flow can only notify enabled status changes, cannot provide the latest status. + */ + fun mobileDataEnabledChangedFlow(subId: Int, sendInitialValue: Boolean = true): Flow = + mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue) + + private fun mobileSettingsGlobalChangedFlow( + name: String, + subId: Int, + sendInitialValue: Boolean = true, + ): Flow { + val flow = context.settingsGlobalChangeFlow(name, sendInitialValue) + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flow + val subIdFlow = + context.settingsGlobalChangeFlow(name = name + subId, sendInitialValue = false) + return merge(flow, subIdFlow) + } + + fun isMobileDataEnabledFlow(subId: Int): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + val telephonyManager = context.telephonyManager(subId) + return mobileDataEnabledChangedFlow(subId) + .map { + telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) + } + .catch { e -> + Log.w(TAG, "[$subId] isMobileDataEnabledFlow: exception", e) + emit(false) + } + .distinctUntilChanged() + .conflate() + .onEach { Log.d(TAG, "[$subId] isMobileDataEnabledFlow: $it") } + .flowOn(Dispatchers.Default) + } + + fun setMobileDataEnabled( + subId: Int, + enabled: Boolean, + wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null, + ) { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return + + Log.d(TAG, "setMobileDataEnabled: $enabled") + MobileNetworkUtils.setMobileDataEnabled( + context, subId, enabled, /* disableOtherSubscriptions= */ true) + + if (wifiPickerTrackerHelper != null && + !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)) { + wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled) + } + } + + private companion object { + private const val TAG = "MobileDataRepository" + } +} diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt index 7c334ee44db..3317c71b333 100644 --- a/src/com/android/settings/network/telephony/TelephonyRepository.kt +++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt @@ -17,98 +17,16 @@ package com.android.settings.network.telephony import android.content.Context -import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager -import android.util.Log -import com.android.settings.network.mobileDataEnabledFlow -import com.android.settings.wifi.WifiPickerTrackerHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach - -class TelephonyRepository( - private val context: Context, - private val subscriptionsChangedFlow: Flow = context.subscriptionsChangedFlow(), -) { - fun isMobileDataPolicyEnabledFlow( - subId: Int, - @TelephonyManager.MobileDataPolicy policy: Int, - ): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - - val telephonyManager = context.telephonyManager(subId) - - return subscriptionsChangedFlow.map { - telephonyManager.isMobileDataPolicyEnabled(policy) - .also { Log.d(TAG, "[$subId] isMobileDataPolicyEnabled($policy): $it") } - }.conflate().flowOn(Dispatchers.Default) - } - - fun setMobileDataPolicyEnabled( - subId: Int, - @TelephonyManager.MobileDataPolicy policy: Int, - enabled: Boolean, - ) { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return - - val telephonyManager = context.telephonyManager(subId) - Log.d(TAG, "[$subId] setMobileDataPolicyEnabled($policy): $enabled") - telephonyManager.setMobileDataPolicyEnabled(policy, enabled) - } - - fun isDataEnabledFlow(subId: Int): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - - return context.mobileDataEnabledFlow(subId) - .map { - val telephonyManager = context.telephonyManager(subId) - telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) - } - .catch { - Log.w(TAG, "[$subId] isDataEnabledFlow: exception", it) - emit(false) - } - .onEach { Log.d(TAG, "[$subId] isDataEnabledFlow: isDataEnabled() = $it") } - .conflate() - .flowOn(Dispatchers.Default) - } - - fun setMobileData( - subId: Int, - enabled: Boolean, - wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null - ) { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return - - Log.d(TAG, "setMobileData: $enabled") - MobileNetworkUtils.setMobileDataEnabled( - context, - subId, - enabled /* enabled */, - true /* disableOtherSubscriptions */ - ) - - if (wifiPickerTrackerHelper != null - && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId) - ) { - wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled) - } - } - - private companion object { - private const val TAG = "TelephonyRepository" - } -} /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */ fun Context.telephonyCallbackFlow( diff --git a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt index 5dcac1ebaac..fb0bd82a7d4 100644 --- a/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt +++ b/src/com/android/settings/network/telephony/wificalling/CrossSimCallingViewModel.kt @@ -24,7 +24,7 @@ import android.telephony.TelephonyManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.android.settings.R -import com.android.settings.network.mobileDataEnabledFlow +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl import com.android.settings.network.telephony.requireSubscriptionManager import com.android.settings.network.telephony.safeGetConfig @@ -54,6 +54,7 @@ class CrossSimCallingViewModel( private val scope = viewModelScope + Dispatchers.Default private val metricsFeatureProvider = featureFactory.metricsFeatureProvider private val updateChannel = Channel() + private val mobileDataRepository = MobileDataRepository(application) init { val resources = application.resources @@ -81,7 +82,7 @@ class CrossSimCallingViewModel( } private fun List.anyMobileDataEnableChangedFlow() = map { subId -> - application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false) + mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false) }.merge() private suspend fun updateCrossSimCalling(activeSubIds: List, newEnabled: Boolean) { diff --git a/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java new file mode 100644 index 00000000000..ee4c702d998 --- /dev/null +++ b/src/com/android/settings/notification/modes/InterruptionFilterPreferenceController.java @@ -0,0 +1,62 @@ +/* + * 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.modes; + +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; +import androidx.preference.TwoStatePreference; + +import com.android.settings.R; + +class InterruptionFilterPreferenceController extends AbstractZenModePreferenceController + implements Preference.OnPreferenceChangeListener { + + public InterruptionFilterPreferenceController(Context context, String key, + ZenModesBackend backend) { + super(context, key, backend); + } + + @Override + public boolean isAvailable(ZenMode zenMode) { + return !zenMode.isManualDnd(); + } + + @Override + public void updateState(Preference preference, @NonNull ZenMode zenMode) { + boolean filteringNotifications = zenMode.getRule().getInterruptionFilter() + != INTERRUPTION_FILTER_ALL; + ((TwoStatePreference) preference).setChecked(filteringNotifications); + preference.setSummary(filteringNotifications ? "" : + mContext.getResources().getString(R.string.mode_no_notification_filter)); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean filterNotifications = ((Boolean) newValue); + return saveMode(zenMode -> { + zenMode.getRule().setInterruptionFilter(filterNotifications + ? INTERRUPTION_FILTER_PRIORITY + : INTERRUPTION_FILTER_ALL); + return zenMode; + }); + } +} diff --git a/src/com/android/settings/notification/modes/ZenMode.java b/src/com/android/settings/notification/modes/ZenMode.java index 1040d1e0021..4ece2e3046d 100644 --- a/src/com/android/settings/notification/modes/ZenMode.java +++ b/src/com/android/settings/notification/modes/ZenMode.java @@ -59,26 +59,8 @@ class ZenMode { private static final String TAG = "ZenMode"; - /** - * Additional value for the {@code @ZenPolicy.ChannelType} enumeration that indicates that all - * channels can bypass DND when this policy is active. - * - *

This value shouldn't be used on "real" ZenPolicy objects sent to or returned from - * {@link android.app.NotificationManager}; it's a way of representing rules with interruption - * filter = {@link NotificationManager#INTERRUPTION_FILTER_ALL} in the UI. - */ - public static final int CHANNEL_POLICY_ALL = -1; - static final String MANUAL_DND_MODE_ID = "manual_dnd"; - @SuppressLint("WrongConstant") - private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALL = - new ZenPolicy.Builder() - .allowChannels(CHANNEL_POLICY_ALL) - .allowAllSounds() - .showAllVisualEffects() - .build(); - // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy. private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS = new ZenPolicy.Builder() @@ -141,10 +123,8 @@ class ZenMode { public ZenPolicy getPolicy() { switch (mRule.getInterruptionFilter()) { case INTERRUPTION_FILTER_PRIORITY: - return requireNonNull(mRule.getZenPolicy()); - case NotificationManager.INTERRUPTION_FILTER_ALL: - return POLICY_INTERRUPTION_FILTER_ALL; + return requireNonNull(mRule.getZenPolicy()); case NotificationManager.INTERRUPTION_FILTER_ALARMS: return POLICY_INTERRUPTION_FILTER_ALARMS; @@ -172,31 +152,8 @@ class ZenMode { return; } - // A policy with CHANNEL_POLICY_ALL is only a UI representation of the - // INTERRUPTION_FILTER_ALL filter. Thus, switching to or away to this value only updates - // the filter, discarding the rest of the supplied policy. - if (policy.getAllowedChannels() == CHANNEL_POLICY_ALL - && currentPolicy.getAllowedChannels() != CHANNEL_POLICY_ALL) { - if (mIsManualDnd) { - throw new IllegalArgumentException("Manual DND cannot have CHANNEL_POLICY_ALL"); - } - mRule.setInterruptionFilter(INTERRUPTION_FILTER_ALL); - // Preserve the existing policy, e.g. if the user goes PRIORITY -> ALL -> PRIORITY that - // shouldn't discard all other policy customizations. The existing policy will be a - // synthetic one if the rule originally had filter NONE or ALARMS_ONLY and that's fine. - if (mRule.getZenPolicy() == null) { - mRule.setZenPolicy(currentPolicy); - } - return; - } else if (policy.getAllowedChannels() != CHANNEL_POLICY_ALL - && currentPolicy.getAllowedChannels() == CHANNEL_POLICY_ALL) { - mRule.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); - // Go back to whatever policy the rule had before, unless the rule never had one, in - // which case we use the supplied policy (which we know has a valid allowedChannels). - if (mRule.getZenPolicy() == null) { - mRule.setZenPolicy(policy); - } - return; + if (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALL) { + Log.wtf(TAG, "Able to change policy without filtering being enabled"); } // If policy is customized from any of the "special" ones, make the rule PRIORITY. diff --git a/src/com/android/settings/notification/modes/ZenModeAppsFragment.java b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java index 73329a242fd..19035ddcf78 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsFragment.java @@ -37,10 +37,6 @@ public class ZenModeAppsFragment extends ZenModeFragmentBase { context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend)); controllers.add(new ZenModeAppsPreferenceController( context, ZenModeAppsPreferenceController.KEY_NONE, mBackend)); - // TODO: b/308819928 - The manual DND mode cannot have the ALL type; - // unify the controllers into one and only create a preference if isManualDnd is false. - controllers.add(new ZenModeAppsPreferenceController( - context, ZenModeAppsPreferenceController.KEY_ALL, mBackend)); return controllers; } diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index 691c92e2e3c..6835e6cd853 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; + import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; import android.content.Context; @@ -45,10 +47,12 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr private static final String TAG = "ZenModeAppsLinkPreferenceController"; private final ZenModeSummaryHelper mSummaryHelper; + private final ApplicationsState mApplicationsState; private ApplicationsState.Session mAppSession; private final ZenHelperBackend mHelperBackend; private ZenMode mZenMode; private Preference mPreference; + private final Fragment mHost; ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, ApplicationsState applicationsState, ZenModesBackend backend, @@ -56,9 +60,13 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr super(context, key, backend); mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); mHelperBackend = helperBackend; - if (applicationsState != null && host != null) { - mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); - } + mApplicationsState = applicationsState; + mHost = host; + } + + @Override + public boolean isAvailable(ZenMode zenMode) { + return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; } @Override @@ -73,6 +81,9 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr .toIntent()); mZenMode = zenMode; mPreference = preference; + if (mApplicationsState != null && mHost != null) { + mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHost.getLifecycle()); + } triggerUpdateAppsBypassingDndSummaryText(); } diff --git a/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java index 704bce04504..d5ef0440b5e 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsPreferenceController.java @@ -38,11 +38,9 @@ public class ZenModeAppsPreferenceController extends static final String KEY_PRIORITY = "zen_mode_apps_priority"; static final String KEY_NONE = "zen_mode_apps_none"; - static final String KEY_ALL = "zen_mode_apps_all"; String mModeId; - public ZenModeAppsPreferenceController(@NonNull Context context, @NonNull String key, @Nullable ZenModesBackend backend) { super(context, key, backend); @@ -79,13 +77,6 @@ public class ZenModeAppsPreferenceController extends == ZenPolicy.CHANNEL_POLICY_NONE; pref.setChecked(policy_none); break; - case KEY_ALL: - // A UI-only setting; the underlying policy never actually has this value, - // but ZenMode acts as though it does for the sake of UI consistency. - boolean policy_all = zenMode.getPolicy().getAllowedChannels() - == ZenMode.CHANNEL_POLICY_ALL; - pref.setChecked(policy_all); - break; } } @@ -96,8 +87,6 @@ public class ZenModeAppsPreferenceController extends return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)); case KEY_NONE: return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)); - case KEY_ALL: - return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL)); } return true; } diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index ee497ae74df..f2f47b99ceb 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -53,6 +53,8 @@ public class ZenModeFragment extends ZenModeFragmentBase { context, "mode_display_settings", mBackend, mHelperBackend)); prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context, "zen_automatic_trigger_category", this, mBackend)); + prefControllers.add(new InterruptionFilterPreferenceController( + context, "allow_filtering", mBackend)); return prefControllers; } diff --git a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java index e086524a427..67cc13beb4a 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java +++ b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java @@ -120,10 +120,6 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase { } for (List list : getPreferenceControllers()) { for (AbstractPreferenceController controller : list) { - if (!controller.isAvailable()) { - continue; - } - try { // Find preference associated with controller final String key = controller.getPreferenceKey(); @@ -137,6 +133,7 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase { String.format("Cannot find preference with key %s in Controller %s", key, controller.getClass().getSimpleName())); } + controller.displayPreference(screen); } catch (ClassCastException e) { // Skip any controllers that aren't AbstractZenModePreferenceController. Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName()); diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java index 15da96ec605..1b1fec4e3f6 100644 --- a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; + import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; import android.content.Context; @@ -36,6 +38,11 @@ class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceC mSummaryBuilder = new ZenModeSummaryHelper(context, helperBackend); } + @Override + public boolean isAvailable(ZenMode zenMode) { + return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; + } + @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { Bundle bundle = new Bundle(); diff --git a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java index 89b719eadb5..f6df9e3e3d7 100644 --- a/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeOtherLinkPreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; + import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; import android.content.Context; @@ -39,6 +41,11 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); } + @Override + public boolean isAvailable(ZenMode zenMode) { + return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; + } + @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { Bundle bundle = new Bundle(); diff --git a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java index 271ca724b32..db8e135e5a2 100644 --- a/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java @@ -16,6 +16,8 @@ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; + import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID; import android.content.Context; @@ -39,6 +41,11 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); } + @Override + public boolean isAvailable(ZenMode zenMode) { + return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; + } + @Override public void updateState(Preference preference, @NonNull ZenMode zenMode) { Bundle bundle = new Bundle(); diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java index bf0bac9f166..bd0b798a309 100644 --- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java +++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java @@ -15,6 +15,7 @@ */ package com.android.settings.notification.modes; +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; @@ -187,7 +188,8 @@ class ZenModeSummaryHelper { String getDisplayEffectsSummary(ZenMode zenMode) { boolean isFirst = true; List enabledEffects = new ArrayList<>(); - if (!zenMode.getPolicy().shouldShowAllVisualEffects()) { + if (!zenMode.getPolicy().shouldShowAllVisualEffects() + && zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) { enabledEffects.add(getBlockedEffectsSummary(zenMode)); isFirst = false; } @@ -411,8 +413,6 @@ class ZenModeSummaryHelper { return formatAppsList(appsBypassing); } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) { return mContext.getResources().getString(R.string.zen_mode_apps_none_apps); - } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) { - return mContext.getResources().getString(R.string.zen_mode_apps_all_apps); } return ""; } diff --git a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt index e79be4a9cc6..e7cc18fb101 100644 --- a/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt +++ b/src/com/android/settings/spa/network/AutomaticDataSwitchingPreference.kt @@ -16,13 +16,11 @@ package com.android.settings.spa.network -import android.telephony.TelephonyManager import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R -import com.android.settings.network.telephony.TelephonyRepository import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel @@ -51,11 +49,3 @@ fun AutomaticDataSwitchingPreference( } ) } - -fun TelephonyRepository.setAutomaticData(subId: Int, newEnabled: Boolean) { - setMobileDataPolicyEnabled( - subId = subId, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - enabled = newEnabled, - ) -} diff --git a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt index 0d40bca4612..4b95d448b5f 100644 --- a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt +++ b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt @@ -16,12 +16,10 @@ package com.android.settings.spa.network -import android.telephony.TelephonyManager import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import com.android.settings.R -import com.android.settings.network.telephony.TelephonyRepository import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import kotlinx.coroutines.Dispatchers diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt index 4b9fcf45764..05a8f6a27de 100644 --- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt +++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt @@ -48,13 +48,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.telephony.DataSubscriptionRepository -import com.android.settings.network.telephony.TelephonyRepository +import com.android.settings.network.telephony.MobileDataRepository import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo import com.android.settings.wifi.WifiPickerTrackerHelper import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.compose.rememberContext import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel @@ -202,21 +203,18 @@ fun MobileDataSectionImpl( ) { val context = LocalContext.current val localLifecycleOwner = LocalLifecycleOwner.current - val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner) - - val subscriptionManager: SubscriptionManager? = - context.getSystemService(SubscriptionManager::class.java) + val mobileDataRepository = rememberContext(::MobileDataRepository) Category(title = stringResource(id = R.string.mobile_data_settings_title)) { val isAutoDataEnabled by remember(nonDds.intValue) { - TelephonyRepository(context).isMobileDataPolicyEnabledFlow( + mobileDataRepository.isMobileDataPolicyEnabledFlow( subId = nonDds.intValue, policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH ) }.collectAsStateWithLifecycle(initialValue = null) val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) { - TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue) + mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue) }.collectAsStateWithLifecycle(initialValue = false) val coroutineScope = rememberCoroutineScope() @@ -226,8 +224,8 @@ fun MobileDataSectionImpl( coroutineScope.launch { setMobileData( context, - subscriptionManager, - wifiPickerTrackerHelper, + context.getSystemService(SubscriptionManager::class.java), + getWifiPickerTrackerHelper(context, localLifecycleOwner), mobileDataSelectedId.intValue, newEnabled ) @@ -238,7 +236,7 @@ fun MobileDataSectionImpl( AutomaticDataSwitchingPreference( isAutoDataEnabled = { isAutoDataEnabled }, setAutoDataEnabled = { newEnabled -> - TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled) + mobileDataRepository.setAutoDataSwitch(nonDds.intValue, newEnabled) }, ) } @@ -426,6 +424,6 @@ suspend fun setMobileData( Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]") subscriptionManager?.setDefaultDataSubId(targetSubId) } - TelephonyRepository(context) - .setMobileData(targetSubId, enabled, wifiPickerTrackerHelper) + MobileDataRepository(context) + .setMobileDataEnabled(targetSubId, enabled, wifiPickerTrackerHelper) } \ No newline at end of file diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index b48c71727d0..71dd43f92a0 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -363,13 +363,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment } if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled() || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN, - mUserInfo.getUserHandle())) { + mUserInfo.getUserHandle()) || !mUserManager.isAdminUser()) { removePreference(KEY_GRANT_ADMIN); } if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls removePreference(KEY_ENABLE_TELEPHONY); removePreference(KEY_REMOVE_USER); - removePreference(KEY_GRANT_ADMIN); removePreference(KEY_APP_AND_CONTENT_ACCESS); removePreference(KEY_APP_COPYING); } else { diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java index d9a917b0cdd..24528ae4dcc 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java @@ -21,26 +21,35 @@ import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.Spatializer; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.preference.PreferenceCategory; import androidx.preference.TwoStatePreference; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.bluetooth.A2dpProfile; +import com.android.settingslib.bluetooth.HearingAidProfile; +import com.android.settingslib.bluetooth.LeAudioProfile; import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.flags.Flags; import com.google.common.collect.ImmutableList; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -54,7 +63,8 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase { - + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C"; private static final String KEY_SPATIAL_AUDIO = "spatial_audio"; private static final String KEY_HEAD_TRACKING = "head_tracking"; @@ -64,6 +74,9 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails @Mock private Lifecycle mSpatialAudioLifecycle; @Mock private PreferenceCategory mProfilesContainer; @Mock private BluetoothDevice mBluetoothDevice; + @Mock private A2dpProfile mA2dpProfile; + @Mock private LeAudioProfile mLeAudioProfile; + @Mock private HearingAidProfile mHearingAidProfile; private AudioDeviceAttributes mAvailableDevice; @@ -83,6 +96,12 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails when(mAudioManager.getSpatializer()).thenReturn(mSpatializer); when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS); when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedDevice.getProfiles()) + .thenReturn(List.of(mA2dpProfile, mLeAudioProfile, mHearingAidProfile)); + when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP); + when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO); + when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID); when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS); when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext)) .thenReturn(mSpatializer); @@ -272,6 +291,52 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails false); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE) + public void refresh_leAudioProfileEnabledForHeadset_useLeAudioHeadsetAttributes() { + when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS)) + .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES); + when(mSpatializer.isAvailableForDevice(any())).thenReturn(true); + + mController.refresh(); + ShadowLooper.idleMainLooper(); + + assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_HEADSET); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE) + public void refresh_leAudioProfileEnabledForSpeaker_useLeAudioSpeakerAttributes() { + when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS)) + .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER); + when(mSpatializer.isAvailableForDevice(any())).thenReturn(true); + + mController.refresh(); + ShadowLooper.idleMainLooper(); + + assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_BLE_SPEAKER); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DETERMINING_SPATIAL_AUDIO_ATTRIBUTES_BY_PROFILE) + public void refresh_hearingAidProfileEnabled_useHearingAidAttributes() { + when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(false); + when(mHearingAidProfile.isEnabled(mBluetoothDevice)).thenReturn(true); + when(mSpatializer.isAvailableForDevice(any())).thenReturn(true); + + mController.refresh(); + ShadowLooper.idleMainLooper(); + + assertThat(mController.mAudioDevice.getType()).isEqualTo(AudioDeviceInfo.TYPE_HEARING_AID); + } + @Test public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() { mController.setAvailableDevice(mAvailableDevice); diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java new file mode 100644 index 00000000000..aeb1b8ed8c4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java @@ -0,0 +1,143 @@ +/* + * 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.modes; + +import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static android.service.notification.ZenPolicy.STATE_ALLOW; +import static android.service.notification.ZenPolicy.STATE_DISALLOW; +import static android.service.notification.ZenPolicy.STATE_UNSET; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.app.AutomaticZenRule; +import android.app.Flags; +import android.content.Context; +import android.net.Uri; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.service.notification.ZenPolicy; + +import androidx.preference.Preference; +import androidx.preference.TwoStatePreference; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +@EnableFlags(Flags.FLAG_MODES_UI) +public final class InterruptionFilterPreferenceControllerTest { + + private InterruptionFilterPreferenceController mController; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + private Context mContext; + @Mock private ZenModesBackend mBackend; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mController = new InterruptionFilterPreferenceController(mContext, "something", mBackend); + } + + @Test + public void testUpdateState_all() { + TwoStatePreference preference = mock(TwoStatePreference.class); + ZenMode zenMode = new ZenMode("id", + new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) + .setType(AutomaticZenRule.TYPE_DRIVING) + .setInterruptionFilter(INTERRUPTION_FILTER_ALL) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) + .build(), true); + mController.updateZenMode(preference, zenMode); + + verify(preference).setChecked(false); + } + + @Test + public void testOnPreferenceChange_fromAll() { + TwoStatePreference preference = mock(TwoStatePreference.class); + ZenMode zenMode = new ZenMode("id", + new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) + .setType(AutomaticZenRule.TYPE_DRIVING) + .setInterruptionFilter(INTERRUPTION_FILTER_ALL) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) + .build(), true); + + mController.updateZenMode(preference, zenMode); + + mController.onPreferenceChange(preference, true); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); + verify(mBackend).updateMode(captor.capture()); + assertThat(captor.getValue().getPolicy().getPriorityCategoryAlarms()) + .isEqualTo(STATE_DISALLOW); + assertThat(captor.getValue().getRule().getInterruptionFilter()) + .isEqualTo(INTERRUPTION_FILTER_PRIORITY); + } + + @Test + public void testUpdateState_priority() { + TwoStatePreference preference = mock(TwoStatePreference.class); + ZenMode zenMode = new ZenMode("id", + new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) + .setType(AutomaticZenRule.TYPE_DRIVING) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build()) + .build(), true); + mController.updateZenMode(preference, zenMode); + + verify(preference).setChecked(true); + } + + @Test + public void testOnPreferenceChange_fromPriority() { + TwoStatePreference preference = mock(TwoStatePreference.class); + ZenMode zenMode = new ZenMode("id", + new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) + .setType(AutomaticZenRule.TYPE_DRIVING) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build()) + .build(), true); + + mController.updateZenMode(preference, zenMode); + + mController.onPreferenceChange(preference, false); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); + verify(mBackend).updateMode(captor.capture()); + assertThat(captor.getValue().getPolicy().getPriorityCategoryAlarms()) + .isEqualTo(STATE_DISALLOW); + assertThat(captor.getValue().getRule().getInterruptionFilter()) + .isEqualTo(INTERRUPTION_FILTER_ALL); + } +} \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java index 8205f3a5f4a..b199a2bf922 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -180,13 +181,30 @@ public final class ZenModeAppsLinkPreferenceControllerTest { @Test public void testOnPackageListChangedTriggersRebuild() { - mController.mAppSessionCallbacks.onPackageListChanged(); + SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class); + // Create a zen mode that allows priority channels to breakthrough. + ZenMode zenMode = createPriorityChannelsZenMode(); + mController.updateState(preference, zenMode); verify(mSession).rebuild(any(), any(), eq(false)); + + mController.mAppSessionCallbacks.onPackageListChanged(); + verify(mSession, times(2)).rebuild(any(), any(), eq(false)); } @Test public void testOnLoadEntriesCompletedTriggersRebuild() { - mController.mAppSessionCallbacks.onLoadEntriesCompleted(); + SelectorWithWidgetPreference preference = mock(SelectorWithWidgetPreference.class); + // Create a zen mode that allows priority channels to breakthrough. + ZenMode zenMode = createPriorityChannelsZenMode(); + mController.updateState(preference, zenMode); verify(mSession).rebuild(any(), any(), eq(false)); + + mController.mAppSessionCallbacks.onLoadEntriesCompleted(); + verify(mSession, times(2)).rebuild(any(), any(), eq(false)); + } + + @Test + public void testNoCrashIfAppsReadyBeforeRuleAvailable() { + mController.mAppSessionCallbacks.onLoadEntriesCompleted(); } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java index 750453dabe6..b67d3328fec 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsPreferenceControllerTest.java @@ -20,7 +20,6 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; -import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_ALL; import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_NONE; import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY; @@ -62,11 +61,9 @@ public final class ZenModeAppsPreferenceControllerTest { @Mock private ZenModesBackend mBackend; private ZenModeAppsPreferenceController mPriorityController; - private ZenModeAppsPreferenceController mAllController; private ZenModeAppsPreferenceController mNoneController; private SelectorWithWidgetPreference mPriorityPref; - private SelectorWithWidgetPreference mAllPref; private SelectorWithWidgetPreference mNonePref; private PreferenceCategory mPrefCategory; private PreferenceScreen mPreferenceScreen; @@ -81,10 +78,8 @@ public final class ZenModeAppsPreferenceControllerTest { mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend); mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend); - mAllController = new ZenModeAppsPreferenceController(mContext, KEY_ALL, mBackend); mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController); - mAllPref = makePreference(KEY_ALL, mAllController); mNonePref = makePreference(KEY_NONE, mNoneController); mPrefCategory = new PreferenceCategory(mContext); @@ -95,10 +90,8 @@ public final class ZenModeAppsPreferenceControllerTest { mPreferenceScreen.addPreference(mPrefCategory); mPrefCategory.addPreference(mPriorityPref); - mPrefCategory.addPreference(mAllPref); mPrefCategory.addPreference(mNonePref); - mAllController.displayPreference(mPreferenceScreen); mPriorityController.displayPreference(mPreferenceScreen); mNoneController.displayPreference(mPreferenceScreen); } @@ -111,36 +104,6 @@ public final class ZenModeAppsPreferenceControllerTest { return pref; } - @Test - public void testUpdateState_All() { - TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .build()) - .build(), true); - mAllController.updateZenMode(preference, zenMode); - - verify(preference).setChecked(true); - } - - @Test - public void testUpdateState_All_Unchecked() { - TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) - .build()) - .build(), true); - mAllController.updateZenMode(preference, zenMode); - - verify(preference).setChecked(false); - } - @Test public void testUpdateState_None() { TwoStatePreference preference = mock(TwoStatePreference.class); @@ -163,7 +126,7 @@ public final class ZenModeAppsPreferenceControllerTest { new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) .setType(AutomaticZenRule.TYPE_DRIVING) .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenMode.CHANNEL_POLICY_ALL) + .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) .build()) .build(), true); mNoneController.updateZenMode(preference, zenMode); @@ -201,67 +164,6 @@ public final class ZenModeAppsPreferenceControllerTest { verify(preference).setChecked(false); } - @Test - public void testOnPreferenceChange_All() { - TwoStatePreference preference = mock(TwoStatePreference.class); - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setInterruptionFilter(INTERRUPTION_FILTER_NONE) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .build()) - .build(), true); - - mAllController.updateZenMode(preference, zenMode); - mAllController.onPreferenceChange(preference, true); - ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); - verify(mBackend).updateMode(captor.capture()); - - assertThat(captor.getValue().getPolicy().getAllowedChannels()) - .isEqualTo(ZenMode.CHANNEL_POLICY_ALL); - } - - @Test - public void testPreferenceClick_passesCorrectCheckedState_All() { - ZenMode zenMode = new ZenMode("id", - new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) - .setType(AutomaticZenRule.TYPE_DRIVING) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenPolicy.CHANNEL_POLICY_NONE) - .build()) - .build(), true); - - - mAllController.updateZenMode(mAllPref, zenMode); - mNoneController.updateZenMode(mNonePref, zenMode); - mPriorityController.updateZenMode(mPriorityPref, zenMode); - - // MPME is checked; ALL and PRIORITY are unchecked. - assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); - - mPrefCategory.findPreference(KEY_ALL).performClick(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); - verify(mBackend).updateMode(captor.capture()); - // Checks the policy value for ALL is set. - // The important part is that the interruption filter is propagated to the backend. - assertThat(captor.getValue().getRule().getInterruptionFilter()) - .isEqualTo(INTERRUPTION_FILTER_ALL); - // ALL is now checked; others are unchecked. - assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) - .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) - .isChecked()); - } - @Test public void testPreferenceClick_passesCorrectCheckedState_None() { ZenMode zenMode = new ZenMode("id", @@ -272,12 +174,9 @@ public final class ZenModeAppsPreferenceControllerTest { .build()) .build(), true); - mAllController.updateZenMode(mAllPref, zenMode); mNoneController.updateZenMode(mNonePref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode); - assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) .isChecked()); assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) @@ -296,8 +195,6 @@ public final class ZenModeAppsPreferenceControllerTest { // NONE is now checked; others are unchecked. assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) .isChecked()); } @@ -312,14 +209,11 @@ public final class ZenModeAppsPreferenceControllerTest { .build()) .build(), true); - mAllController.updateZenMode(mAllPref, zenMode); mNoneController.updateZenMode(mNonePref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode); assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) .isChecked()); @@ -334,8 +228,6 @@ public final class ZenModeAppsPreferenceControllerTest { // PRIORITY is now checked; others are unchecked. assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) .isChecked()); - assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL)) - .isChecked()); assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) .isChecked()); } 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 05286212e12..37b71a5ba34 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java @@ -70,18 +70,6 @@ public class ZenModeTest { assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); } - @Test - public void getPolicy_interruptionFilterAll_returnsPolicyAllowingAll() { - ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_ALL) - .setZenPolicy(ZEN_POLICY) // should be ignored - .build(), false); - - assertThat(zenMode.getPolicy()).isEqualTo( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .allowAllSounds().showAllVisualEffects().build()); - } - @Test public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) @@ -126,69 +114,4 @@ public class ZenModeTest { assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY); } - - @Test - public void setPolicy_withAllChannelsAllowed_setsInterruptionFilterAll() { - ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) - .setZenPolicy(ZEN_POLICY) - .build(), false); - - zenMode.setPolicy( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build()); - - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); - assertThat(zenMode.getPolicy()).isEqualTo( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .allowAllSounds().showAllVisualEffects().build()); - } - - @Test - public void setPolicy_priorityToAllChannelsAndBack_restoresOldPolicy() { - ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(ZEN_POLICY) - .build(), false); - - zenMode.setPolicy( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build()); - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); - assertThat(zenMode.getPolicy()).isEqualTo( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .allowAllSounds().showAllVisualEffects().build()); - - zenMode.setPolicy( - new ZenPolicy.Builder().allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY).build()); - - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo( - INTERRUPTION_FILTER_PRIORITY); - assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); - assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY); - } - - @Test - public void setPolicy_alarmsOnlyToAllChannelsAndBack_restoresPolicySimilarToAlarmsOnly() { - ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) - .build(), false); - - zenMode.setPolicy( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL).build()); - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); - assertThat(zenMode.getPolicy()).isEqualTo( - new ZenPolicy.Builder().allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .allowAllSounds().showAllVisualEffects().build()); - - zenMode.setPolicy( - new ZenPolicy.Builder().allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY).build()); - - // We don't go back to ALARMS, but the policy must be the one the user was seeing before. - ZenPolicy alarmsOnlyLikePolicy = new ZenPolicy.Builder().disallowAllSounds() - .allowAlarms(true).allowMedia(true).allowPriorityChannels(false) - .build(); - assertThat(zenMode.getRule().getInterruptionFilter()).isEqualTo( - INTERRUPTION_FILTER_PRIORITY); - assertThat(zenMode.getPolicy()).isEqualTo(alarmsOnlyLikePolicy); - assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(alarmsOnlyLikePolicy); - } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java index 13ae4eb108b..51368c557ef 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java @@ -331,20 +331,6 @@ public class ZenModesSummaryHelperTest { "Notifications partially hidden, grayscale, and 2 more"); } - @Test - public void getAppsSummary_all() { - AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) - .setType(AutomaticZenRule.TYPE_BEDTIME) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder() - .allowChannels(ZenMode.CHANNEL_POLICY_ALL) - .build()) - .build(); - ZenMode zenMode = new ZenMode("id", rule, true); - - assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("All"); - } - @Test public void getAppsSummary_none() { AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) diff --git a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java index 7bc66c8f819..fe88148be62 100644 --- a/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/users/TimeoutToDockUserPreferenceControllerTest.java @@ -39,7 +39,6 @@ import com.android.settings.testutils.shadow.ShadowSecureSettings; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -126,7 +125,6 @@ public class TimeoutToDockUserPreferenceControllerTest { BasePreferenceController.CONDITIONALLY_UNAVAILABLE); } - @Ignore("b/313530297") @Test public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() { when(mUserManager.getMainUser()).thenReturn(UserHandle.CURRENT); @@ -136,7 +134,6 @@ public class TimeoutToDockUserPreferenceControllerTest { BasePreferenceController.DISABLED_FOR_USER); } - @Ignore("b/313530297") @Test public void getAvailabilityStatus_featureAndMultiUserEnabledAndNonMainUser_returnAvailable() { when(mUserManager.isUserForeground()).thenReturn(true); diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java index 7f273244499..44e1cc6986c 100644 --- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java +++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java @@ -22,7 +22,7 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -63,7 +63,6 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -222,8 +221,8 @@ public class UserDetailsSettingsTest { } @Test - @Ignore("b/313530297") public void onResume_canSwitch_shouldEnableSwitchPref() { + setupSelectedUser(); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK); mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.onAttach(mContext); @@ -234,8 +233,8 @@ public class UserDetailsSettingsTest { } @Test - @Ignore("b/313530297") public void onResume_userInCall_shouldDisableSwitchPref() { + setupSelectedUser(); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_IN_CALL); mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.onAttach(mContext); @@ -246,8 +245,8 @@ public class UserDetailsSettingsTest { } @Test - @Ignore("b/313530297") public void onResume_switchDisallowed_shouldDisableSwitchPref() { + setupSelectedUser(); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED); mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.onAttach(mContext); @@ -258,8 +257,8 @@ public class UserDetailsSettingsTest { } @Test - @Ignore("b/313530297") public void onResume_systemUserLocked_shouldDisableSwitchPref() { + setupSelectedUser(); mUserManager.setSwitchabilityStatus(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED); mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.onAttach(mContext); @@ -269,7 +268,6 @@ public class UserDetailsSettingsTest { verify(mSwitchUserPref).setEnabled(false); } - @Ignore("b/313530297") @Test public void initialize_adminWithTelephony_shouldShowPhonePreference() { setupSelectedUser(); @@ -315,7 +313,6 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS); } - @Ignore("b/313530297") @Test public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() { setupSelectedUser(); @@ -328,7 +325,6 @@ public class UserDetailsSettingsTest { verify(mFragment, never()).removePreference(KEY_REMOVE_USER); } - @Ignore("b/313530297") @Test public void initialize_adminSelectsNewRestrictedUser_shouldOpenAppContentScreen() { setupSelectedRestrictedUser(); @@ -351,7 +347,6 @@ public class UserDetailsSettingsTest { .isEqualTo(true); } - @Ignore("b/313530297") @Test public void initialize_adminSelectsRestrictedUser_shouldSetupPreferences() { setupSelectedRestrictedUser(); @@ -381,7 +376,6 @@ public class UserDetailsSettingsTest { verify(mActivity, never()).startActivity(any(Intent.class)); } - @Ignore("b/313530297") @Test public void initialize_adminSelectsGuest_shouldShowRemovePreference() { setupSelectedGuest(); @@ -425,7 +419,6 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_REMOVE_USER); } - @Ignore("b/313530297") @Test public void initialize_userHasCallRestriction_shouldSetPhoneSwitchUnChecked() { setupSelectedUser(); @@ -438,7 +431,6 @@ public class UserDetailsSettingsTest { verify(mPhonePref).setChecked(false); } - @Ignore("b/313530297") @Test public void initialize_noCallRestriction_shouldSetPhoneSwitchChecked() { setupSelectedUser(); @@ -537,7 +529,6 @@ public class UserDetailsSettingsTest { verify(mFragment, never()).switchUser(); } - @Ignore("b/313530297") @Test public void onPreferenceClick_removeGuestClicked_canDelete_shouldShowDialog() { setupSelectedGuest(); @@ -555,7 +546,6 @@ public class UserDetailsSettingsTest { verify(mFragment).showDialog(DIALOG_CONFIRM_RESET_GUEST); } - @Ignore("b/313530297") @Test public void onPreferenceClick_removeRestrictedClicked_canDelete_shouldShowDialog() { setupSelectedRestrictedUser(); @@ -574,7 +564,6 @@ public class UserDetailsSettingsTest { verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE); } - @Ignore("b/313530297") @Test public void onPreferenceClick_removeClicked_canDelete_shouldShowDialog() { setupSelectedUser(); @@ -666,7 +655,6 @@ public class UserDetailsSettingsTest { assertThat(result).isFalse(); } - @Ignore("b/313530297") @Test public void canDeleteUser_adminSelectsUser_noRestrictions_shouldReturnTrue() { setupSelectedUser(); @@ -700,17 +688,16 @@ public class UserDetailsSettingsTest { assertThat(result).isFalse(); } - @Ignore("b/313530297") @Test public void initialize_userSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() { + assumeTrue(UserManager.isHeadlessSystemUserMode()); setupSelectedUser(); + mUserManager.setIsAdminUser(true); ShadowUserManager.setIsMultipleAdminEnabled(true); mFragment.initialize(mActivity, mArguments); - assertTrue(UserManager.isMultipleAdminEnabled()); verify(mFragment, never()).removePreference(KEY_GRANT_ADMIN); } - @Ignore("b/313530297") @Test public void initialize_userSelected_shouldNotShowGrantAdminPref() { setupSelectedUser(); @@ -718,7 +705,6 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_GRANT_ADMIN); } - @Ignore("b/313530297") @Test public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() { setupSelectedUser(); @@ -729,7 +715,6 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_GRANT_ADMIN); } - @Ignore("b/313530297") @Test public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() { setupSelectedMainUser(); @@ -738,7 +723,6 @@ public class UserDetailsSettingsTest { verify(mFragment).removePreference(KEY_GRANT_ADMIN); } - @Ignore("b/313530297") @Test public void initialize_guestSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() { setupSelectedGuest(); @@ -778,7 +762,7 @@ public class UserDetailsSettingsTest { mUserInfo = new UserInfo(1, "Tom", null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserManager.USER_TYPE_FULL_SECONDARY); - + mFragment.mUserInfo = mUserInfo; mUserManager.addProfile(mUserInfo); } @@ -787,7 +771,7 @@ public class UserDetailsSettingsTest { mUserInfo = new UserInfo(11, "Jerry", null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MAIN, UserManager.USER_TYPE_FULL_SECONDARY); - + mFragment.mUserInfo = mUserInfo; mUserManager.addProfile(mUserInfo); } @@ -796,7 +780,7 @@ public class UserDetailsSettingsTest { mUserInfo = new UserInfo(12, "Andy", null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN, UserManager.USER_TYPE_FULL_SECONDARY); - + mFragment.mUserInfo = mUserInfo; mUserManager.addProfile(mUserInfo); } @@ -805,7 +789,7 @@ public class UserDetailsSettingsTest { mUserInfo = new UserInfo(23, "Guest", null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST, UserManager.USER_TYPE_FULL_GUEST); - + mFragment.mUserInfo = mUserInfo; mUserManager.addProfile(mUserInfo); } @@ -814,7 +798,7 @@ public class UserDetailsSettingsTest { mUserInfo = new UserInfo(21, "Bob", null, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED, UserManager.USER_TYPE_FULL_RESTRICTED); - + mFragment.mUserInfo = mUserInfo; mUserManager.addProfile(mUserInfo); } } diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java index 417b102dcc9..02ed03c3acf 100644 --- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -362,6 +362,10 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager } } + @Implementation + protected boolean isAdminUser() { + return getUserInfo(UserHandle.myUserId()).isAdmin(); + } @Implementation protected boolean isGuestUser() { return mIsGuestUser; diff --git a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt b/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt deleted file mode 100644 index c4611ac6544..00000000000 --- a/tests/spa_unit/src/com/android/settings/network/MobileDataEnabledFlowTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.network - -import android.content.Context -import android.provider.Settings -import android.telephony.SubscriptionManager -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull -import com.android.settingslib.spa.testutils.toListWithTimeout -import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class MobileDataEnabledFlowTest { - private val context: Context = ApplicationProvider.getApplicationContext() - - @Test - fun mobileDataEnabledFlow_notified(): Unit = runBlocking { - val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) - - assertThat(flow.firstWithTimeoutOrNull()).isNotNull() - } - - @Test - fun mobileDataEnabledFlow_changed_notified(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - - val flow = context.mobileDataEnabledFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) - mobileDataEnabled = true - - assertThat(flow.firstWithTimeoutOrNull()).isNotNull() - } - - @Test - fun mobileDataEnabledFlow_forSubIdNotChanged(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - var mobileDataEnabledForSubId - by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) - mobileDataEnabledForSubId = false - - val listDeferred = async { - context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout() - } - - assertThat(listDeferred.await()).hasSize(1) - } - - @Test - fun mobileDataEnabledFlow_forSubIdChanged(): Unit = runBlocking { - var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) - mobileDataEnabled = false - var mobileDataEnabledForSubId - by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) - mobileDataEnabledForSubId = false - - val listDeferred = async { - context.mobileDataEnabledFlow(SUB_ID).toListWithTimeout() - } - delay(100) - mobileDataEnabledForSubId = true - - assertThat(listDeferred.await().size).isAtLeast(2) - } - - private companion object { - const val SUB_ID = 123 - } -} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt new file mode 100644 index 00000000000..268be570048 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt @@ -0,0 +1,168 @@ +/* + * 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.network.telephony + +import android.content.Context +import android.provider.Settings +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBoolean +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class MobileDataRepositoryTest { + private val mockTelephonyManager = + mock { on { createForSubscriptionId(SUB_ID) } doReturn mock } + + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager + } + + private val repository = MobileDataRepository(context, flowOf(Unit)) + + @Test + fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking { + val flow = + repository.isMobileDataPolicyEnabledFlow( + subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + ) + + assertThat(flow.firstWithTimeoutOrNull()).isFalse() + } + + @Test + fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking { + mockTelephonyManager.stub { + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true + } + + val flow = + repository.isMobileDataPolicyEnabledFlow( + subId = SUB_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + ) + + assertThat(flow.firstWithTimeoutOrNull()).isTrue() + } + + @Test + fun setMobileDataPolicyEnabled() = runBlocking { + repository.setMobileDataPolicyEnabled( + subId = SUB_ID, + policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, + enabled = true) + + verify(mockTelephonyManager) + .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true) + } + + @Test + fun mobileDataEnabledChangedFlow_notified(): Unit = runBlocking { + val flow = + repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + + assertThat(flow.firstWithTimeoutOrNull()).isNotNull() + } + + @Test + fun mobileDataEnabledChangedFlow_changed_notified(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + + val flow = + repository.mobileDataEnabledChangedFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) + mobileDataEnabled = true + + assertThat(flow.firstWithTimeoutOrNull()).isNotNull() + } + + @Test + fun mobileDataEnabledChangedFlow_forSubIdNotChanged(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + var mobileDataEnabledForSubId by + context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) + mobileDataEnabledForSubId = false + + val listDeferred = async { + repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout() + } + + assertThat(listDeferred.await()).hasSize(1) + } + + @Test + fun mobileDataEnabledChangedFlow_forSubIdChanged(): Unit = runBlocking { + var mobileDataEnabled by context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA) + mobileDataEnabled = false + var mobileDataEnabledForSubId by + context.settingsGlobalBoolean(Settings.Global.MOBILE_DATA + SUB_ID) + mobileDataEnabledForSubId = false + + val listDeferred = async { + repository.mobileDataEnabledChangedFlow(SUB_ID).toListWithTimeout() + } + delay(100) + mobileDataEnabledForSubId = true + + assertThat(listDeferred.await().size).isAtLeast(2) + } + + @Test + fun isMobileDataEnabledFlow_invalidSub_returnFalse() = runBlocking { + val state = + repository.isMobileDataEnabledFlow( + subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, + ) + + assertThat(state.firstWithTimeoutOrNull()).isFalse() + } + + @Test + fun isMobileDataEnabledFlow_validSub_returnPolicyState() = runBlocking { + mockTelephonyManager.stub { + on { isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) } doReturn true + } + + val state = repository.isMobileDataEnabledFlow(subId = SUB_ID) + + assertThat(state.firstWithTimeoutOrNull()).isTrue() + } + + private companion object { + const val SUB_ID = 123 + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt index 65e8c47023d..12791b8e9c1 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt @@ -17,14 +17,12 @@ package com.android.settings.network.telephony import android.content.Context -import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith @@ -33,91 +31,29 @@ import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy -import org.mockito.kotlin.stub import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class TelephonyRepositoryTest { private var telephonyCallback: TelephonyCallback? = null - private val mockTelephonyManager = mock { - on { createForSubscriptionId(SUB_ID) } doReturn mock - on { registerTelephonyCallback(any(), any()) } doAnswer { - telephonyCallback = it.arguments[1] as TelephonyCallback - } - } - - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { - on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager - } - - private val repository = TelephonyRepository(context, flowOf(Unit)) - - @Test - fun isMobileDataPolicyEnabledFlow_invalidSub_returnFalse() = runBlocking { - val flow = repository.isMobileDataPolicyEnabledFlow( - subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - ) - - assertThat(flow.firstWithTimeoutOrNull()).isFalse() - } - - @Test - fun isMobileDataPolicyEnabledFlow_validSub_returnPolicyState() = runBlocking { - mockTelephonyManager.stub { - on { - isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) - } doReturn true + private val mockTelephonyManager = + mock { + on { createForSubscriptionId(SUB_ID) } doReturn mock + on { registerTelephonyCallback(any(), any()) } doAnswer + { + telephonyCallback = it.arguments[1] as TelephonyCallback + } } - val flow = repository.isMobileDataPolicyEnabledFlow( - subId = SUB_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - ) - - assertThat(flow.firstWithTimeoutOrNull()).isTrue() - } - - @Test - fun setMobileDataPolicyEnabled() = runBlocking { - repository.setMobileDataPolicyEnabled( - subId = SUB_ID, - policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, - enabled = true - ) - - verify(mockTelephonyManager) - .setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, true) - } - - @Test - fun isDataEnabled_invalidSub_returnFalse() = runBlocking { - val state = repository.isDataEnabledFlow( - subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID, - ) - - assertThat(state.firstWithTimeoutOrNull()).isFalse() - } - - @Test - fun isDataEnabled_validSub_returnPolicyState() = runBlocking { - mockTelephonyManager.stub { - on { - isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER) - } doReturn true + private val context: Context = + spy(ApplicationProvider.getApplicationContext()) { + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager } - val state = repository.isDataEnabledFlow(subId = SUB_ID) - - assertThat(state.firstWithTimeoutOrNull()).isTrue() - } - @Test fun telephonyCallbackFlow_callbackRegistered() = runBlocking { - val flow = context.telephonyCallbackFlow(SUB_ID) { - object : TelephonyCallback() {} - } + val flow = context.telephonyCallbackFlow(SUB_ID) { object : TelephonyCallback() {} } flow.firstWithTimeoutOrNull() @@ -126,9 +62,7 @@ class TelephonyRepositoryTest { @Test fun telephonyCallbackFlow_callbackUnregistered() = runBlocking { - val flow = context.telephonyCallbackFlow(SUB_ID) { - object : TelephonyCallback() {} - } + val flow = context.telephonyCallbackFlow(SUB_ID) { object : TelephonyCallback() {} } flow.firstWithTimeoutOrNull()