From d3335c27857e10bff490cf146f252974240cdfbc Mon Sep 17 00:00:00 2001 From: Tetiana Meronyk Date: Thu, 13 Apr 2023 11:27:22 +0000 Subject: [PATCH 01/20] Make user creation flow in a single dialog Bug: 266548780 Test: atest UserDetailsSettingsTest Change-Id: I3df846aa480b80321269ac7b9b8723912597aba4 --- .../android/settings/users/UserSettings.java | 98 ++++++------------- 1 file changed, 32 insertions(+), 66 deletions(-) diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index d904ed0f55c..28e02ec16e2 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -28,7 +28,6 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; @@ -83,6 +82,7 @@ import com.android.settingslib.RestrictedPreference; import com.android.settingslib.drawable.CircleFramedDrawable; import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexableRaw; +import com.android.settingslib.users.CreateUserDialogController; import com.android.settingslib.users.EditUserInfoController; import com.android.settingslib.users.GrantAdminDialogController; import com.android.settingslib.users.UserCreatingDialog; @@ -119,6 +119,7 @@ public class UserSettings extends SettingsPreferenceFragment /** UserId of the user being removed */ private static final String SAVE_REMOVING_USER = "removing_user"; + private static final String SAVE_CREATE_USER = "create_user"; private static final String KEY_USER_LIST = "user_list"; private static final String KEY_USER_ME = "user_me"; @@ -171,9 +172,6 @@ public class UserSettings extends SettingsPreferenceFragment static final int RESULT_GUEST_REMOVED = 100; - private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED = - "key_add_user_long_message_displayed"; - private static final String KEY_TITLE = "title"; private static final String KEY_SUMMARY = "summary"; @@ -222,6 +220,8 @@ public class UserSettings extends SettingsPreferenceFragment new GrantAdminDialogController(); private EditUserInfoController mEditUserInfoController = new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY); + private CreateUserDialogController mCreateUserDialogController = + new CreateUserDialogController(Utils.FILE_PROVIDER_AUTHORITY); private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; private GuestTelephonyPreferenceController mGuestTelephonyPreferenceController; private RemoveGuestOnExitPreferenceController mRemoveGuestOnExitPreferenceController; @@ -233,7 +233,7 @@ public class UserSettings extends SettingsPreferenceFragment private CharSequence mPendingUserName; private Drawable mPendingUserIcon; - private boolean mGrantAdmin; + private boolean mPendingUserIsAdmin; // A place to cache the generated default avatar private Drawable mDefaultIconDrawable; @@ -348,7 +348,11 @@ public class UserSettings extends SettingsPreferenceFragment if (icicle.containsKey(SAVE_REMOVING_USER)) { mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER); } - mEditUserInfoController.onRestoreInstanceState(icicle); + if (icicle.containsKey(SAVE_CREATE_USER)) { + mCreateUserDialogController.onRestoreInstanceState(icicle); + } else { + mEditUserInfoController.onRestoreInstanceState(icicle); + } } mUserCaps = UserCapabilities.create(activity); @@ -440,7 +444,12 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void onSaveInstanceState(Bundle outState) { - mEditUserInfoController.onSaveInstanceState(outState); + if (mCreateUserDialogController.isActive()) { + outState.putBoolean(SAVE_CREATE_USER, mCreateUserDialogController.isActive()); + mCreateUserDialogController.onSaveInstanceState(outState); + } else { + mEditUserInfoController.onSaveInstanceState(outState); + } outState.putInt(SAVE_REMOVING_USER, mRemovingUserId); super.onSaveInstanceState(outState); } @@ -448,6 +457,7 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void startActivityForResult(Intent intent, int requestCode) { mEditUserInfoController.startingActivityForResult(); + mCreateUserDialogController.startingActivityForResult(); super.startActivityForResult(intent, requestCode); } @@ -562,6 +572,7 @@ public class UserSettings extends SettingsPreferenceFragment && resultCode == RESULT_GUEST_REMOVED) { scheduleGuestCreation(); } else { + mCreateUserDialogController.onActivityResult(requestCode, resultCode, data); mEditUserInfoController.onActivityResult(requestCode, resultCode, data); } } @@ -704,37 +715,12 @@ public class UserSettings extends SettingsPreferenceFragment .setPositiveButton(android.R.string.ok, null) .create(); case DIALOG_ADD_USER: { - final SharedPreferences preferences = getActivity().getPreferences( - Context.MODE_PRIVATE); - final boolean longMessageDisplayed = preferences.getBoolean( - KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false); - final int messageResId = longMessageDisplayed - ? com.android.settingslib.R.string.user_add_user_message_short - : com.android.settingslib.R.string.user_add_user_message_long; - Dialog dlg = new AlertDialog.Builder(context) - .setTitle(com.android.settingslib.R.string.user_add_user_title) - .setMessage(messageResId) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - if (!longMessageDisplayed) { - preferences.edit().putBoolean( - KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, - true).apply(); - } - if (UserManager.isMultipleAdminEnabled()) { - showDialog(DIALOG_GRANT_ADMIN); - } else { - showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER); - } - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); - return dlg; - } - case DIALOG_GRANT_ADMIN: { - return buildGrantAdminDialog(); + synchronized (mUserLock) { + mPendingUserName = getString( + com.android.settingslib.R.string.user_new_user_name); + mPendingUserIcon = null; + } + return buildAddUserDialog(USER_TYPE_USER); } case DIALOG_CHOOSE_USER_TYPE: { List> data = new ArrayList>(); @@ -919,17 +905,14 @@ public class UserSettings extends SettingsPreferenceFragment private Dialog buildAddUserDialog(int userType) { Dialog d; synchronized (mUserLock) { - d = mEditUserInfoController.createDialog( + d = mCreateUserDialogController.createDialog( getActivity(), this::startActivityForResult, - null, - mPendingUserName.toString(), - getString(userType == USER_TYPE_USER - ? com.android.settingslib.R.string.user_info_settings_title - : com.android.settingslib.R.string.profile_info_settings_title), - (userName, userIcon) -> { + UserManager.isMultipleAdminEnabled(), + (userName, userIcon, isAdmin) -> { mPendingUserIcon = userIcon; mPendingUserName = userName; + mPendingUserIsAdmin = isAdmin; addUserNow(userType); }, () -> { @@ -943,26 +926,6 @@ public class UserSettings extends SettingsPreferenceFragment return d; } - private Dialog buildGrantAdminDialog() { - return mGrantAdminDialogController.createDialog( - getActivity(), - (grantAdmin) -> { - mGrantAdmin = grantAdmin; - if (mGrantAdmin) { - mMetricsFeatureProvider.action(getActivity(), - SettingsEnums.ACTION_GRANT_ADMIN_FROM_SETTINGS_CREATION_DIALOG); - } else { - mMetricsFeatureProvider.action(getActivity(), - SettingsEnums.ACTION_NOT_GRANT_ADMIN_FROM_SETTINGS_CREATION_DIALOG); - } - showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER); - }, - () -> { - mGrantAdmin = false; - } - ); - } - @Override public int getDialogMetricsCategory(int dialogId) { switch (dialogId) { @@ -1065,7 +1028,7 @@ public class UserSettings extends SettingsPreferenceFragment userName, mUserManager.USER_TYPE_FULL_SECONDARY, 0); - if (mGrantAdmin) { + if (mPendingUserIsAdmin) { mUserManager.setUserAdmin(user.id); } } else { @@ -1665,6 +1628,9 @@ public class UserSettings extends SettingsPreferenceFragment synchronized (mUserLock) { mRemovingUserId = -1; updateUserList(); + if (mCreateUserDialogController.isActive()) { + mCreateUserDialogController.clear(); + } } } From 53a8cc0326da00b66a200384542f216cc089c953 Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Thu, 4 May 2023 08:54:35 +0000 Subject: [PATCH 02/20] Disabled state UI of Flash Notifications preview button When both the camera flash and screen flash toggles are turned off, tapping on the preview button will have no effect and may confuse users. To avoid this, the appearance of the preview button should be updated to clearly indicated that its current state is disabled. This wil help users better understand the situation and prevent confusion. Bug: 276494146 Test: checks the UI manually Test: make RunSettingsRoboTests ROBOTEST_FILTER=FlashNotificationsPreviewPreferenceTest Change-Id: I55b11188fde6e551921a9b0f7c89daa20a8b766b --- color-check-baseline.xml | 64 +++++++++++++++++ res/drawable/switch_bar_bg_disabled.xml | 26 +++++++ .../flash_notification_preview_preference.xml | 1 + res/values-night/colors.xml | 3 + res/values/colors.xml | 3 + .../FlashNotificationsPreviewPreference.java | 37 ++++++++++ ...ificationsPreviewPreferenceController.java | 15 ++-- ...ashNotificationsPreviewPreferenceTest.java | 69 +++++++++++-------- 8 files changed, 185 insertions(+), 33 deletions(-) create mode 100644 res/drawable/switch_bar_bg_disabled.xml diff --git a/color-check-baseline.xml b/color-check-baseline.xml index 2fd63fc8307..df1ad610f25 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -1581,6 +1581,22 @@ column="5"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/flash_notification_preview_preference.xml b/res/layout/flash_notification_preview_preference.xml index ecc6f4f01e4..39dcc801eb3 100644 --- a/res/layout/flash_notification_preview_preference.xml +++ b/res/layout/flash_notification_preview_preference.xml @@ -24,6 +24,7 @@ android:background="@android:color/transparent"> #FFFFFF + + + #1FE3E3E3 diff --git a/res/values/colors.xml b/res/values/colors.xml index 06e0734ba28..657ba110545 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -214,4 +214,7 @@ #4DFF017E #4DFF00FE #667F00FF + + + #1F1F1F1F diff --git a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreference.java b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreference.java index 9028bf182be..0141084cb12 100644 --- a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreference.java +++ b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreference.java @@ -17,16 +17,26 @@ package com.android.settings.accessibility; import android.content.Context; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; +import androidx.annotation.ColorInt; import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; import com.android.settings.R; +import com.android.settingslib.Utils; /** * Preference for Flash notifications preview. */ public class FlashNotificationsPreviewPreference extends Preference { + private Drawable mBackgroundEnabled; + private Drawable mBackgroundDisabled; + @ColorInt + private int mTextColorDisabled; public FlashNotificationsPreviewPreference(Context context) { super(context); @@ -52,5 +62,32 @@ public class FlashNotificationsPreviewPreference extends Preference { private void init() { setLayoutResource(R.layout.flash_notification_preview_preference); + mBackgroundEnabled = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); + mBackgroundDisabled = getContext().getDrawable(R.drawable.switch_bar_bg_disabled); + mTextColorDisabled = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.textColorPrimary); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + final boolean enabled = isEnabled(); + final View frame = holder.findViewById(R.id.frame); + if (frame != null) { + frame.setBackground(enabled ? mBackgroundEnabled : mBackgroundDisabled); + } + final TextView title = (TextView) holder.findViewById(android.R.id.title); + if (title != null) { + @ColorInt final int textColorEnabled = title.getCurrentTextColor(); + title.setAlpha(enabled ? 1f : 0.38f); + title.setTextColor(enabled ? textColorEnabled : mTextColorDisabled); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + notifyChanged(); } } diff --git a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java index 5a16a30bc9e..f13758439fd 100644 --- a/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java +++ b/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceController.java @@ -55,7 +55,7 @@ public class FlashNotificationsPreviewPreferenceController extends new Handler(Looper.getMainLooper())) { @Override public void onChange(boolean selfChange, @Nullable Uri uri) { - onSettingChanged(); + updateState(mPreference); } }; @@ -73,7 +73,7 @@ public class FlashNotificationsPreviewPreferenceController extends public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); mPreference = screen.findPreference(getPreferenceKey()); - onSettingChanged(); + updateState(mPreference); } @Override @@ -103,10 +103,13 @@ public class FlashNotificationsPreviewPreferenceController extends } } - private void onSettingChanged() { - if (mPreference == null) return; - - mPreference.setEnabled(FlashNotificationsUtil.getFlashNotificationsState(mContext) + @Override + public void updateState(Preference preference) { + super.updateState(preference); + if (preference == null) { + return; + } + preference.setEnabled(FlashNotificationsUtil.getFlashNotificationsState(mContext) != FlashNotificationsUtil.State.OFF); } } diff --git a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceTest.java index ab38c1a734a..1e7f0898392 100644 --- a/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/FlashNotificationsPreviewPreferenceTest.java @@ -19,20 +19,26 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; +import androidx.annotation.ColorInt; +import androidx.preference.PreferenceViewHolder; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settingslib.Utils; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; @RunWith(RobolectricTestRunner.class) public class FlashNotificationsPreviewPreferenceTest { @@ -41,37 +47,46 @@ public class FlashNotificationsPreviewPreferenceTest { public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); - private final AttributeSet mAttributeSet = Robolectric.buildAttributeSet().build(); + private FlashNotificationsPreviewPreference mFlashNotificationsPreviewPreference; + private PreferenceViewHolder mPreferenceViewHolder; - @Test - public void constructor_assertLayoutResource_P00() { - FlashNotificationsPreviewPreference preference = new FlashNotificationsPreviewPreference( - mContext); - assertThat(preference.getLayoutResource()) - .isEqualTo(R.layout.flash_notification_preview_preference); + @Before + public void setUp() { + mPreferenceViewHolder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate( + R.layout.flash_notification_preview_preference, null)); + mFlashNotificationsPreviewPreference = new FlashNotificationsPreviewPreference(mContext); } @Test - public void constructor_assertLayoutResource_P01() { - FlashNotificationsPreviewPreference preference = new FlashNotificationsPreviewPreference( - mContext, mAttributeSet); - assertThat(preference.getLayoutResource()) - .isEqualTo(R.layout.flash_notification_preview_preference); + public void setEnabled_true_verifyEnabledUi() { + @ColorInt final int textColorEnabled = ((TextView) mPreferenceViewHolder.findViewById( + android.R.id.title)).getCurrentTextColor(); + + mFlashNotificationsPreviewPreference.setEnabled(true); + mFlashNotificationsPreviewPreference.onBindViewHolder(mPreferenceViewHolder); + + final View frame = mPreferenceViewHolder.findViewById(R.id.frame); + final int backgroundResId = Shadows.shadowOf(frame.getBackground()).getCreatedFromResId(); + assertThat(backgroundResId).isEqualTo(R.drawable.settingslib_switch_bar_bg_on); + final TextView title = (TextView) mPreferenceViewHolder.findViewById(android.R.id.title); + assertThat(title.getAlpha()).isEqualTo(1f); + assertThat(title.getCurrentTextColor()).isEqualTo(textColorEnabled); } @Test - public void constructor_assertLayoutResource_P02() { - FlashNotificationsPreviewPreference preference = new FlashNotificationsPreviewPreference( - mContext, mAttributeSet, 0); - assertThat(preference.getLayoutResource()) - .isEqualTo(R.layout.flash_notification_preview_preference); - } + public void setEnabled_false_verifyDisabledUi() { + @ColorInt final int textColorDisabled = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); - @Test - public void constructor_assertLayoutResource_P03() { - FlashNotificationsPreviewPreference preference = new FlashNotificationsPreviewPreference( - mContext, mAttributeSet, 0, 0); - assertThat(preference.getLayoutResource()) - .isEqualTo(R.layout.flash_notification_preview_preference); + mFlashNotificationsPreviewPreference.setEnabled(false); + mFlashNotificationsPreviewPreference.onBindViewHolder(mPreferenceViewHolder); + + final View frame = mPreferenceViewHolder.findViewById(R.id.frame); + final int backgroundResId = Shadows.shadowOf(frame.getBackground()).getCreatedFromResId(); + assertThat(backgroundResId).isEqualTo(R.drawable.switch_bar_bg_disabled); + final TextView title = (TextView) mPreferenceViewHolder.findViewById(android.R.id.title); + assertThat(title.getAlpha()).isEqualTo(0.38f); + assertThat(title.getCurrentTextColor()).isEqualTo(textColorDisabled); } -} +} \ No newline at end of file From eac02feb2d3868c3db2e37e39db8edaac410c0b2 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 19:02:21 -0700 Subject: [PATCH 03/20] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I752b8c12cf13fcb989dbb335b3230198d3be3201 --- res/values-pl/arrays.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml index 68f5d71dac8..55408c4302a 100644 --- a/res/values-pl/arrays.xml +++ b/res/values-pl/arrays.xml @@ -30,13 +30,13 @@ "Wszystkie" - "15 sekund" - "30 sekund" - "1 minuta" - "2 minuty" - "5 minut" - "10 minut" - "30 minut" + "15 sek." + "30 sek." + "1 min" + "2 min" + "5 min" + "10 min" + "30 min" "Nigdy" From 7a494fc7c56b0f800bb936558cb4c87728031512 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 9 May 2023 19:08:51 -0700 Subject: [PATCH 04/20] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I041ed592d479aeb038cb23092fa85b14d71df157 --- res/values-pl/arrays.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml index 76c5c9c033d..b027bbb4e1c 100644 --- a/res/values-pl/arrays.xml +++ b/res/values-pl/arrays.xml @@ -30,13 +30,13 @@ "Wszystkie" - "15 sekund" - "30 sekund" - "1 minuta" - "2 minuty" - "5 minut" - "10 minut" - "30 minut" + "15 sek." + "30 sek." + "1 min" + "2 min" + "5 min" + "10 min" + "30 min" "Nigdy" From d86c5904401ae13b034f45a9d0ed7aa55d381018 Mon Sep 17 00:00:00 2001 From: Elliot Sisteron Date: Wed, 10 May 2023 13:41:51 +0000 Subject: [PATCH 05/20] Remove search entries from old pages when SC is on. There was already some code to guard this, but this wasn't overriding the `isPageSearchEnabled` as well to support dynamic indexing. Bug: 280596858 Test: manual Change-Id: If4793d9fa2a7d68eac78258c18d04e9fb7d8fcea --- .../privacy/PrivacyDashboardFragment.java | 15 +++++-- .../MoreSecurityPrivacyFragment.java | 27 +++++++----- .../security/SecurityAdvancedSettings.java | 42 ++++++++++--------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java index 19683b85b6d..4d762776a6a 100644 --- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java +++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java @@ -59,8 +59,10 @@ public class PrivacyDashboardFragment extends DashboardFragment { SafetyCenterUtils.getEnterpriseOverrideStringForPrivacyEntries(); for (int i = 0; i < privacyOverrideStrings.size(); i++) { EnterpriseOverrideString overrideString = privacyOverrideStrings.get(i); - replaceEnterpriseStringTitle(overrideString.getPreferenceKey(), - overrideString.getOverrideKey(), overrideString.getResource()); + replaceEnterpriseStringTitle( + overrideString.getPreferenceKey(), + overrideString.getOverrideKey(), + overrideString.getResource()); } } @@ -93,7 +95,9 @@ public class PrivacyDashboardFragment extends DashboardFragment { @Override public List getXmlResourcesToIndex( Context context, boolean enabled) { - if (SafetyCenterManagerWrapper.get().isEnabled(context)) { + // NOTE: This check likely should be moved to the super method. This is done + // here to avoid potentially undesired side effects for existing implementors. + if (!isPageSearchEnabled(context)) { return null; } return super.getXmlResourcesToIndex(context, enabled); @@ -120,5 +124,10 @@ public class PrivacyDashboardFragment extends DashboardFragment { keys.add(KEY_NOTIFICATION_WORK_PROFILE_NOTIFICATIONS); return keys; } + + @Override + protected boolean isPageSearchEnabled(Context context) { + return !SafetyCenterManagerWrapper.get().isEnabled(context); + } }; } diff --git a/src/com/android/settings/safetycenter/MoreSecurityPrivacyFragment.java b/src/com/android/settings/safetycenter/MoreSecurityPrivacyFragment.java index 3eb61024239..69ec385f79c 100644 --- a/src/com/android/settings/safetycenter/MoreSecurityPrivacyFragment.java +++ b/src/com/android/settings/safetycenter/MoreSecurityPrivacyFragment.java @@ -77,21 +77,23 @@ public class MoreSecurityPrivacyFragment extends DashboardFragment { SafetyCenterUtils.getEnterpriseOverrideStringForPrivacyEntries(); for (int i = 0; i < privacyOverrideStrings.size(); i++) { EnterpriseOverrideString overrideString = privacyOverrideStrings.get(i); - replaceEnterpriseStringTitle(overrideString.getPreferenceKey(), - overrideString.getOverrideKey(), overrideString.getResource()); + replaceEnterpriseStringTitle( + overrideString.getPreferenceKey(), + overrideString.getOverrideKey(), + overrideString.getResource()); } List securityOverrideStrings = SafetyCenterUtils.getEnterpriseOverrideStringForSecurityEntries(); for (int i = 0; i < securityOverrideStrings.size(); i++) { EnterpriseOverrideString overrideString = securityOverrideStrings.get(i); - replaceEnterpriseStringTitle(overrideString.getPreferenceKey(), - overrideString.getOverrideKey(), overrideString.getResource()); + replaceEnterpriseStringTitle( + overrideString.getPreferenceKey(), + overrideString.getOverrideKey(), + overrideString.getResource()); } } - /** - * see confirmPatternThenDisableAndClear - */ + /** see confirmPatternThenDisableAndClear */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (use(TrustAgentListPreferenceController.class) @@ -117,10 +119,8 @@ public class MoreSecurityPrivacyFragment extends DashboardFragment { controllers.addAll( SafetyCenterUtils.getControllersForAdvancedSecurity(context, lifecycle, host)); return controllers; - } - public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.more_security_privacy_settings) { /** @@ -130,7 +130,9 @@ public class MoreSecurityPrivacyFragment extends DashboardFragment { @Override public List getXmlResourcesToIndex( Context context, boolean enabled) { - if (!SafetyCenterManagerWrapper.get().isEnabled(context)) { + // NOTE: This check likely should be moved to the super method. This is done + // here to avoid potentially undesired side effects for existing implementors. + if (!isPageSearchEnabled(context)) { return null; } return super.getXmlResourcesToIndex(context, enabled); @@ -157,5 +159,10 @@ public class MoreSecurityPrivacyFragment extends DashboardFragment { keys.add(KEY_NOTIFICATION_WORK_PROFILE_NOTIFICATIONS); return keys; } + + @Override + protected boolean isPageSearchEnabled(Context context) { + return SafetyCenterManagerWrapper.get().isEnabled(context); + } }; } diff --git a/src/com/android/settings/security/SecurityAdvancedSettings.java b/src/com/android/settings/security/SecurityAdvancedSettings.java index b2b2782f3d0..61f0975e306 100644 --- a/src/com/android/settings/security/SecurityAdvancedSettings.java +++ b/src/com/android/settings/security/SecurityAdvancedSettings.java @@ -58,8 +58,10 @@ public class SecurityAdvancedSettings extends DashboardFragment { SafetyCenterUtils.getEnterpriseOverrideStringForSecurityEntries(); for (int i = 0; i < securityOverrideStrings.size(); i++) { EnterpriseOverrideString overrideString = securityOverrideStrings.get(i); - replaceEnterpriseStringTitle(overrideString.getPreferenceKey(), - overrideString.getOverrideKey(), overrideString.getResource()); + replaceEnterpriseStringTitle( + overrideString.getPreferenceKey(), + overrideString.getOverrideKey(), + overrideString.getResource()); } } @@ -77,8 +79,7 @@ public class SecurityAdvancedSettings extends DashboardFragment { return CategoryKey.CATEGORY_SECURITY_ADVANCED_SETTINGS; } else { final SecuritySettingsFeatureProvider securitySettingsFeatureProvider = - FeatureFactory.getFactory(context) - .getSecuritySettingsFeatureProvider(); + FeatureFactory.getFactory(context).getSecuritySettingsFeatureProvider(); if (securitySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) { return securitySettingsFeatureProvider.getAlternativeAdvancedSettingsCategoryKey(); @@ -103,9 +104,7 @@ public class SecurityAdvancedSettings extends DashboardFragment { return buildPreferenceControllers(context, getSettingsLifecycle(), this /* host*/); } - /** - * see confirmPatternThenDisableAndClear - */ + /** see confirmPatternThenDisableAndClear */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (use(TrustAgentListPreferenceController.class) @@ -119,14 +118,12 @@ public class SecurityAdvancedSettings extends DashboardFragment { super.onActivityResult(requestCode, resultCode, data); } - private static List buildPreferenceControllers(Context context, - Lifecycle lifecycle, DashboardFragment host) { + private static List buildPreferenceControllers( + Context context, Lifecycle lifecycle, DashboardFragment host) { return SafetyCenterUtils.getControllersForAdvancedSecurity(context, lifecycle, host); } - /** - * For Search. Please keep it in sync when updating "createPreferenceHierarchy()" - */ + /** For Search. Please keep it in sync when updating "createPreferenceHierarchy()" */ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.security_advanced_settings) { /** @@ -134,19 +131,26 @@ public class SecurityAdvancedSettings extends DashboardFragment { * page, and we don't want to index these entries. */ @Override - public List getXmlResourcesToIndex(Context context, - boolean enabled) { - if (SafetyCenterManagerWrapper.get().isEnabled(context)) { + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + // NOTE: This check likely should be moved to the super method. This is done + // here to avoid potentially undesired side effects for existing implementors. + if (!isPageSearchEnabled(context)) { return null; } return super.getXmlResourcesToIndex(context, enabled); } @Override - public List createPreferenceControllers(Context - context) { - return buildPreferenceControllers(context, null /* lifecycle */, - null /* host*/); + public List createPreferenceControllers( + Context context) { + return buildPreferenceControllers( + context, null /* lifecycle */, null /* host*/); + } + + @Override + protected boolean isPageSearchEnabled(Context context) { + return !SafetyCenterManagerWrapper.get().isEnabled(context); } }; } From 061763140833d578d9f1e62d03b9d161ae6cd319 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 11 May 2023 02:23:06 +0800 Subject: [PATCH 06/20] [Settings] Avoid NPE if BT device is changed by framework. - Do not register only one BT device for primary to avoid primary BT devcie change to another. - Register and unregister all BT devices Bug: 280236099 Test: atest passed Change-Id: I610144c7f8f649e40d99cf1dc7f50d1f3b80f109 --- ...ancedBluetoothDetailsHeaderController.java | 51 +++++++++++++---- ...dBluetoothDetailsHeaderControllerTest.java | 57 ++++++++++++++----- 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 07e9633652f..5f1c42259bc 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -57,7 +57,9 @@ import com.android.settingslib.widget.LayoutPreference; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -92,6 +94,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @VisibleForTesting final Map mIconCache; private CachedBluetoothDevice mCachedDevice; + private Set mBluetoothDevices; @VisibleForTesting BluetoothAdapter mBluetoothAdapter; @VisibleForTesting @@ -142,23 +145,13 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont if (!isAvailable()) { return; } - mIsRegisterCallback = true; - mCachedDevice.registerCallback(this); - mBluetoothAdapter.addOnMetadataChangedListener(mCachedDevice.getDevice(), - mContext.getMainExecutor(), mMetadataListener); - + registerBluetoothDevice(); refresh(); } @Override public void onStop() { - if (!mIsRegisterCallback) { - return; - } - mCachedDevice.unregisterCallback(this); - mBluetoothAdapter.removeOnMetadataChangedListener(mCachedDevice.getDevice(), - mMetadataListener); - mIsRegisterCallback = false; + unRegisterBluetoothDevice(); } @Override @@ -176,6 +169,40 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont mCachedDevice = cachedBluetoothDevice; } + private void registerBluetoothDevice() { + if (mBluetoothDevices == null) { + mBluetoothDevices = new HashSet<>(); + } + mBluetoothDevices.clear(); + if (mCachedDevice.getDevice() != null) { + mBluetoothDevices.add(mCachedDevice.getDevice()); + } + mCachedDevice.getMemberDevice().forEach(cbd -> { + if (cbd != null) { + mBluetoothDevices.add(cbd.getDevice()); + } + }); + if (mBluetoothDevices.isEmpty()) { + Log.d(TAG, "No BT devcie to register."); + return; + } + mCachedDevice.registerCallback(this); + mBluetoothDevices.forEach(bd -> + mBluetoothAdapter.addOnMetadataChangedListener(bd, + mContext.getMainExecutor(), mMetadataListener)); + } + + private void unRegisterBluetoothDevice() { + if (mBluetoothDevices == null || mBluetoothDevices.isEmpty()) { + Log.d(TAG, "No BT devcie to unregister."); + return; + } + mCachedDevice.unregisterCallback(this); + mBluetoothDevices.forEach(bd -> mBluetoothAdapter.removeOnMetadataChangedListener(bd, + mMetadataListener)); + mBluetoothDevices.clear(); + } + @VisibleForTesting void refresh() { if (mLayoutPreference != null && mCachedDevice != null) { diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index e334af545cf..2c9fb9975e4 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -56,6 +56,9 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import java.util.HashSet; +import java.util.Set; + @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class}) public class AdvancedBluetoothDetailsHeaderControllerTest { @@ -380,40 +383,68 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("true".getBytes()); + Set cacheBluetoothDevices = new HashSet<>(); + when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); mController.onStart(); + verify(mCachedDevice).registerCallback(mController); verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener); } @Test - public void onStop_isRegisterCallback_unregisterCallback() { - mController.mIsRegisterCallback = true; - - mController.onStop(); - - verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice, - mController.mMetadataListener); - } - - @Test - public void onStart_notAvailable_registerCallback() { + public void onStart_notAvailable_notNeedToRegisterCallback() { when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) .thenReturn("false".getBytes()); mController.onStart(); + verify(mCachedDevice, never()).registerCallback(mController); verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener); } @Test - public void onStop_notRegisterCallback_unregisterCallback() { - mController.mIsRegisterCallback = false; + public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + when(mCachedDevice.getDevice()).thenReturn(null); + Set cacheBluetoothDevices = new HashSet<>(); + when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); + + mController.onStart(); + + verify(mCachedDevice, never()).registerCallback(mController); + verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice, + mContext.getMainExecutor(), mController.mMetadataListener); + } + + @Test + public void onStop_availableAndHasBluetoothDevice_unregisterCallback() { + onStart_isAvailable_registerCallback(); mController.onStop(); + verify(mCachedDevice).unregisterCallback(mController); + verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice, + mController.mMetadataListener); + } + + @Test + public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI, + SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true); + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn("true".getBytes()); + when(mCachedDevice.getDevice()).thenReturn(null); + + mController.onStart(); + mController.onStop(); + + verify(mCachedDevice, never()).unregisterCallback(mController); verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice, mController.mMetadataListener); } From 755a67a9313b01a9c25c0aed6a14a195a0bde980 Mon Sep 17 00:00:00 2001 From: Hao Dong Date: Fri, 5 May 2023 23:15:11 +0000 Subject: [PATCH 07/20] Set EXTRA_KEY_FOR_FINGERPRINT for choose lock screen. Bug: 279823572 Test: atest FingerprintSettingsFragmentTest Test: atest ChooseLockGenericTest Change-Id: Ie4bc18bf245eb7a755862401c710d85381063ff9 Merged-In: Ie4bc18bf245eb7a755862401c710d85381063ff9 --- res/values/strings.xml | 12 +-- .../fingerprint/FingerprintSettings.java | 5 +- .../FingerprintSettingsFragmentTest.java | 100 +++++++++++------- .../password/ChooseLockGenericTest.java | 74 +++++++++---- 4 files changed, 122 insertions(+), 69 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 8fc5ed5d651..aa0671eb438 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1182,20 +1182,20 @@ Current screen lock - Fingerprint + Pattern + Pattern \u2022 Fingerprint - Fingerprint + PIN + PIN \u2022 Fingerprint - Fingerprint + Password + Password \u2022 Fingerprint Continue without fingerprint - Face Unlock + Pattern + Pattern \u2022 Face - Face Unlock + PIN + PIN \u2022 Face - Face Unlock + Password + Password \u2022 Face Continue without Face Unlock diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java index e0f402bdc02..be090e33366 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java @@ -185,7 +185,8 @@ public class FingerprintSettings extends SubSettings { private static final int MSG_FINGER_AUTH_HELP = 1004; private static final int CONFIRM_REQUEST = 101; - private static final int CHOOSE_LOCK_GENERIC_REQUEST = 102; + @VisibleForTesting + static final int CHOOSE_LOCK_GENERIC_REQUEST = 102; @VisibleForTesting static final int ADD_FINGERPRINT_REQUEST = 10; private static final int AUTO_ADD_FIRST_FINGERPRINT_REQUEST = 11; @@ -1014,7 +1015,7 @@ public class FingerprintSettings extends SubSettings { true); intent.putExtra(Intent.EXTRA_USER_ID, mUserId); intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_REQUEST_GK_PW_HANDLE, true); - intent.putExtra(Intent.EXTRA_USER_ID, mUserId); + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, true); startActivityForResult(intent, CHOOSE_LOCK_GENERIC_REQUEST); } } diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java index 2bc81e60a06..18b05add7ad 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -20,8 +20,11 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment; import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.ADD_FINGERPRINT_REQUEST; +import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.CHOOSE_LOCK_GENERIC_REQUEST; import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_FINGERPRINT_ADD; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -54,6 +57,7 @@ import com.android.settings.biometrics.BiometricsSplitScreenDialog; import com.android.settings.password.ChooseLockSettingsHelper; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowFragment; +import com.android.settings.testutils.shadow.ShadowLockPatternUtils; import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.testutils.shadow.ShadowUtils; @@ -63,6 +67,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; @@ -74,7 +79,7 @@ import java.util.ArrayList; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class, - ShadowUserManager.class}) + ShadowUserManager.class, ShadowLockPatternUtils.class}) public class FingerprintSettingsFragmentTest { private FingerprintSettingsFragment mFragment; private Context mContext; @@ -92,10 +97,62 @@ public class FingerprintSettingsFragmentTest { doReturn(true).when(mFingerprintManager).isHardwareDetected(); ShadowUtils.setFingerprintManager(mFingerprintManager); FakeFeatureFactory.setupForTest(); + } + @After + public void tearDown() { + ShadowUtils.reset(); + } + + @Test + public void testAddFingerprint_inFullScreen_noDialog() { + setUpFragment(false); + // Click "Add Fingerprint" + final Preference preference = new Preference(mContext); + preference.setKey(KEY_FINGERPRINT_ADD); + mFragment.onPreferenceTreeClick(preference); + + verify(mFragment).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST)); + verify(mFragmentTransaction, never()).add(any(), + eq(BiometricsSplitScreenDialog.class.getName())); + + } + + @Test + public void testAddFingerprint_inMultiWindow_showsDialog() { + setUpFragment(false); + + doReturn(true).when(mActivity).isInMultiWindowMode(); + + // Click "Add Fingerprint" + final Preference preference = new Preference(mContext); + preference.setKey(KEY_FINGERPRINT_ADD); + mFragment.onPreferenceTreeClick(preference); + + verify(mFragment, times(0)).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST)); + verify(mFragmentTransaction).add(any(), eq(BiometricsSplitScreenDialog.class.getName())); + } + + @Test + public void testChooseLockKeyForFingerprint() { + setUpFragment(true); + ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass( + Intent.class); + verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(), + eq(CHOOSE_LOCK_GENERIC_REQUEST)); + + Intent intent = intentArgumentCaptor.getValue(); + assertThat(intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT, + false)).isTrue(); + } + + private void setUpFragment(boolean showChooseLock) { Intent intent = new Intent(); - intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); - intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L); + if (!showChooseLock) { + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, new byte[0]); + intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L); + } + mActivity = spy(Robolectric.buildActivity(FragmentActivity.class, intent).get()); mContext = spy(ApplicationProvider.getApplicationContext()); @@ -112,49 +169,12 @@ public class FingerprintSettingsFragmentTest { doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt()); setSensor(); - } - @After - public void tearDown() { - ShadowUtils.reset(); - } - - @Test - public void testAddFingerprint_inFullScreen_noDialog() { // Start fragment mFragment.onAttach(mContext); mFragment.onCreate(null); mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY); mFragment.onResume(); - - // Click "Add Fingerprint" - final Preference preference = new Preference(mContext); - preference.setKey(KEY_FINGERPRINT_ADD); - mFragment.onPreferenceTreeClick(preference); - - verify(mFragment).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST)); - verify(mFragmentTransaction, never()).add(any(), - eq(BiometricsSplitScreenDialog.class.getName())); - - } - - @Test - public void testAddFingerprint_inMultiWindow_showsDialog() { - // Start fragment - mFragment.onAttach(mContext); - mFragment.onCreate(null); - mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY); - mFragment.onResume(); - - doReturn(true).when(mActivity).isInMultiWindowMode(); - - // Click "Add Fingerprint" - final Preference preference = new Preference(mContext); - preference.setKey(KEY_FINGERPRINT_ADD); - mFragment.onPreferenceTreeClick(preference); - - verify(mFragment, times(0)).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST)); - verify(mFragmentTransaction).add(any(), eq(BiometricsSplitScreenDialog.class.getName())); } private void setSensor() { diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java index dbc60dce731..12a540d7c33 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java @@ -30,6 +30,8 @@ import static com.android.settings.password.ChooseLockGeneric.ChooseLockGenericF import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_CALLER_APP_NAME; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_DEVICE_PASSWORD_REQUIREMENT_ONLY; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_BIOMETRICS; +import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FACE; +import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_FOR_FINGERPRINT; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_IS_CALLING_APP_ADMIN; import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_REQUESTED_MIN_COMPLEXITY; @@ -126,7 +128,9 @@ public class ChooseLockGenericTest { when(mFaceManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFakeFeatureFactory.mFaceFeatureProvider.isSetupWizardSupported(any())).thenReturn( - false); + true); + ShadowUtils.setFingerprintManager(mFingerprintManager); + ShadowUtils.setFaceManager(mFaceManager); } @After @@ -540,35 +544,63 @@ public class ChooseLockGenericTest { @Test public void updatePreferenceText_supportBiometrics_showFaceAndFingerprint() { - ShadowLockPatternUtils.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_LOW); - final PasswordPolicy policy = new PasswordPolicy(); - policy.quality = PASSWORD_QUALITY_ALPHABETIC; - ShadowLockPatternUtils.setRequestedProfilePasswordMetrics(policy.getMinMetrics()); - + ShadowStorageManager.setIsFileEncrypted(false); final Intent intent = new Intent().putExtra(EXTRA_KEY_FOR_BIOMETRICS, true); initActivity(intent); - final Intent passwordIntent = mFragment.getLockPatternIntent(); - assertThat(passwordIntent.getIntExtra(ChooseLockPassword.EXTRA_KEY_MIN_COMPLEXITY, - PASSWORD_COMPLEXITY_NONE)).isEqualTo(PASSWORD_COMPLEXITY_LOW); final String supportFingerprint = capitalize(mActivity.getResources().getString( R.string.security_settings_fingerprint)); final String supportFace = capitalize(mActivity.getResources().getString( R.string.keywords_face_settings)); + String pinTitle = + (String) mFragment.findPreference(ScreenLockType.PIN.preferenceKey).getTitle(); + String patternTitle = + (String) mFragment.findPreference(ScreenLockType.PATTERN.preferenceKey).getTitle(); + String passwordTitle = + (String) mFragment.findPreference(ScreenLockType.PASSWORD.preferenceKey).getTitle(); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains( - supportFingerprint); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PIN)).contains( - supportFace); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains( - supportFingerprint); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PATTERN)).contains( - supportFace); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains( - supportFingerprint); - assertThat(mFragment.getBiometricsPreferenceTitle(ScreenLockType.PASSWORD)).contains( - supportFace); + assertThat(pinTitle).contains(supportFingerprint); + assertThat(pinTitle).contains(supportFace); + assertThat(patternTitle).contains(supportFingerprint); + assertThat(patternTitle).contains(supportFace); + assertThat(passwordTitle).contains(supportFingerprint); + assertThat(passwordTitle).contains(supportFace); + } + + @Test + public void updatePreferenceText_supportFingerprint_showFingerprint() { + ShadowStorageManager.setIsFileEncrypted(false); + final Intent intent = new Intent().putExtra(EXTRA_KEY_FOR_FINGERPRINT, true); + initActivity(intent); + mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */); + + assertThat(mFragment.findPreference(ScreenLockType.PIN.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.fingerprint_unlock_set_unlock_pin)); + assertThat(mFragment.findPreference( + ScreenLockType.PATTERN.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.fingerprint_unlock_set_unlock_pattern)); + assertThat(mFragment.findPreference( + ScreenLockType.PASSWORD.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.fingerprint_unlock_set_unlock_password)); + } + + @Test + public void updatePreferenceText_supportFace_showFace() { + + ShadowStorageManager.setIsFileEncrypted(false); + final Intent intent = new Intent().putExtra(EXTRA_KEY_FOR_FACE, true); + initActivity(intent); + mFragment.updatePreferencesOrFinish(false /* isRecreatingActivity */); + + assertThat(mFragment.findPreference(ScreenLockType.PIN.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.face_unlock_set_unlock_pin)); + assertThat(mFragment.findPreference( + ScreenLockType.PATTERN.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.face_unlock_set_unlock_pattern)); + assertThat(mFragment.findPreference( + ScreenLockType.PASSWORD.preferenceKey).getTitle()).isEqualTo( + mFragment.getString(R.string.face_unlock_set_unlock_password)); } private void initActivity(@Nullable Intent intent) { From 8e9119f5abd8c2d685215377ae7ae3653196578c Mon Sep 17 00:00:00 2001 From: Becca Hughes Date: Wed, 10 May 2023 18:25:59 +0000 Subject: [PATCH 08/20] Show disable dialog box when none is selected Also remove from CMPP since that will be hidden. Test: ondevice Bug: 281147573 Merged-In: Ic2b59c42ed4ddbc83a770b8bb99c641d2f1a383d Change-Id: Ic2b59c42ed4ddbc83a770b8bb99c641d2f1a383d --- res/values/strings.xml | 11 ++- ...CredentialManagerPreferenceController.java | 83 +------------------ .../credentials/DefaultCombinedPicker.java | 7 +- 3 files changed, 17 insertions(+), 84 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 8fc5ed5d651..877603c9f0f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10306,7 +10306,16 @@ Turn off %1$s\? - Saved info like addresses or payment methods won\'t be filled in when you sign in. To keep your saved info filled in, set enable a password, passkey and data/or service. + + Turn off this service? +
+
+ Save info like passwords, passkeys, payment methods, and other info won\'t be filled + in when you sign in. To use your saved info, choose a password, passkey, or data + service. + ]]> +
Use %1$s\? diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java index 9b959c5f622..6747b36936d 100644 --- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java +++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java @@ -554,19 +554,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl } return true; } else { - // If we are disabling the last enabled provider then show a warning. - if (mEnabledPackageNames.size() <= 1) { - final DialogFragment fragment = - newConfirmationDialogFragment(packageName, title, pref); - - if (fragment == null || mFragmentManager == null) { - return true; - } - - fragment.show(mFragmentManager, ConfirmationDialogFragment.TAG); - } else { - togglePackageNameDisabled(packageName); - } + togglePackageNameDisabled(packageName); } return true; @@ -682,35 +670,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl return new ErrorDialogFragment(host); } - private @Nullable ConfirmationDialogFragment newConfirmationDialogFragment( - @NonNull String packageName, - @NonNull CharSequence appName, - @NonNull SwitchPreference pref) { - DialogHost host = - new DialogHost() { - @Override - public void onDialogClick(int whichButton) { - if (whichButton == DialogInterface.BUTTON_POSITIVE) { - // Since the package is now enabled then we - // should remove it from the enabled list. - togglePackageNameDisabled(packageName); - } else if (whichButton == DialogInterface.BUTTON_NEGATIVE) { - // Set the checked back to true because we - // backed out of turning this off. - pref.setChecked(true); - } - } - - @Override - public void onCancel() { - // If we dismiss the dialog then re-enable. - pref.setChecked(true); - } - }; - - return new ConfirmationDialogFragment(host, packageName, appName); - } - protected int getUser() { if (mIsWorkProfile) { UserHandle workProfile = Utils.getManagedProfile(UserManager.get(mContext)); @@ -800,46 +759,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl public void onClick(DialogInterface dialog, int which) {} } - /** - * Confirmation dialog fragment shows a dialog to the user to confirm that they are disabling a - * provider. - */ - public static class ConfirmationDialogFragment extends CredentialManagerDialogFragment { - - ConfirmationDialogFragment( - DialogHost dialogHost, @NonNull String packageName, @NonNull CharSequence appName) { - super(dialogHost); - - final Bundle argument = new Bundle(); - argument.putString(PACKAGE_NAME_KEY, packageName); - argument.putCharSequence(APP_NAME_KEY, appName); - setArguments(argument); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Bundle bundle = getArguments(); - final String title = - getContext() - .getString( - R.string.credman_confirmation_message_title, - bundle.getCharSequence( - CredentialManagerDialogFragment.APP_NAME_KEY)); - - return new AlertDialog.Builder(getActivity()) - .setTitle(title) - .setMessage(getContext().getString(R.string.credman_confirmation_message)) - .setPositiveButton(R.string.credman_confirmation_message_positive_button, this) - .setNegativeButton(android.R.string.cancel, this) - .create(); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - getDialogHost().onDialogClick(which); - } - } - /** * Confirmation dialog fragment shows a dialog to the user to confirm that they would like to * enable the new provider. diff --git a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java index b8d104c904f..dcf8fa8244e 100644 --- a/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java +++ b/src/com/android/settings/applications/credentials/DefaultCombinedPicker.java @@ -273,8 +273,13 @@ public class DefaultCombinedPicker extends DefaultAppPickerFragment { @Override protected CharSequence getConfirmationMessage(CandidateInfo appInfo) { + // If we are selecting none then show a warning label. if (appInfo == null) { - return null; + final String message = + getContext() + .getString( + R.string.credman_confirmation_message); + return Html.fromHtml(message); } final CharSequence appName = appInfo.loadLabel(); final String message = From 8f561144894c33e1f0fcab8ae447ed7a491977db Mon Sep 17 00:00:00 2001 From: ykhung Date: Thu, 11 May 2023 00:02:10 +0800 Subject: [PATCH 09/20] Record app optimization mode backup into BatteryHistoricalLog App optimization mode format: https://screenshot.googleplex.com/di9DDzBfYf7ihfV App optimization mode backup format: https://screenshot.googleplex.com/GkVW5HrgGvmv5yh Bug: 192523697 Test: make SettingsRoboTests Change-Id: I60a9a76a8ffc89d625ee3f77c138a19181c81c38 --- protos/fuelgauge_log.proto | 3 +- .../fuelgauge/BatteryBackupHelper.java | 21 +++++++ .../fuelgauge/BatteryHistoricalLogUtil.java | 62 ++++++++++--------- .../fuelgauge/BatteryBackupHelperTest.java | 16 +++++ .../BatteryHistoricalLogUtilTest.java | 4 +- 5 files changed, 73 insertions(+), 33 deletions(-) diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto index cf87dc7b7b0..8512cb82506 100644 --- a/protos/fuelgauge_log.proto +++ b/protos/fuelgauge_log.proto @@ -19,10 +19,11 @@ message BatteryOptimizeHistoricalLogEntry { APPLY = 2; RESET = 3; RESTORE = 4; + BACKUP = 5; } optional string package_name = 1; optional Action action = 2; optional string action_description = 3; optional int64 timestamp = 4; -} \ No newline at end of file +} diff --git a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java index 0558d46c3b8..79df57ab08d 100644 --- a/src/com/android/settings/fuelgauge/BatteryBackupHelper.java +++ b/src/com/android/settings/fuelgauge/BatteryBackupHelper.java @@ -22,6 +22,7 @@ import android.app.backup.BackupDataInputStream; import android.app.backup.BackupDataOutput; import android.app.backup.BackupHelper; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.os.IDeviceIdleController; @@ -34,9 +35,11 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settingslib.fuelgauge.PowerAllowlistBackend; import java.io.IOException; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -47,6 +50,8 @@ public final class BatteryBackupHelper implements BackupHelper { /** An inditifier for {@link BackupHelper}. */ public static final String TAG = "BatteryBackupHelper"; private static final String DEVICE_IDLE_SERVICE = "deviceidle"; + private static final String BATTERY_OPTIMIZE_BACKUP_FILE_NAME = + "battery_optimize_backup_historical_logs"; static final String DELIMITER = ","; static final String DELIMITER_MODE = ":"; @@ -141,6 +146,7 @@ public final class BatteryBackupHelper implements BackupHelper { int backupCount = 0; final StringBuilder builder = new StringBuilder(); final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); + final SharedPreferences sharedPreferences = getSharedPreferences(mContext); // Converts application into the AppUsageState. for (ApplicationInfo info : applications) { final int mode = BatteryOptimizeUtils.getMode(appOps, info.uid, info.packageName); @@ -157,6 +163,9 @@ public final class BatteryBackupHelper implements BackupHelper { info.packageName + DELIMITER_MODE + optimizationMode; builder.append(packageOptimizeMode + DELIMITER); Log.d(TAG, "backupOptimizationMode: " + packageOptimizeMode); + BatteryHistoricalLogUtil.writeLog( + sharedPreferences, Action.BACKUP, info.packageName, + /* actionDescription */ "mode: " + optimizationMode); backupCount++; } @@ -210,6 +219,18 @@ public final class BatteryBackupHelper implements BackupHelper { restoreCount, (System.currentTimeMillis() - timestamp))); } + /** Dump the app optimization mode backup history data. */ + public static void dumpHistoricalData(Context context, PrintWriter writer) { + BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog( + getSharedPreferences(context), writer); + } + + @VisibleForTesting + static SharedPreferences getSharedPreferences(Context context) { + return context.getSharedPreferences( + BATTERY_OPTIMIZE_BACKUP_FILE_NAME, Context.MODE_PRIVATE); + } + private void restoreOptimizationMode( String packageName, @BatteryOptimizeUtils.OptimizationMode int mode) { final int uid = BatteryUtils.getInstance(mContext).getPackageUid(packageName); diff --git a/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java b/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java index a827e6df74a..f82b7031749 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtil.java @@ -37,40 +37,40 @@ public final class BatteryHistoricalLogUtil { @VisibleForTesting static final int MAX_ENTRIES = 40; - /** - * Writes a log entry. - * - *

Keeps up to {@link #MAX_ENTRIES} in the log, once that number is exceeded, it prunes the - * oldest one. - */ - static void writeLog(Context context, Action action, String pkg, String actionDescription) { + /** Writes a log entry for battery optimization mode. */ + static void writeLog( + Context context, Action action, String packageName, String actionDescription) { + writeLog(getSharedPreferences(context), action, packageName, actionDescription); + } + + static void writeLog(SharedPreferences sharedPreferences, Action action, + String packageName, String actionDescription) { writeLog( - context, + sharedPreferences, BatteryOptimizeHistoricalLogEntry.newBuilder() - .setPackageName(pkg) + .setPackageName(packageName) .setAction(action) .setActionDescription(actionDescription) .setTimestamp(System.currentTimeMillis()) .build()); } - private static void writeLog(Context context, BatteryOptimizeHistoricalLogEntry logEntry) { - SharedPreferences sharedPreferences = getSharedPreferences(context); - + private static void writeLog( + SharedPreferences sharedPreferences, BatteryOptimizeHistoricalLogEntry logEntry) { BatteryOptimizeHistoricalLog existingLog = parseLogFromString(sharedPreferences.getString(LOGS_KEY, "")); BatteryOptimizeHistoricalLog.Builder newLogBuilder = existingLog.toBuilder(); - // Prune old entries + // Prune old entries to limit the max logging data count. if (existingLog.getLogEntryCount() >= MAX_ENTRIES) { newLogBuilder.removeLogEntry(0); } newLogBuilder.addLogEntry(logEntry); + String loggingContent = + Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT); sharedPreferences .edit() - .putString( - LOGS_KEY, - Base64.encodeToString(newLogBuilder.build().toByteArray(), Base64.DEFAULT)) + .putString(LOGS_KEY, loggingContent) .apply(); } @@ -79,34 +79,36 @@ public final class BatteryHistoricalLogUtil { storedLogs, BatteryOptimizeHistoricalLog.getDefaultInstance()); } - /** - * Prints the historical log that has previously been stored by this utility. - */ + /** Prints the historical log that has previously been stored by this utility. */ public static void printBatteryOptimizeHistoricalLog(Context context, PrintWriter writer) { + printBatteryOptimizeHistoricalLog(getSharedPreferences(context), writer); + } + + /** Prints the historical log that has previously been stored by this utility. */ + public static void printBatteryOptimizeHistoricalLog( + SharedPreferences sharedPreferences, PrintWriter writer) { writer.println("Battery optimize state history:"); - SharedPreferences sharedPreferences = getSharedPreferences(context); BatteryOptimizeHistoricalLog existingLog = parseLogFromString(sharedPreferences.getString(LOGS_KEY, "")); List logEntryList = existingLog.getLogEntryList(); if (logEntryList.isEmpty()) { - writer.println("\tNo past logs."); + writer.println("\tnothing to dump"); } else { - writer.println("0:RESTRICTED 1:UNRESTRICTED 2:OPTIMIZED 3:UNKNOWN"); + writer.println("0:UNKNOWN 1:RESTRICTED 2:UNRESTRICTED 3:OPTIMIZED"); logEntryList.forEach(entry -> writer.println(toString(entry))); } } - /** - * Gets the unique key for logging, combined with package name, delimiter and user id. - */ - static String getPackageNameWithUserId(String pkgName, int userId) { - return pkgName + ":" + userId; + /** Gets the unique key for logging. */ + static String getPackageNameWithUserId(String packageName, int userId) { + return packageName + ":" + userId; } private static String toString(BatteryOptimizeHistoricalLogEntry entry) { - return String.format("%s\tAction:%s\tEvent:%s\tTimestamp:%s", entry.getPackageName(), - entry.getAction(), entry.getActionDescription(), - ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp())); + return String.format("%s\t%s\taction:%s\tevent:%s", + ConvertUtils.utcToLocalTimeForLogging(entry.getTimestamp()), + entry.getPackageName(), entry.getAction(), + entry.getActionDescription()); } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java index f3d6816c5b1..89707305add 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBackupHelperTest.java @@ -70,6 +70,8 @@ import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.Resetter; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -84,6 +86,8 @@ public final class BatteryBackupHelperTest { private static final int UID1 = 1; private Context mContext; + private PrintWriter mPrintWriter; + private StringWriter mStringWriter; private BatteryBackupHelper mBatteryBackupHelper; @Mock @@ -109,6 +113,8 @@ public final class BatteryBackupHelperTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); + mStringWriter = new StringWriter(); + mPrintWriter = new PrintWriter(mStringWriter); doReturn(mContext).when(mContext).getApplicationContext(); doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); @@ -126,6 +132,7 @@ public final class BatteryBackupHelperTest { @After public void resetShadows() { ShadowUserHandle.reset(); + BatteryBackupHelper.getSharedPreferences(mContext).edit().clear().apply(); } @Test @@ -216,6 +223,8 @@ public final class BatteryBackupHelperTest { // 2 for UNRESTRICTED mode and 1 for RESTRICTED mode. final String expectedResult = PACKAGE_NAME1 + ":2," + PACKAGE_NAME2 + ":1,"; verifyBackupData(expectedResult); + verifyDumpHistoryData("com.android.testing.1\taction:BACKUP\tevent:mode: 2"); + verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1"); } @Test @@ -232,6 +241,7 @@ public final class BatteryBackupHelperTest { // "com.android.testing.2" for RESTRICTED mode. final String expectedResult = PACKAGE_NAME2 + ":1,"; verifyBackupData(expectedResult); + verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1"); } @Test @@ -248,6 +258,7 @@ public final class BatteryBackupHelperTest { // "com.android.testing.2" for RESTRICTED mode. final String expectedResult = PACKAGE_NAME2 + ":1,"; verifyBackupData(expectedResult); + verifyDumpHistoryData("com.android.testing.2\taction:BACKUP\tevent:mode: 1"); } @Test @@ -357,6 +368,11 @@ public final class BatteryBackupHelperTest { doReturn(dataKey).when(mBackupDataInputStream).getKey(); } + private void verifyDumpHistoryData(String expectedResult) { + BatteryBackupHelper.dumpHistoricalData(mContext, mPrintWriter); + assertThat(mStringWriter.toString().contains(expectedResult)).isTrue(); + } + private void verifyBackupData(String expectedResult) throws Exception { final byte[] expectedBytes = expectedResult.getBytes(); final ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java index 74f62ad936d..cb5de7d43a5 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHistoricalLogUtilTest.java @@ -49,7 +49,7 @@ public final class BatteryHistoricalLogUtilTest { @Test public void printHistoricalLog_withDefaultLogs() { BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter); - assertThat(mTestStringWriter.toString()).contains("No past logs"); + assertThat(mTestStringWriter.toString()).contains("nothing to dump"); } @Test @@ -58,7 +58,7 @@ public final class BatteryHistoricalLogUtilTest { BatteryHistoricalLogUtil.printBatteryOptimizeHistoricalLog(mContext, mTestPrintWriter); assertThat(mTestStringWriter.toString()).contains( - "pkg1\tAction:APPLY\tEvent:logs\tTimestamp:"); + "pkg1\taction:APPLY\tevent:logs"); } @Test From f4c5c8ef5e776e427db5fb473cb2add53da4bb4a Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Thu, 11 May 2023 12:30:59 +0800 Subject: [PATCH 10/20] Update the wording for LE Audio Add the summary for LE Audio toggle Bug: 280000165 Test: build pass Change-Id: Ic1eee71d6ff70d01e9a37ab0ec06cfaf639fe963 --- res/values/strings.xml | 2 ++ .../bluetooth/BluetoothDetailsProfilesController.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index b12f390de71..13100d9d8e8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1503,6 +1503,8 @@ Disconnect App? %1$s app will no longer connect to your %2$s + + Experimental. Improves audio quality. Forget device diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java index 208fba7716c..d0fb16a91aa 100644 --- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java +++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java @@ -120,6 +120,10 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll pref.setTitle(profile.getNameResource(mCachedDevice.getDevice())); pref.setOnPreferenceClickListener(this); pref.setOrder(profile.getOrdinal()); + + if (profile instanceof LeAudioProfile) { + pref.setSummary(R.string.device_details_leaudio_toggle_summary); + } return pref; } From fb0d23955d13eefc46a966b22145505caa616879 Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Thu, 11 May 2023 05:14:32 +0000 Subject: [PATCH 11/20] Fix non-hearing devices show in pair new hearing devices page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Non-hearing devices are listed in "pair new hearing devices page" if we click the "See more devices" button and go back from "general pair new devices page". Root cause: Two types of Bluetooth scanning happen simultaneously and the scanning results of different scanning methods are handled in the same place. Currently the BLE scanning callback “onScanResult()” directly calls “onDeviceAdded()” method to handle the new scanned devices. This method is mainly called when receiving a broadcast of a new device found in Bluetooth classic scanning. The general pair new devices page uses Bluetooth classic scanning and the pair new hearing devices page uses Bluetooth LE scanning. The life cycle ordering when going back from general pair new devices page to pair new hearing devices page will be "pair new hearing devices page".onStart() -> "general pair new devices page".onStop(). It means the classic scanning is not stopped while the BLE scanning starts and this causes the scanning results from classic scanning unexpectedly added to the list which should only show the scanning results from BLE scanning. Solution: Separates the handling part of two scanning methods. Leaves "onDeviceAdded" handling Bluetooth classic scanning results only, and handles BLE scanning results in the “onScanResult” callback directly. Bug: 279374435 Test: checks the result by switching back from "general pair new devices page" to "pair new hearing devices page" Test: make RunSettingsRoboTests ROBOTEST_FILTER=DeviceListPreferenceFragmentTest Change-Id: Iebdde401ffb3dc0569478730a140a5dd7add115b --- .../bluetooth/DeviceListPreferenceFragment.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index 522b5cb10b0..a4a98917974 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -196,10 +196,11 @@ public abstract class DeviceListPreferenceFragment extends } // Prevent updates while the list shows one of the state messages - if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) return; + if (mBluetoothAdapter.getState() != BluetoothAdapter.STATE_ON) { + return; + } - if (mLeScanFilters != null - || (mFilter != null && mFilter.matches(cachedDevice.getDevice()))) { + if (mFilter != null && mFilter.matches(cachedDevice.getDevice())) { createDevicePreference(cachedDevice); } } @@ -325,7 +326,12 @@ public abstract class DeviceListPreferenceFragment extends if (cachedDevice == null) { cachedDevice = mCachedDeviceManager.addDevice(device); } - onDeviceAdded(cachedDevice); + // Only add device preference when it's not found in the map and there's no other + // state message showing in the list + if (mDevicePreferenceMap.get(cachedDevice) == null + && mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { + createDevicePreference(cachedDevice); + } } @Override From 7eeed19d280c29da6a5b2e7fc7348ec00a83ff7e Mon Sep 17 00:00:00 2001 From: jasonwshsu Date: Wed, 10 May 2023 14:31:09 +0800 Subject: [PATCH 12/20] Update hearing aids to hearing devices * We change the string 'hearing aids' to 'hearing devices', here are some left strings need to be updated. Bug: 281782174 Test: flash rom and check UI Change-Id: I0e3395c91d42b037158d135d1cbe519acac50aa0 --- res/values/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index dd9746610ae..77b7ad2338c 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -119,9 +119,9 @@ Pair your other ear - Your left hearing aid is connected.\n\nTo pair the right one, make sure it\u2019s turned on and ready to pair. + Your left hearing device is connected.\n\nTo pair the right one, make sure it\u2019s turned on and ready to pair. - Your right hearing aid is connected.\n\nTo pair the left one, make sure it\u2019s turned on and ready to pair. + Your right hearing device is connected.\n\nTo pair the left one, make sure it\u2019s turned on and ready to pair. Pair right ear @@ -4668,7 +4668,7 @@ You can use hearing aids, cochlear implants, and other amplification devices with your phone - No hearing aids connected + No hearing devices connected Add hearing aids From 7fdd0e9ef9a86c0036900688177bf94743784069 Mon Sep 17 00:00:00 2001 From: ykhung Date: Thu, 11 May 2023 19:13:38 +0800 Subject: [PATCH 13/20] Avoid showing "0 minute" in the charge time label Remove the charge to full time label if its value is invalid https://screenshot.googleplex.com/5psbvFpcm2CFdRB Test: make test RunSettingsRoboTests ROBOTEST_FILTER=com.android.settings.fuelgauge.* Fix: 278740808 Change-Id: I765cdcdee8525adb2583d5950bc646c604c744de --- src/com/android/settings/fuelgauge/BatteryInfo.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java index d164e931a21..98240447aeb 100644 --- a/src/com/android/settings/fuelgauge/BatteryInfo.java +++ b/src/com/android/settings/fuelgauge/BatteryInfo.java @@ -299,9 +299,10 @@ public class BatteryInfo { (double) PowerUtil.convertUsToMs(info.remainingTimeUs), false /* withSeconds */, true /* collapseTimeUnit */); int resId = R.string.power_charging_duration; - info.remainingLabel = context.getString(R.string.power_remaining_charging_duration_only, - timeString); - info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString); + info.remainingLabel = chargeTimeMs <= 0 ? null : context.getString( + R.string.power_remaining_charging_duration_only, timeString); + info.chargeLabel = chargeTimeMs <= 0 ? info.batteryPercentString + : context.getString(resId, info.batteryPercentString, timeString); } else if (dockDefenderMode == BatteryUtils.DockDefenderMode.FUTURE_BYPASS) { // Dock defender will be triggered in the future, charging will be optimized. info.chargeLabel = context.getString(R.string.power_charging_future_paused, From 571d9b1f377c6046c11c7e12cfec70e5af2c82af Mon Sep 17 00:00:00 2001 From: Weng Su Date: Thu, 11 May 2023 20:43:34 +0800 Subject: [PATCH 14/20] Fix failing test case in AllInOneTetherSettingsTest - Add the necessary mock classes Bug: 280067520 Test: Manual test atest -c AllInOneTetherSettingsTest Change-Id: I6576c6997b43103ea810008b7a1a245e3e42015c --- .../src/com/android/settings/AllInOneTetherSettingsTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java index 01ec42e6a94..f2a55c1d935 100644 --- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java +++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java @@ -45,6 +45,8 @@ import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; import com.android.settings.core.FeatureFlags; +import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settings.wifi.repository.WifiHotspotRepository; import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController; import com.android.settings.wifi.tether.WifiTetherSecurityPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; @@ -93,6 +95,8 @@ public class AllInOneTetherSettingsTest { mContext = spy(RuntimeEnvironment.application); MockitoAnnotations.initMocks(this); + when(FakeFeatureFactory.setupForTest().getWifiFeatureProvider().getWifiHotspotRepository()) + .thenReturn(mock(WifiHotspotRepository.class)); doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class); doReturn(mConnectivityManager) .when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE); From 50dc4e857b08b895563d68c67c55e0128237307b Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 11 May 2023 20:44:22 +0800 Subject: [PATCH 15/20] Ordering the SIM display name by slot ID to the preference summary. Bug: 271975836 Test: Manual Test Change-Id: Ib28062c8d0f1299cf30d494561c161fc4532dbc9 --- .../settings/network/MobileNetworkSummaryController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/settings/network/MobileNetworkSummaryController.java b/src/com/android/settings/network/MobileNetworkSummaryController.java index 99a2731e916..1474836d7f1 100644 --- a/src/com/android/settings/network/MobileNetworkSummaryController.java +++ b/src/com/android/settings/network/MobileNetworkSummaryController.java @@ -24,7 +24,6 @@ import android.content.Intent; import android.os.UserManager; import android.telephony.SubscriptionManager; import android.telephony.euicc.EuiccManager; -import android.util.Log; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; @@ -135,6 +134,7 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController return mContext.getString(R.string.mobile_network_tap_to_activate, displayName); } else { return mSubInfoEntityList.stream() + .sorted((e1, e2) -> Integer.compare(e1.simSlotIndex, e2.simSlotIndex)) .map(SubscriptionInfoEntity::getUniqueDisplayName) .collect(Collectors.joining(", ")); } From 75b24e421eda1178b449fc2d1916dc65c65a5022 Mon Sep 17 00:00:00 2001 From: tom hsu Date: Thu, 11 May 2023 21:12:29 +0800 Subject: [PATCH 16/20] [Settings] Remove redundant code. Bug: 280236099 Test: make passed Change-Id: I34e0632887ac38658761abd7d6a7e9f1e0f84bf1 --- .../bluetooth/AdvancedBluetoothDetailsHeaderController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 4679eb2b3d2..ebaa2fa640e 100644 --- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java +++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java @@ -100,8 +100,6 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper()); @VisibleForTesting - boolean mIsRegisterCallback = false; - @VisibleForTesting boolean mIsLeftDeviceEstimateReady; @VisibleForTesting boolean mIsRightDeviceEstimateReady; From e4856156627ee327def08ba2bbe991e8c108550d Mon Sep 17 00:00:00 2001 From: Weng Su Date: Thu, 11 May 2023 21:53:26 +0800 Subject: [PATCH 17/20] Fix failing test cases in MobileNetworkSummaryControllerTest - Remove obsolete test items - Update the new callback interface Bug: 280044539 Bug: 280044731 Test: Manual test atest -c MobileNetworkSummaryControllerTest Change-Id: I8460c620c62981dd5ee3b280c1a7467c43dbceb1 --- .../MobileNetworkSummaryControllerTest.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java index 5310ae09801..8d6d2d9bc4e 100644 --- a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java +++ b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java @@ -133,18 +133,12 @@ public class MobileNetworkSummaryControllerTest { assertThat(mController.isAvailable()).isFalse(); } - @Ignore @Test - public void getSummary_noSubscriptions_correctSummaryAndClickHandler() { + public void getSummary_noSubscriptions_returnSummaryCorrectly() { mController.displayPreference(mPreferenceScreen); mController.onResume(); - assertThat(mController.getSummary()).isEqualTo("Add a network"); - final ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); - doNothing().when(mContext).startActivity(intentCaptor.capture()); - mPreference.getOnPreferenceClickListener().onPreferenceClick(mPreference); - assertThat(intentCaptor.getValue().getAction()).isEqualTo( - EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION); + assertThat(mController.getSummary()).isEqualTo("Add a network"); } @Test @@ -300,15 +294,13 @@ public class MobileNetworkSummaryControllerTest { assertThat(captor.getValue()).isFalse(); } - @Ignore @Test - public void onResume_noSubscriptionEsimDisabled_isDisabled() { + public void onAvailableSubInfoChanged_noSubscriptionEsimDisabled_isDisabled() { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); - SubscriptionUtil.setAvailableSubscriptionsForTesting(null); when(mEuiccManager.isEnabled()).thenReturn(false); mController.displayPreference(mPreferenceScreen); - mController.onResume(); + mController.onAvailableSubInfoChanged(null); assertThat(mPreference.isEnabled()).isFalse(); } From cddf296c8255f12e178cad3afc4e40279a3c381b Mon Sep 17 00:00:00 2001 From: Hao Dong Date: Wed, 10 May 2023 21:48:07 +0000 Subject: [PATCH 18/20] Hide description text view if there is overlap. 1. Includes udfps enroll view in xml files and cleans up FingerprintEnrollEnrolling adding udfps enroll view code. 2. Clean up SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS flag. 3. If description text view has overlap with udfps progress view, hide the description text view. Test: atest FingerprintEnrollEnrollingTest Test: manually test by setting both display and text largest size and start enrolling fingerprint; the description is hidden if it's too long. Bug: 260970216 Change-Id: I085dc62993ffa22d018dd57517c96d69e9d9cbcb --- res/layout-land/udfps_enroll_enrolling.xml | 2 + res/layout/udfps_enroll_enrolling.xml | 9 +- .../FingerprintEnrollEnrolling.java | 106 +++++++++--------- .../fingerprint/UdfpsEnrollView.java | 30 +++-- .../FingerprintEnrollEnrollingTest.java | 55 +++++---- 5 files changed, 110 insertions(+), 92 deletions(-) diff --git a/res/layout-land/udfps_enroll_enrolling.xml b/res/layout-land/udfps_enroll_enrolling.xml index f3237887e9a..743684fb02e 100644 --- a/res/layout-land/udfps_enroll_enrolling.xml +++ b/res/layout-land/udfps_enroll_enrolling.xml @@ -96,4 +96,6 @@ + + \ No newline at end of file diff --git a/res/layout/udfps_enroll_enrolling.xml b/res/layout/udfps_enroll_enrolling.xml index c97591d6d52..05556ffe46c 100644 --- a/res/layout/udfps_enroll_enrolling.xml +++ b/res/layout/udfps_enroll_enrolling.xml @@ -18,6 +18,7 @@ + android:layout_gravity="center_horizontal|bottom" + tools:ignore="Suspicious0dp"> + + + { + if (view.getVisibility() == View.VISIBLE + && hasOverlap(view, udfpsEnrollView)) { + view.setVisibility(View.GONE); + } + }); + setOnHoverListener(false, layout, udfpsEnrollView); setContentView(layout); break; case Surface.ROTATION_270: default: - if (FeatureFlagUtils.isEnabled(getApplicationContext(), - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) { - final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0)); - layout.addView(udfpsEnrollView); - setOnHoverListener(true, layout, udfpsEnrollView); - } - + setOnHoverListener(true, layout, udfpsEnrollView); setContentView(layout); break; } @@ -1235,10 +1223,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { } } - private UdfpsEnrollView addUdfpsEnrollView(FingerprintSensorPropertiesInternal udfpsProps) { - UdfpsEnrollView udfpsEnrollView = (UdfpsEnrollView) getLayoutInflater().inflate( - R.layout.udfps_enroll_view, null, false); - + private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView, + FingerprintSensorPropertiesInternal udfpsProps) { DisplayInfo displayInfo = new DisplayInfo(); getDisplay().getDisplayInfo(displayInfo); mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo); @@ -1305,6 +1291,24 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { : R.id.sud_layout_content).setOnHoverListener(onHoverListener); } + + @VisibleForTesting boolean hasOverlap(View view1, View view2) { + int[] firstPosition = new int[2]; + int[] secondPosition = new int[2]; + + view1.getLocationOnScreen(firstPosition); + view2.getLocationOnScreen(secondPosition); + + // Rect constructor parameters: left, top, right, bottom + Rect rectView1 = new Rect(firstPosition[0], firstPosition[1], + firstPosition[0] + view1.getMeasuredWidth(), + firstPosition[1] + view1.getMeasuredHeight()); + Rect rectView2 = new Rect(secondPosition[0], secondPosition[1], + secondPosition[0] + view2.getMeasuredWidth(), + secondPosition[1] + view2.getMeasuredHeight()); + return rectView1.intersect(rectView2); + } + public static class IconTouchDialog extends InstrumentedDialogFragment { @Override diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java index 6e42059395f..5ded91ec68c 100644 --- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java +++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java @@ -161,22 +161,20 @@ public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Li MarginLayoutParams marginLayoutParams = (MarginLayoutParams) getLayoutParams(); FrameLayout.LayoutParams params = (LayoutParams) getLayoutParams(); if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { - parentView.getViewTreeObserver().addOnDrawListener(() -> { - final int[] coords = parentView.getLocationOnScreen(); - final int parentLeft = coords[0]; - final int parentTop = coords[1]; - final int parentRight = parentLeft + parentView.getWidth(); - params.gravity = Gravity.RIGHT | Gravity.TOP; - final int rightMargin = parentRight - rotatedBounds.right - getPaddingX(); - final int topMargin = rotatedBounds.top - parentTop - getPaddingY(); - if (marginLayoutParams.rightMargin == rightMargin - && marginLayoutParams.topMargin == topMargin) { - return; - } - marginLayoutParams.rightMargin = rightMargin; - marginLayoutParams.topMargin = topMargin; - setLayoutParams(params); - }); + final int[] coords = parentView.getLocationOnScreen(); + final int parentLeft = coords[0]; + final int parentTop = coords[1]; + final int parentRight = parentLeft + parentView.getWidth(); + params.gravity = Gravity.RIGHT | Gravity.TOP; + final int rightMargin = parentRight - rotatedBounds.right - getPaddingX(); + final int topMargin = rotatedBounds.top - parentTop - getPaddingY(); + if (marginLayoutParams.rightMargin == rightMargin + && marginLayoutParams.topMargin == topMargin) { + return; + } + marginLayoutParams.rightMargin = rightMargin; + marginLayoutParams.topMargin = topMargin; + setLayoutParams(params); } else { final int[] coords = parentView.getLocationOnScreen(); final int parentLeft = coords[0]; diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 7282be357c1..959c6426894 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -53,7 +53,6 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Vibrator; -import android.util.FeatureFlagUtils; import android.view.Display; import android.view.Surface; import android.view.View; @@ -203,8 +202,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void fingerprintUdfpsOverlayEnrollment_showOverlayPortrait() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); @@ -216,8 +213,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void fingerprintUdfpsOverlayEnrollment_showOverlayLandscape() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_90); @@ -229,8 +224,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void fingerprintUdfpsOverlayEnrollment_usesCorrectProgressBarFillColor() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); final TypedArray ta = mActivity.obtainStyledAttributes(null, R.styleable.BiometricsEnrollView, R.attr.biometricsEnrollStyle, @@ -250,9 +243,7 @@ public class FingerprintEnrollEnrollingTest { @Test public void fingerprintUdfpsOverlayEnrollment_checkViewOverlapPortrait() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); - when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_90); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); initializeActivityFor(TYPE_UDFPS_OPTICAL); final GlifLayout defaultLayout = mActivity.findViewById(R.id.setup_wizard_layout); @@ -294,9 +285,9 @@ public class FingerprintEnrollEnrollingTest { udfpsEnrollView.getViewTreeObserver().addOnDrawListener(() -> { udfpsEnrollView.getLocationOnScreen(udfpsEnrollViewPosition); rectUdfpsEnrollView.set(new Rect(udfpsEnrollViewPosition[0], - udfpsEnrollViewPosition[1], udfpsEnrollViewPosition[0] - + udfpsEnrollView.getWidth(), udfpsEnrollViewPosition[1] - + udfpsEnrollView.getHeight())); + udfpsEnrollViewPosition[1], udfpsEnrollViewPosition[0] + + udfpsEnrollView.getWidth(), udfpsEnrollViewPosition[1] + + udfpsEnrollView.getHeight())); }); lottieAnimationContainer.getViewTreeObserver().addOnDrawListener(() -> { @@ -320,10 +311,36 @@ public class FingerprintEnrollEnrollingTest { .intersect(rectUdfpsEnrollView.get())).isFalse(); } + @Test + public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() { + initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL); + doReturn(true).when(mActivity).hasOverlap(any(), any()); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); + createActivity(); + + final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout)); + final TextView descriptionTextView = defaultLayout.getDescriptionTextView(); + + defaultLayout.getViewTreeObserver().dispatchOnDraw(); + assertThat(descriptionTextView.getVisibility()).isEqualTo(View.GONE); + } + + @Test + public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() { + initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL); + doReturn(false).when(mActivity).hasOverlap(any(), any()); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); + createActivity(); + + final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout)); + final TextView descriptionTextView = defaultLayout.getDescriptionTextView(); + + defaultLayout.getViewTreeObserver().dispatchOnDraw(); + assertThat(descriptionTextView.getVisibility()).isEqualTo(View.VISIBLE); + } + @Test public void forwardEnrollProgressEvents() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); @@ -337,8 +354,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void forwardEnrollHelpEvents() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); @@ -352,8 +367,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void forwardEnrollAcquiredEvents() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); @@ -368,8 +381,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void forwardEnrollPointerDownEvents() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); @@ -383,8 +394,6 @@ public class FingerprintEnrollEnrollingTest { @Test public void forwardEnrollPointerUpEvents() { - FeatureFlagUtils.setEnabled(mContext, - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, true); initializeActivityFor(TYPE_UDFPS_OPTICAL); EnrollListener listener = new EnrollListener(mActivity); From f7c4d9c90350f29d51e40529c32817a597b3bca4 Mon Sep 17 00:00:00 2001 From: Becca Hughes Date: Thu, 11 May 2023 17:45:30 +0000 Subject: [PATCH 19/20] Fix string Test: make Bug: 281047738 Change-Id: I7f65749cbdee95ab9447dc0b055d66fd962547dc --- res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 56116b0bfc9..a2699ef77a2 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10313,7 +10313,7 @@ Turn off this service?

- Save info like passwords, passkeys, payment methods, and other info won\'t be filled + Saved info like passwords, passkeys, payment methods, and other info won\'t be filled in when you sign in. To use your saved info, choose a password, passkey, or data service. ]]> From 715a70603c1161e6e8f18888df0c346040961b07 Mon Sep 17 00:00:00 2001 From: Yuxin Hu Date: Mon, 1 May 2023 21:48:33 +0000 Subject: [PATCH 20/20] Add AndroidJUnitTest for developer option switch Bug: b/270994705 Test: m -j45 atest SettingsRoboTests:GraphicsDriverEnableAngleAsSystemDriverControllerTest atest -c GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest Change-Id: Idbb93458a64894c3eba78a8f9373c40e3ddf35c5 --- ...erEnableAngleAsSystemDriverController.java | 47 ++- ...GraphicsDriverSystemPropertiesWrapper.java | 44 +++ ...ableAngleAsSystemDriverControllerTest.java | 22 +- ...ngleAsSystemDriverControllerJUnitTest.java | 325 ++++++++++++++++++ 4 files changed, 418 insertions(+), 20 deletions(-) create mode 100644 src/com/android/settings/development/graphicsdriver/GraphicsDriverSystemPropertiesWrapper.java create mode 100644 tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java index b9f34137111..04252fa0d56 100644 --- a/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java +++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverController.java @@ -48,6 +48,8 @@ public class GraphicsDriverEnableAngleAsSystemDriverController private final DevelopmentSettingsDashboardFragment mFragment; + private final GraphicsDriverSystemPropertiesWrapper mSystemProperties; + @VisibleForTesting static final String PROPERTY_RO_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; @@ -57,11 +59,34 @@ public class GraphicsDriverEnableAngleAsSystemDriverController @VisibleForTesting static final String ANGLE_DRIVER_SUFFIX = "angle"; + @VisibleForTesting + static class Injector { + public GraphicsDriverSystemPropertiesWrapper createSystemPropertiesWrapper() { + return new GraphicsDriverSystemPropertiesWrapper() { + @Override + public String get(String key, String def) { + return SystemProperties.get(key, def); + } + + @Override + public void set(String key, String val) { + SystemProperties.set(key, val); + } + }; + } + } public GraphicsDriverEnableAngleAsSystemDriverController( Context context, DevelopmentSettingsDashboardFragment fragment) { + this(context, fragment, new Injector()); + } + + @VisibleForTesting + GraphicsDriverEnableAngleAsSystemDriverController( + Context context, DevelopmentSettingsDashboardFragment fragment, Injector injector) { super(context); mFragment = fragment; + mSystemProperties = injector.createSystemPropertiesWrapper(); } @Override @@ -76,20 +101,27 @@ public class GraphicsDriverEnableAngleAsSystemDriverController // set "persist.graphics.egl" to "" if enableAngleAsSystemDriver is false GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(enableAngleAsSystemDriver); // pop up a window asking user to reboot to make the new "persist.graphics.egl" take effect + showRebootDialog(); + return true; + } + + @VisibleForTesting + void showRebootDialog() { RebootConfirmationDialogFragment.show( mFragment, R.string.reboot_dialog_enable_angle_as_system_driver, R.string.cancel, this); - return true; } + @Override public void updateState(Preference preference) { // set switch on if "persist.graphics.egl" is "angle" and angle is built in /vendor // set switch off otherwise. - final String currentGlesDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + final String currentGlesDriver = + mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); final boolean isAngle = TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver); - final boolean isAngleSupported = - TextUtils.equals(SystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED), "true"); + final boolean isAngleSupported = TextUtils + .equals(mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true"); ((SwitchPreference) mPreference).setChecked(isAngle && isAngleSupported); ((SwitchPreference) mPreference).setEnabled(isAngleSupported); } @@ -98,8 +130,8 @@ public class GraphicsDriverEnableAngleAsSystemDriverController protected void onDeveloperOptionsSwitchEnabled() { // only enable the switch if ro.gfx.angle.supported is true // we use ro.gfx.angle.supported to indicate if ANGLE libs are installed under /vendor - final boolean isAngleSupported = - TextUtils.equals(SystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED), "true"); + final boolean isAngleSupported = TextUtils + .equals(mSystemProperties.get(PROPERTY_RO_GFX_ANGLE_SUPPORTED, ""), "true"); ((SwitchPreference) mPreference).setEnabled(isAngleSupported); } @@ -116,7 +148,8 @@ public class GraphicsDriverEnableAngleAsSystemDriverController @Override public void onRebootCancelled() { // if user presses button "Cancel", do not reboot the device, and toggles switch back - final String currentGlesDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + final String currentGlesDriver = + mSystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); if (TextUtils.equals(ANGLE_DRIVER_SUFFIX, currentGlesDriver)) { // if persist.graphics.egl = "angle", set the property value back to "" GraphicsEnvironment.getInstance().toggleAngleAsSystemDriver(false); diff --git a/src/com/android/settings/development/graphicsdriver/GraphicsDriverSystemPropertiesWrapper.java b/src/com/android/settings/development/graphicsdriver/GraphicsDriverSystemPropertiesWrapper.java new file mode 100644 index 00000000000..549cd81c565 --- /dev/null +++ b/src/com/android/settings/development/graphicsdriver/GraphicsDriverSystemPropertiesWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.development.graphicsdriver; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.SystemProperties; +/** + * Wrapper interface to access {@link SystemProperties}. + * + * @hide + */ +interface GraphicsDriverSystemPropertiesWrapper { + /** + * Get the String value for the given {@code key}. + * + * @param key the key to lookup + * @param def the default value in case the property is not set or empty + * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty + * string otherwise + */ + @NonNull + String get(@NonNull String key, @Nullable String def); + /** + * Set the value for the given {@code key} to {@code val}. + * + * @throws IllegalArgumentException if the {@code val} exceeds 91 characters + * @throws RuntimeException if the property cannot be set, for example, if it was blocked by + * SELinux. libc will log the underlying reason. + */ + void set(@NonNull String key, @Nullable String val); +} diff --git a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java index 314f8c38091..de380c40c8e 100644 --- a/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerTest.java @@ -81,9 +81,10 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest { // since GraphicsEnvironment is mocked in Robolectric test environment, // we will override the system property persist.graphics.egl as if it is changed by // mGraphicsEnvironment.toggleAngleAsSystemDriver(true). - // TODO: b/270994705 yuxinhu: - // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver() - // works properly on Android devices / emulators. + + // for test that actually verifies mGraphicsEnvironment.toggleAngleAsSystemDriver(true) + // on a device/emulator, please refer to + // GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ANGLE_DRIVER_SUFFIX); mController.onPreferenceChange(mPreference, true); final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); @@ -97,9 +98,10 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest { // since GraphicsEnvironment is mocked in Robolectric test environment, // we will override the system property persist.graphics.egl as if it is changed by // mGraphicsEnvironment.toggleAngleAsSystemDriver(false). - // TODO: b/270994705 yuxinhu: - // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver() - // works properly on Android devices / emulators. + + // for test that actually verifies mGraphicsEnvironment.toggleAngleAsSystemDriver(true) + // on a device/emulator, please refer to + // GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); mController.onPreferenceChange(mPreference, false); final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); @@ -124,20 +126,14 @@ public class GraphicsDriverEnableAngleAsSystemDriverControllerTest { @Test public void updateState_angleSupported_angleUsed_preferenceShouldBeChecked() { ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true"); - // TODO: b/270994705 yuxinhu: - // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver() - // works properly on Android devices / emulators. ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ANGLE_DRIVER_SUFFIX); mController.updateState(mPreference); - verify(mPreference).setChecked(true); //false + verify(mPreference).setChecked(true); } @Test public void updateState_angleSupported_angleNotUsed_preferenceShouldNotBeChecked() { ShadowSystemProperties.override(PROPERTY_RO_GFX_ANGLE_SUPPORTED, "true"); - // TODO: b/270994705 yuxinhu: - // add test coverage to test mGraphicsEnvironment.toggleAngleAsSystemDriver(false) - // works properly on Android devices / emulators. ShadowSystemProperties.override(PROPERTY_PERSISTENT_GRAPHICS_EGL, ""); mController.updateState(mPreference); verify(mPreference).setChecked(false); diff --git a/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java new file mode 100644 index 00000000000..3f85535df53 --- /dev/null +++ b/tests/unit/src/com/android/settings/development/graphicsdriver/GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest.java @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development.graphicsdriver; + +import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.ANGLE_DRIVER_SUFFIX; +import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.Injector; +import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_PERSISTENT_GRAPHICS_EGL; +import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableAngleAsSystemDriverController.PROPERTY_RO_GFX_ANGLE_SUPPORTED; + +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.when; + +import android.content.Context; +import android.os.Looper; +import android.os.SystemProperties; + +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.preference.SwitchPreference; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.settings.development.DevelopmentSettingsDashboardFragment; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class GraphicsDriverEnableAngleAsSystemDriverControllerJUnitTest { + private Context mContext; + private SwitchPreference mPreference; + + private GraphicsDriverEnableAngleAsSystemDriverController mController; + + @Mock + private DevelopmentSettingsDashboardFragment mFragment; + + @Mock + private GraphicsDriverSystemPropertiesWrapper mSystemPropertiesMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + mContext = ApplicationProvider.getApplicationContext(); + + // Construct a GraphicsDriverEnableAngleAsSystemDriverController with two Overrides: + // 1) Override the mSystemProperties with mSystemPropertiesMock, + // so we can force the SystemProperties with values we need to run tests. + // 2) Override the showRebootDialog() to do nothing. + // We do not need to pop up the reboot dialog in the test. + mController = new GraphicsDriverEnableAngleAsSystemDriverController( + mContext, mFragment, new Injector(){ + @Override + public GraphicsDriverSystemPropertiesWrapper createSystemPropertiesWrapper() { + return mSystemPropertiesMock; + } + }) { + @Override + void showRebootDialog() { + // do nothing + } + }; + + final PreferenceManager preferenceManager = new PreferenceManager(mContext); + final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); + mPreference = new SwitchPreference(mContext); + mPreference.setKey(mController.getPreferenceKey()); + screen.addPreference(mPreference); + mController.displayPreference(screen); + } + + @Test + public void onPreferenceChange_switchOn_shouldEnableAngleAsSystemDriver() { + // Add a callback when SystemProperty changes. + // This allows the thread to wait until + // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. + final CountDownLatch countDownLatch = new CountDownLatch(1); + Runnable countDown = new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }; + SystemProperties.addChangeCallback(countDown); + + // Test onPreferenceChange(true) updates the persist.graphics.egl to "angle" + mController.onPreferenceChange(mPreference, true); + try { + countDownLatch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX); + + // Done with the test, remove the callback + SystemProperties.removeChangeCallback(countDown); + } + + @Test + public void onPreferenceChange_switchOff_shouldDisableAngleAsSystemDriver() { + // Add a callback when SystemProperty changes. + // This allows the thread to wait until + // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. + final CountDownLatch countDownLatch = new CountDownLatch(1); + Runnable countDown = new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }; + SystemProperties.addChangeCallback(countDown); + + // Test onPreferenceChange(false) updates the persist.graphics.egl to "" + mController.onPreferenceChange(mPreference, false); + try { + countDownLatch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + assertThat(systemEGLDriver).isEqualTo(""); + + // Done with the test, remove the callback + SystemProperties.removeChangeCallback(countDown); + } + + @Test + public void updateState_angleNotSupported_PreferenceShouldDisabled() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())).thenReturn(""); + mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isFalse(); + } + + @Test + public void updateState_angleNotSupported_PreferenceShouldNotBeChecked() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn(""); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void updateState_angleSupported_PreferenceShouldEnabled() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); + mController.updateState(mPreference); + assertThat(mPreference.isEnabled()).isTrue(); + } + + @Test + public void updateState_angleSupported_angleIsSystemGLESDriver_PreferenceShouldBeChecked() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); + when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())) + .thenReturn(ANGLE_DRIVER_SUFFIX); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isTrue(); + } + + @Test + public void + updateState_angleSupported_angleIsNotSystemGLESDriver_PreferenceShouldNotBeChecked() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); + when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())) + .thenReturn(""); + mController.updateState(mPreference); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void onDeveloperOptionSwitchEnabled_angleSupported_PreferenceShouldEnabled() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("true"); + mController.onDeveloperOptionsSwitchEnabled(); + assertThat(mPreference.isEnabled()).isTrue(); + } + + @Test + public void onDeveloperOptionSwitchEnabled_angleNotSupported_PrefenceShouldDisabled() { + when(mSystemPropertiesMock.get(eq(PROPERTY_RO_GFX_ANGLE_SUPPORTED), any())) + .thenReturn("false"); + mController.onDeveloperOptionsSwitchEnabled(); + assertThat(mPreference.isEnabled()).isFalse(); + } + + @Test + public void onDeveloperOptionSwitchDisabled_angleIsNotSystemGLESDriver() { + // Add a callback when SystemProperty changes. + // This allows the thread to wait until + // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. + final CountDownLatch countDownLatch = new CountDownLatch(1); + Runnable countDown = new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }; + SystemProperties.addChangeCallback(countDown); + + // Test that onDeveloperOptionSwitchDisabled, + // persist.graphics.egl updates to "" + mController.onDeveloperOptionsSwitchDisabled(); + try { + countDownLatch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + assertThat(systemEGLDriver).isEqualTo(""); + + // Done with the test, remove the callback + SystemProperties.removeChangeCallback(countDown); + } + + @Test + public void onDeveloperOptionSwitchDisabled_PreferenceShouldNotBeChecked() { + mController.onDeveloperOptionsSwitchDisabled(); + assertThat(mPreference.isChecked()).isFalse(); + } + + @Test + public void onDeveloperOptionSwitchDisabled_PreferenceShouldDisabled() { + mController.onDeveloperOptionsSwitchDisabled(); + assertThat(mPreference.isEnabled()).isFalse(); + } + + @Test + public void onRebootCancelled_ToggleSwitchFromOnToOff() { + // Add a callback when SystemProperty changes. + // This allows the thread to wait until + // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. + final CountDownLatch countDownLatch = new CountDownLatch(1); + Runnable countDown = new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }; + SystemProperties.addChangeCallback(countDown); + + // Test that if the current persist.graphics.egl is "angle", + // when reboot is cancelled, persist.graphics.egl is changed back to "", + // and switch is set to unchecked. + when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())) + .thenReturn(ANGLE_DRIVER_SUFFIX); + mController.onRebootCancelled(); + try { + countDownLatch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + + final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + assertThat(systemEGLDriver).isEqualTo(""); + assertThat(mPreference.isChecked()).isFalse(); + + // Done with the test, remove the callback. + SystemProperties.removeChangeCallback(countDown); + } + + @Test + public void onRebootCancelled_ToggleSwitchFromOffToOn() { + // Add a callback when SystemProperty changes. + // This allows the thread to wait until + // GpuService::toggleAngleAsSystemDriver() updates the persist.graphics.egl. + final CountDownLatch countDownLatch = new CountDownLatch(1); + Runnable countDown = new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }; + SystemProperties.addChangeCallback(countDown); + + // Test that if the current persist.graphics.egl is "", + // when reboot is cancelled, persist.graphics.egl is changed back to "angle", + // and switch is set to checked. + when(mSystemPropertiesMock.get(eq(PROPERTY_PERSISTENT_GRAPHICS_EGL), any())) + .thenReturn(""); + mController.onRebootCancelled(); + try { + countDownLatch.await(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } + + final String systemEGLDriver = SystemProperties.get(PROPERTY_PERSISTENT_GRAPHICS_EGL); + assertThat(systemEGLDriver).isEqualTo(ANGLE_DRIVER_SUFFIX); + assertThat(mPreference.isChecked()).isTrue(); + + // Done with the test, remove the callback. + SystemProperties.removeChangeCallback(countDown); + } + +}