diff --git a/color-check-baseline.xml b/color-check-baseline.xml index 509d9ae0e5a..7a5e80d0a8a 100644 --- a/color-check-baseline.xml +++ b/color-check-baseline.xml @@ -1581,6 +1581,22 @@ column="5"/> + + + + + + + + + + + + + + + + + + + + + + + + + + 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/flash_notification_preview_preference.xml b/res/layout/flash_notification_preview_preference.xml index b4be0f62df1..97f2245df0b 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"> + android:layout_gravity="center_horizontal|bottom" + tools:ignore="Suspicious0dp"> + + + #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/res/values/strings.xml b/res/values/strings.xml index 8fc5ed5d651..add226ab8f8 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 @@ -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 @@ -1505,6 +1505,8 @@ Disconnect App? %1$s app will no longer connect to your %2$s + + Experimental. Improves audio quality. Forget device @@ -4670,7 +4672,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 @@ -10306,7 +10308,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? +
+
+ 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. + ]]> +
Use %1$s\? 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/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 = diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index 57915287389..9aee087f3e0 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -261,6 +261,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { int rotation = getApplicationContext().getDisplay().getRotation(); final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate( R.layout.udfps_enroll_enrolling, null, false); + final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view); + updateUdfpsEnrollView(udfpsEnrollView, props.get(0)); switch (rotation) { case Surface.ROTATION_90: final View sudContent = layout.findViewById(R.id.sud_layout_content); @@ -282,66 +284,52 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { ? 0 : (int) getResources().getDimension( R.dimen.rotation_90_enroll_padding_end), 0); layoutContainer.setLayoutParams(lp); - 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, lp); break; case Surface.ROTATION_0: case Surface.ROTATION_180: - if (FeatureFlagUtils.isEnabled(getApplicationContext(), - FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) { - final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0)); - // In the portrait mode, set layout_container's height 0, so it's - // always shown at the bottom of the screen. - // Add udfps enroll view into layout_container instead of - // udfps_enroll_enrolling, so that when the content is too long to - // make udfps_enroll_enrolling larger than the screen, udfps enroll - // view could still be set to right position by setting bottom margin to - // its parent view (layout_container) because it's always at the - // bottom of the screen. - final FrameLayout portraitLayoutContainer = layout.findViewById( - R.id.layout_container); - final ViewGroup.LayoutParams containerLp = - portraitLayoutContainer.getLayoutParams(); - containerLp.height = 0; + // In the portrait mode, layout_container's height is 0, so it's + // always shown at the bottom of the screen. + final FrameLayout portraitLayoutContainer = layout.findViewById( + R.id.layout_container); - // In the portrait mode, the title and lottie animation view may - // overlap when title needs three lines, so adding some paddings - // between them, and adjusting the fp progress view here accordingly. - final int layoutLottieAnimationPadding = (int) getResources() - .getDimension(R.dimen.udfps_lottie_padding_top); - portraitLayoutContainer.setPadding(0, - layoutLottieAnimationPadding, 0, 0); - final ImageView progressView = udfpsEnrollView.findViewById( - R.id.udfps_enroll_animation_fp_progress_view); - progressView.setPadding(0, -(layoutLottieAnimationPadding), - 0, layoutLottieAnimationPadding); - final ImageView fingerprintView = udfpsEnrollView.findViewById( - R.id.udfps_enroll_animation_fp_view); - fingerprintView.setPadding(0, -layoutLottieAnimationPadding, - 0, layoutLottieAnimationPadding); + // In the portrait mode, the title and lottie animation view may + // overlap when title needs three lines, so adding some paddings + // between them, and adjusting the fp progress view here accordingly. + final int layoutLottieAnimationPadding = (int) getResources() + .getDimension(R.dimen.udfps_lottie_padding_top); + portraitLayoutContainer.setPadding(0, + layoutLottieAnimationPadding, 0, 0); + final ImageView progressView = udfpsEnrollView.findViewById( + R.id.udfps_enroll_animation_fp_progress_view); + progressView.setPadding(0, -(layoutLottieAnimationPadding), + 0, layoutLottieAnimationPadding); + final ImageView fingerprintView = udfpsEnrollView.findViewById( + R.id.udfps_enroll_animation_fp_view); + fingerprintView.setPadding(0, -layoutLottieAnimationPadding, + 0, layoutLottieAnimationPadding); - portraitLayoutContainer.addView(udfpsEnrollView); - setOnHoverListener(false, layout, udfpsEnrollView); - } + // TODO(b/260970216) Instead of hiding the description text view, we should + // make the header view scrollable if the text is too long. + // If description text view has overlap with udfps progress view, hide it. + View view = layout.getDescriptionTextView(); + layout.getViewTreeObserver().addOnDrawListener(() -> { + 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; } @@ -1241,10 +1229,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); @@ -1311,6 +1297,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/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/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/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java index 666a7385dcf..ebaa2fa640e 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,13 +94,12 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont @VisibleForTesting final Map mIconCache; private CachedBluetoothDevice mCachedDevice; + private Set mBluetoothDevices; @VisibleForTesting BluetoothAdapter mBluetoothAdapter; @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper()); @VisibleForTesting - boolean mIsRegisterCallback = false; - @VisibleForTesting boolean mIsLeftDeviceEstimateReady; @VisibleForTesting boolean mIsRightDeviceEstimateReady; @@ -141,23 +142,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 @@ -175,6 +166,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/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; } 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 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/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/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, 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(", ")); } 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); } }; } 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(); + } } } diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java index 9eb2cee3254..f4a20feb28f 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; @@ -94,6 +96,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); 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 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); 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/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); } 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/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 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(); } 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) { 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); + } + +}