Snap for 12565212 from f201899e7c to 25Q1-release

Change-Id: Id37b31cdeb0482a31e4b467c4d3a62a14a1992a9
This commit is contained in:
Android Build Coastguard Worker
2024-10-28 23:23:48 +00:00
17 changed files with 363 additions and 108 deletions

View File

@@ -421,11 +421,13 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
List<String> invisibleProfiles = List.of(); List<String> invisibleProfiles = List.of();
if (Flags.enableBluetoothDeviceDetailsPolish()) { if (Flags.enableBluetoothDeviceDetailsPolish()) {
mFormatter = if (mFormatter == null) {
FeatureFactory.getFeatureFactory() mFormatter =
.getBluetoothFeatureProvider() FeatureFactory.getFeatureFactory()
.getDeviceDetailsFragmentFormatter( .getBluetoothFeatureProvider()
requireContext(), this, mBluetoothAdapter, mCachedDevice); .getDeviceDetailsFragmentFormatter(
requireContext(), this, mBluetoothAdapter, mCachedDevice);
}
invisibleProfiles = invisibleProfiles =
mFormatter.getInvisibleBluetoothProfiles( mFormatter.getInvisibleBluetoothProfiles(
FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE); FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE);

View File

@@ -25,7 +25,6 @@ import android.media.Spatializer;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleCoroutineScope;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
@@ -34,12 +33,12 @@ import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository; import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository;
import kotlinx.coroutines.CoroutineScope;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
/** /** Provider for bluetooth related features. */
* Provider for bluetooth related features.
*/
public interface BluetoothFeatureProvider { public interface BluetoothFeatureProvider {
/** /**
@@ -86,26 +85,25 @@ public interface BluetoothFeatureProvider {
/** /**
* Gets the bluetooth profile preference keys which should be hidden in the device details page. * Gets the bluetooth profile preference keys which should be hidden in the device details page.
* *
* @param context Context * @param context Context
* @param bluetoothDevice the bluetooth device * @param bluetoothDevice the bluetooth device
* @return the profiles which should be hidden * @return the profiles which should be hidden
*/ */
Set<String> getInvisibleProfilePreferenceKeys( Set<String> getInvisibleProfilePreferenceKeys(Context context, BluetoothDevice bluetoothDevice);
Context context, BluetoothDevice bluetoothDevice);
/** Gets DeviceSettingRepository. */ /** Gets DeviceSettingRepository. */
@NonNull @NonNull
DeviceSettingRepository getDeviceSettingRepository( DeviceSettingRepository getDeviceSettingRepository(
@NonNull Context context, @NonNull Context context,
@NonNull BluetoothAdapter bluetoothAdapter, @NonNull BluetoothAdapter bluetoothAdapter,
@NonNull LifecycleCoroutineScope scope); @NonNull CoroutineScope scope);
/** Gets spatial audio interactor. */ /** Gets spatial audio interactor. */
@NonNull @NonNull
SpatialAudioInteractor getSpatialAudioInteractor( SpatialAudioInteractor getSpatialAudioInteractor(
@NonNull Context context, @NonNull Context context,
@NonNull AudioManager audioManager, @NonNull AudioManager audioManager,
@NonNull LifecycleCoroutineScope scope); @NonNull CoroutineScope scope);
/** Gets device details fragment layout formatter. */ /** Gets device details fragment layout formatter. */
@NonNull @NonNull

View File

@@ -22,6 +22,7 @@ import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import android.media.Spatializer import android.media.Spatializer
import android.net.Uri import android.net.Uri
import android.util.Log
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import androidx.preference.Preference import androidx.preference.Preference
import com.android.settings.SettingsPreferenceFragment import com.android.settings.SettingsPreferenceFragment
@@ -37,6 +38,7 @@ import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
import com.android.settingslib.media.domain.interactor.SpatializerInteractor import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet import com.google.common.collect.ImmutableSet
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
/** Impl of [BluetoothFeatureProvider] */ /** Impl of [BluetoothFeatureProvider] */
@@ -76,14 +78,14 @@ open class BluetoothFeatureProviderImpl : BluetoothFeatureProvider {
override fun getDeviceSettingRepository( override fun getDeviceSettingRepository(
context: Context, context: Context,
bluetoothAdapter: BluetoothAdapter, bluetoothAdapter: BluetoothAdapter,
scope: LifecycleCoroutineScope scope: CoroutineScope
): DeviceSettingRepository = ): DeviceSettingRepository =
DeviceSettingRepositoryImpl(context, bluetoothAdapter, scope, Dispatchers.IO) DeviceSettingRepositoryImpl(context, bluetoothAdapter, scope, Dispatchers.IO)
override fun getSpatialAudioInteractor( override fun getSpatialAudioInteractor(
context: Context, context: Context,
audioManager: AudioManager, audioManager: AudioManager,
scope: LifecycleCoroutineScope scope: CoroutineScope,
): SpatialAudioInteractor { ): SpatialAudioInteractor {
return SpatialAudioInteractorImpl( return SpatialAudioInteractorImpl(
context, audioManager, context, audioManager,

View File

@@ -147,7 +147,7 @@ class SpatialAudioInteractorImpl(
} }
companion object { companion object {
private const val TAG = "SpatialAudioInteractorImpl" private const val TAG = "SpatialAudioInteractor"
private const val INDEX_SPATIAL_AUDIO_OFF = 0 private const val INDEX_SPATIAL_AUDIO_OFF = 0
private const val INDEX_SPATIAL_AUDIO_ON = 1 private const val INDEX_SPATIAL_AUDIO_ON = 1
private const val INDEX_HEAD_TRACKING_ENABLED = 2 private const val INDEX_HEAD_TRACKING_ENABLED = 2

View File

@@ -19,11 +19,10 @@ package com.android.settings.bluetooth.ui.view
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -33,14 +32,12 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference import androidx.preference.Preference
import com.android.settings.R import com.android.settings.R
import com.android.settings.SettingsPreferenceFragment import com.android.settings.SettingsPreferenceFragment
@@ -52,7 +49,6 @@ import com.android.settings.bluetooth.ui.model.FragmentTypeModel
import com.android.settings.bluetooth.ui.view.DeviceDetailsMoreSettingsFragment.Companion.KEY_DEVICE_ADDRESS import com.android.settings.bluetooth.ui.view.DeviceDetailsMoreSettingsFragment.Companion.KEY_DEVICE_ADDRESS
import com.android.settings.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel import com.android.settings.bluetooth.ui.viewmodel.BluetoothDeviceDetailsViewModel
import com.android.settings.core.SubSettingLauncher import com.android.settings.core.SubSettingLauncher
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settings.spa.preference.ComposePreference import com.android.settings.spa.preference.ComposePreference
import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingActionModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingActionModel
@@ -97,29 +93,17 @@ interface DeviceDetailsFragmentFormatter {
class DeviceDetailsFragmentFormatterImpl( class DeviceDetailsFragmentFormatterImpl(
private val context: Context, private val context: Context,
private val fragment: SettingsPreferenceFragment, private val fragment: SettingsPreferenceFragment,
bluetoothAdapter: BluetoothAdapter, private val bluetoothAdapter: BluetoothAdapter,
private val cachedDevice: CachedBluetoothDevice, private val cachedDevice: CachedBluetoothDevice,
private val backgroundCoroutineContext: CoroutineContext, private val backgroundCoroutineContext: CoroutineContext,
) : DeviceDetailsFragmentFormatter { ) : DeviceDetailsFragmentFormatter {
private val repository =
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
fragment.requireActivity().application,
bluetoothAdapter,
fragment.lifecycleScope,
)
private val spatialAudioInteractor =
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
fragment.requireActivity().application,
context.getSystemService(AudioManager::class.java),
fragment.lifecycleScope,
)
private val viewModel: BluetoothDeviceDetailsViewModel = private val viewModel: BluetoothDeviceDetailsViewModel =
ViewModelProvider( ViewModelProvider(
fragment, fragment,
BluetoothDeviceDetailsViewModel.Factory( BluetoothDeviceDetailsViewModel.Factory(
fragment.requireActivity().application, fragment.requireActivity().application,
repository, bluetoothAdapter,
spatialAudioInteractor,
cachedDevice, cachedDevice,
backgroundCoroutineContext, backgroundCoroutineContext,
), ),
@@ -224,8 +208,8 @@ class DeviceDetailsFragmentFormatterImpl(
val settings = contents val settings = contents
AnimatedVisibility( AnimatedVisibility(
visible = settings.isNotEmpty(), visible = settings.isNotEmpty(),
enter = expandVertically(expandFrom = Alignment.Top), enter = fadeIn(),
exit = shrinkVertically(shrinkTowards = Alignment.Top), exit = fadeOut(),
) { ) {
Box { Box {
Box( Box(

View File

@@ -120,13 +120,15 @@ class DeviceDetailsMoreSettingsFragment : DashboardFragment() {
finish() finish()
return emptyList() return emptyList()
} }
formatter = if (!this::formatter.isInitialized) {
featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter( formatter =
requireContext(), featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(
this, requireContext(),
bluetoothManager.adapter, this,
cachedDevice, bluetoothManager.adapter,
) cachedDevice,
)
}
helpItem = helpItem =
formatter formatter
.getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) .getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)

View File

@@ -17,20 +17,22 @@
package com.android.settings.bluetooth.ui.viewmodel package com.android.settings.bluetooth.ui.viewmodel
import android.app.Application import android.app.Application
import android.bluetooth.BluetoothAdapter
import android.media.AudioManager
import android.util.Log
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.android.settings.R import com.android.settings.R
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutColumn import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutColumn
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settings.bluetooth.ui.model.FragmentTypeModel
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
@@ -47,12 +49,24 @@ import kotlinx.coroutines.flow.stateIn
class BluetoothDeviceDetailsViewModel( class BluetoothDeviceDetailsViewModel(
private val application: Application, private val application: Application,
private val deviceSettingRepository: DeviceSettingRepository, private val bluetoothAdapter: BluetoothAdapter,
private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice, private val cachedDevice: CachedBluetoothDevice,
backgroundCoroutineContext: CoroutineContext, backgroundCoroutineContext: CoroutineContext,
) : AndroidViewModel(application) { ) : AndroidViewModel(application) {
private val deviceSettingRepository =
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
application,
bluetoothAdapter,
viewModelScope,
)
private val spatialAudioInteractor =
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
application,
application.getSystemService(AudioManager::class.java),
viewModelScope,
)
private val items = private val items =
viewModelScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) { viewModelScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
deviceSettingRepository.getDeviceSettingsConfig(cachedDevice) deviceSettingRepository.getDeviceSettingsConfig(cachedDevice)
@@ -202,8 +216,7 @@ class BluetoothDeviceDetailsViewModel(
class Factory( class Factory(
private val application: Application, private val application: Application,
private val deviceSettingRepository: DeviceSettingRepository, private val bluetoothAdapter: BluetoothAdapter,
private val spatialAudioInteractor: SpatialAudioInteractor,
private val cachedDevice: CachedBluetoothDevice, private val cachedDevice: CachedBluetoothDevice,
private val backgroundCoroutineContext: CoroutineContext, private val backgroundCoroutineContext: CoroutineContext,
) : ViewModelProvider.Factory { ) : ViewModelProvider.Factory {
@@ -211,8 +224,7 @@ class BluetoothDeviceDetailsViewModel(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return BluetoothDeviceDetailsViewModel( return BluetoothDeviceDetailsViewModel(
application, application,
deviceSettingRepository, bluetoothAdapter,
spatialAudioInteractor,
cachedDevice, cachedDevice,
backgroundCoroutineContext, backgroundCoroutineContext,
) )

View File

@@ -74,8 +74,11 @@ class SimOnboardingService {
} }
var isEsimProfileEnabled: Boolean = false var isEsimProfileEnabled: Boolean = false
get() { get() {
activeSubInfoList.stream().anyMatch { it.isEmbedded } return activeSubInfoList.stream().anyMatch { it.isEmbedded }
return false }
var isRemovableSimProfileEnabled: Boolean = false
get() {
return activeSubInfoList.stream().anyMatch { !it.isEmbedded }
} }
var doesTargetSimActive = false var doesTargetSimActive = false
get() { get() {
@@ -288,8 +291,8 @@ class SimOnboardingService {
Log.d(TAG, "Hardware does not support DSDS.") Log.d(TAG, "Hardware does not support DSDS.")
return false return false
} }
val isActiveSim = activeSubInfoList.isNotEmpty() val anyActiveSim = activeSubInfoList.isNotEmpty()
if (isMultipleEnabledProfilesSupported && isActiveSim) { if (isMultipleEnabledProfilesSupported && anyActiveSim) {
Log.d(TAG, Log.d(TAG,
"Device supports MEP and eSIM operation and eSIM profile is enabled." "Device supports MEP and eSIM operation and eSIM profile is enabled."
+ " DSDS condition satisfied." + " DSDS condition satisfied."
@@ -297,15 +300,13 @@ class SimOnboardingService {
return true return true
} }
if (doesTargetSimHaveEsimOperation) { if (doesTargetSimHaveEsimOperation && isRemovableSimProfileEnabled) {
if (UiccSlotRepository(telephonyManager).anyRemovablePhysicalSimEnabled()) { Log.d(TAG,
Log.d( "eSIM operation and removable PSIM is enabled. DSDS condition satisfied."
TAG, )
"eSIM operation and removable PSIM is enabled. DSDS condition satisfied." return true
) }
return true if (!doesTargetSimHaveEsimOperation && isEsimProfileEnabled) {
}
} else if (isEsimProfileEnabled) {
Log.d(TAG, Log.d(TAG,
"Removable SIM operation and eSIM profile is enabled. DSDS condition" "Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied." + " satisfied."

View File

@@ -557,15 +557,17 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Log.d(TAG, "Hardware does not support DSDS."); Log.d(TAG, "Hardware does not support DSDS.");
return false; return false;
} }
boolean isActiveSim = SubscriptionUtil.getActiveSubscriptions( boolean anyActiveSim = SubscriptionUtil.getActiveSubscriptions(
mSubscriptionManager).size() > 0; mSubscriptionManager).size() > 0;
if (isMultipleEnabledProfilesSupported() && isActiveSim) { if (isMultipleEnabledProfilesSupported() && anyActiveSim) {
Log.d(TAG, Log.d(TAG,
"Device supports MEP and eSIM operation and eSIM profile is enabled." "Device supports MEP and eSIM operation and eSIM profile is enabled."
+ " DSDS condition satisfied."); + " DSDS condition satisfied.");
return true; return true;
} }
boolean isRemovableSimEnabled = isRemovableSimEnabled(); boolean isRemovableSimEnabled =
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
.anyMatch(subInfo-> !subInfo.isEmbedded());
if (mIsEsimOperation && isRemovableSimEnabled) { if (mIsEsimOperation && isRemovableSimEnabled) {
Log.d(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied."); Log.d(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
return true; return true;
@@ -583,7 +585,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
} }
private boolean isRemovableSimEnabled() { private boolean isRemovableSimEnabled() {
return new UiccSlotRepository(mTelMgr).anyRemovablePhysicalSimEnabled(); return new UiccSlotRepository(mTelMgr).anyRemovablePhysicalSimSlotActiveAndInserted();
} }
private boolean isMultipleEnabledProfilesSupported() { private boolean isMultipleEnabledProfilesSupported() {

View File

@@ -22,17 +22,17 @@ import android.util.Log
class UiccSlotRepository(private val telephonyManager: TelephonyManager?) { class UiccSlotRepository(private val telephonyManager: TelephonyManager?) {
/** Returns whether any removable physical sim is enabled. */ /** Returns whether any removable physical sim slot is active and the sim is inserted. */
fun anyRemovablePhysicalSimEnabled(): Boolean { fun anyRemovablePhysicalSimSlotActiveAndInserted(): Boolean {
val result = val result =
telephonyManager?.uiccSlotsInfo?.any { uiccSlotInfo: UiccSlotInfo? -> telephonyManager?.uiccSlotsInfo?.any { uiccSlotInfo: UiccSlotInfo? ->
uiccSlotInfo.isRemovablePhysicalSimEnabled() uiccSlotInfo.isRemovablePhysicalSimSlotActiveAndInserted()
} ?: false } ?: false
Log.i(TAG, "anyRemovablePhysicalSimEnabled: $result") Log.i(TAG, "anyRemovablePhysicalSimEnabled: $result")
return result return result
} }
private fun UiccSlotInfo?.isRemovablePhysicalSimEnabled(): Boolean { private fun UiccSlotInfo?.isRemovablePhysicalSimSlotActiveAndInserted(): Boolean {
return this != null && return this != null &&
isRemovable && isRemovable &&
!isEuicc && !isEuicc &&

View File

@@ -40,6 +40,7 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenMode;
import com.android.settingslib.notification.modes.ZenModesBackend; import com.android.settingslib.notification.modes.ZenModesBackend;
@@ -108,7 +109,9 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference
tryParseScheduleConditionId(mode.getRule().getConditionId()); tryParseScheduleConditionId(mode.getRule().getConditionId());
if (schedule != null) { if (schedule != null) {
preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule)); preference.setTitle(SystemZenRules.getTimeSummary(mContext, schedule));
preference.setSummary(SystemZenRules.getShortDaysSummary(mContext, schedule)); preference.setSummary(Utils.createAccessibleSequence(
SystemZenRules.getDaysOfWeekShort(mContext, schedule),
SystemZenRules.getDaysOfWeekFull(mContext, schedule)));
} else { } else {
// Fallback, but shouldn't happen. // Fallback, but shouldn't happen.
Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode); Log.wtf(TAG, "SCHEDULE_TIME mode without schedule: " + mode);

View File

@@ -57,6 +57,9 @@ public class WifiTetherSwitchBarController implements
private final ConnectivityManager mConnectivityManager; private final ConnectivityManager mConnectivityManager;
private final WifiManager mWifiManager; private final WifiManager mWifiManager;
@VisibleForTesting
boolean mIsSwitchBusy;
@VisibleForTesting @VisibleForTesting
DataSaverBackend mDataSaverBackend; DataSaverBackend mDataSaverBackend;
@VisibleForTesting @VisibleForTesting
@@ -102,8 +105,8 @@ public class WifiTetherSwitchBarController implements
@Override @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Filter out unnecessary callbacks when switch is disabled. // Filter out inappropriate callbacks when switch is busy.
if (!buttonView.isEnabled()) return; if (mIsSwitchBusy) return;
if (isChecked) { if (isChecked) {
startTether(); startTether();
@@ -115,14 +118,14 @@ public class WifiTetherSwitchBarController implements
void stopTether() { void stopTether() {
if (!isWifiApActivated()) return; if (!isWifiApActivated()) return;
mSwitchBar.setEnabled(false); mIsSwitchBusy = true;
mConnectivityManager.stopTethering(TETHERING_WIFI); mConnectivityManager.stopTethering(TETHERING_WIFI);
} }
void startTether() { void startTether() {
if (isWifiApActivated()) return; if (isWifiApActivated()) return;
mSwitchBar.setEnabled(false); mIsSwitchBusy = true;
mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */, mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */,
mOnStartTetheringCallback, new Handler(Looper.getMainLooper())); mOnStartTetheringCallback, new Handler(Looper.getMainLooper()));
} }
@@ -159,6 +162,7 @@ public class WifiTetherSwitchBarController implements
private void updateWifiSwitch() { private void updateWifiSwitch() {
mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled()); mSwitchBar.setEnabled(!mDataSaverBackend.isDataSaverEnabled());
mIsSwitchBusy = false;
} }
@Override @Override

View File

@@ -19,6 +19,7 @@ package com.android.settings.bluetooth.ui.viewmodel
import android.app.Application import android.app.Application
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.graphics.Bitmap import android.graphics.Bitmap
import android.media.AudioManager
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
@@ -46,7 +47,9 @@ import org.junit.Before
import org.junit.Rule 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.ArgumentMatchers.eq
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.times import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
@@ -76,11 +79,21 @@ class BluetoothDeviceDetailsViewModelTest {
val application = ApplicationProvider.getApplicationContext<Application>() val application = ApplicationProvider.getApplicationContext<Application>()
featureFactory = FakeFeatureFactory.setupForTest() featureFactory = FakeFeatureFactory.setupForTest()
`when`(
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
eq(application), eq(bluetoothAdapter), any()
))
.thenReturn(repository)
`when`(
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
eq(application), any(AudioManager::class.java), any()
))
.thenReturn(spatialAudioInteractor)
underTest = underTest =
BluetoothDeviceDetailsViewModel( BluetoothDeviceDetailsViewModel(
application, application,
repository, bluetoothAdapter,
spatialAudioInteractor,
cachedDevice, cachedDevice,
testScope.testScheduler) testScope.testScheduler)
} }

View File

@@ -44,6 +44,8 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules; import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig;
import android.text.Spanned;
import android.text.style.TtsSpan;
import android.widget.TextView; import android.widget.TextView;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@@ -293,7 +295,14 @@ public class ZenModeTriggerUpdatePreferenceControllerTest {
assertThat(mPreference.isVisible()).isTrue(); assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM"); assertThat(mPreference.getTitle()).isEqualTo("1:00 AM - 3:00 PM");
assertThat(mPreference.getSummary()).isEqualTo("Mon - Tue, Thu"); Spanned summary = (Spanned) mPreference.getSummary();
assertThat(summary.toString()).isEqualTo("Mon - Tue, Thu");
TtsSpan[] ttsSpans = summary.getSpans(0, summary.length(), TtsSpan.class);
assertThat(ttsSpans).hasLength(1);
assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_TEXT);
assertThat(ttsSpans[0].getArgs().getString(TtsSpan.ARG_TEXT)).isEqualTo(
"Monday to Tuesday, Thursday");
// Destination as written into the intent by SubSettingLauncher // Destination as written into the intent by SubSettingLauncher
assertThat( assertThat(
mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))

View File

@@ -147,8 +147,8 @@ public class WifiTetherSwitchBarControllerTest {
} }
@Test @Test
public void onSwitchChanged_switchNotEnabled_doNothingForTethering() { public void onSwitchChanged_switchIsBusy_doNothingForTethering() {
when(mSwitch.isEnabled()).thenReturn(false); mController.mIsSwitchBusy = true;
mController.onCheckedChanged(mSwitch, true); mController.onCheckedChanged(mSwitch, true);

View File

@@ -16,21 +16,55 @@
package com.android.settings.network package com.android.settings.network
import android.content.Context
import android.telephony.SubscriptionInfo import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.UiccCardInfo
import android.telephony.UiccPortInfo
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Rule import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith 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
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class SimOnboardingServiceTest { class SimOnboardingServiceTest {
val simOnboardingService = SimOnboardingService()
private val mockTelephonyManager = mock<TelephonyManager> {
on { activeModemCount } doReturn 2
on { isMultiSimSupported } doReturn TelephonyManager.MULTISIM_ALLOWED
on { uiccCardsInfo } doReturn mepUiccCardInfoList
}
private val mockSubscriptionManager = mock<SubscriptionManager> {
on { activeSubscriptionInfoList } doReturn listOf(
SUB_INFO_1,
SUB_INFO_2
)
on { availableSubscriptionInfoList } doReturn listOf(
SUB_INFO_1,
SUB_INFO_2,
SUB_INFO_3,
)
}
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
}
@Test @Test
fun addItemForRenaming_addItemWithNewName_findItem() { fun addItemForRenaming_addItemWithNewName_findItem() {
val simOnboardingService = SimOnboardingService()
val newName = "NewName" val newName = "NewName"
simOnboardingService.addItemForRenaming(SUB_INFO_1, newName) simOnboardingService.addItemForRenaming(SUB_INFO_1, newName)
assertThat(simOnboardingService.renameMutableMap) assertThat(simOnboardingService.renameMutableMap)
@@ -39,8 +73,6 @@ class SimOnboardingServiceTest {
@Test @Test
fun addItemForRenaming_sameNameAndItemNotInList_removeItem() { fun addItemForRenaming_sameNameAndItemNotInList_removeItem() {
val simOnboardingService = SimOnboardingService()
simOnboardingService.addItemForRenaming(SUB_INFO_1, DISPLAY_NAME_1) simOnboardingService.addItemForRenaming(SUB_INFO_1, DISPLAY_NAME_1)
assertThat(simOnboardingService.renameMutableMap) assertThat(simOnboardingService.renameMutableMap)
@@ -49,7 +81,6 @@ class SimOnboardingServiceTest {
@Test @Test
fun addItemForRenaming_sameNameAndItemInList_removeItem() { fun addItemForRenaming_sameNameAndItemInList_removeItem() {
val simOnboardingService = SimOnboardingService()
simOnboardingService.renameMutableMap[SUB_INFO_1.subscriptionId] = "NewName" simOnboardingService.renameMutableMap[SUB_INFO_1.subscriptionId] = "NewName"
simOnboardingService.addItemForRenaming(SUB_INFO_1, DISPLAY_NAME_1) simOnboardingService.addItemForRenaming(SUB_INFO_1, DISPLAY_NAME_1)
@@ -58,13 +89,205 @@ class SimOnboardingServiceTest {
.doesNotContainKey(SUB_INFO_1.subscriptionId) .doesNotContainKey(SUB_INFO_1.subscriptionId)
} }
@Test
fun isDsdsConditionSatisfied_isMultiSimEnabled_returnFalse(){
simOnboardingService.initData(SUB_ID_3, context, {})
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isFalse()
}
@Test
fun isDsdsConditionSatisfied_isNotMultiSimSupported_returnFalse() {
mockTelephonyManager.stub {
on { activeModemCount } doReturn 1
on {
isMultiSimSupported
} doReturn TelephonyManager.MULTISIM_NOT_SUPPORTED_BY_HARDWARE
}
simOnboardingService.initData(SUB_ID_3, context, {})
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isFalse()
}
@Test
fun isDsdsConditionSatisfied_mepAndOneActiveSim_returnTrue() = runBlocking {
mockTelephonyManager.stub {
on { activeModemCount } doReturn 1
}
simOnboardingService.initData(SUB_ID_3, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isTrue()
}
@Test
fun isDsdsConditionSatisfied_mepAndNoActiveSim_returnFalse() = runBlocking {
mockTelephonyManager.stub {
on { activeModemCount } doReturn 1
}
mockSubscriptionManager.stub {
on { activeSubscriptionInfoList } doReturn listOf()
}
simOnboardingService.initData(SUB_ID_3, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isFalse()
}
@Test
fun isDsdsConditionSatisfied_insertEsimAndOneActivePsimNoMep_returnTrue() = runBlocking {
mockTelephonyManager.stub {
on { getActiveModemCount() } doReturn 1
on { uiccCardsInfo } doReturn noMepUiccCardInfoList
}
simOnboardingService.initData(SUB_ID_3, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isTrue()
}
@Test
fun isDsdsConditionSatisfied_insertEsimAndNoPsimNoMep_returnFalse() = runBlocking {
mockTelephonyManager.stub {
on { getActiveModemCount() } doReturn 1
on { uiccCardsInfo } doReturn noMepUiccCardInfoList
}
mockSubscriptionManager.stub {
on { activeSubscriptionInfoList } doReturn listOf()
}
simOnboardingService.initData(SUB_ID_3, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isFalse()
}
@Test
fun isDsdsConditionSatisfied_insertPsimAndOneActiveEsimNoMep_returnTrue() = runBlocking {
mockTelephonyManager.stub {
on { getActiveModemCount() } doReturn 1
on { uiccCardsInfo } doReturn noMepUiccCardInfoList
}
mockSubscriptionManager.stub {
on { activeSubscriptionInfoList } doReturn listOf(
SUB_INFO_2
)
}
simOnboardingService.initData(SUB_ID_1, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isTrue()
}
@Test
fun isDsdsConditionSatisfied_insertPsimAndNoEsimNoMep_returnFalse() = runBlocking {
mockTelephonyManager.stub {
on { getActiveModemCount() } doReturn 1
on { uiccCardsInfo } doReturn noMepUiccCardInfoList
}
mockSubscriptionManager.stub {
on { activeSubscriptionInfoList } doReturn listOf()
}
simOnboardingService.initData(SUB_ID_1, context, {})
delay(100)
assertThat(simOnboardingService.isDsdsConditionSatisfied()).isFalse()
}
private companion object { private companion object {
const val SUB_ID_1 = 1 const val SUB_ID_1 = 1
const val SUB_ID_2 = 2
const val SUB_ID_3 = 3
const val SUB_ID_4 = 4
const val DISPLAY_NAME_1 = "Sub 1" const val DISPLAY_NAME_1 = "Sub 1"
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply { val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_1) setId(SUB_ID_1)
setDisplayName(DISPLAY_NAME_1) setDisplayName(DISPLAY_NAME_1)
}.build() }.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_2)
setEmbedded(true)
}.build()
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_3)
setEmbedded(true)
}.build()
val SUB_INFO_4: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_4)
}.build()
private const val REMOVABLE_CARD_ID_1: Int = 25
private const val REMOVABLE_CARD_ID_2: Int = 26
private const val EUICC_CARD_ID_3: Int = 27
private const val EUICC_CARD_ID_4: Int = 28
val noMepUiccCardInfoList: List<UiccCardInfo> = listOf(
createUiccCardInfo(
isEuicc = true,
cardId = EUICC_CARD_ID_3,
physicalSlotIndex = 0,
isRemovable = false,
isMultipleEnabledProfileSupported = false,
logicalSlotIndex = -1,
portIndex = -1
),
createUiccCardInfo(
isEuicc = false,
cardId = REMOVABLE_CARD_ID_1,
physicalSlotIndex = 1,
isRemovable = true,
isMultipleEnabledProfileSupported = false,
logicalSlotIndex = -1,
portIndex = -1
)
)
val mepUiccCardInfoList: List<UiccCardInfo> = listOf(
createUiccCardInfo(
isEuicc = true,
cardId = EUICC_CARD_ID_3,
physicalSlotIndex = 0,
isRemovable = false,
logicalSlotIndex = -1,
portIndex = -1
),
createUiccCardInfo(
isEuicc = false,
cardId = REMOVABLE_CARD_ID_1,
physicalSlotIndex = 1,
isRemovable = true,
logicalSlotIndex = -1,
portIndex = -1
)
)
private fun createUiccCardInfo(
isEuicc: Boolean,
cardId: Int,
physicalSlotIndex: Int,
isRemovable: Boolean,
logicalSlotIndex: Int,
portIndex: Int,
isMultipleEnabledProfileSupported:Boolean = true,
): UiccCardInfo {
return UiccCardInfo(
isEuicc, /* isEuicc */
cardId, /* cardId */
null, /* eid */
physicalSlotIndex, /* physicalSlotIndex */
isRemovable, /* isRemovable */
isMultipleEnabledProfileSupported, /* isMultipleEnabledProfileSupported */
listOf(
UiccPortInfo(
"123451234567890", /* iccId */
portIndex, /* portIdx */
logicalSlotIndex, /* logicalSlotIdx */
true /* isActive */
)
)
)
}
} }
} }

View File

@@ -44,7 +44,7 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }
@@ -61,7 +61,7 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }
@@ -78,7 +78,7 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isTrue() assertThat(result).isTrue()
} }
@@ -95,7 +95,7 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isTrue() assertThat(result).isTrue()
} }
@@ -116,7 +116,7 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }
@@ -137,13 +137,13 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isTrue() assertThat(result).isTrue()
} }
@Test @Test
fun anyRemovablePhysicalSimEnabled_activePsim_returnsTrue() { fun anyRemovablePhysicalSimSlotActiveAndInserted_activePsim_returnsTrue() {
mockTelephonyManager.stub { mockTelephonyManager.stub {
on { uiccSlotsInfo } doReturn on { uiccSlotsInfo } doReturn
arrayOf( arrayOf(
@@ -152,13 +152,13 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isTrue() assertThat(result).isTrue()
} }
@Test @Test
fun anyRemovablePhysicalSimEnabled_inactivePsim_returnsFalse() { fun anyRemovablePhysicalSimSlotActiveAndInserted_inactivePsim_returnsFalse() {
mockTelephonyManager.stub { mockTelephonyManager.stub {
on { uiccSlotsInfo } doReturn on { uiccSlotsInfo } doReturn
arrayOf( arrayOf(
@@ -167,13 +167,13 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }
@Test @Test
fun anyRemovablePhysicalSimEnabled_activeEsimAndActivePsim_returnsTrue() { fun anyRemovablePhysicalSimSlotActiveAndInserted_activeEsimAndActivePsim_returnsTrue() {
mockTelephonyManager.stub { mockTelephonyManager.stub {
on { uiccSlotsInfo } doReturn on { uiccSlotsInfo } doReturn
arrayOf( arrayOf(
@@ -184,13 +184,13 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isTrue() assertThat(result).isTrue()
} }
@Test @Test
fun anyRemovablePhysicalSimEnabled_activeEsimAndInactivePsim_returnsFalse() { fun anyRemovablePhysicalSimSlotActiveAndInserted_activeEsimAndInactivePsim_returnsFalse() {
mockTelephonyManager.stub { mockTelephonyManager.stub {
on { uiccSlotsInfo } doReturn on { uiccSlotsInfo } doReturn
arrayOf( arrayOf(
@@ -201,16 +201,16 @@ class UiccSlotRepositoryTest {
) )
} }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }
@Test @Test
fun anyRemovablePhysicalSimEnabled_uiccSlotInfoIsNull_returnsFalse() { fun anyRemovablePhysicalSimSlotActiveAndInserted_uiccSlotInfoIsNull_returnsFalse() {
mockTelephonyManager.stub { on { uiccSlotsInfo } doReturn arrayOf(null) } mockTelephonyManager.stub { on { uiccSlotsInfo } doReturn arrayOf(null) }
val result = repository.anyRemovablePhysicalSimEnabled() val result = repository.anyRemovablePhysicalSimSlotActiveAndInserted()
assertThat(result).isFalse() assertThat(result).isFalse()
} }