diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 58ac9b74819..4d3b6d0e3f9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2226,6 +2226,7 @@ diff --git a/res/values/config.xml b/res/values/config.xml index 6b42baab6bc..c7ef595e3ae 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -71,9 +71,6 @@ false - - false - - Use Auto-rotate + Use auto-rotate - Face Detection uses the front-facing camera to improve Auto-rotate accuracy. Images are never stored or sent to Google. + Face Detection uses the front-facing camera to improve auto-rotate accuracy. Images are never stored or sent to Google. Sample text @@ -2926,7 +2926,11 @@ On - Face-based - Enable Face Detection + Face Detection + + Automatically adjust the screen orientation when you move your phone between portrait and landscape + + Learn more about auto-rotate Screen resolution @@ -12463,6 +12467,9 @@ + + + diff --git a/res/xml/auto_rotate_settings.xml b/res/xml/auto_rotate_settings.xml index 0c120d68fff..38aa5f0047e 100644 --- a/res/xml/auto_rotate_settings.xml +++ b/res/xml/auto_rotate_settings.xml @@ -20,6 +20,14 @@ xmlns:settings="http://schemas.android.com/apk/res-auto" android:title="@string/accelerometer_title" > + + + + + settings:searchable="false"/> diff --git a/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java index 894d3aea2d4..9f698f20874 100644 --- a/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java +++ b/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceController.java @@ -51,9 +51,7 @@ public class FloatingMenuTransparencyPreferenceController extends SliderPreferen private final ContentResolver mContentResolver; @VisibleForTesting final ContentObserver mContentObserver; - - @VisibleForTesting - SeekBarPreference mPreference; + private SeekBarPreference mPreference; public FloatingMenuTransparencyPreferenceController(Context context, String preferenceKey) { @@ -83,6 +81,7 @@ public class FloatingMenuTransparencyPreferenceController extends SliderPreferen mPreference.setMin(getMin()); mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS); + updateAvailabilityStatus(); updateState(mPreference); } diff --git a/src/com/android/settings/biometrics/BiometricEnrollActivity.java b/src/com/android/settings/biometrics/BiometricEnrollActivity.java index c74e85e54c2..dda3997c28a 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollActivity.java +++ b/src/com/android/settings/biometrics/BiometricEnrollActivity.java @@ -32,7 +32,6 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricManager.BiometricError; -import android.hardware.biometrics.SensorProperties; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintManager; @@ -211,12 +210,6 @@ public class BiometricEnrollActivity extends InstrumentedActivity { // required check if setup has completed instead. final boolean isSettingUp = isSetupWizard || (mParentalOptionsRequired && !WizardManagerHelper.isUserSetupComplete(this)); - if (isSettingUp && isMultiSensor && mIsFaceEnrollable) { - if (props.sensorStrength == SensorProperties.STRENGTH_CONVENIENCE) { - Log.i(TAG, "Excluding face from SuW enrollment (STRENGTH_CONVENIENCE)"); - mIsFaceEnrollable = false; - } - } } } if (mHasFeatureFingerprint) { diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java index 6e7d04f26b5..98cface2bc1 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollBase.java +++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java @@ -254,6 +254,7 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity { intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, mFromSettingsSummary); intent.putExtra(EXTRA_KEY_CHALLENGE, mChallenge); intent.putExtra(EXTRA_KEY_SENSOR_ID, mSensorId); + BiometricUtils.copyMultiBiometricExtras(getIntent(), intent); if (mUserId != UserHandle.USER_NULL) { intent.putExtra(Intent.EXTRA_USER_ID, mUserId); } diff --git a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java index e3607601840..61783c5164c 100644 --- a/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/BiometricEnrollIntroduction.java @@ -24,7 +24,6 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; -import android.util.Log; import android.view.View; import android.widget.TextView; @@ -303,9 +302,17 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + final boolean cameFromMultiBioFpAuthAddAnother = + requestCode == BiometricUtils.REQUEST_ADD_ANOTHER + && BiometricUtils.isMultiBiometricFingerprintEnrollmentFlow(this); if (requestCode == BIOMETRIC_FIND_SENSOR_REQUEST) { - if (isResultSkipOrFinished(resultCode)) { + if (isResultFinished(resultCode)) { handleBiometricResultSkipOrFinished(resultCode, data); + } else if (isResultSkipped(resultCode)) { + if (!BiometricUtils.tryStartingNextBiometricEnroll(this, + ENROLL_NEXT_BIOMETRIC_REQUEST, "BIOMETRIC_FIND_SENSOR_SKIPPED")) { + handleBiometricResultSkipOrFinished(resultCode, data); + } } else if (resultCode == RESULT_TIMEOUT) { setResult(resultCode, data); finish(); @@ -353,10 +360,22 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase } } else if (requestCode == LEARN_MORE_REQUEST) { overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out); - } else if (requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST) { - Log.d(TAG, "ENROLL_NEXT_BIOMETRIC_REQUEST, result: " + resultCode); - if (isResultSkipOrFinished(resultCode)) { + } else if (requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST + || cameFromMultiBioFpAuthAddAnother) { + if (isResultFinished(resultCode)) { handleBiometricResultSkipOrFinished(resultCode, data); + } else if (isResultSkipped(resultCode)) { + if (requestCode == BiometricUtils.REQUEST_ADD_ANOTHER) { + // If we came from an add another request, it still might + // be possible to add another biometric. Check if we can. + if (checkMaxEnrolled() != 0) { + // If we can't enroll any more biometrics, than skip + // this one. + handleBiometricResultSkipOrFinished(resultCode, data); + } + } else { + handleBiometricResultSkipOrFinished(resultCode, data); + } } else if (resultCode != RESULT_CANCELED) { setResult(resultCode, data); finish(); @@ -365,9 +384,17 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase super.onActivityResult(requestCode, resultCode, data); } + private static boolean isResultSkipped(int resultCode) { + return resultCode == RESULT_SKIP + || resultCode == SetupSkipDialog.RESULT_SKIP; + } + + private static boolean isResultFinished(int resultCode) { + return resultCode == RESULT_FINISHED; + } + private static boolean isResultSkipOrFinished(int resultCode) { - return resultCode == RESULT_SKIP || resultCode == SetupSkipDialog.RESULT_SKIP - || resultCode == RESULT_FINISHED; + return isResultSkipped(resultCode) || isResultFinished(resultCode); } private void handleBiometricResultSkipOrFinished(int resultCode, @Nullable Intent data) { @@ -375,6 +402,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase && data.getBooleanExtra( MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, false)) { getIntent().removeExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE); + getIntent().removeExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT); } if (resultCode == RESULT_SKIP) { diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java index febe3c6a93e..be233ed466b 100644 --- a/src/com/android/settings/biometrics/BiometricUtils.java +++ b/src/com/android/settings/biometrics/BiometricUtils.java @@ -51,6 +51,12 @@ import com.google.android.setupcompat.util.WizardManagerHelper; */ public class BiometricUtils { private static final String TAG = "BiometricUtils"; + + /** + * Request was sent for starting another enrollment of a previously + * enrolled biometric of the same type. + */ + public static int REQUEST_ADD_ANOTHER = 7; /** * Given the result from confirming or choosing a credential, request Gatekeeper to generate * a HardwareAuthToken with the Gatekeeper Password together with a biometric challenge. @@ -223,38 +229,66 @@ public class BiometricUtils { } /** + * Used for checking if a multi-biometric enrollment flow starts with Face and + * ends with Fingerprint. + * * @param activity Activity that we want to check - * @return True if the activity is going through a multi-biometric enrollment flow. + * @return True if the activity is going through a multi-biometric enrollment flow, that starts + * with Face. */ - public static boolean isMultiBiometricEnrollmentFlow(@NonNull Activity activity) { + public static boolean isMultiBiometricFaceEnrollmentFlow(@NonNull Activity activity) { return activity.getIntent().hasExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE); } + + /** + * Used for checking if a multi-biometric enrollment flowstarts with Fingerprint + * and ends with Face. + * + * @param activity Activity that we want to check + * @return True if the activity is going through a multi-biometric enrollment flow, that starts + * with Fingerprint. + */ + public static boolean isMultiBiometricFingerprintEnrollmentFlow(@NonNull Activity activity) { + return activity.getIntent().hasExtra( + MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT); + } + public static void copyMultiBiometricExtras(@NonNull Intent fromIntent, @NonNull Intent toIntent) { - final PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra( + PendingIntent pendingIntent = (PendingIntent) fromIntent.getExtra( MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, null); if (pendingIntent != null) { - toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, pendingIntent); + toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE, + pendingIntent); + } + + pendingIntent = (PendingIntent) fromIntent.getExtra( + MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT, null); + if (pendingIntent != null) { + toIntent.putExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT, + pendingIntent); } } /** - * If the current biometric enrollment (e.g. face) should be followed by another one (e.g. - * fingerprint) (see {@link #isMultiBiometricEnrollmentFlow(Activity)}), retrieves the - * PendingIntent pointing to the next enrollment and starts it. The caller will receive the - * result in onActivityResult. + * If the current biometric enrollment (e.g. face/fingerprint) should be followed by another + * one (e.g. fingerprint/face) retrieves the PendingIntent pointing to the next enrollment + * and starts it. The caller will receive the result in onActivityResult. * @return true if the next enrollment was started */ public static boolean tryStartingNextBiometricEnroll(@NonNull Activity activity, int requestCode, String debugReason) { - Log.d(TAG, "tryStartingNextBiometricEnroll, debugReason: " + debugReason); - final PendingIntent pendingIntent = (PendingIntent) activity.getIntent() + PendingIntent pendingIntent = (PendingIntent) activity.getIntent() .getExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FACE); + if (pendingIntent == null) { + pendingIntent = (PendingIntent) activity.getIntent() + .getExtra(MultiBiometricEnrollHelper.EXTRA_ENROLL_AFTER_FINGERPRINT); + } + if (pendingIntent != null) { try { - Log.d(TAG, "Starting pendingIntent: " + pendingIntent); IntentSender intentSender = pendingIntent.getIntentSender(); activity.startIntentSenderForResult(intentSender, requestCode, null /* fillInIntent */, 0 /* flagMask */, 0 /* flagValues */, diff --git a/src/com/android/settings/biometrics/MultiBiometricEnrollHelper.java b/src/com/android/settings/biometrics/MultiBiometricEnrollHelper.java index 5cc45b1b34e..94fbb76ac60 100644 --- a/src/com/android/settings/biometrics/MultiBiometricEnrollHelper.java +++ b/src/com/android/settings/biometrics/MultiBiometricEnrollHelper.java @@ -37,6 +37,7 @@ public class MultiBiometricEnrollHelper { private static final int REQUEST_FINGERPRINT_ENROLL = 3001; public static final String EXTRA_ENROLL_AFTER_FACE = "enroll_after_face"; + public static final String EXTRA_ENROLL_AFTER_FINGERPRINT = "enroll_after_finger"; public static final String EXTRA_SKIP_PENDING_ENROLL = "skip_pending_enroll"; @NonNull private final FragmentActivity mActivity; @@ -55,10 +56,10 @@ public class MultiBiometricEnrollHelper { } void startNextStep() { - if (mRequestEnrollFace) { - launchFaceEnroll(); - } else if (mRequestEnrollFingerprint) { + if (mRequestEnrollFingerprint) { launchFingerprintEnroll(); + } else if (mRequestEnrollFace) { + launchFaceEnroll(); } else { mActivity.setResult(BiometricEnrollIntroduction.RESULT_SKIP); mActivity.finish(); @@ -74,20 +75,6 @@ public class MultiBiometricEnrollHelper { mActivity.getIntent()); faceIntent.putExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId); faceIntent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge); - - if (mRequestEnrollFingerprint) { - // Give FaceEnroll a pendingIntent pointing to fingerprint enrollment, so that it - // can be started when user skips or finishes face enrollment. FLAG_UPDATE_CURRENT - // ensures it is launched with the most recent values. - final Intent fpIntent = BiometricUtils.getFingerprintIntroIntent(mActivity, - mActivity.getIntent()); - fpIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle); - final PendingIntent fpAfterFaceIntent = PendingIntent.getActivity(mActivity, - 0 /* requestCode */, fpIntent, - PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); - faceIntent.putExtra(EXTRA_ENROLL_AFTER_FACE, fpAfterFaceIntent); - } - BiometricUtils.launchEnrollForResult(mActivity, faceIntent, REQUEST_FACE_ENROLL, hardwareAuthToken, mGkPwHandle, mUserId); }); @@ -103,6 +90,19 @@ public class MultiBiometricEnrollHelper { mActivity.getIntent()); intent.putExtra(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId); intent.putExtra(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge); + if (mRequestEnrollFace) { + // Give FingerprintEnroll a pendingIntent pointing to face enrollment, so that it + // can be started when user skips or finishes fingerprint enrollment. + // FLAG_UPDATE_CURRENT ensures it is launched with the most recent values. + final Intent faceIntent = BiometricUtils.getFaceIntroIntent(mActivity, + mActivity.getIntent()); + faceIntent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, mGkPwHandle); + final PendingIntent faceAfterFp = PendingIntent.getActivity(mActivity, + 0 /* requestCode */, faceIntent, + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); + intent.putExtra(EXTRA_ENROLL_AFTER_FINGERPRINT, faceAfterFp); + } + BiometricUtils.launchEnrollForResult(mActivity, intent, REQUEST_FINGERPRINT_ENROLL, hardwareAuthToken, mGkPwHandle, mUserId); })); diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java index efb200fa8ac..f8f34f91092 100644 --- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java @@ -41,8 +41,10 @@ import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollActivity; import com.android.settings.biometrics.BiometricEnrollIntroduction; import com.android.settings.biometrics.BiometricUtils; +import com.android.settings.biometrics.MultiBiometricEnrollHelper; import com.android.settings.overlay.FeatureFactory; import com.android.settings.password.ChooseLockSettingsHelper; +import com.android.settings.password.SetupSkipDialog; import com.android.settings.utils.SensorPrivacyManagerHelper; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -167,6 +169,19 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { Log.v(TAG, "cameraPrivacyEnabled : " + cameraPrivacyEnabled); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // If user has skipped or finished enrolling, don't restart enrollment. + final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST + || requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST; + final boolean isResultSkipOrFinished = resultCode == RESULT_SKIP + || resultCode == SetupSkipDialog.RESULT_SKIP || resultCode == RESULT_FINISHED; + if (isEnrollRequest && isResultSkipOrFinished) { + data = setSkipPendingEnroll(data); + } + super.onActivityResult(requestCode, resultCode, data); + } + protected boolean generateChallengeOnCreate() { return true; } @@ -387,4 +402,13 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction { protected int getMoreButtonTextRes() { return R.string.security_settings_face_enroll_introduction_more; } + + @NonNull + protected static Intent setSkipPendingEnroll(@Nullable Intent data) { + if (data == null) { + data = new Intent(); + } + data.putExtra(MultiBiometricEnrollHelper.EXTRA_SKIP_PENDING_ENROLL, true); + return data; + } } diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java index 74e844abfda..784c5438a38 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFinish.java @@ -31,6 +31,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.biometrics.BiometricEnrollBase; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.password.ChooseLockSettingsHelper; import com.google.android.setupcompat.template.FooterBarMixin; @@ -47,8 +48,6 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { private static final String ACTION_FINGERPRINT_SETTINGS = "android.settings.FINGERPRINT_SETTINGS"; @VisibleForTesting - static final int REQUEST_ADD_ANOTHER = 1; - @VisibleForTesting static final String FINGERPRINT_SUGGESTION_ACTIVITY = "com.android.settings.SetupFingerprintSuggestionActivity"; @@ -160,13 +159,13 @@ public class FingerprintEnrollFinish extends BiometricEnrollBase { } private void onAddAnotherButtonClick(View view) { - startActivityForResult(getFingerprintEnrollingIntent(), REQUEST_ADD_ANOTHER); + startActivityForResult(getFingerprintEnrollingIntent(), BiometricUtils.REQUEST_ADD_ANOTHER); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { updateFingerprintSuggestionEnableState(); - if (requestCode == REQUEST_ADD_ANOTHER && resultCode != RESULT_CANCELED) { + if (requestCode == BiometricUtils.REQUEST_ADD_ANOTHER && resultCode != RESULT_CANCELED) { setResult(resultCode, data); finish(); } else { diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java index 6fe14e6a9cd..b9e9dcc5149 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java @@ -42,7 +42,6 @@ import com.android.settings.biometrics.BiometricEnrollIntroduction; import com.android.settings.biometrics.BiometricUtils; import com.android.settings.biometrics.MultiBiometricEnrollHelper; import com.android.settings.password.ChooseLockSettingsHelper; -import com.android.settings.password.SetupSkipDialog; import com.android.settingslib.HelpUtils; import com.android.settingslib.RestrictedLockUtilsInternal; @@ -106,28 +105,31 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // If user has skipped or finished enrolling, don't restart enrollment. - final boolean isEnrollRequest = requestCode == BIOMETRIC_FIND_SENSOR_REQUEST - || requestCode == ENROLL_NEXT_BIOMETRIC_REQUEST; - final boolean isResultSkipOrFinished = resultCode == RESULT_SKIP - || resultCode == SetupSkipDialog.RESULT_SKIP || resultCode == RESULT_FINISHED; - if (isEnrollRequest && isResultSkipOrFinished) { - data = setSkipPendingEnroll(data); - } super.onActivityResult(requestCode, resultCode, data); } @Override protected void onCancelButtonClick(View view) { - // User has explicitly canceled enroll. Don't restart it automatically. - Intent data = setSkipPendingEnroll(new Intent()); - setResult(RESULT_SKIP, data); - finish(); + if (!BiometricUtils.tryStartingNextBiometricEnroll( + this, ENROLL_NEXT_BIOMETRIC_REQUEST, "cancel")) { + super.onCancelButtonClick(view); + } } @Override protected void onSkipButtonClick(View view) { - onCancelButtonClick(view); + if (!BiometricUtils.tryStartingNextBiometricEnroll( + this, ENROLL_NEXT_BIOMETRIC_REQUEST, "skipped")) { + super.onSkipButtonClick(view); + } + } + + @Override + protected void onFinishedEnrolling(@Nullable Intent data) { + if (!BiometricUtils.tryStartingNextBiometricEnroll( + this, ENROLL_NEXT_BIOMETRIC_REQUEST, "finished")) { + super.onFinishedEnrolling(data); + } } @StringRes @@ -269,6 +271,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction { @Override protected Intent getEnrollingIntent() { final Intent intent = new Intent(this, FingerprintEnrollFindSensor.class); + BiometricUtils.copyMultiBiometricExtras(getIntent(), intent); if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, BiometricUtils.getGatekeeperPasswordHandle(getIntent())); diff --git a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java index 5f2fbb553db..7256511d2e6 100644 --- a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java +++ b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollFindSensor.java @@ -31,6 +31,7 @@ import androidx.fragment.app.FragmentManager; import com.android.settings.R; import com.android.settings.SetupWizardUtils; +import com.android.settings.biometrics.BiometricUtils; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.password.ChooseLockSettingsHelper; @@ -45,6 +46,7 @@ public class SetupFingerprintEnrollFindSensor extends FingerprintEnrollFindSenso if (mUserId != UserHandle.USER_NULL) { intent.putExtra(Intent.EXTRA_USER_ID, mUserId); } + BiometricUtils.copyMultiBiometricExtras(getIntent(), intent); SetupWizardUtils.copySetupExtras(getIntent(), intent); return intent; } diff --git a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java index 7d8d12e8cb5..e88e50d9425 100644 --- a/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java +++ b/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroduction.java @@ -62,8 +62,10 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu @Override protected Intent getEnrollingIntent() { final Intent intent = new Intent(this, SetupFingerprintEnrollFindSensor.class); + BiometricUtils.copyMultiBiometricExtras(getIntent(), intent); if (BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) { - intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, + intent.putExtra( + ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, BiometricUtils.getGatekeeperPasswordHandle(getIntent())); } SetupWizardUtils.copySetupExtras(getIntent(), intent); @@ -118,18 +120,22 @@ public class SetupFingerprintEnrollIntroduction extends FingerprintEnrollIntrodu if (isKeyguardSecure()) { // If the keyguard is already set up securely (maybe the user added a backup screen // lock and skipped fingerprint), return RESULT_SKIP directly. - resultCode = RESULT_SKIP; - data = mAlreadyHadLockScreenSetup ? null : getMetricIntent(null); + if (!BiometricUtils.tryStartingNextBiometricEnroll( + this, ENROLL_NEXT_BIOMETRIC_REQUEST, "cancel")) { + resultCode = RESULT_SKIP; + data = mAlreadyHadLockScreenSetup ? null : getMetricIntent(null); + setResult(resultCode, data); + finish(); + return; + } } else { resultCode = SetupSkipDialog.RESULT_SKIP; - data = null; + data = setSkipPendingEnroll(null); + setResult(resultCode, data); + finish(); } // User has explicitly canceled enroll. Don't restart it automatically. - data = setSkipPendingEnroll(data); - - setResult(resultCode, data); - finish(); } /** diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java index 6cc09e23369..5763d3b93ef 100644 --- a/src/com/android/settings/core/BasePreferenceController.java +++ b/src/com/android/settings/core/BasePreferenceController.java @@ -126,7 +126,6 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl @Nullable private UserHandle mWorkProfileUser; private int mMetricsCategory; - private boolean mIsFirstLaunch; private boolean mPrefVisibility; /** @@ -198,7 +197,6 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl public BasePreferenceController(Context context, String preferenceKey) { super(context); mPreferenceKey = preferenceKey; - mIsFirstLaunch = true; mPrefVisibility = true; if (TextUtils.isEmpty(mPreferenceKey)) { throw new IllegalArgumentException("Preference key must be set"); @@ -331,13 +329,6 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl } } - /** - * Set back the value of whether this is the first launch. - */ - public void revokeFirstLaunch() { - mIsFirstLaunch = false; - } - /** * Launches the specified fragment for the work profile user if the associated * {@link Preference} is clicked. Otherwise just forward it to the super class. @@ -454,7 +445,7 @@ public abstract class BasePreferenceController extends AbstractPreferenceControl * preference visibility. */ protected void updatePreferenceVisibilityDelegate(Preference preference, boolean isVisible) { - if (mUiBlockerFinished || !mIsFirstLaunch) { + if (mUiBlockerFinished) { preference.setVisible(isVisible); return; } diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java index 256a6201cb7..aaa9b3d3bca 100644 --- a/src/com/android/settings/dashboard/DashboardFragment.java +++ b/src/com/android/settings/dashboard/DashboardFragment.java @@ -132,17 +132,22 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment @VisibleForTesting void checkUiBlocker(List controllers) { final List keys = new ArrayList<>(); + final List baseControllers = new ArrayList<>(); controllers.forEach(controller -> { if (controller instanceof BasePreferenceController.UiBlocker && controller.isAvailable()) { ((BasePreferenceController) controller).setUiBlockListener(this); keys.add(controller.getPreferenceKey()); + baseControllers.add((BasePreferenceController) controller); } }); if (!keys.isEmpty()) { mBlockerController = new UiBlockerController(keys); - mBlockerController.start(() -> updatePreferenceVisibility(mPreferenceControllers)); + mBlockerController.start(() -> { + updatePreferenceVisibility(mPreferenceControllers); + baseControllers.forEach(controller -> controller.setUiBlockerFinished(true)); + }); } } @@ -250,11 +255,6 @@ public abstract class DashboardFragment extends SettingsPreferenceFragment } mListeningToCategoryChange = false; } - mControllers.forEach(controller -> { - if (controller instanceof BasePreferenceController.UiBlocker) { - ((BasePreferenceController) controller).revokeFirstLaunch(); - } - }); } @Override diff --git a/src/com/android/settings/display/AutoRotateSwitchBarController.java b/src/com/android/settings/display/AutoRotateSwitchBarController.java index 48dedfd1fa3..d76104aef7f 100644 --- a/src/com/android/settings/display/AutoRotateSwitchBarController.java +++ b/src/com/android/settings/display/AutoRotateSwitchBarController.java @@ -18,86 +18,78 @@ package com.android.settings.display; import android.app.settings.SettingsEnums; import android.content.Context; -import android.widget.Switch; import com.android.internal.view.RotationPolicy; +import com.android.settings.R; import com.android.settings.overlay.FeatureFactory; -import com.android.settings.widget.SettingsMainSwitchBar; +import com.android.settings.widget.SettingsMainSwitchPreferenceController; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; -import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.widget.OnMainSwitchChangeListener; /** - * The switch controller for auto-rotate. + * The main switch controller for auto-rotate. */ -public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener, +public class AutoRotateSwitchBarController extends SettingsMainSwitchPreferenceController implements LifecycleObserver, OnStart, OnStop { - private final SettingsMainSwitchBar mSwitchBar; - private final Context mContext; - private boolean mValidListener; private final MetricsFeatureProvider mMetricsFeatureProvider; + private RotationPolicy.RotationPolicyListener mRotationPolicyListener; - public AutoRotateSwitchBarController(Context context, SettingsMainSwitchBar switchBar, - Lifecycle lifecycle) { - mSwitchBar = switchBar; - mContext = context; + public AutoRotateSwitchBarController(Context context, String key) { + super(context, key); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); - if (lifecycle != null) { - lifecycle.addObserver(this); - } + } + + @Override + public int getAvailabilityStatus() { + return RotationPolicy.isRotationLockToggleVisible(mContext) + && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext) + ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } @Override public void onStart() { - if (!mValidListener) { - mSwitchBar.addOnSwitchChangeListener(this); - mValidListener = true; + if (mRotationPolicyListener == null) { + mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() { + @Override + public void onChange() { + if (mSwitchPreference != null) { + updateState(mSwitchPreference); + } + } + }; } - onChange(); + RotationPolicy.registerRotationPolicyListener(mContext, + mRotationPolicyListener); } @Override public void onStop() { - if (mValidListener) { - mSwitchBar.removeOnSwitchChangeListener(this); - mValidListener = false; + if (mRotationPolicyListener != null) { + RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener); } } - /** - * Listens to the state change of the rotation primary switch. - */ @Override - public void onSwitchChanged(Switch switchView, boolean isChecked) { - setRotationLock(isChecked); + public boolean isChecked() { + return !RotationPolicy.isRotationLocked(mContext); } - - protected void onChange() { - final boolean isEnabled = !RotationPolicy.isRotationLocked(mContext); - if (isEnabled != mSwitchBar.isChecked()) { - // set listener to null so that that code below doesn't trigger onCheckedChanged() - if (mValidListener) { - mSwitchBar.removeOnSwitchChangeListener(this); - } - mSwitchBar.setChecked(isEnabled); - if (mValidListener) { - mSwitchBar.addOnSwitchChangeListener(this); - } - } - } - - private boolean setRotationLock(boolean isChecked) { + @Override + public boolean setChecked(boolean isChecked) { final boolean isLocked = !isChecked; mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATE_ROTATE_MASTER_TOGGLE, - isChecked); + isLocked); RotationPolicy.setRotationLock(mContext, isLocked); return true; } + @Override + public int getSliceHighlightMenuRes() { + return R.string.menu_key_display; + } + } diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java index 9fda03c4d5f..1b4e998db87 100644 --- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java +++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java @@ -20,7 +20,7 @@ import static com.android.settings.display.SmartAutoRotateController.isRotationR import android.app.settings.SettingsEnums; import android.content.Context; import android.os.Bundle; -import android.text.Html; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,12 +28,11 @@ import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; -import com.android.internal.view.RotationPolicy; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.search.BaseSearchIndexProvider; -import com.android.settings.widget.SettingsMainSwitchBar; +import com.android.settingslib.HelpUtils; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.search.Indexable; import com.android.settingslib.search.SearchIndexable; @@ -51,9 +50,11 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment { private static final String TAG = "SmartAutoRotatePreferenceFragment"; - private RotationPolicy.RotationPolicyListener mRotationPolicyListener; - private AutoRotateSwitchBarController mSwitchBarController; - @VisibleForTesting static final String AUTO_ROTATE_SWITCH_PREFERENCE_ID = "auto_rotate_switch"; + @VisibleForTesting + static final String AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY = "auto_rotate_main_switch"; + @VisibleForTesting + static final String AUTO_ROTATE_SWITCH_PREFERENCE_KEY = "auto_rotate_switch"; + private static final String KEY_FOOTER_PREFERENCE = "auto_rotate_footer_preference"; @Override protected int getPreferenceScreenResId() { @@ -81,11 +82,10 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment { final View view = super.onCreateView(inflater, container, savedInstanceState); final SettingsActivity activity = (SettingsActivity) getActivity(); createHeader(activity); - final Preference footerPreference = findPreference(FooterPreference.KEY_FOOTER); + final Preference footerPreference = findPreference(KEY_FOOTER_PREFERENCE); if (footerPreference != null) { - footerPreference.setTitle(Html.fromHtml(getString(R.string.smart_rotate_text_headline), - Html.FROM_HTML_MODE_COMPACT)); footerPreference.setVisible(isRotationResolverServiceAvailable(activity)); + setupFooter(); } return view; } @@ -95,39 +95,9 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment { boolean deviceStateRotationEnabled = DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(activity); if (isRotationResolverServiceAvailable(activity) && !deviceStateRotationEnabled) { - final SettingsMainSwitchBar switchBar = activity.getSwitchBar(); - switchBar.setTitle( - getContext().getString(R.string.auto_rotate_settings_primary_switch_title)); - switchBar.show(); - mSwitchBarController = new AutoRotateSwitchBarController(activity, switchBar, - getSettingsLifecycle()); - findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID).setVisible(false); - } - } - - @Override - public void onResume() { - super.onResume(); - if (mRotationPolicyListener == null) { - mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() { - @Override - public void onChange() { - if (mSwitchBarController != null) { - mSwitchBarController.onChange(); - } - } - }; - } - RotationPolicy.registerRotationPolicyListener(getPrefContext(), - mRotationPolicyListener); - } - - @Override - public void onPause() { - super.onPause(); - if (mRotationPolicyListener != null) { - RotationPolicy.unregisterRotationPolicyListener(getPrefContext(), - mRotationPolicyListener); + findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY).setVisible(false); + } else { + findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY).setVisible(false); } } @@ -141,6 +111,34 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment { return TAG; } + @Override + public int getHelpResource() { + return R.string.help_url_auto_rotate_settings; + } + + // Updates the footer for this page. + @VisibleForTesting + void setupFooter() { + final String mHelpUri = getString(getHelpResource()); + if (!TextUtils.isEmpty(mHelpUri)) { + addHelpLink(); + } + } + + // Changes the text to include a learn more link if the link is defined. + @VisibleForTesting + void addHelpLink() { + final FooterPreference pref = findPreference(KEY_FOOTER_PREFERENCE); + if (pref != null) { + pref.setLearnMoreAction(v -> { + startActivityForResult(HelpUtils.getHelpIntent(getContext(), + getString(getHelpResource()), + /*backupContext=*/ ""), /*requestCode=*/ 0); + }); + pref.setLearnMoreContentDescription(getString(R.string.auto_rotate_link_a11y)); + } + } + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider(R.xml.auto_rotate_settings) { diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java index 743c7f9815a..f59dc60d6e0 100644 --- a/src/com/android/settings/wifi/slice/WifiSlice.java +++ b/src/com/android/settings/wifi/slice/WifiSlice.java @@ -26,13 +26,16 @@ import android.app.PendingIntent; import android.app.settings.SettingsEnums; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.wifi.WifiManager; +import android.os.Binder; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; @@ -49,8 +52,8 @@ import com.android.settings.network.WifiSwitchPreferenceController; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.slices.SliceBuilderUtils; +import com.android.settings.wifi.AppStateChangeWifiStateBridge; import com.android.settings.wifi.WifiDialogActivity; -import com.android.settings.wifi.WifiSettings; import com.android.settings.wifi.WifiUtils; import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.wifitrackerlib.WifiEntry; @@ -67,6 +70,7 @@ public class WifiSlice implements CustomSliceable { @VisibleForTesting static final int DEFAULT_EXPANDED_ROW_COUNT = 3; + private static final String TAG = "WifiSlice"; protected final Context mContext; protected final WifiManager mWifiManager; @@ -83,6 +87,12 @@ public class WifiSlice implements CustomSliceable { @Override public Slice getSlice() { + // If external calling package doesn't have Wi-Fi permission. + if (!Utils.isSettingsIntelligence(mContext) && !isPermissionGranted(mContext)) { + Log.i(TAG, "No wifi permissions to control wifi slice."); + return null; + } + final boolean isWifiEnabled = isWifiEnabled(); ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* wifiSliceItem */); if (!isWifiEnabled) { @@ -120,6 +130,30 @@ public class WifiSlice implements CustomSliceable { return listBuilder.build(); } + private static boolean isPermissionGranted(Context settingsContext) { + final int callingUid = Binder.getCallingUid(); + final String callingPackage = settingsContext.getPackageManager() + .getPackagesForUid(callingUid)[0]; + + Context packageContext; + try { + packageContext = settingsContext.createPackageContext(callingPackage, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Cannot create Context for package: " + callingPackage); + return false; + } + + // If app doesn't have related Wi-Fi permission, they shouldn't show Wi-Fi slice. + final boolean hasPermission = packageContext.checkPermission( + android.Manifest.permission.CHANGE_WIFI_STATE, Binder.getCallingPid(), + callingUid) == PackageManager.PERMISSION_GRANTED; + AppStateChangeWifiStateBridge.WifiSettingsState state = + new AppStateChangeWifiStateBridge(settingsContext, null, null) + .getWifiSettingsInfo(callingPackage, callingUid); + + return hasPermission && state.isPermissible(); + } + protected boolean isApRowCollapsed() { return false; } @@ -175,7 +209,7 @@ public class WifiSlice implements CustomSliceable { tint = Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorControlNormal); } else { tint = Utils.getDisabled(mContext, Utils.getColorAttrDefaultColor(mContext, - android.R.attr.colorControlNormal)); + android.R.attr.colorControlNormal)); } final Drawable drawable = mContext.getDrawable( @@ -275,7 +309,7 @@ public class WifiSlice implements CustomSliceable { final String key = WifiSwitchPreferenceController.KEY; final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext, className, - key, screenTitle, SettingsEnums.DIALOG_WIFI_AP_EDIT, this) + key, screenTitle, SettingsEnums.DIALOG_WIFI_AP_EDIT, this) .setClassName(mContext.getPackageName(), SubSettings.class.getName()) .setData(contentUri); diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java index 55637cd137d..eceb185151c 100644 --- a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuTransparencyPreferenceControllerTest.java @@ -27,6 +27,7 @@ import static com.android.settings.core.BasePreferenceController.DISABLED_DEPEND import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -34,6 +35,7 @@ import android.content.ContentResolver; import android.content.Context; import android.provider.Settings; +import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.widget.SeekBarPreference; @@ -60,11 +62,18 @@ public class FloatingMenuTransparencyPreferenceControllerTest { @Mock private ContentResolver mContentResolver; private FloatingMenuTransparencyPreferenceController mController; + private SeekBarPreference mSeekBarPreference; + + @Mock + private PreferenceScreen mScreen; @Before public void setUp() { when(mContext.getContentResolver()).thenReturn(mContentResolver); mController = new FloatingMenuTransparencyPreferenceController(mContext, "test_key"); + + mSeekBarPreference = new SeekBarPreference(mContext); + doReturn(mSeekBarPreference).when(mScreen).findPreference("test_key"); } @Test @@ -84,14 +93,67 @@ public class FloatingMenuTransparencyPreferenceControllerTest { } @Test - public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() { - mController.mPreference = new SeekBarPreference(mContext); + public void displayPreference_floatingMenuMode_fadeEnabled_preferenceEnabled() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, /* ON */ 1); + + mController.displayPreference(mScreen); + + assertThat(mSeekBarPreference.isEnabled()).isTrue(); + } + + @Test + public void displayPreference_floatingMenuMode_fadeDisabled_preferenceDisabled() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, /* OFF */ 0); + + mController.displayPreference(mScreen); + + assertThat(mSeekBarPreference.isEnabled()).isFalse(); + } + + @Test + public void displayPreference_navigationBarMode_preferenceDisabled() { Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + mController.displayPreference(mScreen); + + assertThat(mSeekBarPreference.isEnabled()).isFalse(); + } + + @Test + public void onChange_floatingMenuModeChangeToNavigationBar_preferenceDisabled() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, /* ON */ 1); + mController.displayPreference(mScreen); + + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); mController.mContentObserver.onChange(false); - assertThat(mController.mPreference.isEnabled()).isFalse(); + assertThat(mSeekBarPreference.isEnabled()).isFalse(); + } + + @Test + public void onChange_navigationBarModeChangeToFloatingMenu_preferenceDisabled() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR); + Settings.Secure.putInt(mContentResolver, + Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, /* ON */ 1); + mController.displayPreference(mScreen); + + Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE, + ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU); + mController.mContentObserver.onChange(false); + + assertThat(mSeekBarPreference.isEnabled()).isTrue(); } @Test diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java index 942fed6f619..e5374196681 100644 --- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java @@ -16,7 +16,8 @@ package com.android.settings.display; -import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_ID; +import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY; +import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_KEY; import static com.google.common.truth.Truth.assertThat; @@ -45,7 +46,6 @@ import com.android.settings.SettingsActivity; import com.android.settings.testutils.ResolveInfoBuilder; import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager; import com.android.settings.testutils.shadow.ShadowRotationPolicy; -import com.android.settings.widget.SettingsMainSwitchBar; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; @@ -71,8 +71,6 @@ public class SmartAutoRotatePreferenceFragmentTest { private SmartAutoRotatePreferenceFragment mFragment; - private SettingsMainSwitchBar mSwitchBar; - @Mock private PackageManager mPackageManager; @@ -87,6 +85,9 @@ public class SmartAutoRotatePreferenceFragmentTest { private Resources mResources; private Context mContext; + @Mock + private Preference mRotateMainSwitchPreference; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -112,21 +113,21 @@ public class SmartAutoRotatePreferenceFragmentTest { when(mFragment.getContext()).thenReturn(mContext); doReturn(mView).when(mFragment).getView(); - when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID)).thenReturn( + when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY)).thenReturn( mRotateSwitchPreference); - mSwitchBar = spy(new SettingsMainSwitchBar(mContext)); - when(mActivity.getSwitchBar()).thenReturn(mSwitchBar); - doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar); - ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); + + when(mFragment.findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY)).thenReturn( + mRotateMainSwitchPreference); } @Test public void createHeader_faceDetectionSupported_switchBarIsEnabled() { + ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false); mFragment.createHeader(mActivity); - verify(mSwitchBar, times(1)).show(); + verify(mRotateMainSwitchPreference, never()).setVisible(false); verify(mRotateSwitchPreference, times(1)).setVisible(false); } @@ -137,7 +138,7 @@ public class SmartAutoRotatePreferenceFragmentTest { mFragment.createHeader(mActivity); - verify(mSwitchBar, never()).show(); + verify(mRotateMainSwitchPreference, times(1)).setVisible(false); verify(mRotateSwitchPreference, never()).setVisible(false); } @@ -147,7 +148,7 @@ public class SmartAutoRotatePreferenceFragmentTest { mFragment.createHeader(mActivity); - verify(mSwitchBar, never()).show(); + verify(mRotateMainSwitchPreference, times(1)).setVisible(false); verify(mRotateSwitchPreference, never()).setVisible(false); } @@ -176,6 +177,19 @@ public class SmartAutoRotatePreferenceFragmentTest { DeviceStateAutoRotateSettingController.class); } + @Test + public void setupFooter_linkAddedWhenAppropriate() { + doReturn("").when(mFragment).getText(anyInt()); + doReturn("").when(mFragment).getString(anyInt()); + mFragment.setupFooter(); + verify(mFragment, never()).addHelpLink(); + + doReturn("testString").when(mFragment).getText(anyInt()); + doReturn("testString").when(mFragment).getString(anyInt()); + mFragment.setupFooter(); + verify(mFragment, times(1)).addHelpLink(); + } + private void enableDeviceStateSettableRotationStates(String[] settableStates, String[] settableStatesDescriptions) { when(mResources.getStringArray( diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiSlice.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiSlice.java new file mode 100644 index 00000000000..ed583a4f0fe --- /dev/null +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowWifiSlice.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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.testutils.shadow; + +import android.content.Context; + +import com.android.settings.wifi.slice.WifiSlice; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@Implements(WifiSlice.class) +public class ShadowWifiSlice { + + private static boolean sIsWifiPermissible; + + @Implementation + protected static boolean isPermissionGranted(Context settingsContext) { + return sIsWifiPermissible; + } + + public static void setWifiPermissible(boolean isWifiPermissible) { + sIsWifiPermissible = isWifiPermissible; + } +} diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java index f31c216d260..52dcb5282da 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java @@ -24,9 +24,11 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.wifi.WifiInfo; @@ -44,6 +46,7 @@ import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.SlicesFeatureProviderImpl; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowConnectivityManager; +import com.android.settings.testutils.shadow.ShadowWifiSlice; import org.junit.Before; import org.junit.Test; @@ -53,17 +56,20 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowBinder; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = ShadowConnectivityManager.class) +@Config(shadows = {ShadowConnectivityManager.class, ShadowWifiSlice.class}) public class ContextualWifiSliceTest { private static final String SSID = "123"; @Mock private WifiManager mWifiManager; @Mock + private PackageManager mPackageManager; + @Mock private WifiInfo mWifiInfo; @Mock private Network mNetwork; @@ -88,10 +94,16 @@ public class ContextualWifiSliceTest { doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo(); doReturn(SSID).when(mWifiInfo).getSSID(); doReturn(mNetwork).when(mWifiManager).getCurrentNetwork(); + when(mContext.getPackageManager()).thenReturn(mPackageManager); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + final String siPackageName = + mContext.getString(R.string.config_settingsintelligence_package_name); + ShadowBinder.setCallingUid(1); + when(mPackageManager.getPackagesForUid(1)).thenReturn(new String[]{siPackageName}); + ShadowWifiSlice.setWifiPermissible(true); mWifiSlice = new ContextualWifiSlice(mContext); } diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java index b4a8b05f648..5b7a7d6c33f 100644 --- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java +++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java @@ -31,21 +31,20 @@ import static org.mockito.Mockito.when; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.net.wifi.WifiManager; -import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceItem; -import androidx.slice.SliceMetadata; import androidx.slice.SliceProvider; -import androidx.slice.core.SliceAction; import androidx.slice.core.SliceQuery; import androidx.slice.widget.SliceLiveData; import com.android.settings.R; import com.android.settings.slices.SliceBackgroundWorker; import com.android.settings.testutils.SliceTester; +import com.android.settings.testutils.shadow.ShadowWifiSlice; import com.android.wifitrackerlib.WifiEntry; import com.android.wifitrackerlib.WifiEntry.ConnectedState; @@ -59,24 +58,32 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowBinder; import java.util.ArrayList; import java.util.List; @RunWith(RobolectricTestRunner.class) -@Config(shadows = WifiSliceTest.ShadowSliceBackgroundWorker.class) +@Config(shadows = { + WifiSliceTest.ShadowSliceBackgroundWorker.class, + ShadowWifiSlice.class}) public class WifiSliceTest { private static final String AP1_NAME = "ap1"; private static final String AP2_NAME = "ap2"; private static final String AP3_NAME = "ap3"; + private static final int USER_ID = 1; @Mock private WifiManager mWifiManager; + @Mock + private PackageManager mPackageManager; + private Context mContext; private ContentResolver mResolver; private WifiSlice mWifiSlice; + private String mSIPackageName; @Before public void setUp() { @@ -86,27 +93,46 @@ public class WifiSliceTest { doReturn(mResolver).when(mContext).getContentResolver(); doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class); doReturn(WifiManager.WIFI_STATE_ENABLED).when(mWifiManager).getWifiState(); + when(mContext.getPackageManager()).thenReturn(mPackageManager); // Set-up specs for SliceMetadata. SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + mSIPackageName = mContext.getString(R.string.config_settingsintelligence_package_name); + ShadowBinder.setCallingUid(USER_ID); + when(mPackageManager.getPackagesForUid(USER_ID)).thenReturn(new String[]{mSIPackageName}); + ShadowWifiSlice.setWifiPermissible(true); mWifiSlice = new WifiSlice(mContext); } @Test - public void getWifiSlice_shouldHaveTitleAndToggle() { + public void getWifiSlice_fromSIPackage_shouldHaveTitleAndToggle() { + when(mPackageManager.getPackagesForUid(USER_ID)).thenReturn(new String[]{mSIPackageName}); + ShadowWifiSlice.setWifiPermissible(false); + final Slice wifiSlice = mWifiSlice.getSlice(); - final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice); - assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings)); + assertThat(wifiSlice).isNotNull(); + } - final List toggles = metadata.getToggles(); - assertThat(toggles).hasSize(1); + @Test + public void getWifiSlice_notFromSIPackageAndWithWifiPermission_shouldHaveTitleAndToggle() { + when(mPackageManager.getPackagesForUid(USER_ID)).thenReturn(new String[]{"com.test"}); + ShadowWifiSlice.setWifiPermissible(true); - final SliceAction primaryAction = metadata.getPrimaryAction(); - final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext, - R.drawable.ic_settings_wireless); - assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString()); + final Slice wifiSlice = mWifiSlice.getSlice(); + + assertThat(wifiSlice).isNotNull(); + } + + @Test + public void getWifiSlice_notFromSIPackageAndWithoutWifiPermission_shouldNoSlice() { + when(mPackageManager.getPackagesForUid(USER_ID)).thenReturn(new String[]{"com.test"}); + ShadowWifiSlice.setWifiPermissible(false); + + final Slice wifiSlice = mWifiSlice.getSlice(); + + assertThat(wifiSlice).isNull(); } @Test