From 8f0c77bb19f1949f6d1d0c5e53ab13bec69e5bdb Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Tue, 30 Jul 2024 17:31:05 +0000 Subject: [PATCH 01/11] Add mandatory biometric prompt to platform surfaces (4/N) 1. Enable developer options via build info 2. Enable developer options via toggle under system -> developer options Flag: android.hardware.biometrics.flags.mandatory_biometrics Fixes: 355500452 Test: atest BuildNumberPreferenceControllerTest DevelopmentSettingsDashboardFragmentTest Change-Id: Iecbe34024d287e71e235becec3ce5a2bd5c1697f --- .../DevelopmentSettingsDashboardFragment.java | 23 ++++++- .../BuildNumberPreferenceController.java | 19 +++++- ...elopmentSettingsDashboardFragmentTest.java | 61 ++++++++++++++++++- .../BuildNumberPreferenceControllerTest.java | 52 +++++++++++++++- 4 files changed, 149 insertions(+), 6 deletions(-) diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 0389b4521fb..db50676ebbd 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -16,6 +16,7 @@ package com.android.settings.development; +import static android.app.Activity.RESULT_OK; import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED; import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES; import static android.view.flags.Flags.sensitiveContentAppProtectionApi; @@ -100,11 +101,13 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra NfcRebootDialog.OnNfcRebootDialogConfirmedListener, BluetoothSnoopLogHost { private static final String TAG = "DevSettingsDashboard"; + @VisibleForTesting static final int REQUEST_BIOMETRIC_PROMPT = 100; private final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore = new BluetoothA2dpConfigStore(); private boolean mIsAvailable = true; + private boolean mIsBiometricsAuthenticated; private SettingsMainSwitchBar mSwitchBar; private DevelopmentSwitchBarController mSwitchBarController; private List mPreferenceControllers = new ArrayList<>(); @@ -216,6 +219,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra public void onStart() { super.onStart(); final ContentResolver cr = getContext().getContentResolver(); + mIsBiometricsAuthenticated = false; cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver); // Restore UI state based on whether developer options is enabled @@ -360,7 +364,18 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext()); if (isChecked != developmentEnabledState) { if (isChecked) { - EnableDevelopmentSettingWarningDialog.show(this /* host */); + final int userId = getContext().getUserId(); + if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(getContext(), + mIsBiometricsAuthenticated, + false /* biometricsAuthenticationRequested */, userId)) { + mSwitchBar.setChecked(false); + Utils.launchBiometricPromptForMandatoryBiometrics(this, + REQUEST_BIOMETRIC_PROMPT, userId, false /* hideBackground */); + } else { + //Reset biometrics once enable dialog is shown + mIsBiometricsAuthenticated = false; + EnableDevelopmentSettingWarningDialog.show(this /* host */); + } } else { final BluetoothA2dpHwOffloadPreferenceController a2dpController = getDevelopmentOptionsController( @@ -534,6 +549,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { boolean handledResult = false; + if (requestCode == REQUEST_BIOMETRIC_PROMPT) { + if (resultCode == RESULT_OK) { + mIsBiometricsAuthenticated = true; + mSwitchBar.setChecked(true); + } + } for (AbstractPreferenceController controller : mPreferenceControllers) { if (controller instanceof OnActivityResultListener) { // We do not break early because it is possible for multiple controllers to diff --git a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java index 6fe3ca4521b..cf6b3e33e76 100644 --- a/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java +++ b/src/com/android/settings/deviceinfo/BuildNumberPreferenceController.java @@ -55,6 +55,7 @@ public class BuildNumberPreferenceController extends BasePreferenceController im static final int TAPS_TO_BE_A_DEVELOPER = 7; static final int REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF = 100; + static final int REQUEST_IDENTITY_CHECK_FOR_DEV_PREF = 101; private Activity mActivity; private InstrumentedPreferenceFragment mFragment; @@ -217,10 +218,24 @@ public class BuildNumberPreferenceController extends BasePreferenceController im * @return if activity result is handled. */ public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF) { + if (requestCode != REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + && requestCode != REQUEST_IDENTITY_CHECK_FOR_DEV_PREF) { return false; } - if (resultCode == Activity.RESULT_OK) { + if (requestCode == REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + && resultCode == Activity.RESULT_OK) { + final int userId = mContext.getUserId(); + if (Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext, + false /* biometricsSuccessfullyAuthenticated */, + false /* biometricsAuthenticationRequested */, + userId)) { + Utils.launchBiometricPromptForMandatoryBiometrics(mFragment, + REQUEST_IDENTITY_CHECK_FOR_DEV_PREF, userId, false /* hideBackground */); + } else { + enableDevelopmentSettings(); + } + } else if (requestCode == REQUEST_IDENTITY_CHECK_FOR_DEV_PREF + && resultCode == Activity.RESULT_OK) { enableDevelopmentSettings(); } mProcessingLastDevHit = false; diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java index 37a4aeab572..9f45edb4930 100644 --- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -18,13 +18,21 @@ package com.android.settings.development; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; import android.content.Context; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.Flags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.SearchIndexableResource; import android.provider.Settings; @@ -42,6 +50,7 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler; import org.junit.After; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; @@ -51,6 +60,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowBiometricManager; import org.robolectric.shadows.androidx.fragment.FragmentController; import org.robolectric.util.ReflectionHelpers; @@ -61,22 +71,34 @@ import java.util.List; ShadowAlertDialogCompat.class, ShadowUserManager.class, ShadowUserManager.class, + ShadowBiometricManager.class, }) public class DevelopmentSettingsDashboardFragmentTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private Context mContext; private ShadowUserManager mShadowUserManager; + private ShadowBiometricManager mShadowBiometricManager; private DevelopmentSettingsDashboardFragment mDashboard; + private SettingsMainSwitchBar mSwitchBar; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - SettingsMainSwitchBar switchBar = new SettingsMainSwitchBar(mContext); + mSwitchBar = new SettingsMainSwitchBar(mContext); mDashboard = spy(new DevelopmentSettingsDashboardFragment()); - ReflectionHelpers.setField(mDashboard, "mSwitchBar", switchBar); + ReflectionHelpers.setField(mDashboard, "mSwitchBar", mSwitchBar); mShadowUserManager = Shadow.extract(mContext.getSystemService(Context.USER_SERVICE)); mShadowUserManager.setIsAdminUser(true); + mShadowBiometricManager = Shadow.extract(mContext.getSystemService( + Context.BIOMETRIC_SERVICE)); + mShadowBiometricManager.setCanAuthenticate(false); + //TODO(b/352603684): Should be Authenticators.MANDATORY_BIOMETRICS, + // but it is not supported by ShadowBiometricManager + mShadowBiometricManager.setAuthenticatorType( + BiometricManager.Authenticators.BIOMETRIC_STRONG); } @After @@ -176,6 +198,41 @@ public class DevelopmentSettingsDashboardFragmentTest { assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue(); } + @Test + @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) + @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS) + public void onSwitchChanged_turnOn_shouldLaunchBiometricPromptIfMandatoryBiometricsEffective() { + when(mDashboard.getContext()).thenReturn(mContext); + doNothing().when(mDashboard).startActivityForResult(any(), + eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT)); + + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); + mShadowBiometricManager.setCanAuthenticate(true); + mDashboard.onCheckedChanged(null, true /* isChecked */); + + assertThat(mSwitchBar.isChecked()).isFalse(); + verify(mDashboard).startActivityForResult(any(), + eq(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT)); + assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isFalse(); + } + + @Test + @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) + @EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS) + public void onActivityResult_requestBiometricPrompt_shouldShowWarningDialog() { + when(mDashboard.getContext()).thenReturn(mContext); + + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); + mDashboard.onActivityResult(DevelopmentSettingsDashboardFragment.REQUEST_BIOMETRIC_PROMPT, + Activity.RESULT_OK, null); + mDashboard.onCheckedChanged(null, true /* isChecked */); + + assertThat(mSwitchBar.isChecked()).isTrue(); + assertThat(ShadowEnableDevelopmentSettingWarningDialog.mShown).isTrue(); + } + @Test @Ignore @Config(shadows = ShadowEnableDevelopmentSettingWarningDialog.class) diff --git a/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java index 237786bf191..326627a6247 100644 --- a/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java @@ -28,8 +28,13 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.content.Context; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.Flags; import android.os.Looper; import android.os.UserManager; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import androidx.lifecycle.LifecycleOwner; @@ -45,6 +50,7 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.development.DevelopmentSettingsEnabler; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; @@ -53,6 +59,9 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public class BuildNumberPreferenceControllerTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); private static final String KEY_BUILD_NUMBER = "build_number"; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -60,6 +69,7 @@ public class BuildNumberPreferenceControllerTest { private Context mContext; private UserManager mUserManager; + private BiometricManager mBiometricManager; private LifecycleOwner mLifecycleOwner; private Lifecycle mLifecycle; private FakeFeatureFactory mFactory; @@ -76,7 +86,13 @@ public class BuildNumberPreferenceControllerTest { mContext = spy(ApplicationProvider.getApplicationContext()); mUserManager = (UserManager) spy(mContext.getSystemService(Context.USER_SERVICE)); + mBiometricManager = spy(mContext.getSystemService(BiometricManager.class)); + doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE); + when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager); + when(mBiometricManager.canAuthenticate(mContext.getUserId(), + BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) + .thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE); mFactory = FakeFeatureFactory.setupForTest(); mLifecycleOwner = () -> mLifecycle; @@ -156,7 +172,7 @@ public class BuildNumberPreferenceControllerTest { @Test public void onActivityResult_notConfirmPasswordRequest_doNothing() { final boolean activityResultHandled = mController.onActivityResult( - BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 1, + BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF + 2, Activity.RESULT_OK, null); @@ -188,4 +204,38 @@ public class BuildNumberPreferenceControllerTest { assertThat(activityResultHandled).isTrue(); assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue(); } + + @Test + @UiThreadTest + @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS) + public void onActivityResult_confirmPasswordRequestCompleted_launchBiometricPrompt() { + when(mUserManager.isAdminUser()).thenReturn(true); + when(mBiometricManager.canAuthenticate(mContext.getUserId(), + BiometricManager.Authenticators.MANDATORY_BIOMETRICS)) + .thenReturn(BiometricManager.BIOMETRIC_SUCCESS); + + final boolean activityResultHandled = mController.onActivityResult( + BuildNumberPreferenceController.REQUEST_CONFIRM_PASSWORD_FOR_DEV_PREF, + Activity.RESULT_OK, + null); + + assertThat(activityResultHandled).isTrue(); + assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse(); + verify(mFragment).startActivityForResult(any(), + eq(BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF)); + } + + @Test + public void onActivityResult_confirmBiometricAuthentication_enableDevPref() { + when(mUserManager.isAdminUser()).thenReturn(true); + + Looper.prepare(); + final boolean activityResultHandled = mController.onActivityResult( + BuildNumberPreferenceController.REQUEST_IDENTITY_CHECK_FOR_DEV_PREF, + Activity.RESULT_OK, + null); + + assertThat(activityResultHandled).isTrue(); + assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isTrue(); + } } From 874c818b31bf8a31f42247e5c95cf9a699ed1012 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 31 Jul 2024 17:43:37 +0800 Subject: [PATCH 02/11] Settings search for Vo5G Also adding keywords support to MobileNetworkSettingsSearchItem. Fix: 262684662 Flag: EXEMPT bug fix Test: manual - search vo5g Change-Id: Ic73b76a2a7b2ef238b167fe3673835298d8939fd --- res/xml/mobile_network_settings.xml | 4 ++-- .../MobileNetworkSettingsSearchIndex.kt | 6 ++++++ .../NrAdvancedCallingPreferenceController.kt | 16 +++++++++++++++- .../settings/spa/search/SpaSearchRepository.kt | 2 ++ 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index bed6de878c5..6c0f7b5450c 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -293,10 +293,10 @@ settings:controller= "com.android.settings.network.telephony.NullAlgorithmsPreferenceController"/> + diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt index 85ba382fec7..ca50fd0e758 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSettingsSearchIndex.kt @@ -22,6 +22,7 @@ import android.telephony.SubscriptionInfo import com.android.settings.R import com.android.settings.network.SubscriptionUtil import com.android.settings.network.telephony.MmsMessagePreferenceController.Companion.MmsMessageSearchItem +import com.android.settings.network.telephony.NrAdvancedCallingPreferenceController.Companion.NrAdvancedCallingSearchItem import com.android.settings.spa.SpaSearchLanding.BundleValue import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingFragment import com.android.settings.spa.SpaSearchLanding.SpaSearchLandingKey @@ -41,6 +42,9 @@ class MobileNetworkSettingsSearchIndex( val title: String + val keywords: String? + get() = null + fun isAvailable(subId: Int): Boolean } @@ -89,6 +93,7 @@ class MobileNetworkSettingsSearchIndex( context = context, spaSearchLandingKey = key, itemTitle = searchItem.title, + keywords = searchItem.keywords, indexableClass = MobileNetworkSettings::class.java, pageTitle = "$simsTitle > ${subInfo.displayName}", ) @@ -107,6 +112,7 @@ class MobileNetworkSettingsSearchIndex( fun createSearchItems(context: Context): List = listOf( MmsMessageSearchItem(context), + NrAdvancedCallingSearchItem(context), ) } } diff --git a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt index cf47c1f0cd3..5c94e848270 100644 --- a/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt +++ b/src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R +import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel @@ -41,6 +42,7 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( ) : ComposePreferenceController(context, key) { private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID private var repository: VoNrRepository? = null + private val searchItem = NrAdvancedCallingSearchItem(context) /** Initial this PreferenceController. */ @JvmOverloads @@ -50,7 +52,7 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( } override fun getAvailabilityStatus() = - if (repository?.isVoNrAvailable() == true) AVAILABLE else CONDITIONALLY_UNAVAILABLE + if (searchItem.isAvailable(subId)) AVAILABLE else CONDITIONALLY_UNAVAILABLE @Composable override fun Content() { @@ -73,4 +75,16 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( } }) } + + companion object { + class NrAdvancedCallingSearchItem(private val context: Context) : + MobileNetworkSettingsSearchItem { + override val key = "nr_advanced_calling" + override val title: String = context.getString(R.string.nr_advanced_calling_title) + override val keywords: String = context.getString(R.string.keywords_nr_advanced_calling) + + override fun isAvailable(subId: Int): Boolean = + VoNrRepository(context, subId).isVoNrAvailable() + } + } } diff --git a/src/com/android/settings/spa/search/SpaSearchRepository.kt b/src/com/android/settings/spa/search/SpaSearchRepository.kt index 0efcb70890b..e5334dd2d8a 100644 --- a/src/com/android/settings/spa/search/SpaSearchRepository.kt +++ b/src/com/android/settings/spa/search/SpaSearchRepository.kt @@ -93,10 +93,12 @@ class SpaSearchRepository( itemTitle: String, indexableClass: Class<*>, pageTitle: String, + keywords: String? = null, ) = SearchIndexableRaw(context).apply { key = spaSearchLandingKey.toByteString().toStringUtf8() title = itemTitle + this.keywords = keywords intentAction = SEARCH_LANDING_ACTION intentTargetClass = SpaSearchLandingActivity::class.qualifiedName packageName = context.packageName From 124a9e1ebd5b02354ac396a7d837ea76db47408a Mon Sep 17 00:00:00 2001 From: Yiyi Shen Date: Wed, 31 Jul 2024 16:41:53 +0800 Subject: [PATCH 03/11] [Audiosharing] Not announce enable/disable when toggle main switch Fix: 356318679 Test: atest Flag: com.android.settingslib.flags.enable_le_audio_sharing Change-Id: I3331f9ded48352f442712287a6b536da883fe73d --- .../AudioSharingSwitchBarController.java | 19 +++++++++++++++ .../AudioSharingDashboardFragmentTest.java | 4 ++++ .../AudioSharingSwitchBarControllerTest.java | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java index 60b2ee507cd..3c0faba5db8 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java +++ b/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarController.java @@ -30,6 +30,9 @@ import android.content.IntentFilter; import android.util.FeatureFlagUtils; import android.util.Log; import android.util.Pair; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; @@ -284,6 +287,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController : mProfileManager.getLeAudioBroadcastAssistantProfile(); mExecutor = Executors.newSingleThreadExecutor(); mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); + mSwitchBar.getRootView().setAccessibilityDelegate(new MainSwitchAccessibilityDelegate()); } @Override @@ -564,4 +568,19 @@ public class AudioSharingSwitchBarController extends BasePreferenceController } }); } + + private static final class MainSwitchAccessibilityDelegate extends View.AccessibilityDelegate { + @Override + public boolean onRequestSendAccessibilityEvent( + @NonNull ViewGroup host, @NonNull View view, @NonNull AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED + && (event.getContentChangeTypes() + & AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED) + != 0) { + Log.d(TAG, "Skip accessibility event for CONTENT_CHANGE_TYPE_ENABLED"); + return false; + } + return super.onRequestSendAccessibilityEvent(host, view, event); + } + } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java index 8e4915cc8ff..7d8846d0506 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDashboardFragmentTest.java @@ -21,10 +21,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; +import android.view.View; import androidx.test.core.app.ApplicationProvider; @@ -52,6 +54,7 @@ public class AudioSharingDashboardFragmentTest { @Mock private SettingsActivity mActivity; @Mock private SettingsMainSwitchBar mSwitchBar; + @Mock private View mView; @Mock private AudioSharingDeviceVolumeGroupController mVolumeGroupController; @Mock private AudioSharingCallAudioPreferenceController mCallAudioController; @Mock private AudioSharingPlaySoundPreferenceController mPlaySoundController; @@ -61,6 +64,7 @@ public class AudioSharingDashboardFragmentTest { @Before public void setUp() { + when(mSwitchBar.getRootView()).thenReturn(mView); mFragment = new AudioSharingDashboardFragment(); } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java index bfb4f2f754c..711ef6f6955 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingSwitchBarControllerTest.java @@ -51,6 +51,8 @@ import android.os.Looper; import android.platform.test.flag.junit.SetFlagsRule; import android.util.FeatureFlagUtils; import android.util.Pair; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; import android.widget.CompoundButton; import androidx.fragment.app.DialogFragment; @@ -590,4 +592,26 @@ public class AudioSharingSwitchBarControllerTest { mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1); verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider); } + + @Test + public void testAccessibilityDelegate() { + View view = new View(mContext); + AccessibilityEvent event = + new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + assertThat( + mSwitchBar + .getRootView() + .getAccessibilityDelegate() + .onRequestSendAccessibilityEvent(mSwitchBar, view, event)) + .isTrue(); + + event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED); + assertThat( + mSwitchBar + .getRootView() + .getAccessibilityDelegate() + .onRequestSendAccessibilityEvent(mSwitchBar, view, event)) + .isFalse(); + } } From 9804955ad557a389d42d9b1bd54e7a72641daa81 Mon Sep 17 00:00:00 2001 From: ELIYAZ MOMIN Date: Wed, 31 Jul 2024 14:56:30 +0000 Subject: [PATCH 04/11] Revert "Tweak Modes layouts and use updated strings to match mocks" This reverts commit 0359aaeb47e66b1d7eda4e1291aaf7ef5d280627. Reason for revert: Change-Id: I980838773078d822d20608e5975a26cf8dbed2be --- res/values/strings.xml | 72 +++++++++---------- res/xml/modes_calls_settings.xml | 15 ++-- res/xml/modes_list_settings.xml | 5 +- res/xml/modes_messages_settings.xml | 6 -- res/xml/modes_people_settings.xml | 7 -- res/xml/modes_set_calendar.xml | 8 ++- .../ZenModeButtonPreferenceController.java | 4 +- .../modes/ZenModeFragmentBase.java | 2 +- .../modes/ZenModeScheduleChooserDialog.java | 16 ++--- ...ModeTriggerUpdatePreferenceController.java | 9 +-- ...TriggerUpdatePreferenceControllerTest.java | 35 ++++----- 11 files changed, 68 insertions(+), 111 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 28e1f05d584..311b56f81a3 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7955,28 +7955,24 @@ Priority Modes - - Minimize distractions and take control of your attention with modes for sleep, work, driving, and everything in between. - - Create your own mode + Add a mode Only get notified by important people and apps - Set a schedule - - - Schedule based on + Select activation type - Day and time + Time - \"9 AM - 5 PM weekdays\" + Ex. \"9:30 – 5:00 PM\" - Calendar events + Calendar + + Ex. \"Personal calendar\" ON @@ -7996,15 +7992,6 @@ Custom - - Turn on now - - - Turn off - - - Mode not found - Limit interruptions @@ -8042,17 +8029,19 @@ Schedule - When to turn on automatically + Turn on automatically - - Event schedule - - Turn on during events for - - Where invite reply is + + Add a calendar + + + Use your calendar + + + Schedule - Time schedule + Set a schedule Schedule @@ -8060,6 +8049,9 @@ %1$d hr, %2$d min + + Turn on automatically + Schedule @@ -8088,9 +8080,9 @@ Allow visual signals - Notification filters + Stay focused - More settings + Additional actions @@ -8141,7 +8133,7 @@ } - Limit what can notify you + Filter interruptions No interruptions are filtered @@ -9438,7 +9430,7 @@ Edit mode - Create a mode + Add mode Custom mode @@ -9449,26 +9441,26 @@ Calendar events - Bedtime routine + Sleep schedule While driving - App settings + Linked to app Info and settings in %1$s Managed by %1$s - - Disable %1$s? + + Disable Mode - This mode will never turn on when disabled + If you disable this feature, the mode will no longer work as intended and its settings will be hidden. Disable - - Enable %1$s? + + Enable Mode - This mode may turn on automatically based on its settings + If you enable this feature, the mode will activate automatically according to its schedule. Enable diff --git a/res/xml/modes_calls_settings.xml b/res/xml/modes_calls_settings.xml index b564020cf71..f2ba7f13867 100644 --- a/res/xml/modes_calls_settings.xml +++ b/res/xml/modes_calls_settings.xml @@ -24,21 +24,14 @@ + android:title="@string/zen_mode_calls_header" + settings:allowDividerBelow="true"> - - + android:title="@string/zen_mode_repeat_callers_title" + settings:allowDividerAbove="true"/> - diff --git a/res/xml/modes_list_settings.xml b/res/xml/modes_list_settings.xml index 5c672187205..8207af0f7e8 100644 --- a/res/xml/modes_list_settings.xml +++ b/res/xml/modes_list_settings.xml @@ -20,12 +20,11 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/zen_modes_list_title"> - + - + - - diff --git a/res/xml/modes_people_settings.xml b/res/xml/modes_people_settings.xml index af292ad46ff..d58b2d7e355 100644 --- a/res/xml/modes_people_settings.xml +++ b/res/xml/modes_people_settings.xml @@ -17,7 +17,6 @@ @@ -35,10 +34,4 @@ android:title="@string/zen_mode_calls_title" android:icon="@drawable/ic_zen_mode_people_calls" /> - - diff --git a/res/xml/modes_set_calendar.xml b/res/xml/modes_set_calendar.xml index be094f21f39..02eb26e33af 100644 --- a/res/xml/modes_set_calendar.xml +++ b/res/xml/modes_set_calendar.xml @@ -23,18 +23,20 @@ android:title="@string/zen_mode_set_calendar_title"> + android:key="zen_mode_event_category" + android:title="@string/zen_mode_set_calendar_category_title"> + diff --git a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java index bb9d23cd558..998b596e321 100644 --- a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java @@ -84,9 +84,9 @@ class ZenModeButtonPreferenceController extends AbstractZenModePreferenceControl } }); if (zenMode.isActive()) { - mZenButton.setText(R.string.zen_mode_action_deactivate); + mZenButton.setText(R.string.zen_mode_button_turn_off); } else { - mZenButton.setText(R.string.zen_mode_action_activate); + mZenButton.setText(R.string.zen_mode_button_turn_on); } } } diff --git a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java index ed3a71c78c7..c63b3a8c10b 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java +++ b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java @@ -131,7 +131,7 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase { } private void toastAndFinish() { - Toast.makeText(mContext, R.string.zen_mode_not_found_text, Toast.LENGTH_SHORT) + Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) .show(); this.finish(); } diff --git a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java index 6202648fcf9..14264b7a844 100644 --- a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java +++ b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java @@ -50,16 +50,15 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { static final int OPTION_TIME = 0; static final int OPTION_CALENDAR = 1; - private record ScheduleOption(@StringRes int nameResId, - @Nullable @StringRes Integer exampleResId, - @DrawableRes int iconResId) { } + private record ScheduleOption(@StringRes int nameResId, @StringRes int exampleResId, + @DrawableRes int iconResId) {} private static final ImmutableList SCHEDULE_OPTIONS = ImmutableList.of( new ScheduleOption(R.string.zen_mode_select_schedule_time, R.string.zen_mode_select_schedule_time_example, com.android.internal.R.drawable.ic_zen_mode_type_schedule_time), new ScheduleOption(R.string.zen_mode_select_schedule_calendar, - null, + R.string.zen_mode_select_schedule_calendar_example, com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar)); private OnScheduleOptionListener mOptionListener; @@ -86,7 +85,7 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { checkState(getContext() != null); return new AlertDialog.Builder(getContext()) - .setTitle(R.string.zen_mode_select_schedule_title) + .setTitle(R.string.zen_mode_choose_rule_type) .setAdapter(new OptionsAdapter(getContext()), (dialog, which) -> onScheduleTypeSelected(which)) .setNegativeButton(R.string.cancel, null) @@ -116,12 +115,7 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { ScheduleOption option = checkNotNull(getItem(position)); imageView.setImageResource(option.iconResId()); title.setText(option.nameResId()); - if (option.exampleResId() != null) { - subtitle.setVisibility(View.VISIBLE); - subtitle.setText(option.exampleResId()); - } else { - subtitle.setVisibility(View.GONE); - } + subtitle.setText(option.exampleResId()); return convertView; } diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java index 885c4db1fa1..3ee6d9443e4 100644 --- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java @@ -53,8 +53,6 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference private final ConfigurationActivityHelper mConfigurationActivityHelper; private final ZenServiceListing mServiceListing; - private String mModeName; - ZenModeTriggerUpdatePreferenceController(Context context, String key, ZenModesBackend backend) { this(context, key, backend, context.getPackageManager(), @@ -84,7 +82,6 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference return; } - mModeName = zenMode.getName(); PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference; triggerPref.setChecked(zenMode.getRule().isEnabled()); triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener); @@ -192,15 +189,15 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference }; private void confirmChangeEnabled(Preference preference, boolean enabled) { - @StringRes int titleFormat = enabled ? R.string.zen_mode_confirm_enable_mode_title - : R.string.zen_mode_confirm_disable_mode_title; + @StringRes int title = enabled ? R.string.zen_mode_confirm_enable_title + : R.string.zen_mode_confirm_disable_title; @StringRes int message = enabled ? R.string.zen_mode_confirm_enable_message : R.string.zen_mode_confirm_disable_message; @StringRes int confirmButton = enabled ? R.string.zen_mode_action_enable : R.string.zen_mode_action_disable; new AlertDialog.Builder(mContext) - .setTitle(mContext.getString(titleFormat, mModeName)) + .setTitle(title) .setMessage(message) .setPositiveButton(confirmButton, (dialog, which) -> setModeEnabled(enabled)) 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..a3fe57e02f5 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java @@ -47,7 +47,6 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; import android.service.notification.ZenModeConfig; -import android.widget.TextView; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; @@ -175,21 +174,18 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void onPreferenceChange_toggleOn_enablesModeAfterConfirmation() { // Start with a disabled mode - ZenMode zenMode = new TestModeBuilder().setName("The mode").setEnabled(false).build(); + ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); mController.updateZenMode(mPreference, zenMode); // Flip the switch mPreference.callChangeListener(true); verify(mBackend, never()).updateMode(any()); - AlertDialog confirmDialog = ShadowAlertDialog.getLatestAlertDialog(); - assertThat(confirmDialog).isNotNull(); - assertThat(confirmDialog.isShowing()).isTrue(); - assertThat(((TextView) confirmDialog.findViewById(com.android.internal.R.id.alertTitle)) - .getText()).isEqualTo("Enable The mode?"); - // Oh wait, I forgot to confirm! Let's do that - confirmDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); + ShadowAlertDialog.getLatestAlertDialog() + .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); shadowOf(Looper.getMainLooper()).idle(); // Verify the backend got asked to update the mode to be enabled @@ -202,21 +198,18 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void onPreferenceChange_toggleOff_disablesModeAfterConfirmation() { // Start with an enabled mode - ZenMode zenMode = new TestModeBuilder().setName("The mode").setEnabled(true).build(); + ZenMode zenMode = new TestModeBuilder().setEnabled(true).build(); mController.updateZenMode(mPreference, zenMode); // Flip the switch mPreference.callChangeListener(false); verify(mBackend, never()).updateMode(any()); - AlertDialog confirmDialog = ShadowAlertDialog.getLatestAlertDialog(); - assertThat(confirmDialog).isNotNull(); - assertThat(confirmDialog.isShowing()).isTrue(); - assertThat(((TextView) confirmDialog.findViewById(com.android.internal.R.id.alertTitle)) - .getText()).isEqualTo("Disable The mode?"); - // Oh wait, I forgot to confirm! Let's do that - confirmDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); + ShadowAlertDialog.getLatestAlertDialog() + .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); shadowOf(Looper.getMainLooper()).idle(); // Verify the backend got asked to update the mode to be disabled @@ -321,7 +314,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("App settings"); + assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); assertThat(mPreference.getSummary()).isEqualTo("When The Music's Over"); assertThat(mPreference.getIntent()).isEqualTo(configurationIntent); } @@ -338,7 +331,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("App settings"); + assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); assertThat(mPreference.getSummary()).isEqualTo("When the saints go marching in"); assertThat(mPreference.getIntent()).isNull(); } @@ -356,7 +349,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("App settings"); + assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); assertThat(mPreference.getSummary()).isEqualTo("Info and settings in The App Name"); } @@ -372,7 +365,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("App settings"); + assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); assertThat(mPreference.getSummary()).isEqualTo("Managed by The App Name"); } } From 4222bed26462b2741489cc8961ded791b416c8c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 31 Jul 2024 15:43:25 +0000 Subject: [PATCH 05/11] Revert^2 "Tweak Modes layouts and use updated strings to match mocks" This reverts commit 9804955ad557a389d42d9b1bd54e7a72641daa81. Reason for revert: Wrongly reverted; unrelated to the error Change-Id: I94a2b05589fa80218db184e192f1c4a633ab61b0 --- res/values/strings.xml | 72 ++++++++++--------- res/xml/modes_calls_settings.xml | 15 ++-- res/xml/modes_list_settings.xml | 5 +- res/xml/modes_messages_settings.xml | 6 ++ res/xml/modes_people_settings.xml | 7 ++ res/xml/modes_set_calendar.xml | 8 +-- .../ZenModeButtonPreferenceController.java | 4 +- .../modes/ZenModeFragmentBase.java | 2 +- .../modes/ZenModeScheduleChooserDialog.java | 16 +++-- ...ModeTriggerUpdatePreferenceController.java | 9 ++- ...TriggerUpdatePreferenceControllerTest.java | 35 +++++---- 11 files changed, 111 insertions(+), 68 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 311b56f81a3..28e1f05d584 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -7955,24 +7955,28 @@ Priority Modes + + Minimize distractions and take control of your attention with modes for sleep, work, driving, and everything in between. + - Add a mode + Create your own mode Only get notified by important people and apps - Select activation type + Set a schedule + + + Schedule based on - Time + Day and time - Ex. \"9:30 – 5:00 PM\" + \"9 AM - 5 PM weekdays\" - Calendar - - Ex. \"Personal calendar\" + Calendar events ON @@ -7992,6 +7996,15 @@ Custom + + Turn on now + + + Turn off + + + Mode not found + Limit interruptions @@ -8029,19 +8042,17 @@ Schedule - Turn on automatically + When to turn on automatically - - Add a calendar - - - Use your calendar - - - Schedule + + Event schedule + + Turn on during events for + + Where invite reply is - Set a schedule + Time schedule Schedule @@ -8049,9 +8060,6 @@ %1$d hr, %2$d min - - Turn on automatically - Schedule @@ -8080,9 +8088,9 @@ Allow visual signals - Stay focused + Notification filters - Additional actions + More settings @@ -8133,7 +8141,7 @@ } - Filter interruptions + Limit what can notify you No interruptions are filtered @@ -9430,7 +9438,7 @@ Edit mode - Add mode + Create a mode Custom mode @@ -9441,26 +9449,26 @@ Calendar events - Sleep schedule + Bedtime routine While driving - Linked to app + App settings Info and settings in %1$s Managed by %1$s - - Disable Mode + + Disable %1$s? - If you disable this feature, the mode will no longer work as intended and its settings will be hidden. + This mode will never turn on when disabled Disable - - Enable Mode + + Enable %1$s? - If you enable this feature, the mode will activate automatically according to its schedule. + This mode may turn on automatically based on its settings Enable diff --git a/res/xml/modes_calls_settings.xml b/res/xml/modes_calls_settings.xml index f2ba7f13867..b564020cf71 100644 --- a/res/xml/modes_calls_settings.xml +++ b/res/xml/modes_calls_settings.xml @@ -24,14 +24,21 @@ + android:title="@string/zen_mode_calls_header"> + + + android:title="@string/zen_mode_repeat_callers_title" /> + diff --git a/res/xml/modes_list_settings.xml b/res/xml/modes_list_settings.xml index 8207af0f7e8..5c672187205 100644 --- a/res/xml/modes_list_settings.xml +++ b/res/xml/modes_list_settings.xml @@ -20,11 +20,12 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/zen_modes_list_title"> - + - + + + diff --git a/res/xml/modes_people_settings.xml b/res/xml/modes_people_settings.xml index d58b2d7e355..af292ad46ff 100644 --- a/res/xml/modes_people_settings.xml +++ b/res/xml/modes_people_settings.xml @@ -17,6 +17,7 @@ @@ -34,4 +35,10 @@ android:title="@string/zen_mode_calls_title" android:icon="@drawable/ic_zen_mode_people_calls" /> + + diff --git a/res/xml/modes_set_calendar.xml b/res/xml/modes_set_calendar.xml index 02eb26e33af..be094f21f39 100644 --- a/res/xml/modes_set_calendar.xml +++ b/res/xml/modes_set_calendar.xml @@ -23,20 +23,18 @@ android:title="@string/zen_mode_set_calendar_title"> + android:key="zen_mode_event_category"> - diff --git a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java index 998b596e321..bb9d23cd558 100644 --- a/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeButtonPreferenceController.java @@ -84,9 +84,9 @@ class ZenModeButtonPreferenceController extends AbstractZenModePreferenceControl } }); if (zenMode.isActive()) { - mZenButton.setText(R.string.zen_mode_button_turn_off); + mZenButton.setText(R.string.zen_mode_action_deactivate); } else { - mZenButton.setText(R.string.zen_mode_button_turn_on); + mZenButton.setText(R.string.zen_mode_action_activate); } } } diff --git a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java index c63b3a8c10b..ed3a71c78c7 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragmentBase.java +++ b/src/com/android/settings/notification/modes/ZenModeFragmentBase.java @@ -131,7 +131,7 @@ abstract class ZenModeFragmentBase extends ZenModesFragmentBase { } private void toastAndFinish() { - Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) + Toast.makeText(mContext, R.string.zen_mode_not_found_text, Toast.LENGTH_SHORT) .show(); this.finish(); } diff --git a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java index 14264b7a844..6202648fcf9 100644 --- a/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java +++ b/src/com/android/settings/notification/modes/ZenModeScheduleChooserDialog.java @@ -50,15 +50,16 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { static final int OPTION_TIME = 0; static final int OPTION_CALENDAR = 1; - private record ScheduleOption(@StringRes int nameResId, @StringRes int exampleResId, - @DrawableRes int iconResId) {} + private record ScheduleOption(@StringRes int nameResId, + @Nullable @StringRes Integer exampleResId, + @DrawableRes int iconResId) { } private static final ImmutableList SCHEDULE_OPTIONS = ImmutableList.of( new ScheduleOption(R.string.zen_mode_select_schedule_time, R.string.zen_mode_select_schedule_time_example, com.android.internal.R.drawable.ic_zen_mode_type_schedule_time), new ScheduleOption(R.string.zen_mode_select_schedule_calendar, - R.string.zen_mode_select_schedule_calendar_example, + null, com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar)); private OnScheduleOptionListener mOptionListener; @@ -85,7 +86,7 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { checkState(getContext() != null); return new AlertDialog.Builder(getContext()) - .setTitle(R.string.zen_mode_choose_rule_type) + .setTitle(R.string.zen_mode_select_schedule_title) .setAdapter(new OptionsAdapter(getContext()), (dialog, which) -> onScheduleTypeSelected(which)) .setNegativeButton(R.string.cancel, null) @@ -115,7 +116,12 @@ public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment { ScheduleOption option = checkNotNull(getItem(position)); imageView.setImageResource(option.iconResId()); title.setText(option.nameResId()); - subtitle.setText(option.exampleResId()); + if (option.exampleResId() != null) { + subtitle.setVisibility(View.VISIBLE); + subtitle.setText(option.exampleResId()); + } else { + subtitle.setVisibility(View.GONE); + } return convertView; } diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java index 3ee6d9443e4..885c4db1fa1 100644 --- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java @@ -53,6 +53,8 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference private final ConfigurationActivityHelper mConfigurationActivityHelper; private final ZenServiceListing mServiceListing; + private String mModeName; + ZenModeTriggerUpdatePreferenceController(Context context, String key, ZenModesBackend backend) { this(context, key, backend, context.getPackageManager(), @@ -82,6 +84,7 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference return; } + mModeName = zenMode.getName(); PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference; triggerPref.setChecked(zenMode.getRule().isEnabled()); triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener); @@ -189,15 +192,15 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference }; private void confirmChangeEnabled(Preference preference, boolean enabled) { - @StringRes int title = enabled ? R.string.zen_mode_confirm_enable_title - : R.string.zen_mode_confirm_disable_title; + @StringRes int titleFormat = enabled ? R.string.zen_mode_confirm_enable_mode_title + : R.string.zen_mode_confirm_disable_mode_title; @StringRes int message = enabled ? R.string.zen_mode_confirm_enable_message : R.string.zen_mode_confirm_disable_message; @StringRes int confirmButton = enabled ? R.string.zen_mode_action_enable : R.string.zen_mode_action_disable; new AlertDialog.Builder(mContext) - .setTitle(title) + .setTitle(mContext.getString(titleFormat, mModeName)) .setMessage(message) .setPositiveButton(confirmButton, (dialog, which) -> setModeEnabled(enabled)) 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 a3fe57e02f5..80d314c3dc0 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceControllerTest.java @@ -47,6 +47,7 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; import android.service.notification.ZenModeConfig; +import android.widget.TextView; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; @@ -174,18 +175,21 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void onPreferenceChange_toggleOn_enablesModeAfterConfirmation() { // Start with a disabled mode - ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); + ZenMode zenMode = new TestModeBuilder().setName("The mode").setEnabled(false).build(); mController.updateZenMode(mPreference, zenMode); // Flip the switch mPreference.callChangeListener(true); verify(mBackend, never()).updateMode(any()); + AlertDialog confirmDialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(confirmDialog).isNotNull(); + assertThat(confirmDialog.isShowing()).isTrue(); + assertThat(((TextView) confirmDialog.findViewById(com.android.internal.R.id.alertTitle)) + .getText()).isEqualTo("Enable The mode?"); + // Oh wait, I forgot to confirm! Let's do that - assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); - assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); - ShadowAlertDialog.getLatestAlertDialog() - .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + confirmDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); shadowOf(Looper.getMainLooper()).idle(); // Verify the backend got asked to update the mode to be enabled @@ -198,18 +202,21 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { @Test public void onPreferenceChange_toggleOff_disablesModeAfterConfirmation() { // Start with an enabled mode - ZenMode zenMode = new TestModeBuilder().setEnabled(true).build(); + ZenMode zenMode = new TestModeBuilder().setName("The mode").setEnabled(true).build(); mController.updateZenMode(mPreference, zenMode); // Flip the switch mPreference.callChangeListener(false); verify(mBackend, never()).updateMode(any()); + AlertDialog confirmDialog = ShadowAlertDialog.getLatestAlertDialog(); + assertThat(confirmDialog).isNotNull(); + assertThat(confirmDialog.isShowing()).isTrue(); + assertThat(((TextView) confirmDialog.findViewById(com.android.internal.R.id.alertTitle)) + .getText()).isEqualTo("Disable The mode?"); + // Oh wait, I forgot to confirm! Let's do that - assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); - assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); - ShadowAlertDialog.getLatestAlertDialog() - .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + confirmDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); shadowOf(Looper.getMainLooper()).idle(); // Verify the backend got asked to update the mode to be disabled @@ -314,7 +321,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mPreference.getTitle()).isEqualTo("App settings"); assertThat(mPreference.getSummary()).isEqualTo("When The Music's Over"); assertThat(mPreference.getIntent()).isEqualTo(configurationIntent); } @@ -331,7 +338,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mPreference.getTitle()).isEqualTo("App settings"); assertThat(mPreference.getSummary()).isEqualTo("When the saints go marching in"); assertThat(mPreference.getIntent()).isNull(); } @@ -349,7 +356,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mPreference.getTitle()).isEqualTo("App settings"); assertThat(mPreference.getSummary()).isEqualTo("Info and settings in The App Name"); } @@ -365,7 +372,7 @@ public class ZenModeTriggerUpdatePreferenceControllerTest { mController.updateState(mPreference, mode); assertThat(mPreference.isVisible()).isTrue(); - assertThat(mPreference.getTitle()).isEqualTo("Linked to app"); + assertThat(mPreference.getTitle()).isEqualTo("App settings"); assertThat(mPreference.getSummary()).isEqualTo("Managed by The App Name"); } } From 767f58785acff744ff3b088ddb6c6fed10a1e7be Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Tue, 30 Jul 2024 22:06:46 +0000 Subject: [PATCH 06/11] Physical keyboard related options shouldn't be searchable when invisible Physical keyboard options category is hidden when there is no hardware keyboard. It's related preferences are hidden in this case. Hence those preferences shouldn't be searchable. Bug: 351047456 Test: manually search "bounce keys", "sticky keys" and "slow keys", they're not searchable when invisible on Accessibility page Test: atest Flag: com.android.settings.accessibility.fix_a11y_settings_search Change-Id: I1eb465dd34e59d856bb1fa7c06bc253971a8c8d0 --- .../accessibility/accessibility_flags.aconfig | 10 ++++++ .../accessibility/AccessibilitySettings.java | 2 +- ...KeyboardBounceKeyPreferenceController.java | 20 +++++++++++- .../KeyboardSlowKeyPreferenceController.java | 19 ++++++++++++ ...KeyboardStickyKeyPreferenceController.java | 20 +++++++++++- ...oardBounceKeyPreferenceControllerTest.java | 31 ++++++++++++++++++- ...yboardSlowKeyPreferenceControllerTest.java | 31 ++++++++++++++++++- ...oardStickyKeyPreferenceControllerTest.java | 31 ++++++++++++++++++- 8 files changed, 158 insertions(+), 6 deletions(-) diff --git a/aconfig/accessibility/accessibility_flags.aconfig b/aconfig/accessibility/accessibility_flags.aconfig index 3092b8fe72b..0338ed8c09c 100644 --- a/aconfig/accessibility/accessibility_flags.aconfig +++ b/aconfig/accessibility/accessibility_flags.aconfig @@ -51,6 +51,16 @@ flag { bug: "301198830" } +flag { + name: "fix_a11y_settings_search" + namespace: "accessibility" + description: "Fix the a11y related search items in Settings app" + bug: "333437173" + metadata { + purpose: PURPOSE_BUGFIX + } +} + flag { name: "hide_magnification_always_on_toggle_when_window_mode_only" namespace: "accessibility" diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 92bf1257605..f3eab935e91 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -613,7 +613,7 @@ public class AccessibilitySettings extends DashboardFragment implements } } - private boolean isAnyHardKeyboardsExist() { + static boolean isAnyHardKeyboardsExist() { for (int deviceId : InputDevice.getDeviceIds()) { final InputDevice device = InputDevice.getDevice(deviceId); if (device != null && !device.isVirtual() && device.isFullKeyboard()) { diff --git a/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java index 6d988ac13be..840caa6fa6c 100644 --- a/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java +++ b/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java @@ -19,16 +19,21 @@ package com.android.settings.accessibility; import android.content.Context; import android.hardware.input.InputSettings; +import android.util.Log; + +import androidx.annotation.NonNull; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; import com.android.settings.inputmethod.PhysicalKeyboardFragment; +import java.util.List; + /** * A toggle preference controller for keyboard bounce key. */ public class KeyboardBounceKeyPreferenceController extends TogglePreferenceController { - + private static final String TAG = "BounceKeyPrefController"; static final String PREF_KEY = "toggle_keyboard_bounce_keys"; public KeyboardBounceKeyPreferenceController(Context context, String preferenceKey) { @@ -58,4 +63,17 @@ public class KeyboardBounceKeyPreferenceController extends TogglePreferenceContr public int getSliceHighlightMenuRes() { return R.string.menu_key_accessibility; } + + @Override + public void updateNonIndexableKeys(@NonNull List keys) { + super.updateNonIndexableKeys(keys); + + if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) { + if (keys.contains(getPreferenceKey())) { + Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list."); + return; + } + keys.add(getPreferenceKey()); + } + } } diff --git a/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java index 8bd231680ee..bb9d95049ad 100644 --- a/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java +++ b/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java @@ -19,15 +19,21 @@ package com.android.settings.accessibility; import android.content.Context; import android.hardware.input.InputSettings; +import android.util.Log; + +import androidx.annotation.NonNull; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; import com.android.settings.inputmethod.PhysicalKeyboardFragment; +import java.util.List; + /** * A toggle preference controller for keyboard slow key. */ public class KeyboardSlowKeyPreferenceController extends TogglePreferenceController { + private static final String TAG = "SlowKeyPrefController"; static final String PREF_KEY = "toggle_keyboard_slow_keys"; @@ -58,4 +64,17 @@ public class KeyboardSlowKeyPreferenceController extends TogglePreferenceControl public int getSliceHighlightMenuRes() { return R.string.menu_key_accessibility; } + + @Override + public void updateNonIndexableKeys(@NonNull List keys) { + super.updateNonIndexableKeys(keys); + + if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) { + if (keys.contains(getPreferenceKey())) { + Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list."); + return; + } + keys.add(getPreferenceKey()); + } + } } diff --git a/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java index ee5559df084..c896c9c0d27 100644 --- a/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java +++ b/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java @@ -19,15 +19,20 @@ package com.android.settings.accessibility; import android.content.Context; import android.hardware.input.InputSettings; +import android.util.Log; + +import androidx.annotation.NonNull; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; +import java.util.List; + /** * A toggle preference controller for keyboard sticky key. */ public class KeyboardStickyKeyPreferenceController extends TogglePreferenceController { - + private static final String TAG = "StickyKeyPrefController"; static final String PREF_KEY = "toggle_keyboard_sticky_keys"; public KeyboardStickyKeyPreferenceController(Context context, String preferenceKey) { @@ -55,4 +60,17 @@ public class KeyboardStickyKeyPreferenceController extends TogglePreferenceContr public int getSliceHighlightMenuRes() { return R.string.menu_key_accessibility; } + + @Override + public void updateNonIndexableKeys(@NonNull List keys) { + super.updateNonIndexableKeys(keys); + + if (Flags.fixA11ySettingsSearch() && !AccessibilitySettings.isAnyHardKeyboardsExist()) { + if (keys.contains(getPreferenceKey())) { + Log.w(TAG, "Skipping updateNonIndexableKeys, key already in list."); + return; + } + keys.add(getPreferenceKey()); + } + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java index 96beb43dd1c..bf6efd097d7 100644 --- a/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java @@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.preference.PreferenceManager; @@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; +import org.junit.Assume; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.List; @RunWith(RobolectricTestRunner.class) public class KeyboardBounceKeyPreferenceControllerTest { - private static final String KEY_ACCESSIBILITY_BOUNCE_KEYS = Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS; private static final int UNKNOWN = -1; + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext)); private final KeyboardBounceKeyPreferenceController mController = @@ -131,4 +138,26 @@ public class KeyboardBounceKeyPreferenceControllerTest { mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, UNKNOWN)).isNotEqualTo(OFF); } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() { + Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).isEmpty(); + } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() { + Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).contains(mController.getPreferenceKey()); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java index 321b69fa834..2721a64874c 100644 --- a/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java @@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.preference.PreferenceManager; @@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; +import org.junit.Assume; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.List; @RunWith(RobolectricTestRunner.class) public class KeyboardSlowKeyPreferenceControllerTest { - private static final String KEY_ACCESSIBILITY_SLOW_KEYS = Settings.Secure.ACCESSIBILITY_SLOW_KEYS; private static final int UNKNOWN = -1; + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext)); private final KeyboardSlowKeyPreferenceController mController = @@ -131,4 +138,26 @@ public class KeyboardSlowKeyPreferenceControllerTest { mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, UNKNOWN)).isNotEqualTo( OFF); } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() { + Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).isEmpty(); + } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() { + Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).contains(mController.getPreferenceKey()); + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java index 31d46b76eab..0001e8552d1 100644 --- a/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java @@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import android.content.Context; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.preference.PreferenceManager; @@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.core.BasePreferenceController; +import org.junit.Assume; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.List; @RunWith(RobolectricTestRunner.class) public class KeyboardStickyKeyPreferenceControllerTest { - private static final String KEY_ACCESSIBILITY_STICKY_KEYS = Settings.Secure.ACCESSIBILITY_STICKY_KEYS; private static final int UNKNOWN = -1; + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext)); private final KeyboardStickyKeyPreferenceController mController = @@ -129,4 +136,26 @@ public class KeyboardStickyKeyPreferenceControllerTest { assertThat(Settings.Secure.getInt( mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, UNKNOWN)).isEqualTo(ON); } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() { + Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).isEmpty(); + } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() { + Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist()); + + List nonIndexableKeys = new ArrayList<>(); + mController.updateNonIndexableKeys(nonIndexableKeys); + + assertThat(nonIndexableKeys).contains(mController.getPreferenceKey()); + } } From a5636c1cb91e088df00b9d401f4a55a2417bcc5e Mon Sep 17 00:00:00 2001 From: Chun-Ku Lin Date: Wed, 31 Jul 2024 19:51:42 +0000 Subject: [PATCH 07/11] Update color correction red-green options text Bug: 354776203 Test: manually (see screenshots in the ticket) Flag: EXEMPT resource only update Change-Id: Iffda7f965e046ef5c6493ac5f9eb410e4db96790 --- 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 311b56f81a3..6b4e79eb3a7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5456,9 +5456,9 @@ Show in Quick Settings - Red-green + Red-green, green weak - Red-green + Red-green, red weak Blue-yellow @@ -5468,9 +5468,9 @@ Unavailable for grayscale mode or when color correction is disabled - Green weak, deuteranomaly + Deuteranomaly - Red weak, protanomaly + Protanomaly Tritanomaly From 90553d91f126c33bdb2bc5340324f63fe695c1e3 Mon Sep 17 00:00:00 2001 From: tomhsu Date: Wed, 31 Jul 2024 07:17:30 +0000 Subject: [PATCH 08/11] [Settings]Check the Satellite modem state instead of enabled state only. Flag: EXEMPT bugfix Fix: 354806125 Test: Manual test pass Change-Id: Iaa8d1c9d3df36e40fafda0117234036d9451a4fa --- .../settings/bluetooth/BluetoothEnabler.java | 2 +- .../bluetooth/BluetoothPairingDetail.java | 2 +- .../AirplaneModePreferenceController.java | 3 +- .../settings/network/SatelliteRepository.kt | 75 ++++++++++++------- .../android/settings/wifi/WifiEnabler.java | 3 +- .../settings/wifi/slice/WifiSlice.java | 2 +- .../network/SatelliteRepositoryTest.kt | 34 ++++++++- 7 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java index df5cc72fae1..a5d0bc67769 100644 --- a/src/com/android/settings/bluetooth/BluetoothEnabler.java +++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java @@ -132,7 +132,7 @@ public final class BluetoothEnabler implements SwitchWidgetController.OnSwitchCh new Thread(() -> { try { - mIsSatelliteOn.set(mSatelliteRepository.requestIsEnabled( + mIsSatelliteOn.set(mSatelliteRepository.requestIsSessionStarted( Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS)); } catch (InterruptedException | ExecutionException | TimeoutException e) { Log.e(TAG, "Error to get satellite status : " + e); diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java index ca538540807..ebb07bdca6e 100644 --- a/src/com/android/settings/bluetooth/BluetoothPairingDetail.java +++ b/src/com/android/settings/bluetooth/BluetoothPairingDetail.java @@ -79,7 +79,7 @@ public class BluetoothPairingDetail extends BluetoothDevicePairingDetailBase imp boolean isSatelliteOn = true; try { isSatelliteOn = - satelliteRepository.requestIsEnabled( + satelliteRepository.requestIsSessionStarted( Executors.newSingleThreadExecutor()).get(3000, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { Log.e(TAG, "Error to get satellite status : " + e); diff --git a/src/com/android/settings/network/AirplaneModePreferenceController.java b/src/com/android/settings/network/AirplaneModePreferenceController.java index b1f6e5052ff..d4bd4a369c0 100644 --- a/src/com/android/settings/network/AirplaneModePreferenceController.java +++ b/src/com/android/settings/network/AirplaneModePreferenceController.java @@ -162,7 +162,8 @@ public class AirplaneModePreferenceController extends TogglePreferenceController public void onResume() { try { mIsSatelliteOn.set( - mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor()) + mSatelliteRepository + .requestIsSessionStarted(Executors.newSingleThreadExecutor()) .get(2000, TimeUnit.MILLISECONDS)); } catch (ExecutionException | TimeoutException | InterruptedException e) { Log.e(TAG, "Error to get satellite status : " + e); diff --git a/src/com/android/settings/network/SatelliteRepository.kt b/src/com/android/settings/network/SatelliteRepository.kt index 565fbf349d4..b7c25f4f658 100644 --- a/src/com/android/settings/network/SatelliteRepository.kt +++ b/src/com/android/settings/network/SatelliteRepository.kt @@ -25,6 +25,7 @@ import androidx.annotation.VisibleForTesting import androidx.concurrent.futures.CallbackToFutureAdapter import com.google.common.util.concurrent.Futures.immediateFuture import com.google.common.util.concurrent.ListenableFuture +import java.util.concurrent.Executor import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor @@ -32,7 +33,6 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flowOf -import java.util.concurrent.Executor import kotlinx.coroutines.flow.flowOn /** @@ -58,20 +58,26 @@ class SatelliteRepository( } return CallbackToFutureAdapter.getFuture { completer -> - satelliteManager.requestIsEnabled(executor, - object : OutcomeReceiver { - override fun onResult(result: Boolean) { - Log.i(TAG, "Satellite modem enabled status: $result") - completer.set(result) - } + try { + satelliteManager.requestIsEnabled(executor, + object : OutcomeReceiver { + override fun onResult(result: Boolean) { + Log.i(TAG, "Satellite modem enabled status: $result") + completer.set(result) + } + + override fun onError(error: SatelliteManager.SatelliteException) { + super.onError(error) + Log.w(TAG, "Can't get satellite modem enabled status", error) + completer.set(false) + } + }) + "requestIsEnabled" + } catch (e: IllegalStateException) { + Log.w(TAG, "IllegalStateException $e") + completer.set(false) + } - override fun onError(error: SatelliteManager.SatelliteException) { - super.onError(error) - Log.w(TAG, "Can't get satellite modem enabled status", error) - completer.set(false) - } - }) - "requestIsEnabled" } } @@ -96,14 +102,21 @@ class SatelliteRepository( val callback = object : SatelliteModemStateCallback { override fun onSatelliteModemStateChanged(state: Int) { val isSessionStarted = isSatelliteSessionStarted(state) - Log.i(TAG, "Satellite modem state changed: state=$state" - + ", isSessionStarted=$isSessionStarted") + Log.i( + TAG, "Satellite modem state changed: state=$state" + + ", isSessionStarted=$isSessionStarted" + ) completer.set(isSessionStarted) satelliteManager.unregisterForModemStateChanged(this) } } - val registerResult = satelliteManager.registerForModemStateChanged(executor, callback) + var registerResult = SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN + try { + registerResult = satelliteManager.registerForModemStateChanged(executor, callback) + } catch (e: IllegalStateException) { + Log.w(TAG, "IllegalStateException $e") + } if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) { Log.w(TAG, "Failed to register for satellite modem state change: $registerResult") completer.set(false) @@ -132,15 +145,21 @@ class SatelliteRepository( return callbackFlow { val callback = SatelliteModemStateCallback { state -> val isSessionStarted = isSatelliteSessionStarted(state) - Log.i(TAG, "Satellite modem state changed: state=$state" - + ", isSessionStarted=$isSessionStarted") + Log.i( + TAG, "Satellite modem state changed: state=$state" + + ", isSessionStarted=$isSessionStarted" + ) trySend(isSessionStarted) } - - val registerResult = satelliteManager.registerForModemStateChanged( - defaultDispatcher.asExecutor(), - callback - ) + var registerResult: Int = SatelliteManager.SATELLITE_RESULT_ERROR + try { + registerResult = satelliteManager.registerForModemStateChanged( + defaultDispatcher.asExecutor(), + callback + ) + } catch (e: IllegalStateException) { + Log.w(TAG, "IllegalStateException $e") + } if (registerResult != SatelliteManager.SATELLITE_RESULT_SUCCESS) { // If the registration failed (e.g., device doesn't support satellite), @@ -150,7 +169,13 @@ class SatelliteRepository( trySend(false) } - awaitClose { satelliteManager.unregisterForModemStateChanged(callback) } + awaitClose { + try { + satelliteManager.unregisterForModemStateChanged(callback) + } catch (e: IllegalStateException) { + Log.w(TAG, "IllegalStateException $e") + } + } }.flowOn(Dispatchers.Default) } diff --git a/src/com/android/settings/wifi/WifiEnabler.java b/src/com/android/settings/wifi/WifiEnabler.java index d1cf7d679cd..bbb014bf34d 100644 --- a/src/com/android/settings/wifi/WifiEnabler.java +++ b/src/com/android/settings/wifi/WifiEnabler.java @@ -139,7 +139,8 @@ public class WifiEnabler implements SwitchWidgetController.OnSwitchChangeListene // Refresh satellite mode status. try { mIsSatelliteOn.set( - mSatelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor()) + mSatelliteRepository + .requestIsSessionStarted(Executors.newSingleThreadExecutor()) .get(2000, TimeUnit.MILLISECONDS)); } catch (ExecutionException | TimeoutException | InterruptedException e) { Log.e(TAG, "Error to get satellite status : " + e); diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index ff448a86692..3bb50d35817 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -431,7 +431,7 @@ public class WifiSlice implements CustomSliceable { boolean isSatelliteOn = false; try { isSatelliteOn = - satelliteRepository.requestIsEnabled(Executors.newSingleThreadExecutor()) + satelliteRepository.requestIsSessionStarted(Executors.newSingleThreadExecutor()) .get(2000, TimeUnit.MILLISECONDS); } catch (ExecutionException | TimeoutException | InterruptedException e) { Log.e(TAG, "Error to get satellite status : " + e); diff --git a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt index 62fd10a951c..619d290f0c4 100644 --- a/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt +++ b/tests/robotests/src/com/android/settings/network/SatelliteRepositoryTest.kt @@ -91,7 +91,8 @@ class SatelliteRepositoryTest { @Test fun requestIsSessionStarted_resultIsTrue() = runBlocking { - `when`(mockSatelliteManager.registerForModemStateChanged(any(), any()) + `when`( + mockSatelliteManager.registerForModemStateChanged(any(), any()) ).thenAnswer { invocation -> val callback = invocation.getArgument(1) callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) @@ -105,7 +106,8 @@ class SatelliteRepositoryTest { @Test fun requestIsSessionStarted_resultIsFalse() = runBlocking { - `when`(mockSatelliteManager.registerForModemStateChanged(any(), any()) + `when`( + mockSatelliteManager.registerForModemStateChanged(any(), any()) ).thenAnswer { invocation -> val callback = invocation.getArgument(1) callback.onSatelliteModemStateChanged(SatelliteManager.SATELLITE_MODEM_STATE_OFF) @@ -119,7 +121,8 @@ class SatelliteRepositoryTest { @Test fun requestIsSessionStarted_registerFailed() = runBlocking { - `when`(mockSatelliteManager.registerForModemStateChanged(any(), any()) + `when`( + mockSatelliteManager.registerForModemStateChanged(any(), any()) ).thenAnswer { SatelliteManager.SATELLITE_RESULT_ERROR } @@ -129,6 +132,17 @@ class SatelliteRepositoryTest { verify(mockSatelliteManager, never()).unregisterForModemStateChanged(any()) } + @Test + fun requestIsSessionStarted_phoneCrash_registerFailed() = runBlocking { + `when`( + mockSatelliteManager.registerForModemStateChanged(any(), any()) + ).thenThrow(IllegalStateException("Telephony is null")) + + val result: ListenableFuture = repository.requestIsSessionStarted(mockExecutor) + assertFalse(result.get()) + verify(mockSatelliteManager, never()).unregisterForModemStateChanged(any()) + } + @Test fun requestIsSessionStarted_nullSatelliteManager() = runBlocking { `when`(spyContext.getSystemService(SatelliteManager::class.java)).thenReturn(null) @@ -157,6 +171,17 @@ class SatelliteRepositoryTest { assertFalse(result.get()) } + @Test + fun requestIsEnabled_phoneCrash_resultIsFalse() = runBlocking { + `when`( + mockSatelliteManager.requestIsEnabled(any(), any()) + ).thenThrow(IllegalStateException("Telephony is null")) + + val result: ListenableFuture = + repository.requestIsEnabled(mockExecutor) + assertFalse(result.get()) + } + @Test fun requestIsEnabled_exceptionFailure() = runBlocking { @@ -232,7 +257,8 @@ class SatelliteRepositoryTest { @Test fun getIsSessionStartedFlow_registerFailed() = runBlocking { - `when`(mockSatelliteManager.registerForModemStateChanged(any(), any()) + `when`( + mockSatelliteManager.registerForModemStateChanged(any(), any()) ).thenAnswer { SatelliteManager.SATELLITE_RESULT_ERROR } From e85e1c823aa7f5f9d887bf09822db331b03a2d65 Mon Sep 17 00:00:00 2001 From: chelseahao Date: Wed, 31 Jul 2024 14:18:43 +0800 Subject: [PATCH 09/11] [Audiosharing] When user enters a bad code, apply error color on summary. Bug: 356071392 Flag: com.android.settingslib.flags.enable_le_audio_sharing Test: atest Change-Id: I427e08a3cce8d0e85f29f821d9b779acc95217b1 --- .../audiostreams/AudioStreamPreference.java | 19 ++------- .../audiostreams/AudioStreamStateHandler.java | 39 ++++++++++++++----- .../AudioStreamPreferenceTest.java | 14 +------ .../AudioStreamStateHandlerTest.java | 19 +++++++-- 4 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java index 071cf57c715..175e0377bea 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreference.java @@ -43,23 +43,12 @@ class AudioStreamPreference extends TwoTargetPreference { * Update preference UI based on connection status * * @param isConnected Is this stream connected - * @param summary Summary text - * @param onPreferenceClickListener Click listener for the preference */ - void setIsConnected( - boolean isConnected, - String summary, - @Nullable OnPreferenceClickListener onPreferenceClickListener) { - if (mIsConnected == isConnected - && getSummary() == summary - && getOnPreferenceClickListener() == onPreferenceClickListener) { - // Nothing to update. - return; + void setIsConnected(boolean isConnected) { + if (mIsConnected != isConnected) { + mIsConnected = isConnected; + notifyChanged(); } - mIsConnected = isConnected; - setSummary(summary); - setOnPreferenceClickListener(onPreferenceClickListener); - notifyChanged(); } @VisibleForTesting diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java index 4bb84751b36..758984fe432 100644 --- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java +++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandler.java @@ -16,8 +16,12 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams; +import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE; + import android.os.Handler; import android.os.Looper; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import android.util.Log; import androidx.annotation.Nullable; @@ -26,6 +30,7 @@ import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.utils.ThreadUtils; @@ -68,15 +73,31 @@ class AudioStreamStateHandler { // Update UI ThreadUtils.postOnMainThread( - () -> - preference.setIsConnected( - newState - == AudioStreamsProgressCategoryController.AudioStreamState - .SOURCE_ADDED, - getSummary() != EMPTY_STRING_RES - ? preference.getContext().getString(getSummary()) - : "", - getOnClickListener(controller))); + () -> { + String summary = + getSummary() != EMPTY_STRING_RES + ? preference.getContext().getString(getSummary()) + : ""; + if (newState + == AudioStreamsProgressCategoryController.AudioStreamState + .ADD_SOURCE_BAD_CODE) { + SpannableString summarySpan = new SpannableString(summary); + int colorError = Utils.getColorErrorDefaultColor(preference.getContext()); + summarySpan.setSpan( + new ForegroundColorSpan(colorError), + 0, + summary.length(), + SPAN_EXCLUSIVE_INCLUSIVE); + preference.setSummary(summarySpan); + } else { + preference.setSummary(summary); + } + preference.setIsConnected( + newState + == AudioStreamsProgressCategoryController.AudioStreamState + .SOURCE_ADDED); + preference.setOnPreferenceClickListener(getOnClickListener(controller)); + }); } /** diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java index c8f93588c78..2515668e39a 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java @@ -28,7 +28,6 @@ import android.content.Context; import android.view.LayoutInflater; import android.view.View; -import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.PreferenceViewHolder; import androidx.test.core.app.ApplicationProvider; @@ -93,17 +92,6 @@ public class AudioStreamPreferenceTest { assertThat(divider.getVisibility()).isEqualTo(View.GONE); } - @Test - public void setConnected_shouldUpdatePreferenceUI() { - String summary = "Connected"; - OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class); - mPreference.setIsConnected(true, summary, listener); - - assertThat(mPreference.getSummary()).isNotNull(); - assertThat(mPreference.getSummary().toString()).isEqualTo(summary); - assertThat(mPreference.getOnPreferenceClickListener()).isEqualTo(listener); - } - @Test public void setAudioStreamMetadata_shouldUpdateMetadata() { AudioStreamPreference p = @@ -147,7 +135,7 @@ public class AudioStreamPreferenceTest { @Test public void shouldHideSecondTarget_connected() { - mPreference.setIsConnected(true, "", null); + mPreference.setIsConnected(true); assertThat(mPreference.shouldHideSecondTarget()).isTrue(); } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java index adc77a183f6..e44dee90e70 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamStateHandlerTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -31,6 +30,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.text.SpannableString; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; @@ -39,6 +39,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -75,7 +76,9 @@ public class AudioStreamStateHandlerTest { verify(mPreference, never()).setAudioStreamState(any()); verify(mHandler, never()).performAction(any(), any(), any()); - verify(mPreference, never()).setIsConnected(anyBoolean(), anyString(), any()); + verify(mPreference, never()).setIsConnected(anyBoolean()); + verify(mPreference, never()).setSummary(any()); + verify(mPreference, never()).setOnPreferenceClickListener(any()); } @Test @@ -93,7 +96,9 @@ public class AudioStreamStateHandlerTest { .setAudioStreamState( AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED); verify(mHandler).performAction(any(), any(), any()); - verify(mPreference).setIsConnected(eq(true), eq(""), eq(null)); + verify(mPreference).setIsConnected(eq(true)); + verify(mPreference).setSummary(eq("")); + verify(mPreference).setOnPreferenceClickListener(eq(null)); } @Test @@ -119,7 +124,13 @@ public class AudioStreamStateHandlerTest { AudioStreamsProgressCategoryController.AudioStreamState .ADD_SOURCE_BAD_CODE); verify(mHandler).performAction(any(), any(), any()); - verify(mPreference).setIsConnected(eq(false), eq(SUMMARY), eq(listener)); + verify(mPreference).setIsConnected(eq(false)); + ArgumentCaptor argumentCaptor = + ArgumentCaptor.forClass(SpannableString.class); + verify(mPreference).setSummary(argumentCaptor.capture()); + assertThat(argumentCaptor.getValue()).isNotNull(); + assertThat(argumentCaptor.getValue().toString()).isEqualTo(SUMMARY); + verify(mPreference).setOnPreferenceClickListener(eq(listener)); } @Test From cd6f77d43259765f9cdc1f7062e77b870678bc86 Mon Sep 17 00:00:00 2001 From: chelseahao Date: Wed, 31 Jul 2024 15:19:54 +0800 Subject: [PATCH 10/11] [Audiosharing] Change text color. Bug: 356080864 Flag: com.android.settingslib.flags.enable_le_audio_sharing Test: atest Change-Id: Id0882547343cb63149f8a9335b023e9f50649e07 --- .../bluetooth_find_broadcast_password_dialog.xml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/res/layout/bluetooth_find_broadcast_password_dialog.xml b/res/layout/bluetooth_find_broadcast_password_dialog.xml index 938f1033b6a..d758f6150c3 100644 --- a/res/layout/bluetooth_find_broadcast_password_dialog.xml +++ b/res/layout/bluetooth_find_broadcast_password_dialog.xml @@ -26,7 +26,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="48dp" - android:textAlignment="viewStart"/> + android:textAlignment="viewStart" + android:textColor="?android:attr/textColorPrimary"/> - \ No newline at end of file From f2d6fe9e32b6f96e906122a80c31f01c1f3099cd Mon Sep 17 00:00:00 2001 From: Abdelrahman Awadalla Date: Thu, 11 Jul 2024 11:18:26 +0000 Subject: [PATCH 11/11] Add "Touchpad visualizer" to input developer options Bug: 286551975 Bug: 350039816 Test: atest TouchpadVisualizerPreferenceControllerTest Test: Manual testing using $ adb shell settings get system touchpad_visualizer Flag: com.android.hardware.input.touchpad_visualizer Change-Id: Ic5de704c49aabf1a80d202aa890055f7337d45d4 --- res/xml/development_settings.xml | 5 + .../DevelopmentSettingsDashboardFragment.java | 1 + src/com/android/settings/development/OWNERS | 3 + ...ouchpadVisualizerPreferenceController.java | 83 +++++++++++++ ...padVisualizerPreferenceControllerTest.java | 111 ++++++++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 src/com/android/settings/development/TouchpadVisualizerPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/TouchpadVisualizerPreferenceControllerTest.java diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 88abadb5233..9032849c21e 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -507,6 +507,11 @@ android:title="@string/show_key_presses" android:summary="@string/show_key_presses_summary" /> + +