Merge "Update the bouncer to be able to move to either side of a wide screen." into sc-dev

This commit is contained in:
Jamie Garside
2021-02-03 16:53:16 +00:00
committed by Android (Google) Code Review
12 changed files with 275 additions and 14 deletions

View File

@@ -13580,6 +13580,28 @@ 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
*/
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.

View File

@@ -4708,4 +4708,7 @@
<!-- Whether to select voice/data/sms preference without user confirmation -->
<bool name="config_voice_data_sms_auto_fallback">false</bool>
<!-- Whether to enable the one-handed keyguard on the lock screen for wide-screen devices. -->
<bool name="config_enableOneHandedKeyguard">false</bool>
</resources>

View File

@@ -4174,4 +4174,6 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
<java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>

View File

@@ -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));
}
}

View File

@@ -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,

View File

@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@@ -41,13 +41,14 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2021 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.
-->
<resources>
<bool name="can_use_one_handed_bouncer">true</bool>
</resources>

View File

@@ -22,4 +22,5 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
<bool name="can_use_one_handed_bouncer">false</bool>
</resources>

View File

@@ -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,11 +240,119 @@ 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 = getChildAt(0);
if (securityView == null) {
return;
}
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
if (mOneHandedMode) {
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
} else {
lp.gravity = Gravity.CENTER;
}
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 = getChildAt(0);
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);
}
}
public void onPause() {
@@ -237,6 +361,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
mOrientationEventListener.disable();
}
@Override
@@ -318,19 +443,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) {
@@ -440,18 +590,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);
}
@@ -489,6 +638,53 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// This is a little hacky, but this element only ever has one wrap_content child, and is
// itself set to match_parent, so we can take a couple of shortcuts compared to
// FrameLayout#onMeasure
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
int count = getChildCount();
int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec) / 2,
MeasureSpec.getMode(widthMeasureSpec));
if (count > 1) {
throw new IllegalStateException("KeyguardSecurityContainer should only have one child");
}
if (count > 0) {
final View securityView = getChildAt(0);
if (securityView.getVisibility() != GONE) {
if (mOneHandedMode) {
measureChildWithMargins(securityView, halfWidthMeasureSpec, 0,
heightMeasureSpec, 0);
} else {
measureChildWithMargins(securityView, widthMeasureSpec, 0,
heightMeasureSpec, 0);
}
final LayoutParams lp = (LayoutParams) securityView.getLayoutParams();
maxWidth = Math.max(maxWidth,
securityView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
securityView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, securityView.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) {

View File

@@ -404,6 +404,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(

View File

@@ -92,4 +92,13 @@ public class KeyguardSecurityModel {
throw new IllegalStateException("Unknown security quality:" + security);
}
}
/**
* Returns whether the given security view should be used in a "one handed" way. This can be
* used to change how the security view is drawn (e.g. take up less of the screen, and align to
* one side).
*/
public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
}
}