From 82b79088783ba852e681d711ed6e13d55da44a7c Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Wed, 14 Aug 2024 00:35:18 +0000 Subject: [PATCH 01/12] [Homepage Revamp] adjust search bar padding Bug: 353607870 Test: manual Flag: EXEMPT bugfix Change-Id: I3a574afc16823fe0a98ebae5e1c9e8fd28f4e522 --- res/layout/search_bar_unified_version.xml | 2 +- res/layout/settings_homepage_container_v2.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/res/layout/search_bar_unified_version.xml b/res/layout/search_bar_unified_version.xml index 14f46ec78e6..e9b3c107b90 100644 --- a/res/layout/search_bar_unified_version.xml +++ b/res/layout/search_bar_unified_version.xml @@ -44,7 +44,7 @@ style="@style/TextAppearance.SearchBar" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="8dp" + android:layout_marginStart="8dp" android:paddingEnd="8dp" android:text="@string/homepage_search"/> diff --git a/res/layout/settings_homepage_container_v2.xml b/res/layout/settings_homepage_container_v2.xml index b2445790347..a67b743ce13 100644 --- a/res/layout/settings_homepage_container_v2.xml +++ b/res/layout/settings_homepage_container_v2.xml @@ -69,7 +69,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingVertical="8dp" + android:paddingTop="8dp" + android:paddingBottom="16dp" android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> From 0f94d4c718b2418b117e9b461cddf0987d89c2a2 Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Fri, 16 Aug 2024 17:37:21 +0800 Subject: [PATCH 02/12] [Audiosharing] Toast message when sharing on/off Test: manual Bug: 358249608 Flag: com.android.settingslib.flags.enable_le_audio_sharing Change-Id: I0f9620d4844b3133f0f213adda24350884fe2d5f --- .../audiosharing/AudioSharingSwitchBarController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 8396e48afd1..a8021326c41 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -43,6 +43,7 @@ import androidx.fragment.app.Fragment; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; +import com.android.settings.R; import com.android.settings.bluetooth.Utils; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; @@ -128,6 +129,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + ", broadcastId = " + broadcastId); updateSwitch(); + AudioSharingUtils.toastMessage( + mContext, mContext.getString(R.string.audio_sharing_sharing_label)); mListener.onAudioSharingStateChanged(); } @@ -161,6 +164,9 @@ public class AudioSharingSwitchBarController extends BasePreferenceController + ", broadcastId = " + broadcastId); updateSwitch(); + AudioSharingUtils.toastMessage( + mContext, + mContext.getString(R.string.audio_sharing_sharing_stopped_label)); mListener.onAudioSharingStateChanged(); } From 78835886535d46c2cf9f6c3bc7b85a8d94cba440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Fri, 16 Aug 2024 18:29:29 +0200 Subject: [PATCH 03/12] Flip the INTERRUPTION_FILTER_ALL switch Instead of "Limit what can notify you", title is now "Allow all notifications" and its behavior is reversed (switch ON hides people/apps/other). Fixes: 360359116 Test: atest InterruptionFilterPreferenceControllerTest + manual Flag: android.app.modes_ui Change-Id: Ibb5334889662ec3aa778cd74c51506718ab0ebc7 --- res/values/strings.xml | 8 +++---- res/xml/modes_rule_settings.xml | 4 ++-- ...nterruptionFilterPreferenceController.java | 21 ++++++++++--------- .../notification/modes/ZenModeFragment.java | 2 +- ...ruptionFilterPreferenceControllerTest.java | 8 +++---- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 4b30dc19abe..99e3313f7bc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -8184,10 +8184,10 @@ other {{effect_1}, {effect_2}, and # more} } - - Limit what can notify you - - No interruptions are filtered + + Allow all notifications + + People, apps, and sounds can interrupt Display options for filtered diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml index d2f573c56cf..4f9b685592e 100644 --- a/res/xml/modes_rule_settings.xml +++ b/res/xml/modes_rule_settings.xml @@ -59,8 +59,8 @@ android:key="modes_filters"> + android:key="allow_all" + android:title="@string/zen_mode_allow_all_notifications"/> { - zenMode.getRule().setInterruptionFilter(filterNotifications - ? INTERRUPTION_FILTER_PRIORITY - : INTERRUPTION_FILTER_ALL); + zenMode.getRule().setInterruptionFilter(allowAll + ? INTERRUPTION_FILTER_ALL + : INTERRUPTION_FILTER_PRIORITY); return zenMode; }); } diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index 37772990152..057f1c363c9 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -77,7 +77,7 @@ public class ZenModeFragment extends ZenModeFragmentBase { new ZenModeTriggerAddPreferenceController(context, "zen_add_automatic_trigger", this, mBackend)); prefControllers.add(new InterruptionFilterPreferenceController( - context, "allow_filtering", mBackend)); + context, "allow_all", mBackend)); prefControllers.add(new ManualDurationPreferenceController( context, "mode_manual_duration", this, mBackend)); return prefControllers; diff --git a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java index 0c3f8e1815d..777d213142f 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/InterruptionFilterPreferenceControllerTest.java @@ -87,7 +87,7 @@ public final class InterruptionFilterPreferenceControllerTest { .build(); mController.updateZenMode(preference, zenMode); - verify(preference).setChecked(false); + verify(preference).setChecked(true); } @Test @@ -99,7 +99,7 @@ public final class InterruptionFilterPreferenceControllerTest { mController.updateZenMode(preference, zenMode); - mController.onPreferenceChange(preference, true); + mController.onPreferenceChange(preference, false); ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); @@ -118,7 +118,7 @@ public final class InterruptionFilterPreferenceControllerTest { .build(); mController.updateZenMode(preference, zenMode); - verify(preference).setChecked(true); + verify(preference).setChecked(false); } @Test @@ -131,7 +131,7 @@ public final class InterruptionFilterPreferenceControllerTest { mController.updateZenMode(preference, zenMode); - mController.onPreferenceChange(preference, false); + mController.onPreferenceChange(preference, true); ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); From 511b67e0b8b4cd06e54f5733f04eb1a7562f17ef Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Mon, 19 Aug 2024 14:17:30 +0800 Subject: [PATCH 04/12] Get the application context and pass into to a background executor. Bug: 360260798 Test: manual Flag: EXEMPT bug fix Change-Id: I77bf9a7be75257be7871714afaf1fba81787f8b1 --- .../settings/fuelgauge/AdvancedPowerUsageDetail.java | 5 +++-- .../settings/fuelgauge/PowerBackgroundUsageDetail.java | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 7bbb06a2d30..e922f7058be 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -278,16 +278,17 @@ public class AdvancedPowerUsageDetail extends DashboardFragment super.onPause(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + final Context applicationContext = requireContext().getApplicationContext(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); mExecutor.execute( () -> { if (currentOptimizeMode != mOptimizationMode) { AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( - getContext(), mBatteryOptimizeUtils.getUid()); + applicationContext, mBatteryOptimizeUtils.getUid()); } BatteryOptimizeLogUtils.writeLog( - getContext().getApplicationContext(), + applicationContext, Action.LEAVE, BatteryOptimizeLogUtils.getPackageNameWithUserId( mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index 2d2c838bc36..e59cc4add46 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -117,17 +117,17 @@ public class PowerBackgroundUsageDetail extends DashboardFragment super.onPause(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); + final Context applicationContext = requireContext().getApplicationContext(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); - mExecutor.execute( () -> { if (currentOptimizeMode != mOptimizationMode) { AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( - getContext(), mBatteryOptimizeUtils.getUid()); + applicationContext, mBatteryOptimizeUtils.getUid()); } BatteryOptimizeLogUtils.writeLog( - getContext().getApplicationContext(), + applicationContext, Action.LEAVE, BatteryOptimizeLogUtils.getPackageNameWithUserId( mBatteryOptimizeUtils.getPackageName(), UserHandle.myUserId()), From 37693f29f12cdab83e21fd1b139e9d79924884f0 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 16 Aug 2024 12:56:36 +0800 Subject: [PATCH 05/12] Create ImsFeatureRepository To be shared with video calling and VoLTE features. Bug: 233327342 Flag: EXEMPT bug fix Test: manual - on Mobile Settings Test: atest ImsFeatureRepositoryTest Change-Id: Ic7bcb532c4bd32c6f7ac4af1eebdd8a70a86ff29 --- .../telephony/ims/ImsFeatureRepository.kt | 61 ++++++++++ .../telephony/ims/ImsMmTelRepository.kt | 15 +++ .../wificalling/WifiCallingRepository.kt | 41 ++----- .../telephony/ims/ImsFeatureRepositoryTest.kt | 108 ++++++++++++++++++ .../wificalling/WifiCallingRepositoryTest.kt | 3 +- 5 files changed, 195 insertions(+), 33 deletions(-) create mode 100644 src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt diff --git a/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt new file mode 100644 index 00000000000..ba332574819 --- /dev/null +++ b/src/com/android/settings/network/telephony/ims/ImsFeatureRepository.kt @@ -0,0 +1,61 @@ +/* + * 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.ims + +import android.content.Context +import android.telephony.AccessNetworkConstants.TransportType +import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability +import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech +import com.android.settings.network.telephony.subscriptionsChangedFlow +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest + +/** + * A repository for the IMS feature. + * + * @throws IllegalArgumentException if the [subId] is invalid. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class ImsFeatureRepository( + private val context: Context, + private val subId: Int, + private val provisioningRepository: ProvisioningRepository = ProvisioningRepository(context), + private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) +) { + /** + * A cold flow that determines the provisioning status for the specified IMS MmTel capability, + * and whether or not the requested MmTel capability is supported by the carrier on the + * specified network transport. + * + * @return true if the feature is provisioned and supported, false otherwise. + */ + fun isReadyFlow( + @MmTelCapability capability: Int, + @ImsRegistrationTech tech: Int, + @TransportType transportType: Int, + ): Flow = + context.subscriptionsChangedFlow().flatMapLatest { + combine( + provisioningRepository.imsFeatureProvisionedFlow(subId, capability, tech), + imsMmTelRepository.isSupportedFlow(capability, transportType), + ) { imsFeatureProvisioned, isSupported -> + imsFeatureProvisioned && isSupported + } + } +} diff --git a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt index 9bc10e555e1..c5d1200a1d4 100644 --- a/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt +++ b/src/com/android/settings/network/telephony/ims/ImsMmTelRepository.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext @@ -47,6 +48,11 @@ interface ImsMmTelRepository { fun imsReadyFlow(): Flow + fun isSupportedFlow( + @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, + @AccessNetworkConstants.TransportType transportType: Int, + ): Flow + suspend fun isSupported( @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, @@ -55,6 +61,11 @@ interface ImsMmTelRepository { suspend fun setCrossSimCallingEnabled(enabled: Boolean) } +/** + * A repository for the IMS MMTel. + * + * @throws IllegalArgumentException if the [subId] is invalid. + */ class ImsMmTelRepositoryImpl( context: Context, private val subId: Int, @@ -126,8 +137,12 @@ class ImsMmTelRepositoryImpl( awaitClose { imsMmTelManager.unregisterImsStateCallback(callback) } }.catch { e -> Log.w(TAG, "[$subId] error while imsReadyFlow", e) + emit(false) }.conflate().flowOn(Dispatchers.Default) + override fun isSupportedFlow(capability: Int, transportType: Int): Flow = + imsReadyFlow().map { imsReady -> imsReady && isSupported(capability, transportType) } + override suspend fun isSupported( @MmTelFeature.MmTelCapabilities.MmTelCapability capability: Int, @AccessNetworkConstants.TransportType transportType: Int, diff --git a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt index 04e687c2d7f..6af0559adb1 100644 --- a/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt +++ b/src/com/android/settings/network/telephony/wificalling/WifiCallingRepository.kt @@ -20,24 +20,17 @@ import android.content.Context import android.telephony.AccessNetworkConstants import android.telephony.CarrierConfigManager import android.telephony.CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL -import android.telephony.SubscriptionManager import android.telephony.ims.ImsMmTelManager.WiFiCallingMode import android.telephony.ims.feature.MmTelFeature import android.telephony.ims.stub.ImsRegistrationImplBase import androidx.lifecycle.LifecycleOwner +import com.android.settings.network.telephony.ims.ImsFeatureRepository import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.android.settings.network.telephony.ims.ImsMmTelRepositoryImpl -import com.android.settings.network.telephony.ims.ProvisioningRepository -import com.android.settings.network.telephony.subscriptionsChangedFlow import com.android.settings.network.telephony.telephonyManager import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext interface IWifiCallingRepository { @@ -50,11 +43,11 @@ class WifiCallingRepository constructor( private val context: Context, private val subId: Int, - private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId) + private val imsFeatureRepository: ImsFeatureRepository = ImsFeatureRepository(context, subId), + private val imsMmTelRepository: ImsMmTelRepository = ImsMmTelRepositoryImpl(context, subId), ) : IWifiCallingRepository { private val telephonyManager = context.telephonyManager(subId) - private val provisioningRepository = ProvisioningRepository(context) private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! @WiFiCallingMode @@ -76,28 +69,12 @@ constructor( wifiCallingReadyFlow().collectLatestWithLifecycle(lifecycleOwner, action = action) } - @OptIn(ExperimentalCoroutinesApi::class) - fun wifiCallingReadyFlow(): Flow { - if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) - return context.subscriptionsChangedFlow().flatMapLatest { - combine( - provisioningRepository.imsFeatureProvisionedFlow( - subId = subId, - capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, - tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, - ), - isWifiCallingSupportedFlow(), - ) { imsFeatureProvisioned, isWifiCallingSupported -> - imsFeatureProvisioned && isWifiCallingSupported - } - } - } - - private fun isWifiCallingSupportedFlow(): Flow { - return imsMmTelRepository.imsReadyFlow().map { imsReady -> - imsReady && isWifiCallingSupported() - } - } + fun wifiCallingReadyFlow(): Flow = + imsFeatureRepository.isReadyFlow( + capability = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, + tech = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, + transportType = AccessNetworkConstants.TRANSPORT_TYPE_WLAN, + ) suspend fun isWifiCallingSupported(): Boolean = withContext(Dispatchers.Default) { imsMmTelRepository.isSupported( diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt new file mode 100644 index 00000000000..3f72b2c0dc4 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/ims/ImsFeatureRepositoryTest.kt @@ -0,0 +1,108 @@ +/* + * 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.ims + +import android.content.Context +import android.telephony.AccessNetworkConstants +import android.telephony.ims.feature.MmTelFeature +import android.telephony.ims.stub.ImsRegistrationImplBase +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.first +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.stub + +@RunWith(AndroidJUnit4::class) +class ImsFeatureRepositoryTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val mockProvisioningRepository = mock() + private val mockImsMmTelRepository = mock() + + @Test + fun isReadyFlow_notProvisioned_returnFalse() = runBlocking { + mockProvisioningRepository.stub { + onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn + flowOf(false) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + provisioningRepository = mockProvisioningRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isFalse() + } + + @Test + fun isReadyFlow_notSupported_returnFalse() = runBlocking { + mockImsMmTelRepository.stub { + onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(false) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + imsMmTelRepository = mockImsMmTelRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isFalse() + } + + @Test + fun isReadyFlow_provisionedAndSupported_returnFalse() = runBlocking { + mockProvisioningRepository.stub { + onBlocking { imsFeatureProvisionedFlow(SUB_ID, CAPABILITY, TECH) } doReturn flowOf(true) + } + mockImsMmTelRepository.stub { + onBlocking { isSupportedFlow(CAPABILITY, TRANSPORT_TYPE) } doReturn flowOf(true) + } + + val repository = + ImsFeatureRepository( + context = context, + subId = SUB_ID, + provisioningRepository = mockProvisioningRepository, + imsMmTelRepository = mockImsMmTelRepository, + ) + + val isReady = repository.isReadyFlow(CAPABILITY, TECH, TRANSPORT_TYPE).first() + + assertThat(isReady).isTrue() + } + + private companion object { + const val SUB_ID = 10 + const val CAPABILITY = MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE + const val TECH = ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN + const val TRANSPORT_TYPE = AccessNetworkConstants.TRANSPORT_TYPE_WLAN + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt index 0144f6669cb..f0a23eb92c9 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/wificalling/WifiCallingRepositoryTest.kt @@ -55,7 +55,8 @@ class WifiCallingRepositoryTest { on { getWiFiCallingMode(any()) } doReturn ImsMmTelManager.WIFI_MODE_UNKNOWN } - private val repository = WifiCallingRepository(context, SUB_ID, mockImsMmTelRepository) + private val repository = + WifiCallingRepository(context, SUB_ID, imsMmTelRepository = mockImsMmTelRepository) @Test fun getWiFiCallingMode_roamingAndNotUseWfcHomeModeForRoaming_returnRoamingSetting() { From c5c158ab38428a08d597f4a70f2a9e3d56867117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Mon, 19 Aug 2024 15:13:15 +0200 Subject: [PATCH 06/12] Use the prebuilt manual DND mode from TestModeBuilder in tests Instead of unnecessarily creating them by hand. This also allows restricting the visibility of the ZenMode constructors (which should only be instantiated by ZenModesBackend in production code). Bug: 360817586 Test: atest com.android.settings.notification.modes Flag: android.app.modes_ui Change-Id: Id5a232a5a0ffc7bc09652fc4b6e6dd2a6383a2ae --- ...anualDurationPreferenceControllerTest.java | 9 ++------- ...ZenModeButtonPreferenceControllerTest.java | 8 ++------ ...odeTriggerAddPreferenceControllerTest.java | 9 +-------- ...iggerCategoryPreferenceControllerTest.java | 10 +--------- ...TriggerUpdatePreferenceControllerTest.java | 10 +--------- .../ZenModesListPreferenceControllerTest.java | 19 +++++++------------ 6 files changed, 14 insertions(+), 51 deletions(-) diff --git a/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java index 29fdfdd6bd3..4edb0d510e1 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ManualDurationPreferenceControllerTest.java @@ -18,11 +18,9 @@ package com.android.settings.notification.modes; import static com.google.common.truth.Truth.assertThat; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.ContentResolver; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -31,7 +29,6 @@ import androidx.fragment.app.Fragment; import androidx.preference.Preference; import com.android.settingslib.notification.modes.TestModeBuilder; -import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; import org.junit.Before; @@ -73,10 +70,8 @@ public class ManualDurationPreferenceControllerTest { @Test public void testIsAvailable_onlyForManualDnd() { assertThat(mPrefController.isAvailable(TestModeBuilder.EXAMPLE)).isFalse(); - - ZenMode manualDnd = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("id", Uri.EMPTY).build(), false); - assertThat(mPrefController.isAvailable(manualDnd)).isTrue(); + assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_ACTIVE)).isTrue(); + assertThat(mPrefController.isAvailable(TestModeBuilder.MANUAL_DND_INACTIVE)).isTrue(); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java index 47078b08697..159dadae273 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeButtonPreferenceControllerTest.java @@ -23,11 +23,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.ContentResolver; import android.content.Context; -import android.net.Uri; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; @@ -192,8 +190,7 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false); + ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE; mController.updateZenMode(pref, zenMode); button.callOnClick(); @@ -207,8 +204,7 @@ public final class ZenModeButtonPreferenceControllerTest { Button button = new Button(mContext); LayoutPreference pref = mock(LayoutPreference.class); when(pref.findViewById(anyInt())).thenReturn(button); - ZenMode zenMode = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("manual", Uri.EMPTY).build(), false); + ZenMode zenMode = TestModeBuilder.MANUAL_DND_INACTIVE; mController.updateZenMode(pref, zenMode); button.callOnClick(); diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java index a56e7230bde..0d20b190844 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerAddPreferenceControllerTest.java @@ -19,7 +19,6 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; @@ -28,7 +27,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.verify; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.net.Uri; @@ -125,12 +123,7 @@ public class ZenModeTriggerAddPreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java index 4510e2048f5..bcafe47f9c7 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerCategoryPreferenceControllerTest.java @@ -18,15 +18,12 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.google.common.truth.Truth.assertThat; -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.SystemZenRules; @@ -116,12 +113,7 @@ public class ZenModeTriggerCategoryPreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java index 80d314c3dc0..b7af71b50fd 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java @@ -19,7 +19,6 @@ package com.android.settings.notification.modes; import static android.app.AutomaticZenRule.TYPE_OTHER; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; -import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.settings.notification.modes.CharSequenceTruth.assertThat; @@ -35,13 +34,11 @@ import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import android.app.AlertDialog; -import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Looper; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -149,12 +146,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void isAvailable_manualDND_false() { - ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb", - Uri.parse("manual")) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(), /* isActive= */ false); - - mController.setZenMode(manualMode); + mController.setZenMode(TestModeBuilder.MANUAL_DND_INACTIVE); assertThat(mController.isAvailable()).isFalse(); } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java index 4c16f2616bf..4fa8b8ac6c4 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesListPreferenceControllerTest.java @@ -18,6 +18,8 @@ package com.android.settings.notification.modes; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; +import static com.android.settingslib.notification.modes.TestModeBuilder.MANUAL_DND_INACTIVE; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; @@ -71,13 +73,6 @@ public class ZenModesListPreferenceControllerTest { .build()) .build(); - private static final ZenMode TEST_MANUAL_MODE = ZenMode.manualDndMode( - new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build(), - false); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); @@ -152,7 +147,7 @@ public class ZenModesListPreferenceControllerTest { @DisableFlags(Flags.FLAG_MODES_UI) public void testModesUiOff_notAvailableAndNoSearchData() { // There exist modes - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE, TEST_MODE)); assertThat(mPrefController.isAvailable()).isFalse(); List data = new ArrayList<>(); @@ -187,20 +182,20 @@ public class ZenModesListPreferenceControllerTest { // Changing mode data so there's a different one mode doesn't keep any previous data // (and setting that state up in the caller) - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE)); List newData = new ArrayList<>(); mPrefController.updateDynamicRawDataToIndex(newData); assertThat(newData).hasSize(1); SearchIndexableRaw newItem = newData.get(0); - assertThat(newItem.key).isEqualTo(TEST_MANUAL_MODE.getId()); + assertThat(newItem.key).isEqualTo(MANUAL_DND_INACTIVE.getId()); assertThat(newItem.title).isEqualTo("Do Not Disturb"); // set above } @Test @EnableFlags(Flags.FLAG_MODES_UI) public void testUpdateDynamicRawDataToIndex_multipleModes() { - when(mBackend.getModes()).thenReturn(List.of(TEST_MANUAL_MODE, TEST_MODE)); + when(mBackend.getModes()).thenReturn(List.of(MANUAL_DND_INACTIVE, TEST_MODE)); List data = new ArrayList<>(); mPrefController.updateDynamicRawDataToIndex(data); @@ -208,7 +203,7 @@ public class ZenModesListPreferenceControllerTest { // Should keep the order presented by getModes() SearchIndexableRaw item0 = data.get(0); - assertThat(item0.key).isEqualTo(TEST_MANUAL_MODE.getId()); + assertThat(item0.key).isEqualTo(MANUAL_DND_INACTIVE.getId()); assertThat(item0.title).isEqualTo("Do Not Disturb"); // set above SearchIndexableRaw item1 = data.get(1); From 7cd4a3987fd55cdb3cc9c8ffefd76e076550106c Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Mon, 19 Aug 2024 17:49:02 +0800 Subject: [PATCH 07/12] Add device details more settings page BUG: 343317785 Test: atest DeviceDetailsFragmentFormatterTest Flag: com.android.settings.flags.enable_bluetooth_device_details_polish Change-Id: Ia9eff049e73e039ac5d0fb26096ab4e9add60315 --- res/values/strings.xml | 7 + ...luetooth_device_more_settings_fragment.xml | 24 +++ .../BluetoothDeviceDetailsFragment.java | 7 +- .../composable/MultiTogglePreferenceGroup.kt | 16 +- .../ui/model/DeviceSettingPreferenceModel.kt | 69 ++++++++ .../bluetooth/ui/model/FragmentTypeModel.kt | 25 +++ .../ui/view/DeviceDetailsFragmentFormatter.kt | 155 +++++++++++------- .../view/DeviceDetailsMoreSettingsFragment.kt | 92 +++++++++++ .../BluetoothDeviceDetailsViewModel.kt | 71 +++++++- .../BluetoothDeviceDetailsFragmentTest.java | 5 +- .../DeviceDetailsFragmentFormatterTest.kt | 27 ++- .../BluetoothDeviceDetailsViewModelTest.kt | 48 +++--- 12 files changed, 428 insertions(+), 118 deletions(-) create mode 100644 res/xml/bluetooth_device_more_settings_fragment.xml create mode 100644 src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt create mode 100644 src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt create mode 100644 src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt diff --git a/res/values/strings.xml b/res/values/strings.xml index 939befe4b74..c405e126cfc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1864,6 +1864,10 @@ Device details Keyboard settings + + More settings + + Firmware updates, about, and more Device\'s Bluetooth address: %1$s @@ -1884,6 +1888,9 @@ Disconnect app + + More settings + Maximum connected Bluetooth audio devices diff --git a/res/xml/bluetooth_device_more_settings_fragment.xml b/res/xml/bluetooth_device_more_settings_fragment.xml new file mode 100644 index 00000000000..4fb4acae03c --- /dev/null +++ b/res/xml/bluetooth_device_more_settings_fragment.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index bd762a1ef11..54250f59f29 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -48,6 +48,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.settings.R; +import com.android.settings.bluetooth.ui.model.FragmentTypeModel; import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter; import com.android.settings.connecteddevice.stylus.StylusDevicesController; import com.android.settings.core.SettingsUIDeviceConfig; @@ -343,7 +344,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment public void onCreatePreferences(@NonNull Bundle savedInstanceState, @NonNull String rootKey) { super.onCreatePreferences(savedInstanceState, rootKey); if (Flags.enableBluetoothDeviceDetailsPolish()) { - mFormatter.updateLayout(); + mFormatter.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE); } } @@ -400,7 +401,9 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment @Override protected void addPreferenceController(AbstractPreferenceController controller) { if (Flags.enableBluetoothDeviceDetailsPolish()) { - List keys = mFormatter.getVisiblePreferenceKeysForMainPage(); + List keys = + mFormatter.getVisiblePreferenceKeys( + FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE); Lifecycle lifecycle = getSettingsLifecycle(); if (keys == null || keys.contains(controller.getPreferenceKey())) { super.addPreferenceController(controller); diff --git a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt index 8fe3c255d34..d29795efee7 100644 --- a/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt +++ b/src/com/android/settings/bluetooth/ui/composable/MultiTogglePreferenceGroup.kt @@ -66,15 +66,14 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.DialogProperties import com.android.settings.R +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel import com.android.settings.bluetooth.ui.composable.Icon as DeviceSettingComposeIcon -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.widget.dialog.getDialogWidth @Composable fun MultiTogglePreferenceGroup( - preferenceModels: List, + preferenceModels: List, ) { var settingIdForPopUp by remember { mutableStateOf(null) } @@ -115,7 +114,7 @@ fun MultiTogglePreferenceGroup( colors = getButtonColors(preferenceModel.isActive), contentPadding = PaddingValues(0.dp)) { DeviceSettingComposeIcon( - preferenceModel.toggles[preferenceModel.state.selectedIndex] + preferenceModel.toggles[preferenceModel.selectedIndex] .icon, modifier = Modifier.size(24.dp)) } @@ -144,7 +143,7 @@ private fun getButtonColors(isActive: Boolean) = @OptIn(ExperimentalMaterial3Api::class) @Composable private fun dialog( - multiTogglePreference: DeviceSettingModel.MultiTogglePreference, + multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference, onDismiss: () -> Unit ) { BasicAlertDialog( @@ -179,7 +178,7 @@ private fun dialog( } @Composable -private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiTogglePreference) { +private fun dialogContent(multiTogglePreference: DeviceSettingPreferenceModel.MultiTogglePreference) { Column { Row( modifier = Modifier.fillMaxWidth().height(24.dp), @@ -219,7 +218,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP } Row { for ((idx, toggle) in multiTogglePreference.toggles.withIndex()) { - val selected = idx == multiTogglePreference.state.selectedIndex + val selected = idx == multiTogglePreference.selectedIndex Column( modifier = Modifier.weight(1f) @@ -237,8 +236,7 @@ private fun dialogContent(multiTogglePreference: DeviceSettingModel.MultiToggleP ) { Button( onClick = { - multiTogglePreference.updateState( - DeviceSettingStateModel.MultiTogglePreferenceState(idx)) + multiTogglePreference.onSelectedChange(idx) }, modifier = Modifier.fillMaxSize(), colors = diff --git a/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt new file mode 100644 index 00000000000..6612591fce4 --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/model/DeviceSettingPreferenceModel.kt @@ -0,0 +1,69 @@ +/* + * 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.bluetooth.ui.model + +import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon +import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel + +/** Models a device setting preference. */ +sealed interface DeviceSettingPreferenceModel { + @DeviceSettingId + val id: Int + + /** Models a plain preference. */ + data class PlainPreference( + @DeviceSettingId override val id: Int, + val title: String, + val summary: String? = null, + val icon: DeviceSettingIcon? = null, + val onClick: (() -> Unit)? = null, + ) : DeviceSettingPreferenceModel + + /** Models a switch preference. */ + data class SwitchPreference( + @DeviceSettingId override val id: Int, + val title: String, + val summary: String? = null, + val icon: DeviceSettingIcon? = null, + val checked: Boolean, + val onCheckedChange: ((Boolean) -> Unit), + val onPrimaryClick: (() -> Unit)? = null, + ) : DeviceSettingPreferenceModel + + /** Models a multi-toggle preference. */ + data class MultiTogglePreference( + @DeviceSettingId override val id: Int, + val title: String, + val toggles: List, + val isActive: Boolean, + val selectedIndex: Int, + val isAllowedChangingState: Boolean, + val onSelectedChange: (Int) -> Unit, + ) : DeviceSettingPreferenceModel + + /** Models a footer preference. */ + data class FooterPreference( + @DeviceSettingId override val id: Int, + val footerText: String, + ) : DeviceSettingPreferenceModel + + /** Models a preference which could navigate to more settings fragment. */ + data class MoreSettingsPreference( + @DeviceSettingId override val id: Int, + ) : DeviceSettingPreferenceModel +} diff --git a/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt new file mode 100644 index 00000000000..19858c4ba7d --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/model/FragmentTypeModel.kt @@ -0,0 +1,25 @@ +/* + * 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.bluetooth.ui.model + +/** Models a device details fragment type. */ +sealed interface FragmentTypeModel { + /** Device details main page. */ + data object DeviceDetailsMainFragment : FragmentTypeModel + /** Device details more settings page. */ + data object DeviceDetailsMoreSettingsFragment : FragmentTypeModel +} diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt index b75579dfa0d..c933c754b7e 100644 --- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt @@ -19,47 +19,52 @@ package com.android.settings.bluetooth.ui.view import android.bluetooth.BluetoothAdapter import android.content.Context import android.media.AudioManager -import android.util.Log +import android.os.Bundle import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.preference.Preference +import com.android.settings.R import com.android.settings.SettingsPreferenceFragment import com.android.settings.bluetooth.ui.composable.Icon import com.android.settings.bluetooth.ui.composable.MultiTogglePreferenceGroup import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +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.viewmodel.BluetoothDeviceDetailsViewModel +import com.android.settings.core.SubSettingLauncher import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.spa.preference.ComposePreference import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel -import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.widget.preference.Preference as SpaPreference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference +import com.android.settingslib.spa.widget.ui.Footer import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking -import com.android.settingslib.spa.widget.preference.Preference as SpaPreference - /** Handles device details fragment layout according to config. */ interface DeviceDetailsFragmentFormatter { /** Gets keys of visible preferences in built-in preference in xml. */ - fun getVisiblePreferenceKeysForMainPage(): List? + fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List? /** Updates device details fragment layout. */ - fun updateLayout() + fun updateLayout(fragmentType: FragmentTypeModel) } @OptIn(ExperimentalCoroutinesApi::class) @@ -79,23 +84,25 @@ class DeviceDetailsFragmentFormatterImpl( ViewModelProvider( fragment, BluetoothDeviceDetailsViewModel.Factory( + fragment.requireActivity().application, repository, spatialAudioInteractor, cachedDevice, )) .get(BluetoothDeviceDetailsViewModel::class.java) - override fun getVisiblePreferenceKeysForMainPage(): List? = runBlocking { - viewModel - .getItems() - ?.filterIsInstance() - ?.mapNotNull { it.preferenceKey } - } + override fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List? = + runBlocking { + viewModel + .getItems(fragmentType) + ?.filterIsInstance() + ?.mapNotNull { it.preferenceKey } + } /** Updates bluetooth device details fragment layout. */ - override fun updateLayout() = runBlocking { - val items = viewModel.getItems() ?: return@runBlocking - val layout = viewModel.getLayout() ?: return@runBlocking + override fun updateLayout(fragmentType: FragmentTypeModel) = runBlocking { + val items = viewModel.getItems(fragmentType) ?: return@runBlocking + val layout = viewModel.getLayout(fragmentType) ?: return@runBlocking val prefKeyToSettingId = items .filterIsInstance() @@ -124,6 +131,8 @@ class DeviceDetailsFragmentFormatterImpl( fragment.preferenceScreen.addPreference(pref) } } + // TODO(b/343317785): figure out how to remove the foot preference. + fragment.preferenceScreen.addPreference(Preference(context).apply { order = 10000 }) } @Composable @@ -132,7 +141,7 @@ class DeviceDetailsFragmentFormatterImpl( remember(row) { layout.rows[row].settingIds.flatMapLatest { settingIds -> if (settingIds.isEmpty()) { - flowOf(emptyList()) + flowOf(emptyList()) } else { combine( settingIds.map { settingId -> @@ -150,72 +159,104 @@ class DeviceDetailsFragmentFormatterImpl( 0 -> {} 1 -> { when (val setting = settings[0]) { - is DeviceSettingModel.ActionSwitchPreference -> { - buildActionSwitchPreference(setting) + is DeviceSettingPreferenceModel.PlainPreference -> { + buildPlainPreference(setting) } - is DeviceSettingModel.MultiTogglePreference -> { + is DeviceSettingPreferenceModel.SwitchPreference -> { + buildSwitchPreference(setting) + } + is DeviceSettingPreferenceModel.MultiTogglePreference -> { buildMultiTogglePreference(listOf(setting)) } - null -> {} - else -> { - Log.w(TAG, "Unknown preference type ${setting.id}, skip.") + is DeviceSettingPreferenceModel.FooterPreference -> { + buildFooterPreference(setting) } + is DeviceSettingPreferenceModel.MoreSettingsPreference -> { + buildMoreSettingsPreference() + } + null -> {} } } else -> { - if (!settings.all { it is DeviceSettingModel.MultiTogglePreference }) { + if (!settings.all { it is DeviceSettingPreferenceModel.MultiTogglePreference }) { return } buildMultiTogglePreference( - settings.filterIsInstance()) + settings.filterIsInstance()) } } } @Composable - private fun buildMultiTogglePreference(prefs: List) { + private fun buildMultiTogglePreference( + prefs: List + ) { MultiTogglePreferenceGroup(prefs) } @Composable - private fun buildActionSwitchPreference(model: DeviceSettingModel.ActionSwitchPreference) { - if (model.switchState != null) { - val switchPrefModel = - object : SwitchPreferenceModel { - override val title = model.title - override val summary = { model.summary ?: "" } - override val checked = { model.switchState?.checked } - override val onCheckedChange = { newChecked: Boolean -> - model.updateState?.invoke( - DeviceSettingStateModel.ActionSwitchPreferenceState(newChecked)) - Unit - } - override val icon = @Composable { deviceSettingIcon(model) } + private fun buildSwitchPreference(model: DeviceSettingPreferenceModel.SwitchPreference) { + val switchPrefModel = + object : SwitchPreferenceModel { + override val title = model.title + override val summary = { model.summary ?: "" } + override val checked = { model.checked } + override val onCheckedChange = { newChecked: Boolean -> + model.onCheckedChange(newChecked) } - if (model.intent != null) { - TwoTargetSwitchPreference(switchPrefModel) { context.startActivity(model.intent) } - } else { - SwitchPreference(switchPrefModel) + override val icon = @Composable { deviceSettingIcon(model.icon) } } + if (model.onPrimaryClick != null) { + TwoTargetSwitchPreference( + switchPrefModel, primaryOnClick = model.onPrimaryClick::invoke) } else { - SpaPreference( - object : PreferenceModel { - override val title = model.title - override val summary = { model.summary ?: "" } - override val onClick = { - model.intent?.let { context.startActivity(it) } - Unit - } - override val icon = @Composable { deviceSettingIcon(model) } - }) + SwitchPreference(switchPrefModel) } } @Composable - private fun deviceSettingIcon(model: DeviceSettingModel.ActionSwitchPreference) { - model.icon?.let { icon -> - Icon(icon, modifier = Modifier.size(SettingsDimension.itemIconSize)) - } + private fun buildPlainPreference(model: DeviceSettingPreferenceModel.PlainPreference) { + SpaPreference( + object : PreferenceModel { + override val title = model.title + override val summary = { model.summary ?: "" } + override val onClick = { + model.onClick?.invoke() + Unit + } + override val icon = @Composable { deviceSettingIcon(model.icon) } + }) + } + + @Composable + fun buildMoreSettingsPreference() { + SpaPreference( + object : PreferenceModel { + override val title = + stringResource(R.string.bluetooth_device_more_settings_preference_title) + override val summary = { + context.getString(R.string.bluetooth_device_more_settings_preference_summary) + } + override val onClick = { + SubSettingLauncher(context) + .setDestination(DeviceDetailsMoreSettingsFragment::class.java.name) + .setSourceMetricsCategory(fragment.getMetricsCategory()) + .setArguments( + Bundle().apply { putString(KEY_DEVICE_ADDRESS, cachedDevice.address) }) + .launch() + } + override val icon = @Composable { deviceSettingIcon(null) } + }) + } + + @Composable + fun buildFooterPreference(model: DeviceSettingPreferenceModel.FooterPreference) { + Footer(footerText = model.footerText) + } + + @Composable + private fun deviceSettingIcon(icon: DeviceSettingIcon?) { + icon?.let { Icon(it, modifier = Modifier.size(SettingsDimension.itemIconSize)) } } private fun getPreferenceKey(settingId: Int) = "DEVICE_SETTING_${settingId}" diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt new file mode 100644 index 00000000000..c648a3e9b43 --- /dev/null +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt @@ -0,0 +1,92 @@ +/* + * 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.bluetooth.ui.view + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.content.Context +import android.os.Bundle +import com.android.settings.R +import com.android.settings.bluetooth.BluetoothDetailsProfilesController +import com.android.settings.bluetooth.Utils +import com.android.settings.bluetooth.ui.model.FragmentTypeModel +import com.android.settings.dashboard.DashboardFragment +import com.android.settings.overlay.FeatureFactory.Companion.featureFactory +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.settingslib.core.AbstractPreferenceController +import com.android.settingslib.core.lifecycle.LifecycleObserver + +class DeviceDetailsMoreSettingsFragment : DashboardFragment() { + private lateinit var formatter: DeviceDetailsFragmentFormatter + private lateinit var localBluetoothManager: LocalBluetoothManager + private lateinit var cachedDevice: CachedBluetoothDevice + + // TODO(b/343317785): add metrics category + override fun getMetricsCategory(): Int = 0 + + override fun getPreferenceScreenResId(): Int { + return R.xml.bluetooth_device_more_settings_fragment + } + + override fun addPreferenceController(controller: AbstractPreferenceController) { + val keys: List? = + formatter.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) + val lifecycle = settingsLifecycle + if (keys == null || keys.contains(controller.preferenceKey)) { + super.addPreferenceController(controller) + } else if (controller is LifecycleObserver) { + lifecycle.removeObserver((controller as LifecycleObserver)) + } + } + + private fun getCachedDevice(): CachedBluetoothDevice? { + val bluetoothAddress = arguments?.getString(KEY_DEVICE_ADDRESS) ?: return null + localBluetoothManager = Utils.getLocalBtManager(context) ?: return null + val remoteDevice: BluetoothDevice = + localBluetoothManager.bluetoothAdapter.getRemoteDevice(bluetoothAddress) ?: return null + return Utils.getLocalBtManager(context).cachedDeviceManager.findDevice(remoteDevice) + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + super.onCreatePreferences(savedInstanceState, rootKey) + formatter.updateLayout(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) + } + + override fun createPreferenceControllers(context: Context): List { + val bluetoothManager = context.getSystemService(BluetoothManager::class.java) + cachedDevice = + getCachedDevice() + ?: run { + finish() + return emptyList() + } + formatter = + featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter( + requireContext(), this, bluetoothManager.adapter, cachedDevice) + return listOf( + BluetoothDetailsProfilesController( + context, this, localBluetoothManager, cachedDevice, settingsLifecycle)) + } + + override fun getLogTag(): String = TAG + + companion object { + const val TAG: String = "DeviceMoreSettingsFrg" + const val KEY_DEVICE_ADDRESS: String = "device_address" + } +} diff --git a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt index befff830da3..c85015cc71b 100644 --- a/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt +++ b/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModel.kt @@ -16,17 +16,22 @@ package com.android.settings.bluetooth.ui.viewmodel +import android.app.Application +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout import com.android.settings.bluetooth.ui.layout.DeviceSettingLayoutRow +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settingslib.bluetooth.CachedBluetoothDevice 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.DeviceSettingModel +import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -38,30 +43,81 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn class BluetoothDeviceDetailsViewModel( + private val application: Application, private val deviceSettingRepository: DeviceSettingRepository, private val spatialAudioInteractor: SpatialAudioInteractor, private val cachedDevice: CachedBluetoothDevice, -) : ViewModel() { +) : AndroidViewModel(application){ + private val items = viewModelScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) { deviceSettingRepository.getDeviceSettingsConfig(cachedDevice) } - suspend fun getItems(): List? = items.await()?.mainItems + suspend fun getItems(fragment: FragmentTypeModel): List? = + when (fragment) { + is FragmentTypeModel.DeviceDetailsMainFragment -> items.await()?.mainItems + is FragmentTypeModel.DeviceDetailsMoreSettingsFragment -> + items.await()?.moreSettingsItems + } fun getDeviceSetting( cachedDevice: CachedBluetoothDevice, @DeviceSettingId settingId: Int - ): Flow { + ): Flow { + if (settingId == DeviceSettingId.DEVICE_SETTING_ID_MORE_SETTINGS) { + return flowOf(DeviceSettingPreferenceModel.MoreSettingsPreference(settingId)) + } return when (settingId) { DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE -> spatialAudioInteractor.getDeviceSetting(cachedDevice) else -> deviceSettingRepository.getDeviceSetting(cachedDevice, settingId) + }.map { it?.toPreferenceModel() } + } + + private fun DeviceSettingModel.toPreferenceModel(): DeviceSettingPreferenceModel? { + return when (this) { + is DeviceSettingModel.ActionSwitchPreference -> { + if (switchState != null) { + DeviceSettingPreferenceModel.SwitchPreference( + id = id, + title = title, + summary = summary, + icon = icon, + checked = switchState?.checked ?: false, + onCheckedChange = { newState -> + updateState?.invoke( + DeviceSettingStateModel.ActionSwitchPreferenceState(newState)) + }, + onPrimaryClick = { intent?.let { application.startActivity(it) } }) + } else { + DeviceSettingPreferenceModel.PlainPreference( + id = id, + title = title, + summary = summary, + icon = icon, + onClick = { intent?.let { application.startActivity(it) } }) + } + } + is DeviceSettingModel.FooterPreference -> + DeviceSettingPreferenceModel.FooterPreference(id = id, footerText = footerText) + is DeviceSettingModel.MultiTogglePreference -> + DeviceSettingPreferenceModel.MultiTogglePreference( + id = id, + title = title, + toggles = toggles, + isActive = isActive, + selectedIndex = state.selectedIndex, + isAllowedChangingState = isAllowedChangingState, + onSelectedChange = { newState -> + updateState(DeviceSettingStateModel.MultiTogglePreferenceState(newState)) + }) + is DeviceSettingModel.Unknown -> null } } - suspend fun getLayout(): DeviceSettingLayout? { - val configItems = getItems() ?: return null + suspend fun getLayout(fragment: FragmentTypeModel): DeviceSettingLayout? { + val configItems = getItems(fragment) ?: return null val idToDeviceSetting = configItems .filterIsInstance() @@ -80,7 +136,7 @@ class BluetoothDeviceDetailsViewModel( if (!isXmlPreference && setting == null) { continue } - if (setting !is DeviceSettingModel.MultiTogglePreference) { + if (setting !is DeviceSettingPreferenceModel.MultiTogglePreference) { multiToggleSettingIds = null positionMapping[i] = listOf(configItem.settingId) continue @@ -103,6 +159,7 @@ class BluetoothDeviceDetailsViewModel( } class Factory( + private val application: Application, private val deviceSettingRepository: DeviceSettingRepository, private val spatialAudioInteractor: SpatialAudioInteractor, private val cachedDevice: CachedBluetoothDevice, @@ -110,7 +167,7 @@ class BluetoothDeviceDetailsViewModel( override fun create(modelClass: Class): T { @Suppress("UNCHECKED_CAST") return BluetoothDeviceDetailsViewModel( - deviceSettingRepository, spatialAudioInteractor, cachedDevice) + application, deviceSettingRepository, spatialAudioInteractor, cachedDevice) as T } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java index 19d0eddd3a4..c84d42c1618 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java @@ -50,6 +50,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.preference.PreferenceScreen; import com.android.settings.R; +import com.android.settings.bluetooth.ui.model.FragmentTypeModel; import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -117,7 +118,9 @@ public class BluetoothDeviceDetailsFragmentTest { FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(), any(), any(), eq(mCachedDevice))).thenReturn(mFormatter); - when(mFormatter.getVisiblePreferenceKeysForMainPage()).thenReturn(null); + when(mFormatter.getVisiblePreferenceKeys( + FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE)) + .thenReturn(null); mFragment = setupFragment(); mFragment.onAttach(mContext); diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt index 609d7679f16..251b814f972 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt @@ -26,6 +26,7 @@ import androidx.preference.PreferenceManager import androidx.preference.PreferenceScreen import androidx.test.core.app.ApplicationProvider import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settings.dashboard.DashboardFragment import com.android.settings.testutils.FakeFeatureFactory import com.android.settingslib.bluetooth.CachedBluetoothDevice @@ -45,7 +46,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.any @@ -111,10 +111,9 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingConfigItemModel.BuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"), ), - listOf(), - "footer")) + listOf())) - val keys = underTest.getVisiblePreferenceKeysForMainPage() + val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons") } @@ -125,7 +124,7 @@ class DeviceDetailsFragmentFormatterTest { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) - val keys = underTest.getVisiblePreferenceKeysForMainPage() + val keys = underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).isNull() } @@ -136,9 +135,9 @@ class DeviceDetailsFragmentFormatterTest { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly("bluetooth_device_header", "action_buttons", "keyboard_settings") } } @@ -157,12 +156,11 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), - listOf(), - "footer")) + listOf())) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly("bluetooth_device_header", "keyboard_settings") } } @@ -183,8 +181,7 @@ class DeviceDetailsFragmentFormatterTest { DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC)) .thenReturn( flowOf( @@ -209,9 +206,9 @@ class DeviceDetailsFragmentFormatterTest { isAllowedChangingState = true, updateState = {}))) - underTest.updateLayout() + underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment) - assertThat(getDisplayedPreferences().map { it.key }) + assertThat(getDisplayedPreferences().mapNotNull { it.key }) .containsExactly( "bluetooth_device_header", "DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}", diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt index a1fadb8b354..378f363f0dd 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt @@ -16,12 +16,14 @@ package com.android.settings.bluetooth.ui.viewmodel +import android.app.Application import android.bluetooth.BluetoothAdapter -import android.content.Context import android.graphics.Bitmap import androidx.test.core.app.ApplicationProvider import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout +import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel +import com.android.settings.bluetooth.ui.model.FragmentTypeModel import com.android.settings.testutils.FakeFeatureFactory import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId @@ -44,8 +46,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -73,26 +73,23 @@ class BluetoothDeviceDetailsViewModelTest { @Before fun setUp() { - val context = ApplicationProvider.getApplicationContext() + val application = ApplicationProvider.getApplicationContext() featureFactory = FakeFeatureFactory.setupForTest() - `when`( - featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository( - eq(context), eq(bluetoothAdapter), any())) - .thenReturn(repository) underTest = - BluetoothDeviceDetailsViewModel(repository, spatialAudioInteractor, cachedDevice) + BluetoothDeviceDetailsViewModel( + application, repository, spatialAudioInteractor, cachedDevice) } @Test - fun getItems_returnConfigMainItems() { + fun getItems_returnConfigMainMainItems() { testScope.runTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)) .thenReturn( DeviceSettingConfigModel( - listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer")) + listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf())) - val keys = underTest.getItems() + val keys = underTest.getItems(FragmentTypeModel.DeviceDetailsMainFragment) assertThat(keys).containsExactly(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2) } @@ -110,19 +107,18 @@ class BluetoothDeviceDetailsViewModelTest { BUILTIN_SETTING_ITEM_1, buildRemoteSettingItem(remoteSettingId1), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) .thenReturn(flowOf(pref)) - var deviceSetting: DeviceSettingModel? = null + var deviceSettingPreference: DeviceSettingPreferenceModel? = null underTest .getDeviceSetting(cachedDevice, remoteSettingId1) - .onEach { deviceSetting = it } + .onEach { deviceSettingPreference = it } .launchIn(testScope.backgroundScope) runCurrent() - assertThat(deviceSetting).isSameInstanceAs(pref) + assertThat(deviceSettingPreference?.id).isEqualTo(pref.id) verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1) } } @@ -141,19 +137,18 @@ class BluetoothDeviceDetailsViewModelTest { buildRemoteSettingItem( DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE), ), - listOf(), - "footer")) + listOf())) `when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref)) - var deviceSetting: DeviceSettingModel? = null + var deviceSettingPreference: DeviceSettingPreferenceModel? = null underTest .getDeviceSetting( cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE) - .onEach { deviceSetting = it } + .onEach { deviceSettingPreference = it } .launchIn(testScope.backgroundScope) runCurrent() - assertThat(deviceSetting).isSameInstanceAs(pref) + assertThat(deviceSettingPreference?.id).isEqualTo(pref.id) verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice) } } @@ -164,9 +159,9 @@ class BluetoothDeviceDetailsViewModelTest { `when`(repository.getDeviceSettingsConfig(cachedDevice)) .thenReturn( DeviceSettingConfigModel( - listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), "footer")) + listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf())) - val layout = underTest.getLayout()!! + val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!! assertThat(getLatestLayout(layout)) .isEqualTo( @@ -191,8 +186,7 @@ class BluetoothDeviceDetailsViewModelTest { buildRemoteSettingItem(remoteSettingId2), buildRemoteSettingItem(remoteSettingId3), ), - listOf(), - "footer")) + listOf())) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1)) .thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1))) `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2)) @@ -200,7 +194,7 @@ class BluetoothDeviceDetailsViewModelTest { `when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3)) .thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3))) - val layout = underTest.getLayout()!! + val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!! assertThat(getLatestLayout(layout)) .isEqualTo( From a2ca48723a661ad77ffb8cac64f1bcea3db40784 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Mon, 19 Aug 2024 17:13:13 -0400 Subject: [PATCH 08/12] Flip order of start/finish Test: long press on a not setup mode tile on a large screen device Flag: android.app.modes_ui Fixes: 360898235 Change-Id: I1d4128c6ee3469dceb00e8cffdd5da7f4b3732b4 --- .../android/settings/notification/modes/ZenModeFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index 0a80977a021..b458c85ff7d 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -110,9 +110,10 @@ public class ZenModeFragment extends ZenModeFragmentBase { if (mode == null || mode.getStatus() != DISABLED_BY_OTHER) { return false; } + + mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode)); // don't come back here from the interstitial finish(); - mContext.startActivity(SetupInterstitialActivity.getIntent(mContext, mode)); return true; } From b3ee1d70c498be6a1d854f30b6cf0268cb088774 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Mon, 19 Aug 2024 12:04:14 +0800 Subject: [PATCH 09/12] Settings search for Carrier settings version Bug: 358238959 Flag: EXEMPT bug fix Test: manual - search carrier Test: unit test Change-Id: I4c13d22a6b689273684ff44df8071789a7a78d6e --- res/xml/mobile_network_settings.xml | 5 +- .../telephony/CarrierConfigRepository.kt | 2 +- ...erSettingsVersionPreferenceController.java | 56 ------------- ...rierSettingsVersionPreferenceController.kt | 62 +++++++++++++++ .../MobileNetworkSettingsSearchIndex.kt | 2 + ...SettingsVersionPreferenceControllerTest.kt | 70 ++++++++++++++++ ...ttingsVersionPreferenceControllerTest.java | 79 ------------------- 7 files changed, 137 insertions(+), 139 deletions(-) delete mode 100644 src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java create mode 100644 src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt create mode 100644 tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt delete mode 100644 tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index 825d72cd3e8..4f16e12f394 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -176,12 +176,11 @@ settings:searchable="false" settings:controller="com.android.settings.network.telephony.EnabledNetworkModePreferenceController"/> + diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt index 3ec529dbe82..3f5c06efb93 100644 --- a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt +++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt @@ -199,7 +199,7 @@ class CarrierConfigRepository(private val context: Context) { } @VisibleForTesting - fun setStringForTest(subId: Int, key: String, value: String) { + fun setStringForTest(subId: Int, key: String, value: String?) { check(key.endsWith("_string")) { "String key should ends with _string" } getPerSubCache(subId)[key] = StringConfigValue(value) } diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java deleted file mode 100644 index 575d19ce35b..00000000000 --- a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 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.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.text.TextUtils; - -import com.android.settings.core.BasePreferenceController; -import com.android.settings.network.CarrierConfigCache; - -public class CarrierSettingsVersionPreferenceController extends BasePreferenceController { - - private int mSubscriptionId; - private CarrierConfigCache mCarrierConfigCache; - - public CarrierSettingsVersionPreferenceController(Context context, String preferenceKey) { - super(context, preferenceKey); - mCarrierConfigCache = CarrierConfigCache.getInstance(context); - mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - } - - public void init(int subscriptionId) { - mSubscriptionId = subscriptionId; - } - - @Override - public CharSequence getSummary() { - final PersistableBundle config = mCarrierConfigCache.getConfigForSubId(mSubscriptionId); - if (config == null) { - return null; - } - return config.getString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING); - } - - @Override - public int getAvailabilityStatus() { - return TextUtils.isEmpty(getSummary()) ? UNSUPPORTED_ON_DEVICE : AVAILABLE; - } -} diff --git a/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt new file mode 100644 index 00000000000..f949ab8a25d --- /dev/null +++ b/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceController.kt @@ -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.network.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import com.android.settings.R +import com.android.settings.core.BasePreferenceController +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult + +class CarrierSettingsVersionPreferenceController(context: Context, preferenceKey: String) : + BasePreferenceController(context, preferenceKey) { + + private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID + private val searchItem = CarrierSettingsVersionSearchItem(context) + + fun init(subId: Int) { + this.subId = subId + } + + override fun getSummary() = searchItem.getSummary(subId) + + override fun getAvailabilityStatus() = + if (searchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE + + companion object { + class CarrierSettingsVersionSearchItem(private val context: Context) : + MobileNetworkSettingsSearchItem { + private val carrierConfigRepository = CarrierConfigRepository(context) + + fun getSummary(subId: Int): String? = + carrierConfigRepository.getString( + subId, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING) + + fun isAvailable(subId: Int): Boolean = !getSummary(subId).isNullOrEmpty() + + override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { + if (!isAvailable(subId)) return null + return MobileNetworkSettingsSearchResult( + key = "carrier_settings_version_key", + title = context.getString(R.string.carrier_settings_version), + ) + } + } + } +} diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index 55cb1fad8f3..c63e7d23bf1 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -21,6 +21,7 @@ import android.provider.Settings import android.telephony.SubscriptionInfo import com.android.settings.R import com.android.settings.network.SubscriptionUtil +import com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController.Companion.CarrierSettingsVersionSearchItem import com.android.settings.network.telephony.DataUsagePreferenceController.Companion.DataUsageSearchItem import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem @@ -115,6 +116,7 @@ class MobileNetworkSettingsSearchIndex( fun createSearchItems(context: Context): List = listOf( + CarrierSettingsVersionSearchItem(context), DataUsageSearchItem(context), MmsMessageSearchItem(context), NrAdvancedCallingSearchItem(context), diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt new file mode 100644 index 00000000000..ed6c02711e3 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.kt @@ -0,0 +1,70 @@ +/* + * 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.telephony.CarrierConfigManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class CarrierSettingsVersionPreferenceControllerTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val controller = + CarrierSettingsVersionPreferenceController(context, TEST_KEY).apply { init(SUB_ID) } + + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() + } + + @Test + fun getSummary_nullConfig_noCrash() { + controller.getSummary() + } + + @Test + fun getSummary_nullVersionString_returnNull() { + CarrierConfigRepository.setStringForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, null) + + val summary = controller.getSummary() + + assertThat(summary).isNull() + } + + @Test + fun getSummary_hasVersionString_returnCorrectSummary() { + CarrierConfigRepository.setStringForTest( + SUB_ID, CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, "test_version_123") + + val summary = controller.getSummary() + + assertThat(summary).isEqualTo("test_version_123") + } + + private companion object { + const val TEST_KEY = "test_key" + const val SUB_ID = 10 + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java deleted file mode 100644 index 40be07f1717..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/CarrierSettingsVersionPreferenceControllerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2020 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 static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.network.CarrierConfigCache; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class CarrierSettingsVersionPreferenceControllerTest { - @Mock - private CarrierConfigCache mCarrierConfigCache; - - private CarrierSettingsVersionPreferenceController mController; - private int mSubscriptionId = 1234; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - Context context = spy(ApplicationProvider.getApplicationContext()); - CarrierConfigCache.setTestInstance(context, mCarrierConfigCache); - mController = new CarrierSettingsVersionPreferenceController(context, "mock_key"); - mController.init(mSubscriptionId); - } - - @Test - public void getSummary_nullConfig_noCrash() { - doReturn(null).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId); - - assertThat(mController.getSummary()).isNull(); - } - - @Test - public void getSummary_nullVersionString_noCrash() { - doReturn(new PersistableBundle()).when(mCarrierConfigCache) - .getConfigForSubId(mSubscriptionId); - assertThat(mController.getSummary()).isNull(); - } - - @Test - public void getSummary_hasVersionString_correctSummary() { - final PersistableBundle bundle = new PersistableBundle(); - bundle.putString(CarrierConfigManager.KEY_CARRIER_CONFIG_VERSION_STRING, - "test_version_123"); - doReturn(bundle).when(mCarrierConfigCache).getConfigForSubId(mSubscriptionId); - - assertThat(mController.getSummary()).isEqualTo("test_version_123"); - } -} From 370445b773aa7ec69c83904dfdcd2e8f182bb777 Mon Sep 17 00:00:00 2001 From: SongFerng Wang Date: Tue, 20 Aug 2024 05:50:11 +0000 Subject: [PATCH 10/12] Fix IllegalArgumentException when getProvisioningStatusForCapability The ProvisioningManager.getProvisioningStatusForCapability() throws UnsupportedOperationException when the capability is not supported. Bug: 346600036 Change-Id: Ib682b336fdfc4c79a8b3e2fa5ccac8fe9c4e7ba1 Test: NA Flag: EXEMPT bugfix --- .../android/settings/network/ims/ImsQueryProvisioningStat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java index 44c45190c54..a43fda04626 100644 --- a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java +++ b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java @@ -56,7 +56,7 @@ public class ImsQueryProvisioningStat implements ImsQuery { final ProvisioningManager privisionManager = ProvisioningManager.createForSubscriptionId(mSubId); return privisionManager.getProvisioningStatusForCapability(mCapability, mTech); - } catch (IllegalArgumentException exception) { + } catch (UnsupportedOperationException exception) { Log.w(LOG_TAG, "fail to get Provisioning stat. subId=" + mSubId, exception); } return false; From 52b69293340b3463a216c520bdc2934d037e530d Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Tue, 20 Aug 2024 17:02:26 +0800 Subject: [PATCH 11/12] Catch IllegalArgumentException when unregister callback BUG: 359585188 Test: local test Flag: EXEMPT minor fix Change-Id: Ie2e485b922b2bf1163d47c14040e6e3d48f55f86 --- .../bluetooth/BluetoothDeviceDetailsFragment.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index bd762a1ef11..25b392c0461 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -255,8 +255,17 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment public void onDetach() { super.onDetach(); mManager.getEventManager().unregisterCallback(mBluetoothCallback); - mBluetoothAdapter.removeOnMetadataChangedListener( - mCachedDevice.getDevice(), mExtraControlMetadataListener); + BluetoothDevice device = mCachedDevice.getDevice(); + try { + mBluetoothAdapter.removeOnMetadataChangedListener( + device, mExtraControlMetadataListener); + } catch (IllegalArgumentException e) { + Log.w( + TAG, + "Unable to unregister metadata change callback for " + + mCachedDevice, + e); + } } private void updateExtraControlUri(int viewWidth) { From 29857065ea38946fbccfc08911c3ef0f91a7326f Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Tue, 20 Aug 2024 11:01:35 +0000 Subject: [PATCH 12/12] Update "bottom right tap" string to "click". Fixes: 318967325 Flag: EXEMPT string update only Test: check on device. Change-Id: Icc996db78e256c02078da0bd600a18f45d4bad73 --- res/values/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index ab883ee1c85..3bf0324442e 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4493,10 +4493,10 @@ Reverse scrolling Content moves up when you scroll down - - Bottom-right tap - - Tap the bottom right corner of the touchpad for more options + + Bottom-right click + + Click in the bottom right corner of the touchpad for more options Pointer speed