Merge "Revert "Removes left/right buttons from bottom area."" into tm-qpr-dev am: 9bf636182f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19101099

Change-Id: I963ccd00485897fb3728d24db97402e82df119a5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Daniel Chapin
2022-06-29 17:01:47 +00:00
committed by Automerger Merge Worker
15 changed files with 1648 additions and 38 deletions

View File

@@ -59,6 +59,26 @@
</LinearLayout> </LinearLayout>
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="@dimen/keyguard_affordance_height"
android:layout_width="@dimen/keyguard_affordance_width"
android:layout_gravity="bottom|end"
android:src="@drawable/ic_camera_alt_24dp"
android:scaleType="center"
android:contentDescription="@string/accessibility_camera_button"
android:tint="?attr/wallpaperTextColor" />
<com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/left_button"
android:layout_height="@dimen/keyguard_affordance_height"
android:layout_width="@dimen/keyguard_affordance_width"
android:layout_gravity="bottom|start"
android:src="@*android:drawable/ic_phone"
android:scaleType="center"
android:contentDescription="@string/accessibility_phone_button"
android:tint="?attr/wallpaperTextColor" />
<ImageView <ImageView
android:id="@+id/wallet_button" android:id="@+id/wallet_button"
android:layout_height="@dimen/keyguard_affordance_fixed_height" android:layout_height="@dimen/keyguard_affordance_fixed_height"

View File

@@ -37,6 +37,12 @@
<item>400</item> <item>400</item>
</integer-array> </integer-array>
<!-- Show mic or phone affordance on Keyguard -->
<bool name="config_keyguardShowLeftAffordance">false</bool>
<!-- Show camera affordance on Keyguard -->
<bool name="config_keyguardShowCameraAffordance">false</bool>
<!-- decay duration (from size_max -> size), in ms --> <!-- decay duration (from size_max -> size), in ms -->
<integer name="navigation_bar_deadzone_hold">333</integer> <integer name="navigation_bar_deadzone_hold">333</integer>
<integer name="navigation_bar_deadzone_decay">333</integer> <integer name="navigation_bar_deadzone_decay">333</integer>

View File

@@ -676,6 +676,9 @@
<!-- The minimum background radius when swiping to a side for the camera / phone affordances. --> <!-- The minimum background radius when swiping to a side for the camera / phone affordances. -->
<dimen name="keyguard_affordance_min_background_radius">30dp</dimen> <dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
<!-- The size of the touch targets on the keyguard for the affordances. -->
<dimen name="keyguard_affordance_touch_target_size">120dp</dimen>
<!-- The grow amount for the camera and phone circles when hinting --> <!-- The grow amount for the camera and phone circles when hinting -->
<dimen name="hint_grow_amount_sideways">60dp</dimen> <dimen name="hint_grow_amount_sideways">60dp</dimen>

View File

@@ -313,6 +313,13 @@
<string name="accessibility_scanning_face">Scanning face</string> <string name="accessibility_scanning_face">Scanning face</string>
<!-- Click action label for accessibility for the smart reply buttons (not shown on-screen).". [CHAR LIMIT=NONE] --> <!-- Click action label for accessibility for the smart reply buttons (not shown on-screen).". [CHAR LIMIT=NONE] -->
<string name="accessibility_send_smart_reply">Send</string> <string name="accessibility_send_smart_reply">Send</string>
<!-- Content description of the manage notification button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
<string name="phone_label">open phone</string>
<!-- Click action label for accessibility for the voice assist button. This is not shown on-screen and is an accessibility label for the icon which launches the voice assist from the lock screen.[CHAR LIMIT=NONE] -->
<string name="voice_assist_label">open voice assist</string>
<!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
<string name="camera_label">open camera</string>
<!-- Button name for "Cancel". [CHAR LIMIT=NONE] --> <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>

View File

@@ -427,6 +427,12 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void onHintFinished(); void onHintFinished();
void onCameraHintStarted();
void onVoiceAssistHintStarted();
void onPhoneHintStarted();
void onTrackingStopped(boolean expand); void onTrackingStopped(boolean expand);
// TODO: Figure out way to remove these. // TODO: Figure out way to remove these.

View File

@@ -388,7 +388,8 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
if (mStatusBarKeyguardViewManager.isBouncerShowing()) { if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */); mStatusBarKeyguardViewManager.reset(true /* hide */);
} }
mNotificationPanelViewController.launchCamera(source); mNotificationPanelViewController.launchCamera(
mCentralSurfaces.isDeviceInteractive() /* animate */, source);
mCentralSurfaces.updateScrimController(); mCentralSurfaces.updateScrimController();
} else { } else {
// We need to defer the camera launch until the screen comes on, since otherwise // We need to defer the camera launch until the screen comes on, since otherwise

View File

@@ -1285,6 +1285,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
backdrop.setScaleY(scale); backdrop.setScaleY(scale);
}); });
mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
// Set up the quick settings tile panel // Set up the quick settings tile panel
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame); final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
if (container != null) { if (container != null) {
@@ -3022,7 +3024,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
@Override @Override
public boolean isInLaunchTransition() { public boolean isInLaunchTransition() {
return mNotificationPanelViewController.isLaunchTransitionFinished(); return mNotificationPanelViewController.isLaunchTransitionRunning()
|| mNotificationPanelViewController.isLaunchTransitionFinished();
} }
/** /**
@@ -3054,7 +3057,11 @@ public class CentralSurfacesImpl extends CoreStartable implements
mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(), mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
}; };
hideRunnable.run(); if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
} else {
hideRunnable.run();
}
} }
private void cancelAfterLaunchTransitionRunnables() { private void cancelAfterLaunchTransitionRunnables() {
@@ -3063,6 +3070,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
} }
mLaunchTransitionEndRunnable = null; mLaunchTransitionEndRunnable = null;
mLaunchTransitionCancelRunnable = null; mLaunchTransitionCancelRunnable = null;
mNotificationPanelViewController.setLaunchTransitionEndRunnable(null);
} }
/** /**
@@ -3490,6 +3498,24 @@ public class CentralSurfacesImpl extends CoreStartable implements
mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
} }
@Override
public void onCameraHintStarted() {
mFalsingCollector.onCameraHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
}
@Override
public void onVoiceAssistHintStarted() {
mFalsingCollector.onLeftAffordanceHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.voice_hint);
}
@Override
public void onPhoneHintStarted() {
mFalsingCollector.onLeftAffordanceHintStarted();
mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
}
@Override @Override
public void onTrackingStopped(boolean expand) { public void onTrackingStopped(boolean expand) {
} }
@@ -3674,7 +3700,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
mWakeUpCoordinator.setFullyAwake(true); mWakeUpCoordinator.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false); mWakeUpCoordinator.setWakingUp(false);
if (mLaunchCameraWhenFinishedWaking) { if (mLaunchCameraWhenFinishedWaking) {
mNotificationPanelViewController.launchCamera(mLastCameraLaunchSource); mNotificationPanelViewController.launchCamera(
false /* animate */, mLastCameraLaunchSource);
mLaunchCameraWhenFinishedWaking = false; mLaunchCameraWhenFinishedWaking = false;
} }
if (mLaunchEmergencyActionWhenFinishedWaking) { if (mLaunchEmergencyActionWhenFinishedWaking) {
@@ -4316,6 +4343,9 @@ public class CentralSurfacesImpl extends CoreStartable implements
if (!mUserSetup) { if (!mUserSetup) {
animateCollapseQuickSettings(); animateCollapseQuickSettings();
} }
if (mNotificationPanelViewController != null) {
mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
}
updateQsExpansionEnabled(); updateQsExpansionEnabled();
} }
} }

View File

@@ -0,0 +1,587 @@
/*
* Copyright (C) 2014 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.systemui.statusbar.phone;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.wm.shell.animation.FlingAnimationUtils;
/**
* A touch handler of the keyguard which is responsible for launching phone and camera affordances.
*/
public class KeyguardAffordanceHelper {
public static final long HINT_PHASE1_DURATION = 200;
private static final long HINT_PHASE2_DURATION = 350;
private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
private static final int HINT_CIRCLE_OPEN_DURATION = 500;
private final Context mContext;
private final Callback mCallback;
private FlingAnimationUtils mFlingAnimationUtils;
private VelocityTracker mVelocityTracker;
private boolean mSwipingInProgress;
private float mInitialTouchX;
private float mInitialTouchY;
private float mTranslation;
private float mTranslationOnDown;
private int mTouchSlop;
private int mMinTranslationAmount;
private int mMinFlingVelocity;
private int mHintGrowAmount;
private KeyguardAffordanceView mLeftIcon;
private KeyguardAffordanceView mRightIcon;
private Animator mSwipeAnimator;
private final FalsingManager mFalsingManager;
private int mMinBackgroundRadius;
private boolean mMotionCancelled;
private int mTouchTargetSize;
private View mTargetedView;
private boolean mTouchSlopExeeded;
private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
mSwipingInProgress = false;
mTargetedView = null;
}
};
private Runnable mAnimationEndRunnable = new Runnable() {
@Override
public void run() {
mCallback.onAnimationToSideEnded();
}
};
KeyguardAffordanceHelper(Callback callback, Context context, FalsingManager falsingManager) {
mContext = context;
mCallback = callback;
initIcons();
updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
mFalsingManager = falsingManager;
initDimens();
}
private void initDimens() {
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
mTouchSlop = configuration.getScaledPagingTouchSlop();
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_min_swipe_amount);
mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_affordance_min_background_radius);
mTouchTargetSize = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_affordance_touch_target_size);
mHintGrowAmount =
mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
mFlingAnimationUtils = new FlingAnimationUtils(mContext.getResources().getDisplayMetrics(),
0.4f);
}
private void initIcons() {
mLeftIcon = mCallback.getLeftIcon();
mRightIcon = mCallback.getRightIcon();
updatePreviews();
}
public void updatePreviews() {
mLeftIcon.setPreviewView(mCallback.getLeftPreview());
mRightIcon.setPreviewView(mCallback.getRightPreview());
}
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
if (mMotionCancelled && action != MotionEvent.ACTION_DOWN) {
return false;
}
final float y = event.getY();
final float x = event.getX();
boolean isUp = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
View targetView = getIconAtPosition(x, y);
if (targetView == null || (mTargetedView != null && mTargetedView != targetView)) {
mMotionCancelled = true;
return false;
}
if (mTargetedView != null) {
cancelAnimation();
} else {
mTouchSlopExeeded = false;
}
startSwiping(targetView);
mInitialTouchX = x;
mInitialTouchY = y;
mTranslationOnDown = mTranslation;
initVelocityTracker();
trackMovement(event);
mMotionCancelled = false;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mMotionCancelled = true;
endMotion(true /* forceSnapBack */, x, y);
break;
case MotionEvent.ACTION_MOVE:
trackMovement(event);
float xDist = x - mInitialTouchX;
float yDist = y - mInitialTouchY;
float distance = (float) Math.hypot(xDist, yDist);
if (!mTouchSlopExeeded && distance > mTouchSlop) {
mTouchSlopExeeded = true;
}
if (mSwipingInProgress) {
if (mTargetedView == mRightIcon) {
distance = mTranslationOnDown - distance;
distance = Math.min(0, distance);
} else {
distance = mTranslationOnDown + distance;
distance = Math.max(0, distance);
}
setTranslation(distance, false /* isReset */, false /* animateReset */);
}
break;
case MotionEvent.ACTION_UP:
isUp = true;
case MotionEvent.ACTION_CANCEL:
boolean hintOnTheRight = mTargetedView == mRightIcon;
trackMovement(event);
endMotion(!isUp, x, y);
if (!mTouchSlopExeeded && isUp) {
mCallback.onIconClicked(hintOnTheRight);
}
break;
}
return true;
}
private void startSwiping(View targetView) {
mCallback.onSwipingStarted(targetView == mRightIcon);
mSwipingInProgress = true;
mTargetedView = targetView;
}
private View getIconAtPosition(float x, float y) {
if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
return mLeftIcon;
}
if (rightSwipePossible() && isOnIcon(mRightIcon, x, y)) {
return mRightIcon;
}
return null;
}
public boolean isOnAffordanceIcon(float x, float y) {
return isOnIcon(mLeftIcon, x, y) || isOnIcon(mRightIcon, x, y);
}
private boolean isOnIcon(View icon, float x, float y) {
float iconX = icon.getX() + icon.getWidth() / 2.0f;
float iconY = icon.getY() + icon.getHeight() / 2.0f;
double distance = Math.hypot(x - iconX, y - iconY);
return distance <= mTouchTargetSize / 2;
}
private void endMotion(boolean forceSnapBack, float lastX, float lastY) {
if (mSwipingInProgress) {
flingWithCurrentVelocity(forceSnapBack, lastX, lastY);
} else {
mTargetedView = null;
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private boolean rightSwipePossible() {
return mRightIcon.getVisibility() == View.VISIBLE;
}
private boolean leftSwipePossible() {
return mLeftIcon.getVisibility() == View.VISIBLE;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
public void startHintAnimation(boolean right,
Runnable onFinishedListener) {
cancelAnimation();
startHintAnimationPhase1(right, onFinishedListener);
}
private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) {
final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount);
animator.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled;
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if (mCancelled) {
mSwipeAnimator = null;
mTargetedView = null;
onFinishedListener.run();
} else {
startUnlockHintAnimationPhase2(right, onFinishedListener);
}
}
});
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setDuration(HINT_PHASE1_DURATION);
animator.start();
mSwipeAnimator = animator;
mTargetedView = targetView;
}
/**
* Phase 2: Move back.
*/
private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) {
ValueAnimator animator = getAnimatorToRadius(right, 0);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
mTargetedView = null;
onFinishedListener.run();
}
});
animator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
animator.setDuration(HINT_PHASE2_DURATION);
animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
animator.start();
mSwipeAnimator = animator;
}
private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float newRadius = (float) animation.getAnimatedValue();
targetView.setCircleRadiusWithoutAnimation(newRadius);
float translation = getTranslationFromRadius(newRadius);
mTranslation = right ? -translation : translation;
updateIconsFromTranslation(targetView);
}
});
return animator;
}
private void cancelAnimation() {
if (mSwipeAnimator != null) {
mSwipeAnimator.cancel();
}
}
private void flingWithCurrentVelocity(boolean forceSnapBack, float lastX, float lastY) {
float vel = getCurrentVelocity(lastX, lastY);
// We snap back if the current translation is not far enough
boolean snapBack = false;
if (mCallback.needsAntiFalsing()) {
snapBack = snapBack || mFalsingManager.isFalseTouch(
mTargetedView == mRightIcon
? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
}
snapBack = snapBack || isBelowFalsingThreshold();
// or if the velocity is in the opposite direction.
boolean velIsInWrongDirection = vel * mTranslation < 0;
snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
fling(vel, snapBack || forceSnapBack, mTranslation < 0);
}
private boolean isBelowFalsingThreshold() {
return Math.abs(mTranslation) < Math.abs(mTranslationOnDown) + getMinTranslationAmount();
}
private int getMinTranslationAmount() {
float factor = mCallback.getAffordanceFalsingFactor();
return (int) (mMinTranslationAmount * factor);
}
private void fling(float vel, final boolean snapBack, boolean right) {
float target = right ? -mCallback.getMaxTranslationDistance()
: mCallback.getMaxTranslationDistance();
target = snapBack ? 0 : target;
ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mTranslation = (float) animation.getAnimatedValue();
}
});
animator.addListener(mFlingEndListener);
if (!snapBack) {
startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
mCallback.onAnimationToSideStarted(right, mTranslation, vel);
} else {
reset(true);
}
animator.start();
mSwipeAnimator = animator;
if (snapBack) {
mCallback.onSwipingAborted();
}
}
private void startFinishingCircleAnimation(float velocity, Runnable animationEndRunnable,
boolean right) {
KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
targetView.finishAnimation(velocity, animationEndRunnable);
}
private void setTranslation(float translation, boolean isReset, boolean animateReset) {
translation = rightSwipePossible() ? translation : Math.max(0, translation);
translation = leftSwipePossible() ? translation : Math.min(0, translation);
float absTranslation = Math.abs(translation);
if (translation != mTranslation || isReset) {
KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
float alpha = absTranslation / getMinTranslationAmount();
// We interpolate the alpha of the other icons to 0
float fadeOutAlpha = 1.0f - alpha;
fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
boolean animateIcons = isReset && animateReset;
boolean forceNoCircleAnimation = isReset && !animateReset;
float radius = getRadiusFromTranslation(absTranslation);
boolean slowAnimation = isReset && isBelowFalsingThreshold();
if (!isReset) {
updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
false, false, false, false);
} else {
updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
animateIcons, slowAnimation, true /* isReset */, forceNoCircleAnimation);
}
updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
animateIcons, slowAnimation, isReset, forceNoCircleAnimation);
mTranslation = translation;
}
}
private void updateIconsFromTranslation(KeyguardAffordanceView targetView) {
float absTranslation = Math.abs(mTranslation);
float alpha = absTranslation / getMinTranslationAmount();
// We interpolate the alpha of the other icons to 0
float fadeOutAlpha = 1.0f - alpha;
fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
// We interpolate the alpha of the targetView to 1
KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
updateIconAlpha(targetView, alpha + fadeOutAlpha * targetView.getRestingAlpha(), false);
updateIconAlpha(otherView, fadeOutAlpha * otherView.getRestingAlpha(), false);
}
private float getTranslationFromRadius(float circleSize) {
float translation = (circleSize - mMinBackgroundRadius)
/ BACKGROUND_RADIUS_SCALE_FACTOR;
return translation > 0.0f ? translation + mTouchSlop : 0.0f;
}
private float getRadiusFromTranslation(float translation) {
if (translation <= mTouchSlop) {
return 0.0f;
}
return (translation - mTouchSlop) * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
}
public void animateHideLeftRightIcon() {
cancelAnimation();
updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
}
private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
boolean animate, boolean slowRadiusAnimation, boolean force,
boolean forceNoCircleAnimation) {
if (view.getVisibility() != View.VISIBLE && !force) {
return;
}
if (forceNoCircleAnimation) {
view.setCircleRadiusWithoutAnimation(circleRadius);
} else {
view.setCircleRadius(circleRadius, slowRadiusAnimation);
}
updateIconAlpha(view, alpha, animate);
}
private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
float scale = getScale(alpha, view);
alpha = Math.min(1.0f, alpha);
view.setImageAlpha(alpha, animate);
view.setImageScale(scale, animate);
}
private float getScale(float alpha, KeyguardAffordanceView icon) {
float scale = alpha / icon.getRestingAlpha() * 0.2f +
KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
}
private void trackMovement(MotionEvent event) {
if (mVelocityTracker != null) {
mVelocityTracker.addMovement(event);
}
}
private void initVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
}
mVelocityTracker = VelocityTracker.obtain();
}
private float getCurrentVelocity(float lastX, float lastY) {
if (mVelocityTracker == null) {
return 0;
}
mVelocityTracker.computeCurrentVelocity(1000);
float aX = mVelocityTracker.getXVelocity();
float aY = mVelocityTracker.getYVelocity();
float bX = lastX - mInitialTouchX;
float bY = lastY - mInitialTouchY;
float bLen = (float) Math.hypot(bX, bY);
// Project the velocity onto the distance vector: a * b / |b|
float projectedVelocity = (aX * bX + aY * bY) / bLen;
if (mTargetedView == mRightIcon) {
projectedVelocity = -projectedVelocity;
}
return projectedVelocity;
}
public void onConfigurationChanged() {
initDimens();
initIcons();
}
public void onRtlPropertiesChanged() {
initIcons();
}
public void reset(boolean animate) {
cancelAnimation();
setTranslation(0.0f, true /* isReset */, animate);
mMotionCancelled = true;
if (mSwipingInProgress) {
mCallback.onSwipingAborted();
mSwipingInProgress = false;
}
}
public boolean isSwipingInProgress() {
return mSwipingInProgress;
}
public void launchAffordance(boolean animate, boolean left) {
if (mSwipingInProgress) {
// We don't want to mess with the state if the user is actually swiping already.
return;
}
KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
startSwiping(targetView);
// Do not animate the circle expanding if the affordance isn't visible,
// otherwise the circle will be meaningless.
if (targetView.getVisibility() != View.VISIBLE) {
animate = false;
}
if (animate) {
fling(0, false, !left);
updateIcon(otherView, 0.0f, 0, true, false, true, false);
} else {
mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
mTranslation = left ? mCallback.getMaxTranslationDistance()
: mCallback.getMaxTranslationDistance();
updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
targetView.instantFinishAnimation();
mFlingEndListener.onAnimationEnd(null);
mAnimationEndRunnable.run();
}
}
public interface Callback {
/**
* Notifies the callback when an animation to a side page was started.
*
* @param rightPage Is the page animated to the right page?
*/
void onAnimationToSideStarted(boolean rightPage, float translation, float vel);
/**
* Notifies the callback the animation to a side page has ended.
*/
void onAnimationToSideEnded();
float getMaxTranslationDistance();
void onSwipingStarted(boolean rightIcon);
void onSwipingAborted();
void onIconClicked(boolean rightIcon);
KeyguardAffordanceView getLeftIcon();
KeyguardAffordanceView getRightIcon();
View getLeftPreview();
View getRightPreview();
/**
* @return The factor the minimum swipe amount should be multiplied with.
*/
float getAffordanceFalsingFactor();
boolean needsAntiFalsing();
}
}

View File

@@ -16,30 +16,47 @@
package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE; import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE; import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE; import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.service.quickaccesswallet.GetWalletCardsError; import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsResponse; import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient; import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -47,35 +64,81 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.Dependency; import com.android.systemui.Dependency;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.Interpolators;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.camera.CameraIntents;
import com.android.systemui.controls.ControlsServiceInfo;
import com.android.systemui.controls.dagger.ControlsComponent; import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsActivity; import com.android.systemui.controls.ui.ControlsActivity;
import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.PreviewInflater;
import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.systemui.wallet.controller.QuickAccessWalletController;
import java.util.List;
/** /**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text. * text.
*/ */
public class KeyguardBottomAreaView extends FrameLayout { public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
KeyguardStateController.Callback,
AccessibilityController.AccessibilityStateChangedCallback {
private static final String TAG = "CentralSurfaces/KeyguardBottomAreaView"; final static String TAG = "CentralSurfaces/KeyguardBottomAreaView";
public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
public static final String CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER = "lift_to_launch_ml";
public static final String EXTRA_CAMERA_LAUNCH_SOURCE
= "com.android.systemui.camera_launch_source";
private static final String LEFT_BUTTON_PLUGIN
= "com.android.systemui.action.PLUGIN_LOCKSCREEN_LEFT_BUTTON";
private static final String RIGHT_BUTTON_PLUGIN
= "com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON";
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
// TODO(b/179494051): May no longer be needed
private final boolean mShowLeftAffordance;
private final boolean mShowCameraAffordance;
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
private ImageView mWalletButton; private ImageView mWalletButton;
private ImageView mQRCodeScannerButton; private ImageView mQRCodeScannerButton;
private ImageView mControlsButton; private ImageView mControlsButton;
private boolean mHasCard = false; private boolean mHasCard = false;
private final WalletCardRetriever mCardRetriever = new WalletCardRetriever(); private WalletCardRetriever mCardRetriever = new WalletCardRetriever();
private QuickAccessWalletController mQuickAccessWalletController; private QuickAccessWalletController mQuickAccessWalletController;
private QRCodeScannerController mQRCodeScannerController; private QRCodeScannerController mQRCodeScannerController;
private ControlsComponent mControlsComponent; private ControlsComponent mControlsComponent;
@@ -85,42 +148,54 @@ public class KeyguardBottomAreaView extends FrameLayout {
private ViewGroup mIndicationArea; private ViewGroup mIndicationArea;
private TextView mIndicationText; private TextView mIndicationText;
private TextView mIndicationTextBottom; private TextView mIndicationTextBottom;
private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer; private ViewGroup mOverlayContainer;
private View mLeftPreview;
private View mCameraPreview;
private ActivityStarter mActivityStarter; private ActivityStarter mActivityStarter;
private KeyguardStateController mKeyguardStateController; private KeyguardStateController mKeyguardStateController;
private FlashlightController mFlashlightController;
private PreviewInflater mPreviewInflater;
private AccessibilityController mAccessibilityController;
private CentralSurfaces mCentralSurfaces; private CentralSurfaces mCentralSurfaces;
private KeyguardAffordanceHelper mAffordanceHelper;
private FalsingManager mFalsingManager; private FalsingManager mFalsingManager;
private boolean mUserSetupComplete;
private boolean mLeftIsVoiceAssist;
private Drawable mLeftAssistIcon;
private IntentButton mRightButton = new DefaultRightButton();
private Extension<IntentButton> mRightExtension;
private String mRightButtonStr;
private IntentButton mLeftButton = new DefaultLeftButton();
private Extension<IntentButton> mLeftExtension;
private String mLeftButtonStr;
private boolean mDozing; private boolean mDozing;
private int mIndicationBottomMargin; private int mIndicationBottomMargin;
private int mIndicationPadding; private int mIndicationPadding;
private float mDarkAmount; private float mDarkAmount;
private int mBurnInXOffset; private int mBurnInXOffset;
private int mBurnInYOffset; private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ControlsListingController.ControlsListingCallback mListingCallback = private ControlsListingController.ControlsListingCallback mListingCallback =
serviceInfos -> post(() -> { new ControlsListingController.ControlsListingCallback() {
boolean available = !serviceInfos.isEmpty(); public void onServicesUpdated(List<ControlsServiceInfo> serviceInfos) {
post(() -> {
boolean available = !serviceInfos.isEmpty();
if (available != mControlServicesAvailable) { if (available != mControlServicesAvailable) {
mControlServicesAvailable = available; mControlServicesAvailable = available;
updateControlsVisibility(); updateControlsVisibility();
updateAffordanceColors(); updateAffordanceColors();
}
});
} }
}); };
private final KeyguardStateController.Callback mKeyguardStateCallback =
new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
if (mKeyguardStateController.isShowing()) {
if (mQuickAccessWalletController != null) {
mQuickAccessWalletController.queryWalletCards(mCardRetriever);
}
}
}
};
public KeyguardBottomAreaView(Context context) { public KeyguardBottomAreaView(Context context) {
this(context, null); this(context, null);
@@ -137,8 +212,43 @@ public class KeyguardBottomAreaView extends FrameLayout {
public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
mShowLeftAffordance = getResources().getBoolean(R.bool.config_keyguardShowLeftAffordance);
mShowCameraAffordance = getResources()
.getBoolean(R.bool.config_keyguardShowCameraAffordance);
} }
private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
String label = null;
if (host == mRightAffordanceView) {
label = getResources().getString(R.string.camera_label);
} else if (host == mLeftAffordanceView) {
if (mLeftIsVoiceAssist) {
label = getResources().getString(R.string.voice_assist_label);
} else {
label = getResources().getString(R.string.phone_label);
}
}
info.addAction(new AccessibilityAction(ACTION_CLICK, label));
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == ACTION_CLICK) {
if (host == mRightAffordanceView) {
launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
return true;
} else if (host == mLeftAffordanceView) {
launchLeftAffordance();
return true;
}
}
return super.performAccessibilityAction(host, action, args);
}
};
public void initFrom(KeyguardBottomAreaView oldBottomArea) { public void initFrom(KeyguardBottomAreaView oldBottomArea) {
setCentralSurfaces(oldBottomArea.mCentralSurfaces); setCentralSurfaces(oldBottomArea.mCentralSurfaces);
@@ -169,7 +279,11 @@ public class KeyguardBottomAreaView extends FrameLayout {
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext),
new ActivityIntentHelper(mContext));
mOverlayContainer = findViewById(R.id.overlay_container); mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
mLeftAffordanceView = findViewById(R.id.left_button);
mWalletButton = findViewById(R.id.wallet_button); mWalletButton = findViewById(R.id.wallet_button);
mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button); mQRCodeScannerButton = findViewById(R.id.qr_code_scanner_button);
mControlsButton = findViewById(R.id.controls_button); mControlsButton = findViewById(R.id.controls_button);
@@ -181,11 +295,18 @@ public class KeyguardBottomAreaView extends FrameLayout {
R.dimen.keyguard_indication_margin_bottom); R.dimen.keyguard_indication_margin_bottom);
mBurnInYOffset = getResources().getDimensionPixelSize( mBurnInYOffset = getResources().getDimensionPixelSize(
R.dimen.default_burn_in_prevention_offset); R.dimen.default_burn_in_prevention_offset);
updateCameraVisibility();
mKeyguardStateController = Dependency.get(KeyguardStateController.class); mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mKeyguardStateController.addCallback(mKeyguardStateCallback); mKeyguardStateController.addCallback(this);
setClipChildren(false); setClipChildren(false);
setClipToPadding(false); setClipToPadding(false);
mRightAffordanceView.setOnClickListener(this);
mLeftAffordanceView.setOnClickListener(this);
initAccessibility();
mActivityStarter = Dependency.get(ActivityStarter.class); mActivityStarter = Dependency.get(ActivityStarter.class);
mFlashlightController = Dependency.get(FlashlightController.class);
mAccessibilityController = Dependency.get(AccessibilityController.class);
mActivityIntentHelper = new ActivityIntentHelper(getContext());
mIndicationPadding = getResources().getDimensionPixelSize( mIndicationPadding = getResources().getDimensionPixelSize(
R.dimen.keyguard_indication_area_padding); R.dimen.keyguard_indication_area_padding);
@@ -194,18 +315,51 @@ public class KeyguardBottomAreaView extends FrameLayout {
updateControlsVisibility(); updateControlsVisibility();
} }
/**
* Set the container where the previews are rendered.
*/
public void setPreviewContainer(ViewGroup previewContainer) {
mPreviewContainer = previewContainer;
inflateCameraPreview();
updateLeftAffordance();
}
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
mAccessibilityController.addStateChangedCallback(this);
mRightExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
.withPlugin(IntentButtonProvider.class, RIGHT_BUTTON_PLUGIN,
p -> p.getIntentButton())
.withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_RIGHT_BUTTON))
.withDefault(() -> new DefaultRightButton())
.withCallback(button -> setRightButton(button))
.build();
mLeftExtension = Dependency.get(ExtensionController.class).newExtension(IntentButton.class)
.withPlugin(IntentButtonProvider.class, LEFT_BUTTON_PLUGIN,
p -> p.getIntentButton())
.withTunerFactory(new LockButtonFactory(mContext, LOCKSCREEN_LEFT_BUTTON))
.withDefault(() -> new DefaultLeftButton())
.withCallback(button -> setLeftButton(button))
.build();
final IntentFilter filter = new IntentFilter(); final IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
mKeyguardStateController.addCallback(mKeyguardStateCallback); getContext().registerReceiverAsUser(mDevicePolicyReceiver,
UserHandle.ALL, filter, null, null);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
mKeyguardStateController.addCallback(this);
} }
@Override @Override
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
mKeyguardStateController.removeCallback(mKeyguardStateCallback); mKeyguardStateController.removeCallback(this);
mAccessibilityController.removeStateChangedCallback(this);
mRightExtension.destroy();
mLeftExtension.destroy();
getContext().unregisterReceiver(mDevicePolicyReceiver);
mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
if (mQuickAccessWalletController != null) { if (mQuickAccessWalletController != null) {
mQuickAccessWalletController.unregisterWalletChangeObservers( mQuickAccessWalletController.unregisterWalletChangeObservers(
@@ -224,6 +378,11 @@ public class KeyguardBottomAreaView extends FrameLayout {
} }
} }
private void initAccessibility() {
mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
mRightAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
}
@Override @Override
protected void onConfigurationChanged(Configuration newConfig) { protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig); super.onConfigurationChanged(newConfig);
@@ -245,7 +404,19 @@ public class KeyguardBottomAreaView extends FrameLayout {
getResources().getDimensionPixelSize( getResources().getDimensionPixelSize(
com.android.internal.R.dimen.text_size_small_material)); com.android.internal.R.dimen.text_size_small_material));
ViewGroup.LayoutParams lp = mWalletButton.getLayoutParams(); ViewGroup.LayoutParams lp = mRightAffordanceView.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mRightAffordanceView.setLayoutParams(lp);
updateRightAffordanceIcon();
lp = mLeftAffordanceView.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
lp = mWalletButton.getLayoutParams();
lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width); lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width);
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height); lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height);
mWalletButton.setLayoutParams(lp); mWalletButton.setLayoutParams(lp);
@@ -268,8 +439,74 @@ public class KeyguardBottomAreaView extends FrameLayout {
updateAffordanceColors(); updateAffordanceColors();
} }
private void updateRightAffordanceIcon() {
IconState state = mRightButton.getIcon();
mRightAffordanceView.setVisibility(!mDozing && state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mRightAffordanceView.getDrawable()
|| state.tint != mRightAffordanceView.shouldTint()) {
mRightAffordanceView.setImageDrawable(state.drawable, state.tint);
}
mRightAffordanceView.setContentDescription(state.contentDescription);
}
public void setCentralSurfaces(CentralSurfaces centralSurfaces) { public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
mCentralSurfaces = centralSurfaces; mCentralSurfaces = centralSurfaces;
updateCameraVisibility(); // in case onFinishInflate() was called too early
}
public void setAffordanceHelper(KeyguardAffordanceHelper affordanceHelper) {
mAffordanceHelper = affordanceHelper;
}
public void setUserSetupComplete(boolean userSetupComplete) {
mUserSetupComplete = userSetupComplete;
updateCameraVisibility();
updateLeftAffordanceIcon();
}
private Intent getCameraIntent() {
return mRightButton.getIntent();
}
/**
* Resolves the intent to launch the camera application.
*/
public ResolveInfo resolveCameraIntent() {
return mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(),
PackageManager.MATCH_DEFAULT_ONLY,
KeyguardUpdateMonitor.getCurrentUser());
}
private void updateCameraVisibility() {
if (mRightAffordanceView == null) {
// Things are not set up yet; reply hazy, ask again later
return;
}
mRightAffordanceView.setVisibility(!mDozing && mShowCameraAffordance
&& mRightButton.getIcon().isVisible ? View.VISIBLE : View.GONE);
}
/**
* Set an alternate icon for the left assist affordance (replace the mic icon)
*/
public void setLeftAssistIcon(Drawable drawable) {
mLeftAssistIcon = drawable;
updateLeftAffordanceIcon();
}
private void updateLeftAffordanceIcon() {
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
}
IconState state = mLeftButton.getIcon();
mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
|| state.tint != mLeftAffordanceView.shouldTint()) {
mLeftAffordanceView.setImageDrawable(state.drawable, state.tint);
}
mLeftAffordanceView.setContentDescription(state.contentDescription);
} }
private void updateWalletVisibility() { private void updateWalletVisibility() {
@@ -315,6 +552,73 @@ public class KeyguardBottomAreaView extends FrameLayout {
} }
} }
public boolean isLeftVoiceAssist() {
return mLeftIsVoiceAssist;
}
private boolean isPhoneVisible() {
PackageManager pm = mContext.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
&& pm.resolveActivity(PHONE_INTENT, 0) != null;
}
@Override
public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
mRightAffordanceView.setClickable(touchExplorationEnabled);
mLeftAffordanceView.setClickable(touchExplorationEnabled);
mRightAffordanceView.setFocusable(accessibilityEnabled);
mLeftAffordanceView.setFocusable(accessibilityEnabled);
}
@Override
public void onClick(View v) {
if (v == mRightAffordanceView) {
launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
} else if (v == mLeftAffordanceView) {
launchLeftAffordance();
}
}
public void launchCamera(String source) {
final Intent intent = getCameraIntent();
intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
boolean wouldLaunchResolverActivity = mActivityIntentHelper.wouldLaunchResolverActivity(
intent, KeyguardUpdateMonitor.getCurrentUser());
if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
// Normally an activity will set it's requested rotation
// animation on its window. However when launching an activity
// causes the orientation to change this is too late. In these cases
// the default animation is used. This doesn't look good for
// the camera (as it rotates the camera contents out of sync
// with physical reality). So, we ask the WindowManager to
// force the crossfade animation if an orientation change
// happens to occur during the launch.
ActivityOptions o = ActivityOptions.makeBasic();
o.setDisallowEnterPictureInPictureWhileLaunching(true);
o.setRotationAnimationHint(
WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
try {
ActivityTaskManager.getService().startActivityAsUser(
null, getContext().getBasePackageName(),
getContext().getAttributionTag(), intent,
intent.resolveTypeIfNeeded(getContext().getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
UserHandle.CURRENT.getIdentifier());
} catch (RemoteException e) {
Log.w(TAG, "Unable to start camera activity", e);
}
}
});
} else {
// We need to delay starting the activity because ResolverActivity finishes itself if
// launched behind lockscreen.
mActivityStarter.startActivity(intent, false /* dismissShade */);
}
}
public void setDarkAmount(float darkAmount) { public void setDarkAmount(float darkAmount) {
if (darkAmount == mDarkAmount) { if (darkAmount == mDarkAmount) {
return; return;
@@ -323,6 +627,77 @@ public class KeyguardBottomAreaView extends FrameLayout {
dozeTimeTick(); dozeTimeTick();
} }
public void launchLeftAffordance() {
if (mLeftIsVoiceAssist) {
launchVoiceAssist();
} else {
launchPhone();
}
}
@VisibleForTesting
void launchVoiceAssist() {
Runnable runnable = new Runnable() {
@Override
public void run() {
Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard();
}
};
if (!mKeyguardStateController.canDismissLockScreen()) {
Dependency.get(Dependency.BACKGROUND_EXECUTOR).execute(runnable);
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
mCentralSurfaces.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
dismissShade, false /* afterKeyguardGone */, true /* deferred */);
}
}
private boolean canLaunchVoiceAssist() {
return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard();
}
private void launchPhone() {
final TelecomManager tm = TelecomManager.from(mContext);
if (tm.isInCall()) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
tm.showInCallScreen(false /* showDialpad */);
}
});
} else {
boolean dismissShade = !TextUtils.isEmpty(mLeftButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_LEFT_UNLOCK, 1) != 0;
mActivityStarter.startActivity(mLeftButton.getIntent(), dismissShade);
}
}
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (changedView == this && visibility == VISIBLE) {
updateCameraVisibility();
}
}
public KeyguardAffordanceView getLeftView() {
return mLeftAffordanceView;
}
public KeyguardAffordanceView getRightView() {
return mRightAffordanceView;
}
public View getLeftPreview() {
return mLeftPreview;
}
public View getRightPreview() {
return mCameraPreview;
}
public View getIndicationArea() { public View getIndicationArea() {
return mIndicationArea; return mIndicationArea;
} }
@@ -332,6 +707,66 @@ public class KeyguardBottomAreaView extends FrameLayout {
return false; return false;
} }
@Override
public void onUnlockedChanged() {
updateCameraVisibility();
}
@Override
public void onKeyguardShowingChanged() {
if (mKeyguardStateController.isShowing()) {
if (mQuickAccessWalletController != null) {
mQuickAccessWalletController.queryWalletCards(mCardRetriever);
}
}
}
private void inflateCameraPreview() {
if (mPreviewContainer == null) {
return;
}
View previewBefore = mCameraPreview;
boolean visibleBefore = false;
if (previewBefore != null) {
mPreviewContainer.removeView(previewBefore);
visibleBefore = previewBefore.getVisibility() == View.VISIBLE;
}
mCameraPreview = mPreviewInflater.inflatePreview(getCameraIntent());
if (mCameraPreview != null) {
mPreviewContainer.addView(mCameraPreview);
mCameraPreview.setVisibility(visibleBefore ? View.VISIBLE : View.INVISIBLE);
}
if (mAffordanceHelper != null) {
mAffordanceHelper.updatePreviews();
}
}
private void updateLeftPreview() {
if (mPreviewContainer == null) {
return;
}
View previewBefore = mLeftPreview;
if (previewBefore != null) {
mPreviewContainer.removeView(previewBefore);
}
if (mLeftIsVoiceAssist) {
if (Dependency.get(AssistManager.class).getVoiceInteractorComponentName() != null) {
mLeftPreview = mPreviewInflater.inflatePreviewFromService(
Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
}
} else {
mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
}
if (mLeftPreview != null) {
mPreviewContainer.addView(mLeftPreview);
mLeftPreview.setVisibility(View.INVISIBLE);
}
if (mAffordanceHelper != null) {
mAffordanceHelper.updatePreviews();
}
}
public void startFinishDozeAnimation() { public void startFinishDozeAnimation() {
long delay = 0; long delay = 0;
if (mWalletButton.getVisibility() == View.VISIBLE) { if (mWalletButton.getVisibility() == View.VISIBLE) {
@@ -343,6 +778,13 @@ public class KeyguardBottomAreaView extends FrameLayout {
if (mControlsButton.getVisibility() == View.VISIBLE) { if (mControlsButton.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mControlsButton, delay); startFinishDozeAnimationElement(mControlsButton, delay);
} }
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
}
if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mRightAffordanceView, delay);
}
} }
private void startFinishDozeAnimationElement(View element, long delay) { private void startFinishDozeAnimationElement(View element, long delay) {
@@ -356,9 +798,58 @@ public class KeyguardBottomAreaView extends FrameLayout {
.setDuration(DOZE_ANIMATION_ELEMENT_DURATION); .setDuration(DOZE_ANIMATION_ELEMENT_DURATION);
} }
private final BroadcastReceiver mDevicePolicyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
post(new Runnable() {
@Override
public void run() {
updateCameraVisibility();
}
});
}
};
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
public void onUserSwitchComplete(int userId) {
updateCameraVisibility();
}
@Override
public void onUserUnlocked() {
inflateCameraPreview();
updateCameraVisibility();
updateLeftAffordance();
}
};
public void updateLeftAffordance() {
updateLeftAffordanceIcon();
updateLeftPreview();
}
private void setRightButton(IntentButton button) {
mRightButton = button;
updateRightAffordanceIcon();
updateCameraVisibility();
inflateCameraPreview();
}
private void setLeftButton(IntentButton button) {
mLeftButton = button;
if (!(mLeftButton instanceof DefaultLeftButton)) {
mLeftIsVoiceAssist = false;
}
updateLeftAffordance();
}
public void setDozing(boolean dozing, boolean animate) { public void setDozing(boolean dozing, boolean animate) {
mDozing = dozing; mDozing = dozing;
updateCameraVisibility();
updateLeftAffordanceIcon();
updateWalletVisibility(); updateWalletVisibility();
updateControlsVisibility(); updateControlsVisibility();
updateQRCodeButtonVisibility(); updateQRCodeButtonVisibility();
@@ -397,12 +888,77 @@ public class KeyguardBottomAreaView extends FrameLayout {
* Sets the alpha of the indication areas and affordances, excluding the lock icon. * Sets the alpha of the indication areas and affordances, excluding the lock icon.
*/ */
public void setAffordanceAlpha(float alpha) { public void setAffordanceAlpha(float alpha) {
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha); mIndicationArea.setAlpha(alpha);
mWalletButton.setAlpha(alpha); mWalletButton.setAlpha(alpha);
mQRCodeScannerButton.setAlpha(alpha); mQRCodeScannerButton.setAlpha(alpha);
mControlsButton.setAlpha(alpha); mControlsButton.setAlpha(alpha);
} }
private class DefaultLeftButton implements IntentButton {
private IconState mIconState = new IconState();
@Override
public IconState getIcon() {
mLeftIsVoiceAssist = canLaunchVoiceAssist();
if (mLeftIsVoiceAssist) {
mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance;
if (mLeftAssistIcon == null) {
mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
} else {
mIconState.drawable = mLeftAssistIcon;
}
mIconState.contentDescription = mContext.getString(
R.string.accessibility_voice_assist_button);
} else {
mIconState.isVisible = mUserSetupComplete && mShowLeftAffordance
&& isPhoneVisible();
mIconState.drawable = mContext.getDrawable(
com.android.internal.R.drawable.ic_phone);
mIconState.contentDescription = mContext.getString(
R.string.accessibility_phone_button);
}
return mIconState;
}
@Override
public Intent getIntent() {
return PHONE_INTENT;
}
}
private class DefaultRightButton implements IntentButton {
private IconState mIconState = new IconState();
@Override
public IconState getIcon() {
boolean isCameraDisabled = (mCentralSurfaces != null)
&& !mCentralSurfaces.isCameraAllowedByAdmin();
mIconState.isVisible = !isCameraDisabled
&& mShowCameraAffordance
&& mUserSetupComplete
&& resolveCameraIntent() != null;
mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
mIconState.contentDescription =
mContext.getString(R.string.accessibility_camera_button);
return mIconState;
}
@Override
public Intent getIntent() {
boolean canDismissLs = mKeyguardStateController.canDismissLockScreen();
boolean secure = mKeyguardStateController.isMethodSecure();
if (secure && !canDismissLs) {
return CameraIntents.getSecureCameraIntent(getContext());
} else {
return CameraIntents.getInsecureCameraIntent(getContext());
}
}
}
@Override @Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) { public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int bottom = insets.getDisplayCutout() != null int bottom = insets.getDisplayCutout() != null

View File

@@ -50,6 +50,8 @@ import android.app.ActivityManager;
import android.app.Fragment; import android.app.Fragment;
import android.app.StatusBarManager; import android.app.StatusBarManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.Canvas; import android.graphics.Canvas;
@@ -143,6 +145,7 @@ import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -249,6 +252,9 @@ public class NotificationPanelViewController extends PanelViewController {
private final OnOverscrollTopChangedListener private final OnOverscrollTopChangedListener
mOnOverscrollTopChangedListener = mOnOverscrollTopChangedListener =
new OnOverscrollTopChangedListener(); new OnOverscrollTopChangedListener();
private final KeyguardAffordanceHelperCallback
mKeyguardAffordanceHelperCallback =
new KeyguardAffordanceHelperCallback();
private final OnEmptySpaceClickListener private final OnEmptySpaceClickListener
mOnEmptySpaceClickListener = mOnEmptySpaceClickListener =
new OnEmptySpaceClickListener(); new OnEmptySpaceClickListener();
@@ -332,6 +338,8 @@ public class NotificationPanelViewController extends PanelViewController {
// Current max allowed keyguard notifications determined by measuring the panel // Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications; private int mMaxAllowedKeyguardNotifications;
private ViewGroup mPreviewContainer;
private KeyguardAffordanceHelper mAffordanceHelper;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
private KeyguardUserSwitcherController mKeyguardUserSwitcherController; private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar; private KeyguardStatusBarView mKeyguardStatusBar;
@@ -429,6 +437,8 @@ public class NotificationPanelViewController extends PanelViewController {
*/ */
private boolean mQsAnimatorExpand; private boolean mQsAnimatorExpand;
private boolean mIsLaunchTransitionFinished; private boolean mIsLaunchTransitionFinished;
private boolean mIsLaunchTransitionRunning;
private Runnable mLaunchAnimationEndRunnable;
private boolean mOnlyAffordanceInThisMotion; private boolean mOnlyAffordanceInThisMotion;
private ValueAnimator mQsSizeChangeAnimator; private ValueAnimator mQsSizeChangeAnimator;
@@ -445,8 +455,10 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mClosingWithAlphaFadeOut; private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway; private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance; private boolean mLaunchingAffordance;
private boolean mAffordanceHasPreview;
private final FalsingManager mFalsingManager; private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector; private final FalsingCollector mFalsingCollector;
private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
private Runnable mHeadsUpExistenceChangedRunnable = () -> { private Runnable mHeadsUpExistenceChangedRunnable = () -> {
setHeadsUpAnimatingAway(false); setHeadsUpAnimatingAway(false);
@@ -479,6 +491,7 @@ public class NotificationPanelViewController extends PanelViewController {
private float mLinearDarkAmount; private float mLinearDarkAmount;
private boolean mPulsing; private boolean mPulsing;
private boolean mUserSetupComplete;
private boolean mHideIconsDuringLaunchAnimation = true; private boolean mHideIconsDuringLaunchAnimation = true;
private int mStackScrollerMeasuringPass; private int mStackScrollerMeasuringPass;
/** /**
@@ -995,6 +1008,8 @@ public class NotificationPanelViewController extends PanelViewController {
mOnEmptySpaceClickListener); mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
mPreviewContainer = mView.findViewById(R.id.preview_container);
mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
initBottomArea(); initBottomArea();
@@ -1018,6 +1033,7 @@ public class NotificationPanelViewController extends PanelViewController {
mView.setRtlChangeListener(layoutDirection -> { mView.setRtlChangeListener(layoutDirection -> {
if (layoutDirection != mOldLayoutDirection) { if (layoutDirection != mOldLayoutDirection) {
mAffordanceHelper.onRtlPropertiesChanged();
mOldLayoutDirection = layoutDirection; mOldLayoutDirection = layoutDirection;
} }
}); });
@@ -1251,6 +1267,7 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate( mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
R.layout.keyguard_bottom_area, mView, false); R.layout.keyguard_bottom_area, mView, false);
mKeyguardBottomArea.initFrom(oldBottomArea); mKeyguardBottomArea.initFrom(oldBottomArea);
mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
mView.addView(mKeyguardBottomArea, index); mView.addView(mKeyguardBottomArea, index);
initBottomArea(); initBottomArea();
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
@@ -1287,7 +1304,11 @@ public class NotificationPanelViewController extends PanelViewController {
} }
private void initBottomArea() { private void initBottomArea() {
mAffordanceHelper = new KeyguardAffordanceHelper(
mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces); mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
mKeyguardBottomArea.setFalsingManager(mFalsingManager); mKeyguardBottomArea.setFalsingManager(mFalsingManager);
mKeyguardBottomArea.initWallet(mQuickAccessWalletController); mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
mKeyguardBottomArea.initControls(mControlsComponent); mKeyguardBottomArea.initControls(mControlsComponent);
@@ -1652,6 +1673,10 @@ public class NotificationPanelViewController extends PanelViewController {
public void resetViews(boolean animate) { public void resetViews(boolean animate) {
mIsLaunchTransitionFinished = false; mIsLaunchTransitionFinished = false;
mBlockTouches = false; mBlockTouches = false;
if (!mLaunchingAffordance) {
mAffordanceHelper.reset(false);
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
if (animate && !isFullyCollapsed()) { if (animate && !isFullyCollapsed()) {
@@ -2194,6 +2219,11 @@ public class NotificationPanelViewController extends PanelViewController {
return isFullyCollapsed() || mBarState != StatusBarState.SHADE; return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
} }
@Override
protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
return !mAffordanceHelper.isOnAffordanceIcon(x, y);
}
private void onQsTouch(MotionEvent event) { private void onQsTouch(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer); int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) { if (pointerIndex < 0) {
@@ -3358,6 +3388,9 @@ public class NotificationPanelViewController extends PanelViewController {
mQsExpandImmediate = true; mQsExpandImmediate = true;
setShowShelfOnly(true); setShowShelfOnly(true);
} }
if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
mAffordanceHelper.animateHideLeftRightIcon();
}
mNotificationStackScrollLayoutController.onPanelTrackingStarted(); mNotificationStackScrollLayoutController.onPanelTrackingStarted();
cancelPendingPanelCollapse(); cancelPendingPanelCollapse();
} }
@@ -3371,6 +3404,12 @@ public class NotificationPanelViewController extends PanelViewController {
true /* animate */); true /* animate */);
} }
mNotificationStackScrollLayoutController.onPanelTrackingStopped(); mNotificationStackScrollLayoutController.onPanelTrackingStopped();
if (expand && (mBarState == KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED)) {
if (!mHintAnimationRunning) {
mAffordanceHelper.reset(true);
}
}
// If we unlocked from a swipe, the user's finger might still be down after the // If we unlocked from a swipe, the user's finger might still be down after the
// unlock animation ends. We need to wait until ACTION_UP to enable blurs again. // unlock animation ends. We need to wait until ACTION_UP to enable blurs again.
@@ -3443,6 +3482,10 @@ public class NotificationPanelViewController extends PanelViewController {
return mIsLaunchTransitionFinished; return mIsLaunchTransitionFinished;
} }
public boolean isLaunchTransitionRunning() {
return mIsLaunchTransitionRunning;
}
@Override @Override
public void setIsLaunchAnimationRunning(boolean running) { public void setIsLaunchAnimationRunning(boolean running) {
boolean wasRunning = mIsLaunchAnimationRunning; boolean wasRunning = mIsLaunchAnimationRunning;
@@ -3461,6 +3504,10 @@ public class NotificationPanelViewController extends PanelViewController {
} }
} }
public void setLaunchTransitionEndRunnable(Runnable r) {
mLaunchAnimationEndRunnable = r;
}
private void updateDozingVisibilities(boolean animate) { private void updateDozingVisibilities(boolean animate) {
mKeyguardBottomArea.setDozing(mDozing, animate); mKeyguardBottomArea.setDozing(mDozing, animate);
if (!mDozing && animate) { if (!mDozing && animate) {
@@ -3639,8 +3686,30 @@ public class NotificationPanelViewController extends PanelViewController {
&& mBarState == StatusBarState.SHADE; && mBarState == StatusBarState.SHADE;
} }
/** Launches the camera. */ public void launchCamera(boolean animate, int source) {
public void launchCamera(int source) { if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
} else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
} else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
} else {
// Default.
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
// If we are launching it when we are occluded already we don't want it to animate,
// nor setting these flags, since the occluded state doesn't change anymore, hence it's
// never reset.
if (!isFullyCollapsed()) {
setLaunchingAffordance(true);
} else {
animate = false;
}
mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
mAffordanceHelper.launchAffordance(
animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
} }
public void onAffordanceLaunchEnded() { public void onAffordanceLaunchEnded() {
@@ -3653,6 +3722,9 @@ public class NotificationPanelViewController extends PanelViewController {
*/ */
private void setLaunchingAffordance(boolean launchingAffordance) { private void setLaunchingAffordance(boolean launchingAffordance) {
mLaunchingAffordance = launchingAffordance; mLaunchingAffordance = launchingAffordance;
mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
launchingAffordance);
mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
} }
@@ -3660,14 +3732,24 @@ public class NotificationPanelViewController extends PanelViewController {
* Return true when a bottom affordance is launching an occluded activity with a splash screen. * Return true when a bottom affordance is launching an occluded activity with a splash screen.
*/ */
public boolean isLaunchingAffordanceWithPreview() { public boolean isLaunchingAffordanceWithPreview() {
return mLaunchingAffordance; return mLaunchingAffordance && mAffordanceHasPreview;
} }
/** /**
* Whether the camera application can be launched for the camera launch gesture. * Whether the camera application can be launched for the camera launch gesture.
*/ */
public boolean canCameraGestureBeLaunched() { public boolean canCameraGestureBeLaunched() {
return false; if (!mCentralSurfaces.isCameraAllowedByAdmin()) {
return false;
}
ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
String
packageToLaunch =
(resolveInfo == null || resolveInfo.activityInfo == null) ? null
: resolveInfo.activityInfo.packageName;
return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
} }
/** /**
@@ -3756,6 +3838,9 @@ public class NotificationPanelViewController extends PanelViewController {
@Override @Override
public void setTouchAndAnimationDisabled(boolean disabled) { public void setTouchAndAnimationDisabled(boolean disabled) {
super.setTouchAndAnimationDisabled(disabled); super.setTouchAndAnimationDisabled(disabled);
if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
mAffordanceHelper.reset(false /* animate */);
}
mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled); mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
} }
@@ -3838,6 +3923,11 @@ public class NotificationPanelViewController extends PanelViewController {
return mKeyguardBottomArea; return mKeyguardBottomArea;
} }
public void setUserSetupComplete(boolean userSetupComplete) {
mUserSetupComplete = userSetupComplete;
mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
}
public void applyLaunchAnimationProgress(float linearProgress) { public void applyLaunchAnimationProgress(float linearProgress) {
boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
@@ -4191,6 +4281,10 @@ public class NotificationPanelViewController extends PanelViewController {
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
} }
boolean handled = false; boolean handled = false;
if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
&& mBarState != StatusBarState.SHADE && !mDozing) {
handled |= mAffordanceHelper.onTouchEvent(event);
}
if (mOnlyAffordanceInThisMotion) { if (mOnlyAffordanceInThisMotion) {
return true; return true;
} }
@@ -4433,6 +4527,139 @@ public class NotificationPanelViewController extends PanelViewController {
} }
} }
private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
@Override
public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
boolean
start =
mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
: !rightPage;
mIsLaunchTransitionRunning = true;
mLaunchAnimationEndRunnable = null;
float displayDensity = mCentralSurfaces.getDisplayDensity();
int lengthDp = Math.abs((int) (translation / displayDensity));
int velocityDp = Math.abs((int) (vel / displayDensity));
if (start) {
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
mFalsingCollector.onLeftAffordanceOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchLeftAffordance(), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
} else {
mKeyguardBottomArea.launchLeftAffordance();
}
} else {
if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
mLastCameraLaunchSource)) {
mLockscreenGestureLogger.write(
MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA);
}
mFalsingCollector.onCameraOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
} else {
mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}
mCentralSurfaces.startLaunchTransitionTimeout();
mBlockTouches = true;
}
@Override
public void onAnimationToSideEnded() {
mIsLaunchTransitionRunning = false;
mIsLaunchTransitionFinished = true;
if (mLaunchAnimationEndRunnable != null) {
mLaunchAnimationEndRunnable.run();
mLaunchAnimationEndRunnable = null;
}
mCentralSurfaces.readyForKeyguardDone();
}
@Override
public float getMaxTranslationDistance() {
return (float) Math.hypot(mView.getWidth(), getHeight());
}
@Override
public void onSwipingStarted(boolean rightIcon) {
mFalsingCollector.onAffordanceSwipingStarted(rightIcon);
mView.requestDisallowInterceptTouchEvent(true);
mOnlyAffordanceInThisMotion = true;
mQsTracking = false;
}
@Override
public void onSwipingAborted() {
mFalsingCollector.onAffordanceSwipingAborted();
}
@Override
public void onIconClicked(boolean rightIcon) {
if (mHintAnimationRunning) {
return;
}
mHintAnimationRunning = true;
mAffordanceHelper.startHintAnimation(rightIcon, () -> {
mHintAnimationRunning = false;
mCentralSurfaces.onHintFinished();
});
rightIcon =
mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
: rightIcon;
if (rightIcon) {
mCentralSurfaces.onCameraHintStarted();
} else {
if (mKeyguardBottomArea.isLeftVoiceAssist()) {
mCentralSurfaces.onVoiceAssistHintStarted();
} else {
mCentralSurfaces.onPhoneHintStarted();
}
}
}
@Override
public KeyguardAffordanceView getLeftIcon() {
return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
}
@Override
public KeyguardAffordanceView getRightIcon() {
return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
}
@Override
public View getLeftPreview() {
return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
}
@Override
public View getRightPreview() {
return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
}
@Override
public float getAffordanceFalsingFactor() {
return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
public boolean needsAntiFalsing() {
return mBarState == KEYGUARD;
}
}
private class OnEmptySpaceClickListener implements private class OnEmptySpaceClickListener implements
NotificationStackScrollLayout.OnEmptySpaceClickListener { NotificationStackScrollLayout.OnEmptySpaceClickListener {
@Override @Override
@@ -4891,6 +5118,15 @@ public class NotificationPanelViewController extends PanelViewController {
} }
} }
private class OnConfigurationChangedListener extends
PanelViewController.OnConfigurationChangedListener {
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mAffordanceHelper.onConfigurationChanged();
}
}
private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
// the same types of insets that are handled in NotificationShadeWindowView // the same types of insets that are handled in NotificationShadeWindowView

View File

@@ -484,6 +484,8 @@ public abstract class PanelViewController {
protected abstract boolean shouldGestureWaitForTouchSlop(); protected abstract boolean shouldGestureWaitForTouchSlop();
protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
protected void onTrackingStopped(boolean expand) { protected void onTrackingStopped(boolean expand) {
mTracking = false; mTracking = false;
mCentralSurfaces.onTrackingStopped(expand); mCentralSurfaces.onTrackingStopped(expand);
@@ -1331,7 +1333,7 @@ public abstract class PanelViewController {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop(); mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
mIgnoreXTouchSlop = true; mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
} }
switch (event.getActionMasked()) { switch (event.getActionMasked()) {

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2014 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.systemui.statusbar.policy;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
import java.util.List;
/**
* Utility class to inflate previews for phone and camera affordance.
*/
public class PreviewInflater {
private static final String TAG = "PreviewInflater";
private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
private final ActivityIntentHelper mActivityIntentHelper;
private Context mContext;
private LockPatternUtils mLockPatternUtils;
public PreviewInflater(Context context, LockPatternUtils lockPatternUtils,
ActivityIntentHelper activityIntentHelper) {
mContext = context;
mLockPatternUtils = lockPatternUtils;
mActivityIntentHelper = activityIntentHelper;
}
public View inflatePreview(Intent intent) {
WidgetInfo info = getWidgetInfo(intent);
return inflatePreview(info);
}
public View inflatePreviewFromService(ComponentName componentName) {
WidgetInfo info = getWidgetInfoFromService(componentName);
return inflatePreview(info);
}
private KeyguardPreviewContainer inflatePreview(WidgetInfo info) {
if (info == null) {
return null;
}
View v = inflateWidgetView(info);
if (v == null) {
return null;
}
KeyguardPreviewContainer container = new KeyguardPreviewContainer(mContext, null);
container.addView(v);
return container;
}
private View inflateWidgetView(WidgetInfo widgetInfo) {
View widgetView = null;
try {
Context appContext = mContext.createPackageContext(
widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
LayoutInflater appInflater = (LayoutInflater)
appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
appInflater = appInflater.cloneInContext(appContext);
widgetView = appInflater.inflate(widgetInfo.layoutId, null, false);
} catch (PackageManager.NameNotFoundException|RuntimeException e) {
Log.w(TAG, "Error creating widget view", e);
}
return widgetView;
}
private WidgetInfo getWidgetInfoFromService(ComponentName componentName) {
PackageManager packageManager = mContext.getPackageManager();
// Look for the preview specified in the service meta-data
try {
Bundle metaData = packageManager.getServiceInfo(
componentName, PackageManager.GET_META_DATA).metaData;
return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString()
+ " not found", e);
}
return null;
}
private WidgetInfo getWidgetInfoFromMetaData(String contextPackage,
Bundle metaData) {
if (metaData == null) {
return null;
}
int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
if (layoutId == 0) {
return null;
}
WidgetInfo info = new WidgetInfo();
info.contextPackage = contextPackage;
info.layoutId = layoutId;
return info;
}
private WidgetInfo getWidgetInfo(Intent intent) {
PackageManager packageManager = mContext.getPackageManager();
int flags = PackageManager.MATCH_DEFAULT_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
intent, flags, KeyguardUpdateMonitor.getCurrentUser());
if (appList.size() == 0) {
return null;
}
ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
flags | PackageManager.GET_META_DATA,
KeyguardUpdateMonitor.getCurrentUser());
if (mActivityIntentHelper.wouldLaunchResolverActivity(resolved, appList)) {
return null;
}
if (resolved == null || resolved.activityInfo == null) {
return null;
}
return getWidgetInfoFromMetaData(resolved.activityInfo.packageName,
resolved.activityInfo.metaData);
}
private static class WidgetInfo {
String contextPackage;
int layoutId;
}
}

View File

@@ -848,6 +848,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl(); mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state // Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true); when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
mCentralSurfaces.updateScrimController(); mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
@@ -884,6 +885,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mCentralSurfaces.showKeyguardImpl(); mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state // Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false); when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
mCentralSurfaces.updateScrimController(); mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD)); verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));

View File

@@ -12,12 +12,12 @@ import com.android.systemui.statusbar.policy.AccessibilityController
import com.android.systemui.statusbar.policy.FlashlightController import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService
import java.util.concurrent.Executor
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import java.util.concurrent.Executor
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@@ -51,5 +51,6 @@ class KeyguardBottomAreaTest : SysuiTestCase() {
null, false) as KeyguardBottomAreaView null, false) as KeyguardBottomAreaView
other.initFrom(mKeyguardBottomArea) other.initFrom(mKeyguardBottomArea)
other.launchVoiceAssist()
} }
} }

View File

@@ -106,6 +106,7 @@ import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -400,6 +401,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) when(mNotificationStackScrollLayoutController.getHeadsUpCallback())
.thenReturn(mHeadsUpCallback); .thenReturn(mHeadsUpCallback);
when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view)) when(mView.findViewById(R.id.keyguard_status_view))