Merge "Implemented basic camera and phone affordance." into lmp-preview-dev
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
>
|
||||
<com.android.systemui.statusbar.phone.SwipeAffordanceView
|
||||
<com.android.systemui.statusbar.AlphaImageView
|
||||
android:id="@+id/camera_button"
|
||||
android:layout_height="64dp"
|
||||
android:layout_width="64dp"
|
||||
@@ -30,10 +30,9 @@
|
||||
android:tint="#ffffffff"
|
||||
android:src="@drawable/ic_camera_alt_24dp"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/accessibility_camera_button"
|
||||
systemui:swipeDirection="start"/>
|
||||
android:contentDescription="@string/accessibility_camera_button" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.SwipeAffordanceView
|
||||
<com.android.systemui.statusbar.AlphaImageView
|
||||
android:id="@+id/phone_button"
|
||||
android:layout_height="64dp"
|
||||
android:layout_width="64dp"
|
||||
@@ -41,8 +40,7 @@
|
||||
android:tint="#ffffffff"
|
||||
android:src="@drawable/ic_phone_24dp"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="@string/accessibility_phone_button"
|
||||
systemui:swipeDirection="end"/>
|
||||
android:contentDescription="@string/accessibility_phone_button" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.KeyguardIndicationTextView
|
||||
android:id="@+id/keyguard_indication_text"
|
||||
@@ -54,15 +52,13 @@
|
||||
android:textColor="#ffffff"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
|
||||
<ImageView
|
||||
<com.android.systemui.statusbar.AlphaImageView
|
||||
android:id="@+id/lock_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:src="@drawable/ic_lock_24dp"
|
||||
android:scaleType="center"
|
||||
android:alpha="0.7"
|
||||
android:layerType="hardware"
|
||||
android:tint="#ffffffff"/>
|
||||
android:tint="#ffffffff" />
|
||||
|
||||
</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
|
||||
|
||||
@@ -43,12 +43,6 @@
|
||||
<declare-styleable name="BatteryMeterView">
|
||||
<attr name="frameColor" format="color" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="SwipeAffordanceView">
|
||||
<attr name="swipeDirection" format="enum">
|
||||
<enum name="start" value="0" />
|
||||
<enum name="end" value="1" />
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
<declare-styleable name="Clock">
|
||||
<attr name="amPmStyle" format="enum">
|
||||
<enum name="normal" value="0" />
|
||||
|
||||
@@ -294,6 +294,9 @@
|
||||
<dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
|
||||
<dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
|
||||
|
||||
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
|
||||
<dimen name="keyguard_min_swipe_amount">75dp</dimen>
|
||||
|
||||
<!-- Volume panel dialog y offset -->
|
||||
<dimen name="volume_panel_top">16dp</dimen>
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* An ImageView which does not have overlapping renderings commands and therefore does not need a
|
||||
* layer when alpha is changed.
|
||||
*/
|
||||
public class AlphaImageView extends ImageView {
|
||||
public AlphaImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AlphaImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
@@ -46,6 +47,8 @@ public class FlingAnimationUtils {
|
||||
private float mMaxLengthSeconds;
|
||||
private float mHighVelocityPxPerSecond;
|
||||
|
||||
private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
|
||||
|
||||
public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
|
||||
mMaxLengthSeconds = maxLengthSeconds;
|
||||
mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
|
||||
@@ -72,6 +75,20 @@ public class FlingAnimationUtils {
|
||||
apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the interpolator and length to the animator, such that the fling animation is
|
||||
* consistent with the finger motion.
|
||||
*
|
||||
* @param animator the animator to apply
|
||||
* @param currValue the current value
|
||||
* @param endValue the end value of the animator
|
||||
* @param velocity the current velocity of the motion
|
||||
*/
|
||||
public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
|
||||
float velocity) {
|
||||
apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the interpolator and length to the animator, such that the fling animation is
|
||||
* consistent with the finger motion.
|
||||
@@ -85,13 +102,40 @@ public class FlingAnimationUtils {
|
||||
*/
|
||||
public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
|
||||
float maxDistance) {
|
||||
AnimatorProperties properties = getProperties(currValue, endValue, velocity,
|
||||
maxDistance);
|
||||
animator.setDuration(properties.duration);
|
||||
animator.setInterpolator(properties.interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the interpolator and length to the animator, such that the fling animation is
|
||||
* consistent with the finger motion.
|
||||
*
|
||||
* @param animator the animator to apply
|
||||
* @param currValue the current value
|
||||
* @param endValue the end value of the animator
|
||||
* @param velocity the current velocity of the motion
|
||||
* @param maxDistance the maximum distance for this interaction; the maximum animation length
|
||||
* gets multiplied by the ratio between the actual distance and this value
|
||||
*/
|
||||
public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
|
||||
float velocity, float maxDistance) {
|
||||
AnimatorProperties properties = getProperties(currValue, endValue, velocity,
|
||||
maxDistance);
|
||||
animator.setDuration(properties.duration);
|
||||
animator.setInterpolator(properties.interpolator);
|
||||
}
|
||||
|
||||
private AnimatorProperties getProperties(float currValue,
|
||||
float endValue, float velocity, float maxDistance) {
|
||||
float maxLengthSeconds = (float) (mMaxLengthSeconds
|
||||
* Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
|
||||
float diff = Math.abs(endValue - currValue);
|
||||
float velAbs = Math.abs(velocity);
|
||||
float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
|
||||
if (durationSeconds <= maxLengthSeconds) {
|
||||
animator.setInterpolator(mLinearOutSlowIn);
|
||||
mAnimatorProperties.interpolator = mLinearOutSlowIn;
|
||||
} else if (velAbs >= mMinVelocityPxPerSecond) {
|
||||
|
||||
// Cross fade between fast-out-slow-in and linear interpolator with current velocity.
|
||||
@@ -100,14 +144,15 @@ public class FlingAnimationUtils {
|
||||
= new VelocityInterpolator(durationSeconds, velAbs, diff);
|
||||
InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
|
||||
velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
|
||||
animator.setInterpolator(superInterpolator);
|
||||
mAnimatorProperties.interpolator = superInterpolator;
|
||||
} else {
|
||||
|
||||
// Just use a normal interpolator which doesn't take the velocity into account.
|
||||
durationSeconds = maxLengthSeconds;
|
||||
animator.setInterpolator(mFastOutSlowIn);
|
||||
mAnimatorProperties.interpolator = mFastOutSlowIn;
|
||||
}
|
||||
animator.setDuration((long) (durationSeconds * 1000));
|
||||
mAnimatorProperties.duration = (long) (durationSeconds * 1000);
|
||||
return mAnimatorProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +169,34 @@ public class FlingAnimationUtils {
|
||||
*/
|
||||
public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
|
||||
float velocity, float maxDistance) {
|
||||
AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
|
||||
maxDistance);
|
||||
animator.setDuration(properties.duration);
|
||||
animator.setInterpolator(properties.interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the interpolator and length to the animator, such that the fling animation is
|
||||
* consistent with the finger motion for the case when the animation is making something
|
||||
* disappear.
|
||||
*
|
||||
* @param animator the animator to apply
|
||||
* @param currValue the current value
|
||||
* @param endValue the end value of the animator
|
||||
* @param velocity the current velocity of the motion
|
||||
* @param maxDistance the maximum distance for this interaction; the maximum animation length
|
||||
* gets multiplied by the ratio between the actual distance and this value
|
||||
*/
|
||||
public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
|
||||
float velocity, float maxDistance) {
|
||||
AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
|
||||
maxDistance);
|
||||
animator.setDuration(properties.duration);
|
||||
animator.setInterpolator(properties.interpolator);
|
||||
}
|
||||
|
||||
private AnimatorProperties getDismissingProperties(float currValue, float endValue,
|
||||
float velocity, float maxDistance) {
|
||||
float maxLengthSeconds = (float) (mMaxLengthSeconds
|
||||
* Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
|
||||
float diff = Math.abs(endValue - currValue);
|
||||
@@ -135,7 +208,7 @@ public class FlingAnimationUtils {
|
||||
Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, 1, y2);
|
||||
float durationSeconds = startGradient * diff / velAbs;
|
||||
if (durationSeconds <= maxLengthSeconds) {
|
||||
animator.setInterpolator(mLinearOutFasterIn);
|
||||
mAnimatorProperties.interpolator = mLinearOutFasterIn;
|
||||
} else if (velAbs >= mMinVelocityPxPerSecond) {
|
||||
|
||||
// Cross fade between linear-out-faster-in and linear interpolator with current
|
||||
@@ -145,14 +218,15 @@ public class FlingAnimationUtils {
|
||||
= new VelocityInterpolator(durationSeconds, velAbs, diff);
|
||||
InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
|
||||
velocityInterpolator, mLinearOutFasterIn, mLinearOutSlowIn);
|
||||
animator.setInterpolator(superInterpolator);
|
||||
mAnimatorProperties.interpolator = superInterpolator;
|
||||
} else {
|
||||
|
||||
// Just use a normal interpolator which doesn't take the velocity into account.
|
||||
durationSeconds = maxLengthSeconds;
|
||||
animator.setInterpolator(mFastOutLinearIn);
|
||||
mAnimatorProperties.interpolator = mFastOutLinearIn;
|
||||
}
|
||||
animator.setDuration((long) (durationSeconds * 1000));
|
||||
mAnimatorProperties.duration = (long) (durationSeconds * 1000);
|
||||
return mAnimatorProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,4 +295,10 @@ public class FlingAnimationUtils {
|
||||
return time * mVelocity / mDiff;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnimatorProperties {
|
||||
Interpolator interpolator;
|
||||
long duration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.MediaStore;
|
||||
@@ -33,26 +32,23 @@ import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.R;
|
||||
|
||||
/**
|
||||
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
|
||||
* text.
|
||||
*/
|
||||
public class KeyguardBottomAreaView extends FrameLayout
|
||||
implements SwipeAffordanceView.AffordanceListener,
|
||||
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
|
||||
UnlockMethodCache.OnUnlockMethodChangedListener {
|
||||
|
||||
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
|
||||
|
||||
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
|
||||
|
||||
private SwipeAffordanceView mCameraButton;
|
||||
private SwipeAffordanceView mPhoneButton;
|
||||
private ImageView mCameraImageView;
|
||||
private ImageView mPhoneImageView;
|
||||
private ImageView mLockIcon;
|
||||
|
||||
private PowerManager mPowerManager;
|
||||
private ActivityStarter mActivityStarter;
|
||||
private UnlockMethodCache mUnlockMethodCache;
|
||||
|
||||
@@ -76,11 +72,9 @@ public class KeyguardBottomAreaView extends FrameLayout
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mCameraButton = (SwipeAffordanceView) findViewById(R.id.camera_button);
|
||||
mPhoneButton = (SwipeAffordanceView) findViewById(R.id.phone_button);
|
||||
mCameraImageView = (ImageView) findViewById(R.id.camera_button);
|
||||
mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
|
||||
mLockIcon = (ImageView) findViewById(R.id.lock_icon);
|
||||
mCameraButton.setAffordanceListener(this);
|
||||
mPhoneButton.setAffordanceListener(this);
|
||||
watchForDevicePolicyChanges();
|
||||
watchForAccessibilityChanges();
|
||||
updateCameraVisibility();
|
||||
@@ -88,7 +82,6 @@ public class KeyguardBottomAreaView extends FrameLayout
|
||||
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
|
||||
mUnlockMethodCache.addListener(this);
|
||||
updateTrust();
|
||||
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
}
|
||||
|
||||
public void setActivityStarter(ActivityStarter activityStarter) {
|
||||
@@ -97,12 +90,12 @@ public class KeyguardBottomAreaView extends FrameLayout
|
||||
|
||||
private void updateCameraVisibility() {
|
||||
boolean visible = !isCameraDisabledByDpm();
|
||||
mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void updatePhoneVisibility() {
|
||||
boolean visible = isPhoneVisible();
|
||||
mPhoneButton.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private boolean isPhoneVisible() {
|
||||
@@ -162,33 +155,31 @@ public class KeyguardBottomAreaView extends FrameLayout
|
||||
}
|
||||
|
||||
private void enableAccessibility(boolean touchExplorationEnabled) {
|
||||
mCameraButton.enableAccessibility(touchExplorationEnabled);
|
||||
mPhoneButton.enableAccessibility(touchExplorationEnabled);
|
||||
mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
|
||||
mCameraImageView.setClickable(touchExplorationEnabled);
|
||||
mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
|
||||
mPhoneImageView.setClickable(touchExplorationEnabled);
|
||||
}
|
||||
|
||||
private void launchCamera() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == mCameraImageView) {
|
||||
launchCamera();
|
||||
} else if (v == mPhoneImageView) {
|
||||
launchPhone();
|
||||
}
|
||||
}
|
||||
|
||||
public void launchCamera() {
|
||||
mContext.startActivityAsUser(
|
||||
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
|
||||
UserHandle.CURRENT);
|
||||
}
|
||||
|
||||
private void launchPhone() {
|
||||
public void launchPhone() {
|
||||
mActivityStarter.startActivity(PHONE_INTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserActivity(long when) {
|
||||
mPowerManager.userActivity(when, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActionPerformed(SwipeAffordanceView view) {
|
||||
if (view == mCameraButton) {
|
||||
launchCamera();
|
||||
} else if (view == mPhoneButton) {
|
||||
launchPhone();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onVisibilityChanged(View changedView, int visibility) {
|
||||
@@ -208,6 +199,18 @@ public class KeyguardBottomAreaView extends FrameLayout
|
||||
mLockIcon.setImageResource(iconRes);
|
||||
}
|
||||
|
||||
public ImageView getPhoneImageView() {
|
||||
return mPhoneImageView;
|
||||
}
|
||||
|
||||
public ImageView getCameraImageView() {
|
||||
return mCameraImageView;
|
||||
}
|
||||
|
||||
public ImageView getLockIcon() {
|
||||
return mLockIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodSecureChanged(boolean methodSecure) {
|
||||
updateTrust();
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 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.os.PowerManager;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A touch handler of the Keyguard which is responsible for swiping the content left or right.
|
||||
*/
|
||||
public class KeyguardPageSwipeHelper {
|
||||
|
||||
private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
|
||||
private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
|
||||
private final Context mContext;
|
||||
|
||||
private FlingAnimationUtils mFlingAnimationUtils;
|
||||
private Callback mCallback;
|
||||
private int mTrackingPointer;
|
||||
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 PowerManager mPowerManager;
|
||||
private final View mLeftIcon;
|
||||
private final View mCenterIcon;
|
||||
private final View mRightIcon;
|
||||
private Interpolator mFastOutSlowIn;
|
||||
private Animator mSwipeAnimator;
|
||||
private boolean mCallbackCalled;
|
||||
|
||||
KeyguardPageSwipeHelper(Callback callback, Context context) {
|
||||
mContext = context;
|
||||
mCallback = callback;
|
||||
mLeftIcon = mCallback.getLeftIcon();
|
||||
mCenterIcon = mCallback.getCenterIcon();
|
||||
mRightIcon = mCallback.getRightIcon();
|
||||
updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
|
||||
updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
|
||||
updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
|
||||
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||
initDimens();
|
||||
}
|
||||
|
||||
private void initDimens() {
|
||||
final ViewConfiguration configuration = ViewConfiguration.get(mContext);
|
||||
mTouchSlop = configuration.getScaledTouchSlop();
|
||||
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
|
||||
mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.keyguard_min_swipe_amount);
|
||||
mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
|
||||
mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
int pointerIndex = event.findPointerIndex(mTrackingPointer);
|
||||
if (pointerIndex < 0) {
|
||||
pointerIndex = 0;
|
||||
mTrackingPointer = event.getPointerId(pointerIndex);
|
||||
}
|
||||
final float y = event.getY(pointerIndex);
|
||||
final float x = event.getX(pointerIndex);
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mSwipingInProgress) {
|
||||
cancelAnimations();
|
||||
}
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
mTranslationOnDown = mTranslation;
|
||||
initVelocityTracker();
|
||||
trackMovement(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
final int upPointer = event.getPointerId(event.getActionIndex());
|
||||
if (mTrackingPointer == upPointer) {
|
||||
// gesture is ongoing, find a new pointer to track
|
||||
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
|
||||
final float newY = event.getY(newIndex);
|
||||
final float newX = event.getX(newIndex);
|
||||
mTrackingPointer = event.getPointerId(newIndex);
|
||||
mInitialTouchY = newY;
|
||||
mInitialTouchX = newX;
|
||||
mTranslationOnDown = mTranslation;
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float w = x - mInitialTouchX;
|
||||
trackMovement(event);
|
||||
if (((leftSwipePossible() && w > mTouchSlop)
|
||||
|| (rightSwipePossible() && w < -mTouchSlop))
|
||||
&& Math.abs(w) > Math.abs(y - mInitialTouchY)
|
||||
&& !mSwipingInProgress) {
|
||||
cancelAnimations();
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
mTranslationOnDown = mTranslation;
|
||||
mSwipingInProgress = true;
|
||||
}
|
||||
if (mSwipingInProgress) {
|
||||
setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
|
||||
onUserActivity(event.getEventTime());
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mTrackingPointer = -1;
|
||||
trackMovement(event);
|
||||
if (mSwipingInProgress) {
|
||||
flingWithCurrentVelocity();
|
||||
}
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean rightSwipePossible() {
|
||||
return mRightIcon.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
private boolean leftSwipePossible() {
|
||||
return mLeftIcon.getVisibility() == View.VISIBLE;
|
||||
}
|
||||
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onUserActivity(long when) {
|
||||
mPowerManager.userActivity(when, false);
|
||||
}
|
||||
|
||||
private void cancelAnimations() {
|
||||
ArrayList<View> targetViews = mCallback.getTranslationViews();
|
||||
for (View target : targetViews) {
|
||||
target.animate().cancel();
|
||||
}
|
||||
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
|
||||
targetView.animate().cancel();
|
||||
if (mSwipeAnimator != null) {
|
||||
mSwipeAnimator.removeAllListeners();
|
||||
mSwipeAnimator.cancel();
|
||||
hideInactiveIcons(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void flingWithCurrentVelocity() {
|
||||
float vel = getCurrentVelocity();
|
||||
|
||||
// We snap back if the current translation is not far enough
|
||||
boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
private void fling(float vel, final boolean snapBack) {
|
||||
float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
|
||||
target = snapBack ? 0 : target;
|
||||
|
||||
// translation Animation
|
||||
startTranslationAnimations(vel, target);
|
||||
|
||||
// animate left / right icon
|
||||
startIconAnimation(vel, snapBack, 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(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mSwipeAnimator = null;
|
||||
mSwipingInProgress = false;
|
||||
if (!snapBack && !mCallbackCalled) {
|
||||
|
||||
// ensure that the callback is called eventually
|
||||
mCallback.onAnimationToSideStarted(mTranslation < 0);
|
||||
mCallbackCalled = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!snapBack) {
|
||||
mCallbackCalled = false;
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
int frameNumber;
|
||||
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
if (frameNumber == 2 && !mCallbackCalled) {
|
||||
|
||||
// we have to wait for the second frame for this call,
|
||||
// until the render thread has definitely kicked in, to avoid a lag.
|
||||
mCallback.onAnimationToSideStarted(mTranslation < 0);
|
||||
mCallbackCalled = true;
|
||||
}
|
||||
frameNumber++;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showAllIcons(true);
|
||||
}
|
||||
animator.start();
|
||||
mSwipeAnimator = animator;
|
||||
}
|
||||
|
||||
private void startTranslationAnimations(float vel, float target) {
|
||||
ArrayList<View> targetViews = mCallback.getTranslationViews();
|
||||
for (View targetView : targetViews) {
|
||||
ViewPropertyAnimator animator = targetView.animate();
|
||||
mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
|
||||
animator.translationX(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void startIconAnimation(float vel, boolean snapBack, float target) {
|
||||
float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
|
||||
float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
|
||||
View targetView = mTranslation > 0
|
||||
? mLeftIcon
|
||||
: mRightIcon;
|
||||
if (targetView.getVisibility() == View.VISIBLE) {
|
||||
ViewPropertyAnimator iconAnimator = targetView.animate();
|
||||
mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
|
||||
iconAnimator.scaleX(scale);
|
||||
iconAnimator.scaleY(scale);
|
||||
iconAnimator.alpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTranslation(float translation, boolean isReset) {
|
||||
translation = rightSwipePossible() ? translation : Math.max(0, translation);
|
||||
translation = leftSwipePossible() ? translation : Math.min(0, translation);
|
||||
if (translation != mTranslation) {
|
||||
ArrayList<View> translatedViews = mCallback.getTranslationViews();
|
||||
for (View view : translatedViews) {
|
||||
view.setTranslationX(translation);
|
||||
}
|
||||
if (translation == 0.0f) {
|
||||
boolean animate = !isReset;
|
||||
showAllIcons(animate);
|
||||
} else {
|
||||
View targetView = translation > 0 ? mLeftIcon : mRightIcon;
|
||||
float progress = Math.abs(translation) / mCallback.getPageWidth();
|
||||
progress = Math.min(progress, 1.0f);
|
||||
float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
|
||||
float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
|
||||
updateIcon(targetView, scale, alpha, false);
|
||||
View otherView = translation < 0 ? mLeftIcon : mRightIcon;
|
||||
if (mTranslation * translation <= 0) {
|
||||
// The sign of the translation has changed so we need to hide the other icons
|
||||
updateIcon(otherView, 0, 0, true);
|
||||
updateIcon(mCenterIcon, 0, 0, true);
|
||||
}
|
||||
}
|
||||
mTranslation = translation;
|
||||
}
|
||||
}
|
||||
|
||||
private void showAllIcons(boolean animate) {
|
||||
float scale = 1.0f;
|
||||
float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
|
||||
updateIcon(mRightIcon, scale, alpha, animate);
|
||||
updateIcon(mCenterIcon, scale, alpha, animate);
|
||||
updateIcon(mLeftIcon, scale, alpha, animate);
|
||||
}
|
||||
|
||||
private void hideInactiveIcons(boolean animate){
|
||||
View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
|
||||
updateIcon(otherView, 0, 0, animate);
|
||||
updateIcon(mCenterIcon, 0, 0, animate);
|
||||
}
|
||||
|
||||
private void updateIcon(View view, float scale, float alpha, boolean animate) {
|
||||
if (view.getVisibility() != View.VISIBLE) {
|
||||
return;
|
||||
}
|
||||
if (!animate) {
|
||||
view.setAlpha(alpha);
|
||||
view.setScaleX(scale);
|
||||
view.setScaleY(scale);
|
||||
// TODO: remove this invalidate once the property setters invalidate it properly
|
||||
view.invalidate();
|
||||
} else {
|
||||
if (view.getAlpha() != alpha || view.getScaleX() != scale) {
|
||||
view.animate()
|
||||
.setInterpolator(mFastOutSlowIn)
|
||||
.alpha(alpha)
|
||||
.scaleX(scale)
|
||||
.scaleY(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if (mVelocityTracker == null) {
|
||||
return 0;
|
||||
}
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
return mVelocityTracker.getXVelocity();
|
||||
}
|
||||
|
||||
public void onConfigurationChanged() {
|
||||
initDimens();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
setTranslation(0.0f, true);
|
||||
mSwipingInProgress = false;
|
||||
}
|
||||
|
||||
public boolean isSwipingInProgress() {
|
||||
return mSwipingInProgress;
|
||||
}
|
||||
|
||||
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 getPageWidth();
|
||||
|
||||
ArrayList<View> getTranslationViews();
|
||||
|
||||
View getLeftIcon();
|
||||
|
||||
View getCenterIcon();
|
||||
|
||||
View getRightIcon();
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
@@ -40,10 +41,13 @@ import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NotificationPanelView extends PanelView implements
|
||||
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
|
||||
View.OnClickListener {
|
||||
View.OnClickListener, KeyguardPageSwipeHelper.Callback {
|
||||
|
||||
private KeyguardPageSwipeHelper mPageSwiper;
|
||||
PhoneStatusBar mStatusBar;
|
||||
private StatusBarHeaderView mHeader;
|
||||
private View mQsContainer;
|
||||
@@ -58,7 +62,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
|
||||
private int mTrackingPointer;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private boolean mTracking;
|
||||
private boolean mQsTracking;
|
||||
|
||||
/**
|
||||
* Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
|
||||
@@ -92,6 +96,11 @@ public class NotificationPanelView extends PanelView implements
|
||||
new KeyguardClockPositionAlgorithm();
|
||||
private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
|
||||
new KeyguardClockPositionAlgorithm.Result();
|
||||
private boolean mIsSwipedHorizontally;
|
||||
private boolean mIsExpanding;
|
||||
private KeyguardBottomAreaView mKeyguardBottomArea;
|
||||
private boolean mBlockTouches;
|
||||
private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
|
||||
|
||||
public NotificationPanelView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -129,6 +138,10 @@ public class NotificationPanelView extends PanelView implements
|
||||
mNotificationStackScroller.setOnHeightChangedListener(this);
|
||||
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
|
||||
android.R.interpolator.fast_out_slow_in);
|
||||
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
|
||||
mSwipeTranslationViews.add(mNotificationStackScroller);
|
||||
mSwipeTranslationViews.add(mKeyguardStatusView);
|
||||
mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,6 +260,12 @@ public class NotificationPanelView extends PanelView implements
|
||||
mQsExpansionEnabled = qsExpansionEnabled;
|
||||
}
|
||||
|
||||
public void resetViews() {
|
||||
mBlockTouches = false;
|
||||
mPageSwiper.reset();
|
||||
closeQs();
|
||||
}
|
||||
|
||||
public void closeQs() {
|
||||
cancelAnimation();
|
||||
setQsExpansion(mQsMinExpansionHeight);
|
||||
@@ -263,9 +282,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
public void fling(float vel, boolean always) {
|
||||
GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
|
||||
if (gr != null) {
|
||||
gr.tag(
|
||||
"fling " + ((vel > 0) ? "open" : "closed"),
|
||||
"notifications,v=" + vel);
|
||||
gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
|
||||
}
|
||||
super.fling(vel, always);
|
||||
}
|
||||
@@ -283,6 +300,9 @@ public class NotificationPanelView extends PanelView implements
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
if (mBlockTouches) {
|
||||
return false;
|
||||
}
|
||||
int pointerIndex = event.findPointerIndex(mTrackingPointer);
|
||||
if (pointerIndex < 0) {
|
||||
pointerIndex = 0;
|
||||
@@ -298,7 +318,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
mInitialTouchX = x;
|
||||
initVelocityTracker();
|
||||
trackMovement(event);
|
||||
if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
|
||||
if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
break;
|
||||
@@ -316,7 +336,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float h = y - mInitialTouchY;
|
||||
trackMovement(event);
|
||||
if (mTracking) {
|
||||
if (mQsTracking) {
|
||||
|
||||
// Already tracking because onOverscrolled was called. We need to update here
|
||||
// so we don't stop for a frame until the next touch event gets handled in
|
||||
@@ -327,12 +347,12 @@ public class NotificationPanelView extends PanelView implements
|
||||
return true;
|
||||
}
|
||||
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
|
||||
&& shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
|
||||
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
|
||||
onQsExpansionStarted();
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
mTracking = true;
|
||||
mQsTracking = true;
|
||||
mIntercepting = false;
|
||||
return true;
|
||||
}
|
||||
@@ -341,9 +361,9 @@ public class NotificationPanelView extends PanelView implements
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP:
|
||||
trackMovement(event);
|
||||
if (mTracking) {
|
||||
flingWithCurrentVelocity();
|
||||
mTracking = false;
|
||||
if (mQsTracking) {
|
||||
flingQsWithCurrentVelocity();
|
||||
mQsTracking = false;
|
||||
}
|
||||
mIntercepting = false;
|
||||
break;
|
||||
@@ -362,7 +382,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
super.requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
|
||||
private void flingWithCurrentVelocity() {
|
||||
private void flingQsWithCurrentVelocity() {
|
||||
float vel = getCurrentVelocity();
|
||||
|
||||
// TODO: Better logic whether we should expand or not.
|
||||
@@ -371,65 +391,83 @@ public class NotificationPanelView extends PanelView implements
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (mBlockTouches) {
|
||||
return false;
|
||||
}
|
||||
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
|
||||
// implementation.
|
||||
if (mTracking) {
|
||||
int pointerIndex = event.findPointerIndex(mTrackingPointer);
|
||||
if (pointerIndex < 0) {
|
||||
pointerIndex = 0;
|
||||
mTrackingPointer = event.getPointerId(pointerIndex);
|
||||
if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
|
||||
mPageSwiper.onTouchEvent(event);
|
||||
if (mPageSwiper.isSwipingInProgress()) {
|
||||
return true;
|
||||
}
|
||||
final float y = event.getY(pointerIndex);
|
||||
final float x = event.getX(pointerIndex);
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mTracking = true;
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
onQsExpansionStarted();
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
initVelocityTracker();
|
||||
trackMovement(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
final int upPointer = event.getPointerId(event.getActionIndex());
|
||||
if (mTrackingPointer == upPointer) {
|
||||
// gesture is ongoing, find a new pointer to track
|
||||
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
|
||||
final float newY = event.getY(newIndex);
|
||||
final float newX = event.getX(newIndex);
|
||||
mTrackingPointer = event.getPointerId(newIndex);
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
mInitialTouchY = newY;
|
||||
mInitialTouchX = newX;
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float h = y - mInitialTouchY;
|
||||
setQsExpansion(h + mInitialHeightOnTouch);
|
||||
trackMovement(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mTracking = false;
|
||||
mTrackingPointer = -1;
|
||||
trackMovement(event);
|
||||
flingWithCurrentVelocity();
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (mQsTracking || mQsExpanded) {
|
||||
return onQsTouch(event);
|
||||
}
|
||||
|
||||
// Consume touch events when QS are expanded.
|
||||
return mQsExpanded || super.onTouchEvent(event);
|
||||
super.onTouchEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasConflictingGestures() {
|
||||
return mStatusBar.getBarState() != StatusBarState.SHADE;
|
||||
}
|
||||
|
||||
private boolean onQsTouch(MotionEvent event) {
|
||||
int pointerIndex = event.findPointerIndex(mTrackingPointer);
|
||||
if (pointerIndex < 0) {
|
||||
pointerIndex = 0;
|
||||
mTrackingPointer = event.getPointerId(pointerIndex);
|
||||
}
|
||||
final float y = event.getY(pointerIndex);
|
||||
final float x = event.getX(pointerIndex);
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mQsTracking = true;
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
onQsExpansionStarted();
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
initVelocityTracker();
|
||||
trackMovement(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
final int upPointer = event.getPointerId(event.getActionIndex());
|
||||
if (mTrackingPointer == upPointer) {
|
||||
// gesture is ongoing, find a new pointer to track
|
||||
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
|
||||
final float newY = event.getY(newIndex);
|
||||
final float newX = event.getX(newIndex);
|
||||
mTrackingPointer = event.getPointerId(newIndex);
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
mInitialTouchY = newY;
|
||||
mInitialTouchX = newX;
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float h = y - mInitialTouchY;
|
||||
setQsExpansion(h + mInitialHeightOnTouch);
|
||||
trackMovement(event);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mQsTracking = false;
|
||||
mTrackingPointer = -1;
|
||||
trackMovement(event);
|
||||
flingQsWithCurrentVelocity();
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,7 +477,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
mInitialHeightOnTouch = mQsExpansionHeight;
|
||||
mInitialTouchY = mLastTouchY;
|
||||
mInitialTouchX = mLastTouchX;
|
||||
mTracking = true;
|
||||
mQsTracking = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +607,7 @@ public class NotificationPanelView extends PanelView implements
|
||||
/**
|
||||
* @return Whether we should intercept a gesture to open Quick Settings.
|
||||
*/
|
||||
private boolean shouldIntercept(float x, float y, float yDiff) {
|
||||
private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
|
||||
if (!mQsExpansionEnabled) {
|
||||
return false;
|
||||
}
|
||||
@@ -647,12 +685,14 @@ public class NotificationPanelView extends PanelView implements
|
||||
protected void onExpandingStarted() {
|
||||
super.onExpandingStarted();
|
||||
mNotificationStackScroller.onExpansionStarted();
|
||||
mIsExpanding = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onExpandingFinished() {
|
||||
super.onExpandingFinished();
|
||||
mNotificationStackScroller.onExpansionStopped();
|
||||
mIsExpanding = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -685,6 +725,12 @@ public class NotificationPanelView extends PanelView implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
mPageSwiper.onConfigurationChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == mHeader.getBackgroundView()) {
|
||||
@@ -696,4 +742,40 @@ public class NotificationPanelView extends PanelView implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationToSideStarted(boolean rightPage) {
|
||||
if (rightPage) {
|
||||
mKeyguardBottomArea.launchCamera();
|
||||
} else {
|
||||
mKeyguardBottomArea.launchPhone();
|
||||
}
|
||||
mBlockTouches = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float getPageWidth() {
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<View> getTranslationViews() {
|
||||
return mSwipeTranslationViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getLeftIcon() {
|
||||
return mKeyguardBottomArea.getPhoneImageView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getCenterIcon() {
|
||||
return mKeyguardBottomArea.getLockIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getRightIcon() {
|
||||
return mKeyguardBottomArea.getCameraImageView();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class PanelView extends FrameLayout {
|
||||
public abstract class PanelView extends FrameLayout {
|
||||
public static final boolean DEBUG = PanelBar.DEBUG;
|
||||
public static final String TAG = PanelView.class.getSimpleName();
|
||||
protected float mOverExpansion;
|
||||
@@ -130,21 +130,24 @@ public class PanelView extends FrameLayout {
|
||||
final float y = event.getY(pointerIndex);
|
||||
final float x = event.getX(pointerIndex);
|
||||
|
||||
boolean waitForTouchSlop = hasConflictingGestures();
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mTracking = true;
|
||||
|
||||
mInitialTouchY = y;
|
||||
mInitialTouchX = x;
|
||||
mInitialOffsetOnTouch = mExpandedHeight;
|
||||
if (mVelocityTracker == null) {
|
||||
initVelocityTracker();
|
||||
}
|
||||
trackMovement(event);
|
||||
if (mHeightAnimator != null) {
|
||||
mHeightAnimator.cancel(); // end any outstanding animations
|
||||
if (!waitForTouchSlop || mHeightAnimator != null) {
|
||||
if (mHeightAnimator != null) {
|
||||
mHeightAnimator.cancel(); // end any outstanding animations
|
||||
}
|
||||
onTrackingStarted();
|
||||
}
|
||||
onTrackingStarted();
|
||||
mInitialOffsetOnTouch = mExpandedHeight;
|
||||
if (mExpandedHeight == 0) {
|
||||
mJustPeeked = true;
|
||||
runPeekAnimation();
|
||||
@@ -166,15 +169,27 @@ public class PanelView extends FrameLayout {
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
|
||||
if (h > mPeekHeight) {
|
||||
float h = y - mInitialTouchY;
|
||||
if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
|
||||
&& Math.abs(h) > Math.abs(x - mInitialTouchX)) {
|
||||
mInitialOffsetOnTouch = mExpandedHeight;
|
||||
mInitialTouchX = x;
|
||||
mInitialTouchY = y;
|
||||
if (mHeightAnimator != null) {
|
||||
mHeightAnimator.cancel(); // end any outstanding animations
|
||||
}
|
||||
onTrackingStarted();
|
||||
h = 0;
|
||||
}
|
||||
final float newHeight = h + mInitialOffsetOnTouch;
|
||||
if (newHeight > mPeekHeight) {
|
||||
if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
|
||||
mPeekAnimator.cancel();
|
||||
}
|
||||
mJustPeeked = false;
|
||||
}
|
||||
if (!mJustPeeked) {
|
||||
setExpandedHeightInternal(h);
|
||||
if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
|
||||
setExpandedHeightInternal(newHeight);
|
||||
mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
|
||||
}
|
||||
|
||||
@@ -183,7 +198,6 @@ public class PanelView extends FrameLayout {
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mTracking = false;
|
||||
mTrackingPointer = -1;
|
||||
trackMovement(event);
|
||||
boolean expand = flingWithCurrentVelocity();
|
||||
@@ -194,14 +208,18 @@ public class PanelView extends FrameLayout {
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
return !waitForTouchSlop || mTracking;
|
||||
}
|
||||
|
||||
protected abstract boolean hasConflictingGestures();
|
||||
|
||||
protected void onTrackingStopped(boolean expand) {
|
||||
mTracking = false;
|
||||
mBar.onTrackingStopped(PanelView.this, expand);
|
||||
}
|
||||
|
||||
protected void onTrackingStarted() {
|
||||
mTracking = true;
|
||||
mBar.onTrackingStarted(PanelView.this);
|
||||
onExpandingStarted();
|
||||
}
|
||||
|
||||
@@ -2774,7 +2774,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|
||||
mKeyguardStatusView.setVisibility(View.VISIBLE);
|
||||
mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
|
||||
mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
|
||||
mNotificationPanel.closeQs();
|
||||
mNotificationPanel.resetViews();
|
||||
} else {
|
||||
mKeyguardStatusView.setVisibility(View.GONE);
|
||||
mKeyguardIndicationTextView.setVisibility(View.GONE);
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* 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.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.policy.KeyButtonView;
|
||||
|
||||
/**
|
||||
* A swipeable button for affordances on the lockscreen. This is used for the camera and phone
|
||||
* affordance.
|
||||
*/
|
||||
public class SwipeAffordanceView extends KeyButtonView {
|
||||
|
||||
private static final int SWIPE_DIRECTION_START = 0;
|
||||
private static final int SWIPE_DIRECTION_END = 1;
|
||||
|
||||
private static final int SWIPE_DIRECTION_LEFT = 0;
|
||||
private static final int SWIPE_DIRECTION_RIGHT = 1;
|
||||
|
||||
private AffordanceListener mListener;
|
||||
private int mScaledTouchSlop;
|
||||
private float mDragDistance;
|
||||
private int mResolvedSwipeDirection;
|
||||
private int mSwipeDirection;
|
||||
|
||||
public SwipeAffordanceView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SwipeAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.SwipeAffordanceView,
|
||||
0, 0);
|
||||
try {
|
||||
mSwipeDirection = a.getInt(R.styleable.SwipeAffordanceView_swipeDirection, 0);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRtlPropertiesChanged(int layoutDirection) {
|
||||
super.onRtlPropertiesChanged(layoutDirection);
|
||||
if (!isLayoutRtl()) {
|
||||
mResolvedSwipeDirection = mSwipeDirection;
|
||||
} else {
|
||||
mResolvedSwipeDirection = mSwipeDirection == SWIPE_DIRECTION_START
|
||||
? SWIPE_DIRECTION_RIGHT
|
||||
: SWIPE_DIRECTION_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mDragDistance = getResources().getDimension(R.dimen.affordance_drag_distance);
|
||||
mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
|
||||
}
|
||||
|
||||
public void enableAccessibility(boolean touchExplorationEnabled) {
|
||||
|
||||
// Add a touch handler or accessibility click listener for camera button.
|
||||
if (touchExplorationEnabled) {
|
||||
setOnTouchListener(null);
|
||||
setOnClickListener(mClickListener);
|
||||
} else {
|
||||
setOnTouchListener(mTouchListener);
|
||||
setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAffordanceListener(AffordanceListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void onActionPerformed() {
|
||||
if (mListener != null) {
|
||||
mListener.onActionPerformed(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void onUserActivity(long when) {
|
||||
if (mListener != null) {
|
||||
mListener.onUserActivity(when);
|
||||
}
|
||||
}
|
||||
|
||||
private final OnClickListener mClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onActionPerformed();
|
||||
}
|
||||
};
|
||||
|
||||
private final OnTouchListener mTouchListener = new OnTouchListener() {
|
||||
private float mStartX;
|
||||
private boolean mTouchSlopReached;
|
||||
private boolean mSkipCancelAnimation;
|
||||
|
||||
@Override
|
||||
public boolean onTouch(final View view, MotionEvent event) {
|
||||
float realX = event.getRawX();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mStartX = realX;
|
||||
mTouchSlopReached = false;
|
||||
mSkipCancelAnimation = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? realX > mStartX
|
||||
: realX < mStartX) {
|
||||
realX = mStartX;
|
||||
}
|
||||
if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? realX < mStartX - mDragDistance
|
||||
: realX > mStartX + mDragDistance) {
|
||||
view.setPressed(true);
|
||||
onUserActivity(event.getEventTime());
|
||||
} else {
|
||||
view.setPressed(false);
|
||||
}
|
||||
if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? realX < mStartX - mScaledTouchSlop
|
||||
: realX > mStartX + mScaledTouchSlop) {
|
||||
mTouchSlopReached = true;
|
||||
}
|
||||
view.setTranslationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? Math.max(realX - mStartX, -mDragDistance)
|
||||
: Math.min(realX - mStartX, mDragDistance));
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? realX < mStartX - mDragDistance
|
||||
: realX > mStartX + mDragDistance) {
|
||||
onActionPerformed();
|
||||
view.animate().x(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? -view.getWidth()
|
||||
: ((View) view.getParent()).getWidth() + view.getWidth())
|
||||
.setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
view.setTranslationX(0);
|
||||
}
|
||||
});
|
||||
mSkipCancelAnimation = true;
|
||||
}
|
||||
if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? realX < mStartX - mScaledTouchSlop
|
||||
: realX > mStartX + mScaledTouchSlop) {
|
||||
mTouchSlopReached = true;
|
||||
}
|
||||
if (!mTouchSlopReached) {
|
||||
mSkipCancelAnimation = true;
|
||||
view.animate().translationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
|
||||
? -mDragDistance / 2
|
||||
: mDragDistance / 2).
|
||||
setInterpolator(new DecelerateInterpolator()).withEndAction(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
view.animate().translationX(0).
|
||||
setInterpolator(new AccelerateInterpolator());
|
||||
}
|
||||
});
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
view.setPressed(false);
|
||||
if (!mSkipCancelAnimation) {
|
||||
view.animate().translationX(0)
|
||||
.setInterpolator(new AccelerateInterpolator(2f));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public interface AffordanceListener {
|
||||
|
||||
/**
|
||||
* Called when the view would like to report user activity.
|
||||
*
|
||||
* @param when The timestamp of the user activity in {@link SystemClock#uptimeMillis} time
|
||||
* base.
|
||||
*/
|
||||
void onUserActivity(long when);
|
||||
|
||||
/**
|
||||
* Called when the action of the affordance has been performed.
|
||||
*/
|
||||
void onActionPerformed(SwipeAffordanceView view);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user