From 868e770b824b5d776d64fd9364f24a6aa1ed5ab5 Mon Sep 17 00:00:00 2001 From: Jamie Garside Date: Tue, 9 Feb 2021 10:25:37 +0000 Subject: [PATCH] Update the bouncer to be able to move to either side of a wide screen. I'd like to test other things (like tap makes the IME move), but to do that means replacing the mock with a proper fake. That can come later, but this at least tests that the flagging works correctly. The changes between this and commit cae34b8e22f8a7baa845bfd31f472ed728c67a89 is the bouncer height is now measured using wrap_content, so the placement can be controlled properly by the outer View (as the position of the bouncer changes depending on the size of the screen). This reverts commit cae34b8e22f8a7baa845bfd31f472ed728c67a89. Test: Included Bug: 170858298 Change-Id: I18428a6df9abf0e8e37c1c179be6843e4eee793c --- core/java/android/provider/Settings.java | 23 ++ core/res/res/values/symbols.xml | 2 + .../validators/GlobalSettingsValidators.java | 5 + .../android/provider/SettingsBackupTest.java | 1 + .../res-keyguard/layout/keyguard_bouncer.xml | 2 +- .../layout/keyguard_host_view.xml | 5 +- .../values-sw600dp-land/bools.xml | 20 ++ .../SystemUI/res-keyguard/values/config.xml | 1 + .../keyguard/KeyguardSecurityContainer.java | 223 +++++++++++++++++- .../KeyguardSecurityContainerController.java | 1 + .../keyguard/KeyguardSecurityModel.java | 9 + .../KeyguardSecurityContainerTest.java | 96 +++++++- 12 files changed, 374 insertions(+), 14 deletions(-) create mode 100644 packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index e979e13d89c5e..6ee7eb25eb2f5 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14614,6 +14614,29 @@ public final class Settings { public static final String POWER_BUTTON_VERY_LONG_PRESS = "power_button_very_long_press"; + + /** + * Keyguard should be on the left hand side of the screen, for wide screen layouts. + * + * @hide + */ + public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0; + + /** + * Keyguard should be on the right hand side of the screen, for wide screen layouts. + * + * @hide + */ + public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1; + /** + * In one handed mode, which side the keyguard should be on. Allowable values are one of + * the ONE_HANDED_KEYGUARD_SIDE_* constants. + * + * @hide + */ + @Readable + public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side"; + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b5af5240b8438..e73d6e3af3cbe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4203,4 +4203,6 @@ + + diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index 66165b6d1ff2e..ad6a5312f1562 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -147,5 +147,10 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put( + Global.ONE_HANDED_KEYGUARD_SIDE, + new InclusiveIntegerRangeValidator( + /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT, + /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT)); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 6719f179ef86c..d715832dfca0b 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -283,6 +283,7 @@ public class SettingsBackupTest { Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS, Settings.Global.FANCY_IME_ANIMATIONS, + Settings.Global.ONE_HANDED_KEYGUARD_SIDE, Settings.Global.FORCE_ALLOW_ON_EXTERNAL, Settings.Global.FORCED_APP_STANDBY_ENABLED, Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml index 79868093fb127..71cdaf5c7091b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml @@ -24,7 +24,7 @@ diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml index 04e645bd0a321..c75ee51517d1d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml @@ -41,13 +41,14 @@ android:layout_gravity="center"> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml new file mode 100644 index 0000000000000..e09bf7e37ed0c --- /dev/null +++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml @@ -0,0 +1,20 @@ + + + + + true + diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml index 8d9d6ee68c67d..6176f7c1dd0ac 100644 --- a/packages/SystemUI/res-keyguard/values/config.xml +++ b/packages/SystemUI/res-keyguard/values/config.xml @@ -22,4 +22,5 @@ false + false diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 5f6fd30ffa1b3..f511ed1c69c49 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -29,12 +29,17 @@ import android.app.AlertDialog; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; +import android.provider.Settings; import android.util.AttributeSet; import android.util.MathUtils; import android.util.TypedValue; +import android.view.Gravity; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.VelocityTracker; +import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewPropertyAnimator; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimationControlListener; @@ -55,6 +60,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import java.util.List; @@ -99,6 +105,12 @@ public class KeyguardSecurityContainer extends FrameLayout { private boolean mDisappearAnimRunning; private SwipeListener mSwipeListener; + private boolean mIsSecurityViewLeftAligned = true; + private boolean mOneHandedMode = false; + private SecurityMode mSecurityMode = SecurityMode.Invalid; + private ViewPropertyAnimator mRunningOneHandedAnimator; + private final OrientationEventListener mOrientationEventListener; + private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -157,16 +169,20 @@ public class KeyguardSecurityContainer extends FrameLayout { // Used to notify the container when something interesting happens. public interface SecurityCallback { boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen); + void userActivity(); + void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); /** - * @param strongAuth wheher the user has authenticated with strong authentication like - * pattern, password or PIN but not by trust agents or fingerprint + * @param strongAuth wheher the user has authenticated with strong authentication like + * pattern, password or PIN but not by trust agents or fingerprint * @param targetUserId a user that needs to be the foreground user at the finish completion. */ void finish(boolean strongAuth, int targetUserId); + void reset(); + void onCancelClicked(); } @@ -224,12 +240,136 @@ public class KeyguardSecurityContainer extends FrameLayout { super(context, attrs, defStyle); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); mViewConfiguration = ViewConfiguration.get(context); + + mOrientationEventListener = new OrientationEventListener(context) { + @Override + public void onOrientationChanged(int orientation) { + updateLayoutForSecurityMode(mSecurityMode); + } + }; } void onResume(SecurityMode securityMode, boolean faceAuthEnabled) { + mSecurityMode = securityMode; mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); updateBiometricRetry(securityMode, faceAuthEnabled); + updateLayoutForSecurityMode(securityMode); + mOrientationEventListener.enable(); + } + + void updateLayoutForSecurityMode(SecurityMode securityMode) { + mSecurityMode = securityMode; + mOneHandedMode = canUseOneHandedBouncer(); + + if (mOneHandedMode) { + mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext); + } + + updateSecurityViewGravity(); + updateSecurityViewLocation(false); + } + + /** Return whether the one-handed keyguard should be enabled. */ + private boolean canUseOneHandedBouncer() { + // Is it enabled? + if (!getResources().getBoolean( + com.android.internal.R.bool.config_enableOneHandedKeyguard)) { + return false; + } + + if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) { + return false; + } + + return getResources().getBoolean(R.bool.can_use_one_handed_bouncer); + } + + /** Read whether the one-handed keyguard should be on the left/right from settings. */ + private boolean isOneHandedKeyguardLeftAligned(Context context) { + try { + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.ONE_HANDED_KEYGUARD_SIDE) + == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT; + } catch (Settings.SettingNotFoundException ex) { + return true; + } + } + + private void updateSecurityViewGravity() { + View securityView = findKeyguardSecurityView(); + + if (securityView == null) { + return; + } + + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams(); + + if (mOneHandedMode) { + lp.gravity = Gravity.LEFT | Gravity.BOTTOM; + } else { + lp.gravity = Gravity.CENTER_HORIZONTAL; + } + + securityView.setLayoutParams(lp); + } + + /** + * Moves the inner security view to the correct location (in one handed mode) with animation. + * This is triggered when the user taps on the side of the screen that is not currently occupied + * by the security view . + */ + private void updateSecurityViewLocation(boolean animate) { + View securityView = findKeyguardSecurityView(); + + if (securityView == null) { + return; + } + + if (!mOneHandedMode) { + securityView.setTranslationX(0); + return; + } + + if (mRunningOneHandedAnimator != null) { + mRunningOneHandedAnimator.cancel(); + mRunningOneHandedAnimator = null; + } + + int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f); + + if (animate) { + mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation); + mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRunningOneHandedAnimator = null; + } + }); + + mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + mRunningOneHandedAnimator.start(); + } else { + securityView.setTranslationX(targetTranslation); + } + } + + @Nullable + private KeyguardSecurityViewFlipper findKeyguardSecurityView() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + + if (isKeyguardSecurityView(child)) { + return (KeyguardSecurityViewFlipper) child; + } + } + + return null; + } + + private boolean isKeyguardSecurityView(View view) { + return view instanceof KeyguardSecurityViewFlipper; } public void onPause() { @@ -238,6 +378,7 @@ public class KeyguardSecurityContainer extends FrameLayout { mAlertDialog = null; } mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); + mOrientationEventListener.disable(); } @Override @@ -319,19 +460,44 @@ public class KeyguardSecurityContainer extends FrameLayout { if (mSwipeListener != null) { mSwipeListener.onSwipeUp(); } + } else { + if (!mIsDragging) { + handleTap(event); + } } } return true; } + private void handleTap(MotionEvent event) { + // If we're using a fullscreen security mode, skip + if (!mOneHandedMode) { + return; + } + + // Did the tap hit the "other" side of the bouncer? + if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f)) + || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) { + mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned; + + Settings.Global.putInt( + mContext.getContentResolver(), + Settings.Global.ONE_HANDED_KEYGUARD_SIDE, + mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT + : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT); + + updateSecurityViewLocation(true); + } + } + void setSwipeListener(SwipeListener swipeListener) { mSwipeListener = swipeListener; } private void startSpringAnimation(float startVelocity) { mSpringAnimation - .setStartVelocity(startVelocity) - .animateToFinalPosition(0); + .setStartVelocity(startVelocity) + .animateToFinalPosition(0); } public void startDisappearAnimation(SecurityMode securitySelection) { @@ -441,18 +607,17 @@ public class KeyguardSecurityContainer extends FrameLayout { return insets.inset(0, 0, 0, inset); } - private void showDialog(String title, String message) { if (mAlertDialog != null) { mAlertDialog.dismiss(); } mAlertDialog = new AlertDialog.Builder(mContext) - .setTitle(title) - .setMessage(message) - .setCancelable(false) - .setNeutralButton(R.string.ok, null) - .create(); + .setTitle(title) + .setMessage(message) + .setCancelable(false) + .setNeutralButton(R.string.ok, null) + .create(); if (!(mContext instanceof Activity)) { mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); } @@ -490,6 +655,44 @@ public class KeyguardSecurityContainer extends FrameLayout { } } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(widthMeasureSpec) / 2, + MeasureSpec.getMode(widthMeasureSpec)); + + for (int i = 0; i < getChildCount(); i++) { + final View view = getChildAt(i); + if (view.getVisibility() != GONE) { + if (mOneHandedMode && isKeyguardSecurityView(view)) { + measureChildWithMargins(view, halfWidthMeasureSpec, 0, + heightMeasureSpec, 0); + } else { + measureChildWithMargins(view, widthMeasureSpec, 0, + heightMeasureSpec, 0); + } + final LayoutParams lp = (LayoutParams) view.getLayoutParams(); + maxWidth = Math.max(maxWidth, + view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); + maxHeight = Math.max(maxHeight, + view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); + childState = combineMeasuredStates(childState, view.getMeasuredState()); + } + } + + // Check against our minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + } + void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { String message = null; switch (userType) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 1a8d420fb3945..fdab8db67431f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -404,6 +404,7 @@ public class KeyguardSecurityContainerController extends ViewController