Merge "Update the bouncer to be able to move to either side of a wide screen." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1ddf2b3ac4
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
20
packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
Normal file
20
packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user