Snap for 11996894 from b14a7842b1 to 24Q4-release

Change-Id: I9b903a1874762446f7dcf89c34d05f4920ef85f0
This commit is contained in:
Android Build Coastguard Worker
2024-06-20 23:23:55 +00:00
47 changed files with 813 additions and 650 deletions

View File

@@ -16,6 +16,7 @@
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="horizontal"

View File

@@ -17,6 +17,7 @@
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"

View File

@@ -19,6 +19,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:clipChildren="true"> android:clipChildren="true">
<RelativeLayout <RelativeLayout

View File

@@ -8016,7 +8016,7 @@
<string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string> <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] --> <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
<string name="mode_interruption_filter_title">Notifications that can reach you</string> <string name="mode_interruption_filter_title">Stay focused</string>
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] --> <!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
<string name="mode_device_effects_title">Additional actions</string> <string name="mode_device_effects_title">Additional actions</string>
@@ -8060,7 +8060,10 @@
other {{effect_1}, {effect_2}, and # more} other {{effect_1}, {effect_2}, and # more}
} }
</string> </string>
<!-- Modes: setting for whether the mode should filter (silence/hide) notifications/volume streams -->
<string name="mode_notification_filter_title">Filter interruptions</string>
<!-- Modes: subtext when a mode is not filtering (silence/hide) notifications/volume streams -->
<string name="mode_no_notification_filter">No interruptions are filtered</string>
<!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] --> <!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
<string name="zen_mode_restrict_notifications_title">Display options for filtered <string name="zen_mode_restrict_notifications_title">Display options for filtered

View File

@@ -35,13 +35,18 @@
<PreferenceCategory <PreferenceCategory
android:title="@string/mode_interruption_filter_title" android:title="@string/mode_interruption_filter_title"
android:key="modes_filters"> android:key="modes_filters">
<SwitchPreferenceCompat
android:key="allow_filtering"
android:title="@string/mode_notification_filter_title"/>
<Preference <Preference
android:key="zen_mode_people" android:key="zen_mode_people"
android:title="@string/zen_category_people"/> android:title="@string/zen_category_people"/>
<Preference <Preference
android:key="zen_mode_apps" android:key="zen_mode_apps"
android:title="@string/zen_category_apps" /> android:title="@string/zen_category_apps"/>
<Preference <Preference
android:key="zen_other_settings" android:key="zen_other_settings"

View File

@@ -19,13 +19,16 @@ package com.android.settings.bluetooth;
import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothProfile;
import android.content.Context; import android.content.Context;
import android.media.AudioDeviceAttributes; import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo; import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.Spatializer; import android.media.Spatializer;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceCategory;
@@ -37,9 +40,14 @@ import androidx.preference.TwoStatePreference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
@@ -53,22 +61,27 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
private static final String KEY_SPATIAL_AUDIO = "spatial_audio"; private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
private static final String KEY_HEAD_TRACKING = "head_tracking"; private static final String KEY_HEAD_TRACKING = "head_tracking";
private final AudioManager mAudioManager;
private final Spatializer mSpatializer; private final Spatializer mSpatializer;
@VisibleForTesting @VisibleForTesting
PreferenceCategory mProfilesContainer; PreferenceCategory mProfilesContainer;
@VisibleForTesting @VisibleForTesting @Nullable AudioDeviceAttributes mAudioDevice = null;
AudioDeviceAttributes mAudioDevice = null;
AtomicBoolean mHasHeadTracker = new AtomicBoolean(false); AtomicBoolean mHasHeadTracker = new AtomicBoolean(false);
AtomicBoolean mInitialRefresh = new AtomicBoolean(true); AtomicBoolean mInitialRefresh = new AtomicBoolean(true);
public static final Set<Integer> SA_PROFILES =
ImmutableSet.of(
BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
public BluetoothDetailsSpatialAudioController( public BluetoothDetailsSpatialAudioController(
Context context, Context context,
PreferenceFragmentCompat fragment, PreferenceFragmentCompat fragment,
CachedBluetoothDevice device, CachedBluetoothDevice device,
Lifecycle lifecycle) { Lifecycle lifecycle) {
super(context, fragment, device, lifecycle); super(context, fragment, device, lifecycle);
mAudioManager = context.getSystemService(AudioManager.class);
mSpatializer = FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider() mSpatializer = FeatureFactory.getFeatureFactory().getBluetoothFeatureProvider()
.getSpatializer(context); .getSpatializer(context);
} }
@@ -142,8 +155,12 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
@Override @Override
protected void refresh() { protected void refresh() {
if (mAudioDevice == null) { if (Flags.enableDeterminingSpatialAudioAttributesByProfile()) {
getAvailableDevice(); getAvailableDeviceByProfileState();
} else {
if (mAudioDevice == null) {
getAvailableDevice();
}
} }
ThreadUtils.postOnBackgroundThread( ThreadUtils.postOnBackgroundThread(
() -> { () -> {
@@ -274,6 +291,77 @@ public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsCont
+ ", type : " + (mAudioDevice == null ? "no type" : mAudioDevice.getType())); + ", 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 @VisibleForTesting
void setAvailableDevice(AudioDeviceAttributes audioDevice) { void setAvailableDevice(AudioDeviceAttributes audioDevice) {
mAudioDevice = audioDevice; mAudioDevice = audioDevice;

View File

@@ -21,7 +21,7 @@ import android.os.INetworkManagementService
import android.os.ServiceManager import android.os.ServiceManager
import android.util.Log import android.util.Log
import androidx.annotation.OpenForTesting 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 com.android.settingslib.spaprivileged.framework.common.userManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -36,13 +36,13 @@ open class BillingCycleRepository @JvmOverloads constructor(
INetworkManagementService.Stub.asInterface( INetworkManagementService.Stub.asInterface(
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE) ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)
), ),
private val telephonyRepository: TelephonyRepository = TelephonyRepository(context), private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context),
) { ) {
private val userManager = context.userManager private val userManager = context.userManager
fun isModifiableFlow(subId: Int): Flow<Boolean> = fun isModifiableFlow(subId: Int): Flow<Boolean> =
telephonyRepository.isDataEnabledFlow(subId).map { isDataEnabled -> mobileDataRepository.isMobileDataEnabledFlow(subId).map { mobileDataEnabled ->
isDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser mobileDataEnabled && isBandwidthControlEnabled() && userManager.isAdminUser
}.conflate().flowOn(Dispatchers.Default) }.conflate().flowOn(Dispatchers.Default)
open fun isBandwidthControlEnabled(): Boolean = try { open fun isBandwidthControlEnabled(): Boolean = try {

View File

@@ -23,11 +23,13 @@ import android.os.Bundle;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import com.android.settings.fuelgauge.batteryusage.BatteryDiffData;
import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType; import com.android.settings.fuelgauge.batteryusage.DetectRequestSourceType;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.fuelgauge.Estimate;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** Feature Provider used in power usage */ /** Feature Provider used in power usage */
@@ -157,4 +159,8 @@ public interface PowerUsageFeatureProvider {
/** Whether the device is under the battery defender mode */ /** Whether the device is under the battery defender mode */
boolean isBatteryDefend(BatteryInfo info); boolean isBatteryDefend(BatteryInfo info);
/** Collect and process battery reattribute data if needed. */
boolean processBatteryReattributeData(
Context context, Map<Long, BatteryDiffData> batteryDiffDataMap);
} }

View File

@@ -28,12 +28,14 @@ import android.util.ArraySet;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import com.android.internal.util.ArrayUtils; 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.DetectRequestSourceType;
import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList; import com.android.settings.fuelgauge.batteryusage.PowerAnomalyEventList;
import com.android.settingslib.fuelgauge.Estimate; import com.android.settingslib.fuelgauge.Estimate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** Implementation of {@code PowerUsageFeatureProvider} */ /** Implementation of {@code PowerUsageFeatureProvider} */
@@ -245,4 +247,10 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
public boolean isBatteryDefend(BatteryInfo info) { public boolean isBatteryDefend(BatteryInfo info) {
return info.isBatteryDefender && !isExtraDefend(); return info.isBatteryDefender && !isExtraDefend();
} }
@Override
public boolean processBatteryReattributeData(
Context context, Map<Long, BatteryDiffData> batteryDiffDataMap) {
return false;
}
} }

View File

@@ -77,11 +77,13 @@ public class BatteryDiffData {
processAndSortEntries(mSystemEntries); processAndSortEntries(mSystemEntries);
} }
long getStartTimestamp() { /** Gets the start timestamp. */
public long getStartTimestamp() {
return mStartTimestamp; return mStartTimestamp;
} }
long getEndTimestamp() { /** Gets the end timestamp. */
public long getEndTimestamp() {
return mEndTimestamp; return mEndTimestamp;
} }

View File

@@ -128,6 +128,9 @@ public final class BatteryUsageDataLoader {
final PowerUsageFeatureProvider featureProvider = final PowerUsageFeatureProvider featureProvider =
FeatureFactory.getFeatureFactory() FeatureFactory.getFeatureFactory()
.getPowerUsageFeatureProvider(); .getPowerUsageFeatureProvider();
// Collect and process battery reattribute data.
featureProvider.processBatteryReattributeData(
context, batteryDiffDataMap);
DatabaseUtils.sendBatteryUsageSlotData( DatabaseUtils.sendBatteryUsageSlotData(
context, context,
ConvertUtils.convertToBatteryUsageSlotList( ConvertUtils.convertToBatteryUsageSlotList(

View File

@@ -55,12 +55,12 @@ public final class BugReportContentProvider extends ContentProvider {
} }
writer.println("dump BatteryUsage and AppUsage states:"); writer.println("dump BatteryUsage and AppUsage states:");
LogUtils.dumpAppOptimizationModeEventHist(context, writer); LogUtils.dumpAppOptimizationModeEventHist(context, writer);
LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
LogUtils.dumpBatteryUsageDatabaseHist(context, writer); LogUtils.dumpBatteryUsageDatabaseHist(context, writer);
LogUtils.dumpAppUsageDatabaseHist(context, writer); LogUtils.dumpAppUsageDatabaseHist(context, writer);
LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer); LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer);
LogUtils.dumpBatteryEventDatabaseHist(context, writer); LogUtils.dumpBatteryEventDatabaseHist(context, writer);
LogUtils.dumpBatteryStateDatabaseHist(context, writer); LogUtils.dumpBatteryStateDatabaseHist(context, writer);
LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
} }
@Override @Override

View File

@@ -58,12 +58,16 @@ public class BatteryReattributeEntity {
@NonNull @NonNull
@Override @Override
public String toString() { public String toString() {
final BatteryReattribute batteryReattribute =
ConvertUtils.decodeBatteryReattribute(reattributeData);
final StringBuilder builder = new StringBuilder() final StringBuilder builder = new StringBuilder()
.append("\nBatteryReattributeEntity{") .append("\nBatteryReattributeEntity{")
.append("\n\t" + utcToLocalTimeForLogging(timestampStart)) .append("\n\t" + utcToLocalTimeForLogging(timestampStart))
.append("\n\t" + utcToLocalTimeForLogging(timestampEnd)) .append("\n\t" + utcToLocalTimeForLogging(timestampEnd))
.append("\n\t" + ConvertUtils.decodeBatteryReattribute(reattributeData)) .append("\n\t" + batteryReattribute);
.append("\n}"); if (batteryReattribute != null) {
return builder.toString(); builder.append("\n\t" + batteryReattribute.getReattributeDataMap());
}
return builder.append("\n}").toString();
} }
} }

View File

@@ -33,7 +33,7 @@ import androidx.room.RoomDatabase;
BatteryUsageSlotEntity.class, BatteryUsageSlotEntity.class,
BatteryReattributeEntity.class BatteryReattributeEntity.class
}, },
version = 1) version = 2)
public abstract class BatteryStateDatabase extends RoomDatabase { public abstract class BatteryStateDatabase extends RoomDatabase {
private static final String TAG = "BatteryStateDatabase"; private static final String TAG = "BatteryStateDatabase";
private static final String DB_FILE_NAME = "battery-usage-db-v10"; private static final String DB_FILE_NAME = "battery-usage-db-v10";

View File

@@ -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<Unit> {
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)
}
}
}

View File

@@ -27,9 +27,8 @@ import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo import android.telephony.UiccSlotInfo
import android.util.Log import android.util.Log
import com.android.settings.network.SimOnboardingActivity.Companion.CallbackType 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.sim.SimActivationNotifier
import com.android.settings.spa.network.setAutomaticData
import com.android.settings.spa.network.setDefaultData import com.android.settings.spa.network.setDefaultData
import com.android.settings.spa.network.setDefaultSms import com.android.settings.spa.network.setDefaultSms
import com.android.settings.spa.network.setDefaultVoice import com.android.settings.spa.network.setDefaultVoice
@@ -366,7 +365,7 @@ class SimOnboardingService {
wifiPickerTrackerHelper, wifiPickerTrackerHelper,
targetPrimarySimMobileData targetPrimarySimMobileData
) )
TelephonyRepository(context).setAutomaticData( MobileDataRepository(context).setAutoDataSwitch(
targetNonDds, targetNonDds,
targetPrimarySimAutoDataSwitch.value targetPrimarySimAutoDataSwitch.value
) )

View File

@@ -22,7 +22,6 @@ import android.telephony.TelephonyManager
import android.telephony.data.ApnSetting import android.telephony.data.ApnSetting
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.android.settings.network.mobileDataEnabledFlow
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@@ -71,7 +70,7 @@ class MmsMessagePreferenceController @JvmOverloads constructor(
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
combine( combine(
mContext.mobileDataEnabledFlow(mSubId), MobileDataRepository(mContext).mobileDataEnabledChangedFlow(mSubId),
mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes
) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) {
preferenceScreen?.let { super.displayPreference(it) } preferenceScreen?.let { super.displayPreference(it) }

View File

@@ -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<Unit> = context.subscriptionsChangedFlow(),
) {
fun isMobileDataPolicyEnabledFlow(subId: Int, @MobileDataPolicy policy: Int): Flow<Boolean> {
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<Unit> =
mobileSettingsGlobalChangedFlow(Settings.Global.MOBILE_DATA, subId, sendInitialValue)
private fun mobileSettingsGlobalChangedFlow(
name: String,
subId: Int,
sendInitialValue: Boolean = true,
): Flow<Unit> {
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<Boolean> {
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"
}
}

View File

@@ -17,98 +17,16 @@
package com.android.settings.network.telephony package com.android.settings.network.telephony
import android.content.Context import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager 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.Dispatchers
import kotlinx.coroutines.asExecutor import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn 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<Unit> = context.subscriptionsChangedFlow(),
) {
fun isMobileDataPolicyEnabledFlow(
subId: Int,
@TelephonyManager.MobileDataPolicy policy: Int,
): Flow<Boolean> {
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<Boolean> {
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]. */ /** Creates an instance of a cold Flow for Telephony callback of given [subId]. */
fun <T> Context.telephonyCallbackFlow( fun <T> Context.telephonyCallbackFlow(

View File

@@ -24,7 +24,7 @@ import android.telephony.TelephonyManager
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.R 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.ims.ImsMmTelRepositoryImpl
import com.android.settings.network.telephony.requireSubscriptionManager import com.android.settings.network.telephony.requireSubscriptionManager
import com.android.settings.network.telephony.safeGetConfig import com.android.settings.network.telephony.safeGetConfig
@@ -54,6 +54,7 @@ class CrossSimCallingViewModel(
private val scope = viewModelScope + Dispatchers.Default private val scope = viewModelScope + Dispatchers.Default
private val metricsFeatureProvider = featureFactory.metricsFeatureProvider private val metricsFeatureProvider = featureFactory.metricsFeatureProvider
private val updateChannel = Channel<Unit>() private val updateChannel = Channel<Unit>()
private val mobileDataRepository = MobileDataRepository(application)
init { init {
val resources = application.resources val resources = application.resources
@@ -81,7 +82,7 @@ class CrossSimCallingViewModel(
} }
private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId -> private fun List<Int>.anyMobileDataEnableChangedFlow() = map { subId ->
application.mobileDataEnabledFlow(subId = subId, sendInitialValue = false) mobileDataRepository.mobileDataEnabledChangedFlow(subId = subId, sendInitialValue = false)
}.merge() }.merge()
private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) { private suspend fun updateCrossSimCalling(activeSubIds: List<Int>, newEnabled: Boolean) {

View File

@@ -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;
});
}
}

View File

@@ -59,26 +59,8 @@ class ZenMode {
private static final String TAG = "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.
*
* <p>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"; 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. // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS = private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
new ZenPolicy.Builder() new ZenPolicy.Builder()
@@ -141,10 +123,8 @@ class ZenMode {
public ZenPolicy getPolicy() { public ZenPolicy getPolicy() {
switch (mRule.getInterruptionFilter()) { switch (mRule.getInterruptionFilter()) {
case INTERRUPTION_FILTER_PRIORITY: case INTERRUPTION_FILTER_PRIORITY:
return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALL: case NotificationManager.INTERRUPTION_FILTER_ALL:
return POLICY_INTERRUPTION_FILTER_ALL; return requireNonNull(mRule.getZenPolicy());
case NotificationManager.INTERRUPTION_FILTER_ALARMS: case NotificationManager.INTERRUPTION_FILTER_ALARMS:
return POLICY_INTERRUPTION_FILTER_ALARMS; return POLICY_INTERRUPTION_FILTER_ALARMS;
@@ -172,31 +152,8 @@ class ZenMode {
return; return;
} }
// A policy with CHANNEL_POLICY_ALL is only a UI representation of the if (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALL) {
// INTERRUPTION_FILTER_ALL filter. Thus, switching to or away to this value only updates Log.wtf(TAG, "Able to change policy without filtering being enabled");
// 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 policy is customized from any of the "special" ones, make the rule PRIORITY. // If policy is customized from any of the "special" ones, make the rule PRIORITY.

View File

@@ -37,10 +37,6 @@ public class ZenModeAppsFragment extends ZenModeFragmentBase {
context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend)); context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend));
controllers.add(new ZenModeAppsPreferenceController( controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_NONE, mBackend)); 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; return controllers;
} }

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; 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 static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context; import android.content.Context;
@@ -45,10 +47,12 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
private static final String TAG = "ZenModeAppsLinkPreferenceController"; private static final String TAG = "ZenModeAppsLinkPreferenceController";
private final ZenModeSummaryHelper mSummaryHelper; private final ZenModeSummaryHelper mSummaryHelper;
private final ApplicationsState mApplicationsState;
private ApplicationsState.Session mAppSession; private ApplicationsState.Session mAppSession;
private final ZenHelperBackend mHelperBackend; private final ZenHelperBackend mHelperBackend;
private ZenMode mZenMode; private ZenMode mZenMode;
private Preference mPreference; private Preference mPreference;
private final Fragment mHost;
ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host,
ApplicationsState applicationsState, ZenModesBackend backend, ApplicationsState applicationsState, ZenModesBackend backend,
@@ -56,9 +60,13 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
super(context, key, backend); super(context, key, backend);
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
mHelperBackend = helperBackend; mHelperBackend = helperBackend;
if (applicationsState != null && host != null) { mApplicationsState = applicationsState;
mAppSession = applicationsState.newSession(mAppSessionCallbacks, host.getLifecycle()); mHost = host;
} }
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
} }
@Override @Override
@@ -73,6 +81,9 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
.toIntent()); .toIntent());
mZenMode = zenMode; mZenMode = zenMode;
mPreference = preference; mPreference = preference;
if (mApplicationsState != null && mHost != null) {
mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHost.getLifecycle());
}
triggerUpdateAppsBypassingDndSummaryText(); triggerUpdateAppsBypassingDndSummaryText();
} }

View File

@@ -38,11 +38,9 @@ public class ZenModeAppsPreferenceController extends
static final String KEY_PRIORITY = "zen_mode_apps_priority"; static final String KEY_PRIORITY = "zen_mode_apps_priority";
static final String KEY_NONE = "zen_mode_apps_none"; static final String KEY_NONE = "zen_mode_apps_none";
static final String KEY_ALL = "zen_mode_apps_all";
String mModeId; String mModeId;
public ZenModeAppsPreferenceController(@NonNull Context context, public ZenModeAppsPreferenceController(@NonNull Context context,
@NonNull String key, @Nullable ZenModesBackend backend) { @NonNull String key, @Nullable ZenModesBackend backend) {
super(context, key, backend); super(context, key, backend);
@@ -79,13 +77,6 @@ public class ZenModeAppsPreferenceController extends
== ZenPolicy.CHANNEL_POLICY_NONE; == ZenPolicy.CHANNEL_POLICY_NONE;
pref.setChecked(policy_none); pref.setChecked(policy_none);
break; 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)); return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY));
case KEY_NONE: case KEY_NONE:
return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)); return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE));
case KEY_ALL:
return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL));
} }
return true; return true;
} }

View File

@@ -53,6 +53,8 @@ public class ZenModeFragment extends ZenModeFragmentBase {
context, "mode_display_settings", mBackend, mHelperBackend)); context, "mode_display_settings", mBackend, mHelperBackend));
prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context, prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context,
"zen_automatic_trigger_category", this, mBackend)); "zen_automatic_trigger_category", this, mBackend));
prefControllers.add(new InterruptionFilterPreferenceController(
context, "allow_filtering", mBackend));
return prefControllers; return prefControllers;
} }

View File

@@ -120,10 +120,6 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase {
} }
for (List<AbstractPreferenceController> list : getPreferenceControllers()) { for (List<AbstractPreferenceController> list : getPreferenceControllers()) {
for (AbstractPreferenceController controller : list) { for (AbstractPreferenceController controller : list) {
if (!controller.isAvailable()) {
continue;
}
try { try {
// Find preference associated with controller // Find preference associated with controller
final String key = controller.getPreferenceKey(); 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", String.format("Cannot find preference with key %s in Controller %s",
key, controller.getClass().getSimpleName())); key, controller.getClass().getSimpleName()));
} }
controller.displayPreference(screen);
} catch (ClassCastException e) { } catch (ClassCastException e) {
// Skip any controllers that aren't AbstractZenModePreferenceController. // Skip any controllers that aren't AbstractZenModePreferenceController.
Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName()); Log.d(TAG, "Could not cast: " + controller.getClass().getSimpleName());

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; 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 static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context; import android.content.Context;
@@ -36,6 +38,11 @@ class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceC
mSummaryBuilder = new ZenModeSummaryHelper(context, helperBackend); mSummaryBuilder = new ZenModeSummaryHelper(context, helperBackend);
} }
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; 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 static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context; import android.content.Context;
@@ -39,6 +41,11 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
} }
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; 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 static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context; import android.content.Context;
@@ -39,6 +41,11 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend);
} }
@Override
public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
}
@Override @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();

View File

@@ -15,6 +15,7 @@
*/ */
package com.android.settings.notification.modes; 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_ANYONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
@@ -187,7 +188,8 @@ class ZenModeSummaryHelper {
String getDisplayEffectsSummary(ZenMode zenMode) { String getDisplayEffectsSummary(ZenMode zenMode) {
boolean isFirst = true; boolean isFirst = true;
List<String> enabledEffects = new ArrayList<>(); List<String> enabledEffects = new ArrayList<>();
if (!zenMode.getPolicy().shouldShowAllVisualEffects()) { if (!zenMode.getPolicy().shouldShowAllVisualEffects()
&& zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
enabledEffects.add(getBlockedEffectsSummary(zenMode)); enabledEffects.add(getBlockedEffectsSummary(zenMode));
isFirst = false; isFirst = false;
} }
@@ -411,8 +413,6 @@ class ZenModeSummaryHelper {
return formatAppsList(appsBypassing); return formatAppsList(appsBypassing);
} else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) { } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
return mContext.getResources().getString(R.string.zen_mode_apps_none_apps); 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 ""; return "";
} }

View File

@@ -16,13 +16,11 @@
package com.android.settings.spa.network package com.android.settings.spa.network
import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel import com.android.settings.network.telephony.wificalling.CrossSimCallingViewModel
import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel 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,
)
}

View File

@@ -16,12 +16,10 @@
package com.android.settings.spa.network package com.android.settings.spa.network
import android.telephony.TelephonyManager
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.android.settings.R 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.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@@ -48,13 +48,14 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.settings.R import com.android.settings.R
import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.SubscriptionInfoListViewModel
import com.android.settings.network.telephony.DataSubscriptionRepository 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.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.wifi.WifiPickerTrackerHelper import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator 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.framework.util.collectLatestWithLifecycle
import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -202,21 +203,18 @@ fun MobileDataSectionImpl(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val localLifecycleOwner = LocalLifecycleOwner.current val localLifecycleOwner = LocalLifecycleOwner.current
val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner) val mobileDataRepository = rememberContext(::MobileDataRepository)
val subscriptionManager: SubscriptionManager? =
context.getSystemService(SubscriptionManager::class.java)
Category(title = stringResource(id = R.string.mobile_data_settings_title)) { Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
val isAutoDataEnabled by remember(nonDds.intValue) { val isAutoDataEnabled by remember(nonDds.intValue) {
TelephonyRepository(context).isMobileDataPolicyEnabledFlow( mobileDataRepository.isMobileDataPolicyEnabledFlow(
subId = nonDds.intValue, subId = nonDds.intValue,
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
) )
}.collectAsStateWithLifecycle(initialValue = null) }.collectAsStateWithLifecycle(initialValue = null)
val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) { val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
TelephonyRepository(context).isDataEnabledFlow(mobileDataSelectedId.intValue) mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue)
}.collectAsStateWithLifecycle(initialValue = false) }.collectAsStateWithLifecycle(initialValue = false)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
@@ -226,8 +224,8 @@ fun MobileDataSectionImpl(
coroutineScope.launch { coroutineScope.launch {
setMobileData( setMobileData(
context, context,
subscriptionManager, context.getSystemService(SubscriptionManager::class.java),
wifiPickerTrackerHelper, getWifiPickerTrackerHelper(context, localLifecycleOwner),
mobileDataSelectedId.intValue, mobileDataSelectedId.intValue,
newEnabled newEnabled
) )
@@ -238,7 +236,7 @@ fun MobileDataSectionImpl(
AutomaticDataSwitchingPreference( AutomaticDataSwitchingPreference(
isAutoDataEnabled = { isAutoDataEnabled }, isAutoDataEnabled = { isAutoDataEnabled },
setAutoDataEnabled = { newEnabled -> 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]") Log.d(NetworkCellularGroupProvider.fileName, "setDefaultData: [$targetSubId]")
subscriptionManager?.setDefaultDataSubId(targetSubId) subscriptionManager?.setDefaultDataSubId(targetSubId)
} }
TelephonyRepository(context) MobileDataRepository(context)
.setMobileData(targetSubId, enabled, wifiPickerTrackerHelper) .setMobileDataEnabled(targetSubId, enabled, wifiPickerTrackerHelper)
} }

View File

@@ -363,13 +363,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
} }
if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled() if (mUserInfo.isMain() || mUserInfo.isGuest() || !UserManager.isMultipleAdminEnabled()
|| mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN, || mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_GRANT_ADMIN,
mUserInfo.getUserHandle())) { mUserInfo.getUserHandle()) || !mUserManager.isAdminUser()) {
removePreference(KEY_GRANT_ADMIN); removePreference(KEY_GRANT_ADMIN);
} }
if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls
removePreference(KEY_ENABLE_TELEPHONY); removePreference(KEY_ENABLE_TELEPHONY);
removePreference(KEY_REMOVE_USER); removePreference(KEY_REMOVE_USER);
removePreference(KEY_GRANT_ADMIN);
removePreference(KEY_APP_AND_CONTENT_ACCESS); removePreference(KEY_APP_AND_CONTENT_ACCESS);
removePreference(KEY_APP_COPYING); removePreference(KEY_APP_COPYING);
} else { } else {

View File

@@ -21,26 +21,35 @@ import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
import static com.google.common.truth.Truth.assertThat; 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.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.media.AudioDeviceAttributes; import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo; import android.media.AudioDeviceInfo;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.Spatializer; import android.media.Spatializer;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference; import androidx.preference.TwoStatePreference;
import com.android.settings.testutils.FakeFeatureFactory; 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.core.lifecycle.Lifecycle;
import com.android.settingslib.flags.Flags;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -54,7 +63,8 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase { 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 MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String KEY_SPATIAL_AUDIO = "spatial_audio"; private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
private static final String KEY_HEAD_TRACKING = "head_tracking"; private static final String KEY_HEAD_TRACKING = "head_tracking";
@@ -64,6 +74,9 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
@Mock private Lifecycle mSpatialAudioLifecycle; @Mock private Lifecycle mSpatialAudioLifecycle;
@Mock private PreferenceCategory mProfilesContainer; @Mock private PreferenceCategory mProfilesContainer;
@Mock private BluetoothDevice mBluetoothDevice; @Mock private BluetoothDevice mBluetoothDevice;
@Mock private A2dpProfile mA2dpProfile;
@Mock private LeAudioProfile mLeAudioProfile;
@Mock private HearingAidProfile mHearingAidProfile;
private AudioDeviceAttributes mAvailableDevice; private AudioDeviceAttributes mAvailableDevice;
@@ -83,6 +96,12 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
when(mAudioManager.getSpatializer()).thenReturn(mSpatializer); when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS); when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice); 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(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext)) when(mFeatureFactory.getBluetoothFeatureProvider().getSpatializer(mContext))
.thenReturn(mSpatializer); .thenReturn(mSpatializer);
@@ -272,6 +291,52 @@ public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetails
false); 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 @Test
public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() { public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
mController.setAvailableDevice(mAvailableDevice); mController.setAvailableDevice(mAvailableDevice);

View File

@@ -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<ZenMode> 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<ZenMode> 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);
}
}

View File

@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -180,13 +181,30 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
@Test @Test
public void testOnPackageListChangedTriggersRebuild() { 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)); verify(mSession).rebuild(any(), any(), eq(false));
mController.mAppSessionCallbacks.onPackageListChanged();
verify(mSession, times(2)).rebuild(any(), any(), eq(false));
} }
@Test @Test
public void testOnLoadEntriesCompletedTriggersRebuild() { 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)); 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();
} }
} }

View File

@@ -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_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; 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_NONE;
import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY; import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY;
@@ -62,11 +61,9 @@ public final class ZenModeAppsPreferenceControllerTest {
@Mock @Mock
private ZenModesBackend mBackend; private ZenModesBackend mBackend;
private ZenModeAppsPreferenceController mPriorityController; private ZenModeAppsPreferenceController mPriorityController;
private ZenModeAppsPreferenceController mAllController;
private ZenModeAppsPreferenceController mNoneController; private ZenModeAppsPreferenceController mNoneController;
private SelectorWithWidgetPreference mPriorityPref; private SelectorWithWidgetPreference mPriorityPref;
private SelectorWithWidgetPreference mAllPref;
private SelectorWithWidgetPreference mNonePref; private SelectorWithWidgetPreference mNonePref;
private PreferenceCategory mPrefCategory; private PreferenceCategory mPrefCategory;
private PreferenceScreen mPreferenceScreen; private PreferenceScreen mPreferenceScreen;
@@ -81,10 +78,8 @@ public final class ZenModeAppsPreferenceControllerTest {
mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend); mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend);
mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend); mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend);
mAllController = new ZenModeAppsPreferenceController(mContext, KEY_ALL, mBackend);
mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController); mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController);
mAllPref = makePreference(KEY_ALL, mAllController);
mNonePref = makePreference(KEY_NONE, mNoneController); mNonePref = makePreference(KEY_NONE, mNoneController);
mPrefCategory = new PreferenceCategory(mContext); mPrefCategory = new PreferenceCategory(mContext);
@@ -95,10 +90,8 @@ public final class ZenModeAppsPreferenceControllerTest {
mPreferenceScreen.addPreference(mPrefCategory); mPreferenceScreen.addPreference(mPrefCategory);
mPrefCategory.addPreference(mPriorityPref); mPrefCategory.addPreference(mPriorityPref);
mPrefCategory.addPreference(mAllPref);
mPrefCategory.addPreference(mNonePref); mPrefCategory.addPreference(mNonePref);
mAllController.displayPreference(mPreferenceScreen);
mPriorityController.displayPreference(mPreferenceScreen); mPriorityController.displayPreference(mPreferenceScreen);
mNoneController.displayPreference(mPreferenceScreen); mNoneController.displayPreference(mPreferenceScreen);
} }
@@ -111,36 +104,6 @@ public final class ZenModeAppsPreferenceControllerTest {
return pref; 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 @Test
public void testUpdateState_None() { public void testUpdateState_None() {
TwoStatePreference preference = mock(TwoStatePreference.class); TwoStatePreference preference = mock(TwoStatePreference.class);
@@ -163,7 +126,7 @@ public final class ZenModeAppsPreferenceControllerTest {
new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING) .setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder() .setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenMode.CHANNEL_POLICY_ALL) .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
.build()) .build())
.build(), true); .build(), true);
mNoneController.updateZenMode(preference, zenMode); mNoneController.updateZenMode(preference, zenMode);
@@ -201,67 +164,6 @@ public final class ZenModeAppsPreferenceControllerTest {
verify(preference).setChecked(false); 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<ZenMode> 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<ZenMode> 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 @Test
public void testPreferenceClick_passesCorrectCheckedState_None() { public void testPreferenceClick_passesCorrectCheckedState_None() {
ZenMode zenMode = new ZenMode("id", ZenMode zenMode = new ZenMode("id",
@@ -272,12 +174,9 @@ public final class ZenModeAppsPreferenceControllerTest {
.build()) .build())
.build(), true); .build(), true);
mAllController.updateZenMode(mAllPref, zenMode);
mNoneController.updateZenMode(mNonePref, zenMode); mNoneController.updateZenMode(mNonePref, zenMode);
mPriorityController.updateZenMode(mPriorityPref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode);
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked()); .isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
@@ -296,8 +195,6 @@ public final class ZenModeAppsPreferenceControllerTest {
// NONE is now checked; others are unchecked. // NONE is now checked; others are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked()); .isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked()); .isChecked());
} }
@@ -312,14 +209,11 @@ public final class ZenModeAppsPreferenceControllerTest {
.build()) .build())
.build(), true); .build(), true);
mAllController.updateZenMode(mAllPref, zenMode);
mNoneController.updateZenMode(mNonePref, zenMode); mNoneController.updateZenMode(mNonePref, zenMode);
mPriorityController.updateZenMode(mPriorityPref, zenMode); mPriorityController.updateZenMode(mPriorityPref, zenMode);
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked()); .isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked()); .isChecked());
@@ -334,8 +228,6 @@ public final class ZenModeAppsPreferenceControllerTest {
// PRIORITY is now checked; others are unchecked. // PRIORITY is now checked; others are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY)) assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked()); .isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE)) assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked()); .isChecked());
} }

View File

@@ -70,18 +70,6 @@ public class ZenModeTest {
assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); 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 @Test
public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() { public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() {
ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) 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.getPolicy()).isEqualTo(ZEN_POLICY);
assertThat(zenMode.getRule().getZenPolicy()).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);
}
} }

View File

@@ -331,20 +331,6 @@ public class ZenModesSummaryHelperTest {
"Notifications partially hidden, grayscale, and 2 more"); "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 @Test
public void getAppsSummary_none() { public void getAppsSummary_none() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed")) AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))

View File

@@ -39,7 +39,6 @@ import com.android.settings.testutils.shadow.ShadowSecureSettings;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -126,7 +125,6 @@ public class TimeoutToDockUserPreferenceControllerTest {
BasePreferenceController.CONDITIONALLY_UNAVAILABLE); BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
} }
@Ignore("b/313530297")
@Test @Test
public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() { public void getAvailabilityStatus_isCurrentlyMainUser_returnDisabledForUser() {
when(mUserManager.getMainUser()).thenReturn(UserHandle.CURRENT); when(mUserManager.getMainUser()).thenReturn(UserHandle.CURRENT);
@@ -136,7 +134,6 @@ public class TimeoutToDockUserPreferenceControllerTest {
BasePreferenceController.DISABLED_FOR_USER); BasePreferenceController.DISABLED_FOR_USER);
} }
@Ignore("b/313530297")
@Test @Test
public void getAvailabilityStatus_featureAndMultiUserEnabledAndNonMainUser_returnAvailable() { public void getAvailabilityStatus_featureAndMultiUserEnabledAndNonMainUser_returnAvailable() {
when(mUserManager.isUserForeground()).thenReturn(true); when(mUserManager.isUserForeground()).thenReturn(true);

View File

@@ -22,7 +22,7 @@ import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED
import static com.google.common.truth.Truth.assertThat; 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.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
@@ -63,7 +63,6 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
@@ -222,8 +221,8 @@ public class UserDetailsSettingsTest {
} }
@Test @Test
@Ignore("b/313530297")
public void onResume_canSwitch_shouldEnableSwitchPref() { public void onResume_canSwitch_shouldEnableSwitchPref() {
setupSelectedUser();
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK);
mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.onAttach(mContext); mFragment.onAttach(mContext);
@@ -234,8 +233,8 @@ public class UserDetailsSettingsTest {
} }
@Test @Test
@Ignore("b/313530297")
public void onResume_userInCall_shouldDisableSwitchPref() { public void onResume_userInCall_shouldDisableSwitchPref() {
setupSelectedUser();
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_IN_CALL); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_IN_CALL);
mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.onAttach(mContext); mFragment.onAttach(mContext);
@@ -246,8 +245,8 @@ public class UserDetailsSettingsTest {
} }
@Test @Test
@Ignore("b/313530297")
public void onResume_switchDisallowed_shouldDisableSwitchPref() { public void onResume_switchDisallowed_shouldDisableSwitchPref() {
setupSelectedUser();
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED); mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.onAttach(mContext); mFragment.onAttach(mContext);
@@ -258,8 +257,8 @@ public class UserDetailsSettingsTest {
} }
@Test @Test
@Ignore("b/313530297")
public void onResume_systemUserLocked_shouldDisableSwitchPref() { public void onResume_systemUserLocked_shouldDisableSwitchPref() {
setupSelectedUser();
mUserManager.setSwitchabilityStatus(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED); mUserManager.setSwitchabilityStatus(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED);
mFragment.mSwitchUserPref = mSwitchUserPref; mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.onAttach(mContext); mFragment.onAttach(mContext);
@@ -269,7 +268,6 @@ public class UserDetailsSettingsTest {
verify(mSwitchUserPref).setEnabled(false); verify(mSwitchUserPref).setEnabled(false);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_adminWithTelephony_shouldShowPhonePreference() { public void initialize_adminWithTelephony_shouldShowPhonePreference() {
setupSelectedUser(); setupSelectedUser();
@@ -315,7 +313,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS); verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() { public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() {
setupSelectedUser(); setupSelectedUser();
@@ -328,7 +325,6 @@ public class UserDetailsSettingsTest {
verify(mFragment, never()).removePreference(KEY_REMOVE_USER); verify(mFragment, never()).removePreference(KEY_REMOVE_USER);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_adminSelectsNewRestrictedUser_shouldOpenAppContentScreen() { public void initialize_adminSelectsNewRestrictedUser_shouldOpenAppContentScreen() {
setupSelectedRestrictedUser(); setupSelectedRestrictedUser();
@@ -351,7 +347,6 @@ public class UserDetailsSettingsTest {
.isEqualTo(true); .isEqualTo(true);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_adminSelectsRestrictedUser_shouldSetupPreferences() { public void initialize_adminSelectsRestrictedUser_shouldSetupPreferences() {
setupSelectedRestrictedUser(); setupSelectedRestrictedUser();
@@ -381,7 +376,6 @@ public class UserDetailsSettingsTest {
verify(mActivity, never()).startActivity(any(Intent.class)); verify(mActivity, never()).startActivity(any(Intent.class));
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_adminSelectsGuest_shouldShowRemovePreference() { public void initialize_adminSelectsGuest_shouldShowRemovePreference() {
setupSelectedGuest(); setupSelectedGuest();
@@ -425,7 +419,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).removePreference(KEY_REMOVE_USER); verify(mFragment).removePreference(KEY_REMOVE_USER);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_userHasCallRestriction_shouldSetPhoneSwitchUnChecked() { public void initialize_userHasCallRestriction_shouldSetPhoneSwitchUnChecked() {
setupSelectedUser(); setupSelectedUser();
@@ -438,7 +431,6 @@ public class UserDetailsSettingsTest {
verify(mPhonePref).setChecked(false); verify(mPhonePref).setChecked(false);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_noCallRestriction_shouldSetPhoneSwitchChecked() { public void initialize_noCallRestriction_shouldSetPhoneSwitchChecked() {
setupSelectedUser(); setupSelectedUser();
@@ -537,7 +529,6 @@ public class UserDetailsSettingsTest {
verify(mFragment, never()).switchUser(); verify(mFragment, never()).switchUser();
} }
@Ignore("b/313530297")
@Test @Test
public void onPreferenceClick_removeGuestClicked_canDelete_shouldShowDialog() { public void onPreferenceClick_removeGuestClicked_canDelete_shouldShowDialog() {
setupSelectedGuest(); setupSelectedGuest();
@@ -555,7 +546,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).showDialog(DIALOG_CONFIRM_RESET_GUEST); verify(mFragment).showDialog(DIALOG_CONFIRM_RESET_GUEST);
} }
@Ignore("b/313530297")
@Test @Test
public void onPreferenceClick_removeRestrictedClicked_canDelete_shouldShowDialog() { public void onPreferenceClick_removeRestrictedClicked_canDelete_shouldShowDialog() {
setupSelectedRestrictedUser(); setupSelectedRestrictedUser();
@@ -574,7 +564,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE); verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE);
} }
@Ignore("b/313530297")
@Test @Test
public void onPreferenceClick_removeClicked_canDelete_shouldShowDialog() { public void onPreferenceClick_removeClicked_canDelete_shouldShowDialog() {
setupSelectedUser(); setupSelectedUser();
@@ -666,7 +655,6 @@ public class UserDetailsSettingsTest {
assertThat(result).isFalse(); assertThat(result).isFalse();
} }
@Ignore("b/313530297")
@Test @Test
public void canDeleteUser_adminSelectsUser_noRestrictions_shouldReturnTrue() { public void canDeleteUser_adminSelectsUser_noRestrictions_shouldReturnTrue() {
setupSelectedUser(); setupSelectedUser();
@@ -700,17 +688,16 @@ public class UserDetailsSettingsTest {
assertThat(result).isFalse(); assertThat(result).isFalse();
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_userSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() { public void initialize_userSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() {
assumeTrue(UserManager.isHeadlessSystemUserMode());
setupSelectedUser(); setupSelectedUser();
mUserManager.setIsAdminUser(true);
ShadowUserManager.setIsMultipleAdminEnabled(true); ShadowUserManager.setIsMultipleAdminEnabled(true);
mFragment.initialize(mActivity, mArguments); mFragment.initialize(mActivity, mArguments);
assertTrue(UserManager.isMultipleAdminEnabled());
verify(mFragment, never()).removePreference(KEY_GRANT_ADMIN); verify(mFragment, never()).removePreference(KEY_GRANT_ADMIN);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_userSelected_shouldNotShowGrantAdminPref() { public void initialize_userSelected_shouldNotShowGrantAdminPref() {
setupSelectedUser(); setupSelectedUser();
@@ -718,7 +705,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).removePreference(KEY_GRANT_ADMIN); verify(mFragment).removePreference(KEY_GRANT_ADMIN);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() { public void initialize_restrictUserSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
setupSelectedUser(); setupSelectedUser();
@@ -729,7 +715,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).removePreference(KEY_GRANT_ADMIN); verify(mFragment).removePreference(KEY_GRANT_ADMIN);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() { public void initialize_mainUserSelected_shouldShowGrantAdminPref_MultipleAdminEnabled() {
setupSelectedMainUser(); setupSelectedMainUser();
@@ -738,7 +723,6 @@ public class UserDetailsSettingsTest {
verify(mFragment).removePreference(KEY_GRANT_ADMIN); verify(mFragment).removePreference(KEY_GRANT_ADMIN);
} }
@Ignore("b/313530297")
@Test @Test
public void initialize_guestSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() { public void initialize_guestSelected_shouldNotShowGrantAdminPref_MultipleAdminEnabled() {
setupSelectedGuest(); setupSelectedGuest();
@@ -778,7 +762,7 @@ public class UserDetailsSettingsTest {
mUserInfo = new UserInfo(1, "Tom", null, mUserInfo = new UserInfo(1, "Tom", null,
UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED,
UserManager.USER_TYPE_FULL_SECONDARY); UserManager.USER_TYPE_FULL_SECONDARY);
mFragment.mUserInfo = mUserInfo;
mUserManager.addProfile(mUserInfo); mUserManager.addProfile(mUserInfo);
} }
@@ -787,7 +771,7 @@ public class UserDetailsSettingsTest {
mUserInfo = new UserInfo(11, "Jerry", null, mUserInfo = new UserInfo(11, "Jerry", null,
UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MAIN, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MAIN,
UserManager.USER_TYPE_FULL_SECONDARY); UserManager.USER_TYPE_FULL_SECONDARY);
mFragment.mUserInfo = mUserInfo;
mUserManager.addProfile(mUserInfo); mUserManager.addProfile(mUserInfo);
} }
@@ -796,7 +780,7 @@ public class UserDetailsSettingsTest {
mUserInfo = new UserInfo(12, "Andy", null, mUserInfo = new UserInfo(12, "Andy", null,
UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN,
UserManager.USER_TYPE_FULL_SECONDARY); UserManager.USER_TYPE_FULL_SECONDARY);
mFragment.mUserInfo = mUserInfo;
mUserManager.addProfile(mUserInfo); mUserManager.addProfile(mUserInfo);
} }
@@ -805,7 +789,7 @@ public class UserDetailsSettingsTest {
mUserInfo = new UserInfo(23, "Guest", null, mUserInfo = new UserInfo(23, "Guest", null,
UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST,
UserManager.USER_TYPE_FULL_GUEST); UserManager.USER_TYPE_FULL_GUEST);
mFragment.mUserInfo = mUserInfo;
mUserManager.addProfile(mUserInfo); mUserManager.addProfile(mUserInfo);
} }
@@ -814,7 +798,7 @@ public class UserDetailsSettingsTest {
mUserInfo = new UserInfo(21, "Bob", null, mUserInfo = new UserInfo(21, "Bob", null,
UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED, UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED,
UserManager.USER_TYPE_FULL_RESTRICTED); UserManager.USER_TYPE_FULL_RESTRICTED);
mFragment.mUserInfo = mUserInfo;
mUserManager.addProfile(mUserInfo); mUserManager.addProfile(mUserInfo);
} }
} }

View File

@@ -362,6 +362,10 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
} }
} }
@Implementation
protected boolean isAdminUser() {
return getUserInfo(UserHandle.myUserId()).isAdmin();
}
@Implementation @Implementation
protected boolean isGuestUser() { protected boolean isGuestUser() {
return mIsGuestUser; return mIsGuestUser;

View File

@@ -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
}
}

View File

@@ -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<TelephonyManager> { 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
}
}

View File

@@ -17,14 +17,12 @@
package com.android.settings.network.telephony package com.android.settings.network.telephony
import android.content.Context import android.content.Context
import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -33,91 +31,29 @@ import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.spy import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class TelephonyRepositoryTest { class TelephonyRepositoryTest {
private var telephonyCallback: TelephonyCallback? = null private var telephonyCallback: TelephonyCallback? = null
private val mockTelephonyManager = mock<TelephonyManager> { private val mockTelephonyManager =
on { createForSubscriptionId(SUB_ID) } doReturn mock mock<TelephonyManager> {
on { registerTelephonyCallback(any(), any()) } doAnswer { on { createForSubscriptionId(SUB_ID) } doReturn mock
telephonyCallback = it.arguments[1] as TelephonyCallback 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
} }
val flow = repository.isMobileDataPolicyEnabledFlow( private val context: Context =
subId = SUB_ID, spy(ApplicationProvider.getApplicationContext()) {
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
)
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
} }
val state = repository.isDataEnabledFlow(subId = SUB_ID)
assertThat(state.firstWithTimeoutOrNull()).isTrue()
}
@Test @Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking { fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull() flow.firstWithTimeoutOrNull()
@@ -126,9 +62,7 @@ class TelephonyRepositoryTest {
@Test @Test
fun telephonyCallbackFlow_callbackUnregistered() = runBlocking { fun telephonyCallbackFlow_callbackUnregistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) { object : TelephonyCallback() {} }
object : TelephonyCallback() {}
}
flow.firstWithTimeoutOrNull() flow.firstWithTimeoutOrNull()