From 31ebeb690e3c7d2ba5cb3b4d3b6bc1654c6acb23 Mon Sep 17 00:00:00 2001 From: Chun-Wei Wang Date: Thu, 30 May 2024 13:57:27 +0800 Subject: [PATCH 1/6] Support alternate button Align the behavior of frp and repair to have them support the alternate button. Bug: 339735656 Test: presubmit Change-Id: Ib785db5ef16a68df4980dee451c6b638692adc5f --- .../settings/password/ConfirmDeviceCredentialActivity.java | 1 + .../password/ConfirmDeviceCredentialBaseFragment.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java index f2ebd1f6b3d..fb3534e357d 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java @@ -239,6 +239,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity { new ChooseLockSettingsHelper.Builder(this); launchedCDC = builder.setHeader(mTitle) .setDescription(mDetails) + .setAlternateButton(alternateButton) .setExternal(true) .setUserId(LockPatternUtils.USER_REPAIR_MODE) .show(); diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java index 2af360a1215..9a50eeff26c 100644 --- a/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java +++ b/src/com/android/settings/password/ConfirmDeviceCredentialBaseFragment.java @@ -197,8 +197,8 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr mCancelButton = view.findViewById(R.id.cancelButton); boolean showCancelButton = mRemoteValidation || getActivity().getIntent().getBooleanExtra( SHOW_CANCEL_BUTTON, false); - boolean hasAlternateButton = (mFrp || mRemoteValidation) && !TextUtils.isEmpty( - mAlternateButtonText); + boolean hasAlternateButton = (mFrp || mRemoteValidation || mRepairMode) + && !TextUtils.isEmpty(mAlternateButtonText); mCancelButton.setVisibility(showCancelButton || hasAlternateButton ? View.VISIBLE : View.GONE); if (hasAlternateButton) { From e401ce51ec831e7047102c72e3f38f8443020de7 Mon Sep 17 00:00:00 2001 From: MiltonWu Date: Wed, 4 Sep 2024 11:58:56 +0000 Subject: [PATCH 2/6] Customize Fingerprint enroll activities Provide an interface for ODM/OEM to override Fingerprint enrollment activities. Bug: 364794493 Flag: EXEMPT can't apply flag for manifest change Test: atest SettingsRoboTests:FingerprintEnrollTest Change-Id: I9e5d904fc9382a9b5eff84a7f462e1a9c16c4ba0 --- AndroidManifest.xml | 18 +++- .../ActivityEmbeddingRulesController.java | 11 ++- .../settings/biometrics/BiometricUtils.java | 7 +- .../fingerprint/FingerprintEnroll.kt | 69 ++++++++++++++ .../FingerprintEnrollActivityClassProvider.kt | 34 +++++++ .../FingerprintFeatureProvider.java | 10 +- .../fingerprint/FingerprintSettings.java | 2 +- .../fingerprint/FingerprintEnrollTest.kt | 94 +++++++++++++++++++ 8 files changed, 231 insertions(+), 14 deletions(-) create mode 100644 src/com/android/settings/biometrics/fingerprint/FingerprintEnroll.kt create mode 100644 src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt create mode 100644 tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d988feaada7..eebef14bc16 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2816,6 +2816,9 @@ + @@ -2826,9 +2829,13 @@ + android:exported="false" + android:theme="@style/GlifTheme.Light" + android:taskAffinity="com.android.settings.root" /> + + @@ -2845,7 +2856,6 @@ - + get() = enrollActivityProvider.setup + } + + /** Inner class representing enrolling fingerprint enrollment from FingerprintSettings */ + class InternalActivity : FingerprintEnroll() { + override val nextActivityClass: Class<*> + get() = enrollActivityProvider.internal + } + + /** + * The class of the next activity to launch. This is open to allow subclasses to provide their + * own behavior. Defaults to the default activity class provided by the + * enrollActivityClassProvider. + */ + open val nextActivityClass: Class<*> + get() = enrollActivityProvider.default + + protected val enrollActivityProvider: FingerprintEnrollActivityClassProvider + get() = featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + /** + * Logs the next activity to be launched, creates an intent for that activity, + * adds flags to forward the result, includes any existing extras from the current intent, + * starts the new activity and then finishes the current one + */ + Log.d("FingerprintEnroll", "forward to $nextActivityClass") + val nextIntent = Intent(this, nextActivityClass) + nextIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) + nextIntent.putExtras(intent) + startActivity(nextIntent) + finish() + } +} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt new file mode 100644 index 00000000000..853a3df01b8 --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.biometrics.fingerprint + +import android.app.Activity + +open class FingerprintEnrollActivityClassProvider { + + open val default: Class + get() = FingerprintEnrollIntroduction::class.java + open val setup: Class + get() = SetupFingerprintEnrollIntroduction::class.java + open val internal: Class + get() = FingerprintEnrollIntroductionInternal::class.java + + companion object { + @JvmStatic + val instance = FingerprintEnrollActivityClassProvider() + } +} diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java index c1e34a579a8..baa88b5655a 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java @@ -33,7 +33,6 @@ public interface FingerprintFeatureProvider { */ SfpsEnrollmentFeature getSfpsEnrollmentFeature(); - /** * Gets calibrator for udfps pre-enroll * @param appContext application context @@ -52,4 +51,13 @@ public interface FingerprintFeatureProvider { * @return the feature implementation */ SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context); + + /** + * Gets the provider for current fingerprint enrollment activity classes + * @return the provider + */ + @NonNull + default FingerprintEnrollActivityClassProvider getEnrollActivityClassProvider() { + return FingerprintEnrollActivityClassProvider.getInstance(); + } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 125691fbf1c..20d453f2ea8 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -1142,7 +1142,7 @@ public class FingerprintSettings extends SubSettings { private void addFirstFingerprint(@Nullable Long gkPwHandle) { Intent intent = new Intent(); intent.setClassName(SETTINGS_PACKAGE_NAME, - FingerprintEnrollIntroductionInternal.class.getName()); + FingerprintEnroll.InternalActivity.class.getName()); intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true); intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE); diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt new file mode 100644 index 00000000000..07cdffb942c --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.biometrics.fingerprint + +import android.app.Activity +import android.content.Intent +import com.android.settings.overlay.FeatureFactory +import com.android.settings.testutils.FakeFeatureFactory +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.`when` +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows + +@RunWith(RobolectricTestRunner::class) +class FingerprintEnrollTest { + + private lateinit var featureFactory: FeatureFactory + + private companion object { + const val INTENT_KEY = "testKey" + const val INTENT_VALUE = "testValue" + val INTENT = Intent().apply { + putExtra(INTENT_KEY, INTENT_VALUE) + } + } + + private val activityProvider = FingerprintEnrollActivityClassProvider() + + @Before + fun setUp() { + featureFactory = FakeFeatureFactory.setupForTest() + `when`(featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider) + .thenReturn(activityProvider) + } + + private fun setupActivity(activityClass: Class): FingerprintEnroll { + return Robolectric.buildActivity(activityClass, INTENT).create().get() + } + + @Test + fun testFinishAndLaunchDefaultActivity() { + // Run + val activity = setupActivity(FingerprintEnroll::class.java) + + // Verify + verifyLaunchNextActivity(activity, activityProvider.default) + } + + @Test + fun testFinishAndLaunchSetupActivity() { + // Run + val activity = setupActivity(FingerprintEnroll.SetupActivity::class.java) + + // Verify + verifyLaunchNextActivity(activity, activityProvider.setup) + } + + @Test + fun testFinishAndLaunchInternalActivity() { + // Run + val activity = setupActivity(FingerprintEnroll.InternalActivity::class.java) + + // Verify + verifyLaunchNextActivity(activity, activityProvider.internal) + } + + private fun verifyLaunchNextActivity( + currentActivityInstance : FingerprintEnroll, + nextActivityClass: Class + ) { + assertThat(currentActivityInstance.isFinishing).isTrue() + val nextActivityIntent = Shadows.shadowOf(currentActivityInstance).nextStartedActivity + assertThat(nextActivityIntent.component!!.className).isEqualTo(nextActivityClass.name) + assertThat(nextActivityIntent.extras!!.size()).isEqualTo(1) + assertThat(nextActivityIntent.getStringExtra(INTENT_KEY)).isEqualTo(INTENT_VALUE) + } +} From 216353a7702b01bc2caa2cc68a7e67e1f7ba17f0 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Thu, 5 Sep 2024 22:38:19 +0000 Subject: [PATCH 3/6] Makes Caption Language searchable only if captions are enabled. Fix: 354785578 Flag: com.android.settings.accessibility.fix_a11y_settings_search Test: atest CaptioningMoreOptionsFragmentTest Test: adb shell pm clear com.google.android.settings.intelligence; disable captioning; search Settings for 'language', observe caption language missing enable captioning; search Settings for 'language', observe caption language present Change-Id: I015d2c77fbd3f7b8fe713bafb3a49a86160ca958 --- .../CaptioningMoreOptionsFragment.java | 15 +++++- .../CaptioningMoreOptionsFragmentTest.java | 46 +++++++++++++++++-- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/accessibility/CaptioningMoreOptionsFragment.java b/src/com/android/settings/accessibility/CaptioningMoreOptionsFragment.java index 2895e751c26..85b48e9ded9 100644 --- a/src/com/android/settings/accessibility/CaptioningMoreOptionsFragment.java +++ b/src/com/android/settings/accessibility/CaptioningMoreOptionsFragment.java @@ -17,6 +17,8 @@ package com.android.settings.accessibility; import android.app.settings.SettingsEnums; +import android.content.Context; +import android.provider.Settings; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; @@ -50,5 +52,16 @@ public class CaptioningMoreOptionsFragment extends DashboardFragment { } public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = - new BaseSearchIndexProvider(R.xml.captioning_more_options); + new BaseSearchIndexProvider(R.xml.captioning_more_options) { + @Override + protected boolean isPageSearchEnabled(Context context) { + if (!Flags.fixA11ySettingsSearch()) { + return super.isPageSearchEnabled(context); + } + // CaptioningMoreOptions is only searchable if captions are enabled, so that we + // don't show search results for settings that will cause no change to the user. + return Settings.Secure.getInt(context.getContentResolver(), + Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, 0) == 1; + } + }; } diff --git a/tests/robotests/src/com/android/settings/accessibility/CaptioningMoreOptionsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/CaptioningMoreOptionsFragmentTest.java index 6969472b485..5486808821a 100644 --- a/tests/robotests/src/com/android/settings/accessibility/CaptioningMoreOptionsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/CaptioningMoreOptionsFragmentTest.java @@ -20,6 +20,9 @@ import static com.google.common.truth.Truth.assertThat; import android.app.settings.SettingsEnums; import android.content.Context; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; @@ -27,15 +30,23 @@ import com.android.settings.R; import com.android.settings.testutils.XmlTestUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; /** Tests for {@link CaptioningMoreOptionsFragment}. */ @RunWith(RobolectricTestRunner.class) public class CaptioningMoreOptionsFragmentTest { + // Language/locale preference key, from captioning_more_options.xml + private static final String CAPTIONING_LOCALE_KEY = "captioning_locale"; + + @Rule + public final SetFlagsRule mSetFlagRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); private CaptioningMoreOptionsFragment mFragment; @@ -65,11 +76,40 @@ public class CaptioningMoreOptionsFragmentTest { @Test public void getNonIndexableKeys_existInXmlLayout() { final List niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER - .getNonIndexableKeys(mContext); + .getNonIndexableKeys(mContext) + .stream().filter(Objects::nonNull).collect(Collectors.toList()); final List keys = - XmlTestUtils.getKeysFromPreferenceXml(mContext, - R.xml.captioning_more_options); + XmlTestUtils.getKeysFromPreferenceXml(mContext, R.xml.captioning_more_options); assertThat(keys).containsAtLeastElementsIn(niks); } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void getNonIndexableKeys_captioningEnabled_localeIsSearchable() { + setCaptioningEnabled(true); + + final List niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(mContext); + + // Not in NonIndexableKeys == searchable + assertThat(niks).doesNotContain(CAPTIONING_LOCALE_KEY); + } + + @Test + @EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH) + public void getNonIndexableKeys_captioningDisabled_localeIsNotSearchable() { + setCaptioningEnabled(false); + + final List niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER + .getNonIndexableKeys(mContext); + + // In NonIndexableKeys == not searchable + assertThat(niks).contains(CAPTIONING_LOCALE_KEY); + } + + private void setCaptioningEnabled(boolean enabled) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, enabled ? 1 : 0); + } } From 994bff003380014fbc06f1fe410ae20428b5e1ba Mon Sep 17 00:00:00 2001 From: "Pechetty Sravani (xWF)" Date: Mon, 9 Sep 2024 04:06:57 +0000 Subject: [PATCH 4/6] Revert "Customize Fingerprint enroll activities" Revert submission 29139483-494-trampoline Reason for revert: DroidMonitor created revert due to b/365453068. Reverted changes: /q/submissionid:29139483-494-trampoline Change-Id: Ie2ab22694e7128e10eb11bf29340023e2bfd1343 --- AndroidManifest.xml | 18 +--- .../ActivityEmbeddingRulesController.java | 11 +-- .../settings/biometrics/BiometricUtils.java | 7 +- .../fingerprint/FingerprintEnroll.kt | 69 -------------- .../FingerprintEnrollActivityClassProvider.kt | 34 ------- .../FingerprintFeatureProvider.java | 10 +- .../fingerprint/FingerprintSettings.java | 2 +- .../fingerprint/FingerprintEnrollTest.kt | 94 ------------------- 8 files changed, 14 insertions(+), 231 deletions(-) delete mode 100644 src/com/android/settings/biometrics/fingerprint/FingerprintEnroll.kt delete mode 100644 src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt delete mode 100644 tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index eebef14bc16..d988feaada7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2816,9 +2816,6 @@ - @@ -2829,13 +2826,9 @@ - + android:exported="false" + android:theme="@style/GlifTheme.Light" + android:taskAffinity="com.android.settings.root" /> - @@ -2856,6 +2845,7 @@ + - get() = enrollActivityProvider.setup - } - - /** Inner class representing enrolling fingerprint enrollment from FingerprintSettings */ - class InternalActivity : FingerprintEnroll() { - override val nextActivityClass: Class<*> - get() = enrollActivityProvider.internal - } - - /** - * The class of the next activity to launch. This is open to allow subclasses to provide their - * own behavior. Defaults to the default activity class provided by the - * enrollActivityClassProvider. - */ - open val nextActivityClass: Class<*> - get() = enrollActivityProvider.default - - protected val enrollActivityProvider: FingerprintEnrollActivityClassProvider - get() = featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - /** - * Logs the next activity to be launched, creates an intent for that activity, - * adds flags to forward the result, includes any existing extras from the current intent, - * starts the new activity and then finishes the current one - */ - Log.d("FingerprintEnroll", "forward to $nextActivityClass") - val nextIntent = Intent(this, nextActivityClass) - nextIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) - nextIntent.putExtras(intent) - startActivity(nextIntent) - finish() - } -} \ No newline at end of file diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt deleted file mode 100644 index 853a3df01b8..00000000000 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollActivityClassProvider.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.biometrics.fingerprint - -import android.app.Activity - -open class FingerprintEnrollActivityClassProvider { - - open val default: Class - get() = FingerprintEnrollIntroduction::class.java - open val setup: Class - get() = SetupFingerprintEnrollIntroduction::class.java - open val internal: Class - get() = FingerprintEnrollIntroductionInternal::class.java - - companion object { - @JvmStatic - val instance = FingerprintEnrollActivityClassProvider() - } -} diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java index baa88b5655a..c1e34a579a8 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintFeatureProvider.java @@ -33,6 +33,7 @@ public interface FingerprintFeatureProvider { */ SfpsEnrollmentFeature getSfpsEnrollmentFeature(); + /** * Gets calibrator for udfps pre-enroll * @param appContext application context @@ -51,13 +52,4 @@ public interface FingerprintFeatureProvider { * @return the feature implementation */ SfpsRestToUnlockFeature getSfpsRestToUnlockFeature(@NonNull Context context); - - /** - * Gets the provider for current fingerprint enrollment activity classes - * @return the provider - */ - @NonNull - default FingerprintEnrollActivityClassProvider getEnrollActivityClassProvider() { - return FingerprintEnrollActivityClassProvider.getInstance(); - } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index 20d453f2ea8..125691fbf1c 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -1142,7 +1142,7 @@ public class FingerprintSettings extends SubSettings { private void addFirstFingerprint(@Nullable Long gkPwHandle) { Intent intent = new Intent(); intent.setClassName(SETTINGS_PACKAGE_NAME, - FingerprintEnroll.InternalActivity.class.getName()); + FingerprintEnrollIntroductionInternal.class.getName()); intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true); intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE); diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt deleted file mode 100644 index 07cdffb942c..00000000000 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollTest.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.settings.biometrics.fingerprint - -import android.app.Activity -import android.content.Intent -import com.android.settings.overlay.FeatureFactory -import com.android.settings.testutils.FakeFeatureFactory -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.`when` -import org.robolectric.Robolectric -import org.robolectric.RobolectricTestRunner -import org.robolectric.Shadows - -@RunWith(RobolectricTestRunner::class) -class FingerprintEnrollTest { - - private lateinit var featureFactory: FeatureFactory - - private companion object { - const val INTENT_KEY = "testKey" - const val INTENT_VALUE = "testValue" - val INTENT = Intent().apply { - putExtra(INTENT_KEY, INTENT_VALUE) - } - } - - private val activityProvider = FingerprintEnrollActivityClassProvider() - - @Before - fun setUp() { - featureFactory = FakeFeatureFactory.setupForTest() - `when`(featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider) - .thenReturn(activityProvider) - } - - private fun setupActivity(activityClass: Class): FingerprintEnroll { - return Robolectric.buildActivity(activityClass, INTENT).create().get() - } - - @Test - fun testFinishAndLaunchDefaultActivity() { - // Run - val activity = setupActivity(FingerprintEnroll::class.java) - - // Verify - verifyLaunchNextActivity(activity, activityProvider.default) - } - - @Test - fun testFinishAndLaunchSetupActivity() { - // Run - val activity = setupActivity(FingerprintEnroll.SetupActivity::class.java) - - // Verify - verifyLaunchNextActivity(activity, activityProvider.setup) - } - - @Test - fun testFinishAndLaunchInternalActivity() { - // Run - val activity = setupActivity(FingerprintEnroll.InternalActivity::class.java) - - // Verify - verifyLaunchNextActivity(activity, activityProvider.internal) - } - - private fun verifyLaunchNextActivity( - currentActivityInstance : FingerprintEnroll, - nextActivityClass: Class - ) { - assertThat(currentActivityInstance.isFinishing).isTrue() - val nextActivityIntent = Shadows.shadowOf(currentActivityInstance).nextStartedActivity - assertThat(nextActivityIntent.component!!.className).isEqualTo(nextActivityClass.name) - assertThat(nextActivityIntent.extras!!.size()).isEqualTo(1) - assertThat(nextActivityIntent.getStringExtra(INTENT_KEY)).isEqualTo(INTENT_VALUE) - } -} From cd7627c9ffdbb4f00168457ba403e9862e4273fc Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Thu, 5 Sep 2024 14:19:35 +0800 Subject: [PATCH 5/6] Make bluetooth profile toggles configurable BUG: 343317785 Test: atest BluetoothDetailsProfilesControllerTest Flag: com.android.settings.flags.enable_bluetooth_device_details_polish Change-Id: I5aea110f7a42ffee20a56dbd9d5621f44311cc66 --- .../BluetoothDetailsProfilesController.java | 36 ++++++++++---- .../BluetoothDeviceDetailsFragment.java | 6 ++- .../ui/view/DeviceDetailsFragmentFormatter.kt | 15 +++++- .../view/DeviceDetailsMoreSettingsFragment.kt | 24 ++++++++-- ...luetoothDetailsProfilesControllerTest.java | 47 ++++++++++++++++--- .../DeviceDetailsFragmentFormatterTest.kt | 15 +++--- .../BluetoothDeviceDetailsViewModelTest.kt | 4 +- 7 files changed, 115 insertions(+), 32 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index 5d9b124f28b..42d39487398 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -26,6 +26,7 @@ import android.sysprop.BluetoothProperties; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; @@ -52,7 +53,9 @@ import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.utils.ThreadUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -82,7 +85,9 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY = "persist.bluetooth.leaudio.toggle_visible"; - private final AtomicReference> mInvisiblePreferenceKey = new AtomicReference<>(); + private Set mInvisibleProfiles = Collections.emptySet(); + private final AtomicReference> mAdditionalInvisibleProfiles = + new AtomicReference<>(); private LocalBluetoothManager mManager; private LocalBluetoothProfileManager mProfileManager; @@ -96,13 +101,21 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll @VisibleForTesting PreferenceCategory mProfilesContainer; - public BluetoothDetailsProfilesController(Context context, PreferenceFragmentCompat fragment, - LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle) { + public BluetoothDetailsProfilesController( + Context context, + PreferenceFragmentCompat fragment, + LocalBluetoothManager manager, + CachedBluetoothDevice device, + Lifecycle lifecycle, + @Nullable List invisibleProfiles) { super(context, fragment, device, lifecycle); mManager = manager; mProfileManager = mManager.getProfileManager(); mCachedDevice = device; mCachedDeviceGroup = Utils.findAllCachedBluetoothDevicesByGroupId(mManager, mCachedDevice); + if (invisibleProfiles != null) { + mInvisibleProfiles = Set.copyOf(invisibleProfiles); + } } @Override @@ -564,7 +577,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll protected void refresh() { ThreadUtils.postOnBackgroundThread( () -> { - mInvisiblePreferenceKey.set( + mAdditionalInvisibleProfiles.set( FeatureFactory.getFeatureFactory() .getBluetoothFeatureProvider() .getInvisibleProfilePreferenceKeys( @@ -605,12 +618,15 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll mProfilesContainer.addPreference(preference); } - Set invisibleKeys = mInvisiblePreferenceKey.get(); - if (invisibleKeys != null) { - for (int i = 0; i < mProfilesContainer.getPreferenceCount(); ++i) { - Preference pref = mProfilesContainer.getPreference(i); - pref.setVisible(pref.isVisible() && !invisibleKeys.contains(pref.getKey())); - } + Set additionalInvisibleProfiles = mAdditionalInvisibleProfiles.get(); + HashSet combinedInvisibleProfiles = new HashSet<>(mInvisibleProfiles); + if (additionalInvisibleProfiles != null) { + combinedInvisibleProfiles.addAll(additionalInvisibleProfiles); + } + Log.i(TAG, "Invisible profiles: " + combinedInvisibleProfiles); + for (int i = 0; i < mProfilesContainer.getPreferenceCount(); ++i) { + Preference pref = mProfilesContainer.getPreference(i); + pref.setVisible(pref.isVisible() && !combinedInvisibleProfiles.contains(pref.getKey())); } } diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index 844a7c05484..0e51d178fe8 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -419,12 +419,16 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment @Override protected List createPreferenceControllers(Context context) { + List invisibleProfiles = List.of(); if (Flags.enableBluetoothDeviceDetailsPolish()) { mFormatter = FeatureFactory.getFeatureFactory() .getBluetoothFeatureProvider() .getDeviceDetailsFragmentFormatter( requireContext(), this, mBluetoothAdapter, mCachedDevice); + invisibleProfiles = + mFormatter.getInvisibleBluetoothProfiles( + FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE); } ArrayList controllers = new ArrayList<>(); @@ -444,7 +448,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment controllers.add(new BluetoothDetailsSpatialAudioController(context, this, mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsProfilesController(context, this, mManager, - mCachedDevice, lifecycle)); + mCachedDevice, lifecycle, invisibleProfiles)); controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, lifecycle)); controllers.add(new StylusDevicesController(context, mInputDevice, mCachedDevice, diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt index 8f0bf3e9dc6..f2a569d2245 100644 --- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatter.kt @@ -43,6 +43,7 @@ import com.android.settings.core.SubSettingLauncher import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.settings.spa.preference.ComposePreference import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon import com.android.settingslib.spa.framework.theme.SettingsDimension @@ -68,6 +69,9 @@ interface DeviceDetailsFragmentFormatter { /** Gets keys of visible preferences in built-in preference in xml. */ fun getVisiblePreferenceKeys(fragmentType: FragmentTypeModel): List? + /** Updates device details fragment layout. */ + fun getInvisibleBluetoothProfiles(fragmentType: FragmentTypeModel): List? + /** Updates device details fragment layout. */ fun updateLayout(fragmentType: FragmentTypeModel) @@ -108,13 +112,22 @@ class DeviceDetailsFragmentFormatterImpl( viewModel .getItems(fragmentType) ?.filterIsInstance() - ?.mapNotNull { it.preferenceKey } + ?.map { it.preferenceKey } + } + + override fun getInvisibleBluetoothProfiles(fragmentType: FragmentTypeModel): List? = + runBlocking { + viewModel + .getItems(fragmentType) + ?.filterIsInstance() + ?.first()?.invisibleProfiles } /** Updates bluetooth device details fragment layout. */ override fun updateLayout(fragmentType: FragmentTypeModel) = runBlocking { val items = viewModel.getItems(fragmentType) ?: return@runBlocking val layout = viewModel.getLayout(fragmentType) ?: return@runBlocking + val prefKeyToSettingId = items .filterIsInstance() diff --git a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt index f69f023d482..c0fbd4f2c4a 100644 --- a/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt +++ b/src/com/android/settings/bluetooth/ui/view/DeviceDetailsMoreSettingsFragment.kt @@ -22,7 +22,6 @@ import android.content.Context import android.graphics.PorterDuff import android.os.Bundle import android.view.Menu -import android.view.MenuInflater import android.view.MenuItem import androidx.lifecycle.lifecycleScope import com.android.settings.R @@ -63,8 +62,10 @@ class DeviceDetailsMoreSettingsFragment : DashboardFragment() { item.icon?.setColorFilter( resources.getColor( com.android.settingslib.widget.theme.R.color - .settingslib_materialColorOnSurface), - PorterDuff.Mode.SRC_ATOP) + .settingslib_materialColorOnSurface + ), + PorterDuff.Mode.SRC_ATOP, + ) item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) } } @@ -116,14 +117,27 @@ class DeviceDetailsMoreSettingsFragment : DashboardFragment() { } formatter = featureFactory.bluetoothFeatureProvider.getDeviceDetailsFragmentFormatter( - requireContext(), this, bluetoothManager.adapter, cachedDevice) + requireContext(), + this, + bluetoothManager.adapter, + cachedDevice, + ) helpItem = formatter .getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment) .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), initialValue = null) return listOf( BluetoothDetailsProfilesController( - context, this, localBluetoothManager, cachedDevice, settingsLifecycle)) + context, + this, + localBluetoothManager, + cachedDevice, + settingsLifecycle, + formatter.getInvisibleBluetoothProfiles( + FragmentTypeModel.DeviceDetailsMoreSettingsFragment + ), + ) + ) } override fun getLogTag(): String = TAG diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java index 4b4dd8b11e1..d137d8287e8 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsProfilesControllerTest.java @@ -120,11 +120,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont .thenAnswer(invocation -> ImmutableList.of(mConnectableProfiles)); setupDevice(mDeviceConfig); - mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager, - mCachedDevice, mLifecycle); - mProfiles.setKey(mController.getPreferenceKey()); - mController.mProfilesContainer = mProfiles; - mScreen.addPreference(mProfiles); + initController(List.of()); BluetoothProperties.le_audio_allow_list(Lists.newArrayList(LE_DEVICE_MODEL)); } @@ -554,6 +550,36 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont @Test public void prefKeyInBlockingList_hideToggle() { + initController(List.of("A2DP")); + setupDevice(makeDefaultDeviceConfig()); + + addA2dpProfileToDevice(true, true, true); + when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any())) + .thenReturn(ImmutableSet.of()); + + showScreen(mController); + + List switches = getProfileSwitches(false); + assertThat(switches.get(0).isVisible()).isFalse(); + } + + @Test + public void prefKeyNotInBlockingList_showToggle() { + initController(List.of()); + setupDevice(makeDefaultDeviceConfig()); + + addA2dpProfileToDevice(true, true, true); + when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any())) + .thenReturn(ImmutableSet.of()); + + showScreen(mController); + + List switches = getProfileSwitches(false); + assertThat(switches.get(0).isVisible()).isTrue(); + } + + @Test + public void prefKeyInFeatureProviderBlockingList_hideToggle() { setupDevice(makeDefaultDeviceConfig()); addA2dpProfileToDevice(true, true, true); @@ -567,7 +593,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont } @Test - public void prefKeyNotInBlockingList_showToggle() { + public void prefKeyNotInFeatureProviderBlockingList_showToggle() { setupDevice(makeDefaultDeviceConfig()); addA2dpProfileToDevice(true, true, true); @@ -627,4 +653,13 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont assertThat(switches.getFirst().getTitle()).isEqualTo( mContext.getString(mLeAudioProfile.getNameResource(mDevice))); } + + private void initController(List invisibleProfiles) { + mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager, + mCachedDevice, mLifecycle, invisibleProfiles); + mProfiles.setKey(mController.getPreferenceKey()); + mController.mProfilesContainer = mProfiles; + mScreen.removeAll(); + mScreen.addPreference(mProfiles); + } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt index a5bc2463cdd..8070b2e5362 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/view/DeviceDetailsFragmentFormatterTest.kt @@ -122,10 +122,11 @@ class DeviceDetailsFragmentFormatterTest { .thenReturn( DeviceSettingConfigModel( listOf( - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_HEADER, - "bluetooth_device_header"), - DeviceSettingConfigItemModel.BuiltinItem( + "bluetooth_device_header" + ), + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons"), ), listOf(), @@ -203,10 +204,10 @@ class DeviceDetailsFragmentFormatterTest { .thenReturn( DeviceSettingConfigModel( listOf( - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_HEADER, "bluetooth_device_header"), - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), @@ -227,12 +228,12 @@ class DeviceDetailsFragmentFormatterTest { .thenReturn( DeviceSettingConfigModel( listOf( - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_HEADER, "bluetooth_device_header"), DeviceSettingConfigItemModel.AppProvidedItem( DeviceSettingId.DEVICE_SETTING_ID_ANC), - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS, "keyboard_settings"), ), diff --git a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt index 9cbe6e38736..6869c23fa95 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt +++ b/tests/robotests/src/com/android/settings/bluetooth/ui/viewmodel/BluetoothDeviceDetailsViewModelTest.kt @@ -282,10 +282,10 @@ class BluetoothDeviceDetailsViewModelTest { private companion object { val BUILTIN_SETTING_ITEM_1 = - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_HEADER, "bluetooth_device_header") val BUILDIN_SETTING_ITEM_2 = - DeviceSettingConfigItemModel.BuiltinItem( + DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem( DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, "action_buttons") val SETTING_ITEM_HELP = DeviceSettingConfigItemModel.AppProvidedItem(12345) } From 386071e91dab6c1ab3df00dff049ba5798a1ec4f Mon Sep 17 00:00:00 2001 From: Jason Chang Date: Sat, 7 Sep 2024 13:29:10 +0000 Subject: [PATCH 6/6] Fix fingerprint enrollment animation is not in correct position. 1. Change SPFS ring progress bar and lottie animaiton from layout_marginLeft/ layout_marginRight to marginStart/marginEnd for RTL language. 2. Modify SPFS ring progress bar and lottie animaiton dimensioin for RTL language and check three different SFPS projects. Flag: NONE bug-fixing Bug: 338911438 Test: manual build and check UI visually. Change-Id: I88edad459eefbb3c6be337263045cd87685810e9 --- res/layout/sfps_enroll_enrolling.xml | 4 ++-- res/values-ldrtl/dimens.xml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/res/layout/sfps_enroll_enrolling.xml b/res/layout/sfps_enroll_enrolling.xml index e0f9d1f8454..bd6e69a4175 100644 --- a/res/layout/sfps_enroll_enrolling.xml +++ b/res/layout/sfps_enroll_enrolling.xml @@ -45,7 +45,7 @@ android:id="@+id/illustration_lottie" android:layout_width="@dimen/fingerprint_progress_bar_max_size" android:layout_height="@dimen/fingerprint_progress_bar_max_size" - android:layout_marginRight="@dimen/sfps_lottie_translate_x" + android:layout_marginEnd="@dimen/sfps_lottie_translate_x" android:layout_marginBottom="@dimen/sfps_lottie_translate_y" android:scaleType="centerInside" android:visibility="gone" @@ -66,7 +66,7 @@ android:id="@+id/fingerprint_progress_bar" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginLeft="@dimen/sfps_progress_bar_translate_x" + android:layout_marginStart="@dimen/sfps_progress_bar_translate_x" android:layout_marginTop="@dimen/sfps_progress_bar_translate_y" android:layout_gravity="center" android:minHeight="@dimen/fingerprint_progress_bar_min_size" diff --git a/res/values-ldrtl/dimens.xml b/res/values-ldrtl/dimens.xml index cbe7eb5abdc..f11b2f458a7 100755 --- a/res/values-ldrtl/dimens.xml +++ b/res/values-ldrtl/dimens.xml @@ -23,4 +23,8 @@ 20dp 0dp 20dp + + + 242dp + 10dp