diff options
Diffstat (limited to 'packages/Keyguard/src')
55 files changed, 1077 insertions, 13257 deletions
diff --git a/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedImageButton.java b/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedImageButton.java index eda790f..58c79b4 100644 --- a/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedImageButton.java +++ b/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedImageButton.java @@ -19,7 +19,6 @@ package com.android.keyguard; import android.content.Context; import android.util.AttributeSet; import android.widget.ImageButton; -import android.widget.RelativeLayout; /** * A frame layout which does not have overlapping renderings commands and therefore does not need a diff --git a/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedLinearLayout.java b/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedLinearLayout.java index 36da6f1..2c6c4fa 100644 --- a/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedLinearLayout.java +++ b/packages/Keyguard/src/com/android/keyguard/AlphaOptimizedLinearLayout.java @@ -18,7 +18,6 @@ package com.android.keyguard; import android.content.Context; import android.util.AttributeSet; -import android.view.View; import android.widget.LinearLayout; /** diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java deleted file mode 100644 index 3ff2cc0..0000000 --- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationCreator.java +++ /dev/null @@ -1,30 +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.keyguard; - -import android.animation.Animator; -import android.view.animation.Interpolator; - -/** - * An interface which can create animations when starting an appear animation with - * {@link com.android.keyguard.AppearAnimationUtils} - */ -public interface AppearAnimationCreator<T> { - void createAnimation(T animatedObject, long delay, long duration, - float translationY, boolean appearing, Interpolator interpolator, - Runnable finishListener); -} diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java deleted file mode 100644 index 9045fe3..0000000 --- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java +++ /dev/null @@ -1,196 +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.keyguard; - -import android.content.Context; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -/** - * A class to make nice appear transitions for views in a tabular layout. - */ -public class AppearAnimationUtils implements AppearAnimationCreator<View> { - - public static final long DEFAULT_APPEAR_DURATION = 220; - - private final Interpolator mInterpolator; - private final float mStartTranslation; - private final AppearAnimationProperties mProperties = new AppearAnimationProperties(); - protected final float mDelayScale; - private final long mDuration; - protected boolean mScaleTranslationWithRow; - protected boolean mAppearing; - - public AppearAnimationUtils(Context ctx) { - this(ctx, DEFAULT_APPEAR_DURATION, - 1.0f, 1.0f, - AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in)); - } - - public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, - float delayScaleFactor, Interpolator interpolator) { - mInterpolator = interpolator; - mStartTranslation = ctx.getResources().getDimensionPixelOffset( - R.dimen.appear_y_translation_start) * translationScaleFactor; - mDelayScale = delayScaleFactor; - mDuration = duration; - mScaleTranslationWithRow = false; - mAppearing = true; - } - - public void startAnimation(View[][] objects, final Runnable finishListener) { - startAnimation(objects, finishListener, this); - } - - public void startAnimation(View[] objects, final Runnable finishListener) { - startAnimation(objects, finishListener, this); - } - - public <T> void startAnimation(T[][] objects, final Runnable finishListener, - AppearAnimationCreator<T> creator) { - AppearAnimationProperties properties = getDelays(objects); - startAnimations(properties, objects, finishListener, creator); - } - - public <T> void startAnimation(T[] objects, final Runnable finishListener, - AppearAnimationCreator<T> creator) { - AppearAnimationProperties properties = getDelays(objects); - startAnimations(properties, objects, finishListener, creator); - } - - private <T> void startAnimations(AppearAnimationProperties properties, T[] objects, - final Runnable finishListener, AppearAnimationCreator<T> creator) { - if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { - finishListener.run(); - return; - } - for (int row = 0; row < properties.delays.length; row++) { - long[] columns = properties.delays[row]; - long delay = columns[0]; - Runnable endRunnable = null; - if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) { - endRunnable = finishListener; - } - creator.createAnimation(objects[row], delay, mDuration, - mStartTranslation, true /* appearing */, mInterpolator, endRunnable); - } - } - - private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects, - final Runnable finishListener, AppearAnimationCreator<T> creator) { - if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { - finishListener.run(); - return; - } - for (int row = 0; row < properties.delays.length; row++) { - long[] columns = properties.delays[row]; - float translation = mScaleTranslationWithRow - ? (float) (Math.pow((properties.delays.length - row), 2) - / properties.delays.length * mStartTranslation) - : mStartTranslation; - for (int col = 0; col < columns.length; col++) { - long delay = columns[col]; - Runnable endRunnable = null; - if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) { - endRunnable = finishListener; - } - creator.createAnimation(objects[row][col], delay, mDuration, - mAppearing ? translation : -translation, - mAppearing, mInterpolator, endRunnable); - } - } - } - - private <T> AppearAnimationProperties getDelays(T[] items) { - long maxDelay = -1; - mProperties.maxDelayColIndex = -1; - mProperties.maxDelayRowIndex = -1; - mProperties.delays = new long[items.length][]; - for (int row = 0; row < items.length; row++) { - mProperties.delays[row] = new long[1]; - long delay = calculateDelay(row, 0); - mProperties.delays[row][0] = delay; - if (items[row] != null && delay > maxDelay) { - maxDelay = delay; - mProperties.maxDelayColIndex = 0; - mProperties.maxDelayRowIndex = row; - } - } - return mProperties; - } - - private <T> AppearAnimationProperties getDelays(T[][] items) { - long maxDelay = -1; - mProperties.maxDelayColIndex = -1; - mProperties.maxDelayRowIndex = -1; - mProperties.delays = new long[items.length][]; - for (int row = 0; row < items.length; row++) { - T[] columns = items[row]; - mProperties.delays[row] = new long[columns.length]; - for (int col = 0; col < columns.length; col++) { - long delay = calculateDelay(row, col); - mProperties.delays[row][col] = delay; - if (items[row][col] != null && delay > maxDelay) { - maxDelay = delay; - mProperties.maxDelayColIndex = col; - mProperties.maxDelayRowIndex = row; - } - } - } - return mProperties; - } - - protected long calculateDelay(int row, int col) { - return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale); - } - - public Interpolator getInterpolator() { - return mInterpolator; - } - - public float getStartTranslation() { - return mStartTranslation; - } - - @Override - public void createAnimation(View view, long delay, long duration, float translationY, - boolean appearing, Interpolator interpolator, Runnable endRunnable) { - if (view != null) { - view.setAlpha(appearing ? 0f : 1.0f); - view.setTranslationY(appearing ? translationY : 0); - view.animate() - .alpha(appearing ? 1f : 0f) - .translationY(appearing ? 0 : translationY) - .setInterpolator(interpolator) - .setDuration(duration) - .setStartDelay(delay); - if (view.hasOverlappingRendering()) { - view.animate().withLayer(); - } - if (endRunnable != null) { - view.animate().withEndAction(endRunnable); - } - } - } - - public class AppearAnimationProperties { - public long[][] delays; - public int maxDelayRowIndex; - public int maxDelayColIndex; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java b/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java deleted file mode 100644 index 230ef81..0000000 --- a/packages/Keyguard/src/com/android/keyguard/BiometricSensorUnlock.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.view.View; - -interface BiometricSensorUnlock { - /** - * Initializes the view provided for the biometric unlock UI to work within. The provided area - * completely covers the backup unlock mechanism. - * @param biometricUnlockView View provided for the biometric unlock UI. - */ - public void initializeView(View biometricUnlockView); - - /** - * Indicates whether the biometric unlock is running. Before - * {@link BiometricSensorUnlock#start} is called, isRunning() returns false. After a successful - * call to {@link BiometricSensorUnlock#start}, isRunning() returns true until the biometric - * unlock completes, {@link BiometricSensorUnlock#stop} has been called, or an error has - * forced the biometric unlock to stop. - * @return whether the biometric unlock is currently running. - */ - public boolean isRunning(); - - /** - * Stops and removes the biometric unlock and shows the backup unlock - */ - public void stopAndShowBackup(); - - /** - * Binds to the biometric unlock service and starts the unlock procedure. Called on the UI - * thread. - * @return false if it can't be started or the backup should be used. - */ - public boolean start(); - - /** - * Stops the biometric unlock procedure and unbinds from the service. Called on the UI thread. - * @return whether the biometric unlock was running when called. - */ - public boolean stop(); - - /** - * Cleans up any resources used by the biometric unlock. - */ - public void cleanUp(); - - /** - * Gets the Device Policy Manager quality of the biometric unlock sensor - * (e.g., PASSWORD_QUALITY_BIOMETRIC_WEAK). - * @return biometric unlock sensor quality, as defined by Device Policy Manager. - */ - public int getQuality(); -} diff --git a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java deleted file mode 100644 index 2bf74ea..0000000 --- a/packages/Keyguard/src/com/android/keyguard/CameraWidgetFrame.java +++ /dev/null @@ -1,523 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Handler; -import android.os.SystemClock; -import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; - -import com.android.keyguard.KeyguardActivityLauncher.CameraWidgetInfo; - -public class CameraWidgetFrame extends KeyguardWidgetFrame implements View.OnClickListener { - private static final String TAG = CameraWidgetFrame.class.getSimpleName(); - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final int WIDGET_ANIMATION_DURATION = 250; // ms - private static final int WIDGET_WAIT_DURATION = 400; // ms - private static final int RECOVERY_DELAY = 1000; // ms - - interface Callbacks { - void onLaunchingCamera(); - void onCameraLaunchedSuccessfully(); - void onCameraLaunchedUnsuccessfully(); - } - - private final Handler mHandler = new Handler(); - private final KeyguardActivityLauncher mActivityLauncher; - private final Callbacks mCallbacks; - private final CameraWidgetInfo mWidgetInfo; - private final WindowManager mWindowManager; - private final Point mRenderedSize = new Point(); - private final int[] mTmpLoc = new int[2]; - - private long mLaunchCameraStart; - private boolean mActive; - private boolean mTransitioning; - private boolean mDown; - - private final Rect mInsets = new Rect(); - - private FixedSizeFrameLayout mPreview; - private View mFullscreenPreview; - private View mFakeNavBar; - private boolean mUseFastTransition; - - private final Runnable mTransitionToCameraRunnable = new Runnable() { - @Override - public void run() { - transitionToCamera(); - }}; - - private final Runnable mTransitionToCameraEndAction = new Runnable() { - @Override - public void run() { - if (!mTransitioning) - return; - Handler worker = getWorkerHandler() != null ? getWorkerHandler() : mHandler; - mLaunchCameraStart = SystemClock.uptimeMillis(); - if (DEBUG) Log.d(TAG, "Launching camera at " + mLaunchCameraStart); - mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable); - }}; - - private final Runnable mPostTransitionToCameraEndAction = new Runnable() { - @Override - public void run() { - mHandler.post(mTransitionToCameraEndAction); - }}; - - private final Runnable mRecoverRunnable = new Runnable() { - @Override - public void run() { - recover(); - }}; - - private final Runnable mRenderRunnable = new Runnable() { - @Override - public void run() { - render(); - }}; - - private final Runnable mSecureCameraActivityStartedRunnable = new Runnable() { - @Override - public void run() { - onSecureCameraActivityStarted(); - } - }; - - private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { - private boolean mShowing; - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - if (mShowing == showing) - return; - mShowing = showing; - CameraWidgetFrame.this.onKeyguardVisibilityChanged(mShowing); - } - }; - - private static final class FixedSizeFrameLayout extends FrameLayout { - int width; - int height; - - FixedSizeFrameLayout(Context context) { - super(context); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - measureChildren( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - setMeasuredDimension(width, height); - } - } - - private CameraWidgetFrame(Context context, Callbacks callbacks, - KeyguardActivityLauncher activityLauncher, - CameraWidgetInfo widgetInfo, View previewWidget) { - super(context); - mCallbacks = callbacks; - mActivityLauncher = activityLauncher; - mWidgetInfo = widgetInfo; - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback); - - mPreview = new FixedSizeFrameLayout(context); - mPreview.addView(previewWidget); - addView(mPreview); - - View clickBlocker = new View(context); - clickBlocker.setBackgroundColor(Color.TRANSPARENT); - clickBlocker.setOnClickListener(this); - addView(clickBlocker); - - setContentDescription(context.getString(R.string.keyguard_accessibility_camera)); - if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId()); - } - - public static CameraWidgetFrame create(Context context, Callbacks callbacks, - KeyguardActivityLauncher launcher) { - if (context == null || callbacks == null || launcher == null) - return null; - - CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo(); - if (widgetInfo == null) - return null; - View previewWidget = getPreviewWidget(context, widgetInfo); - if (previewWidget == null) - return null; - - return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget); - } - - private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) { - return widgetInfo.layoutId > 0 ? - inflateWidgetView(context, widgetInfo) : - inflateGenericWidgetView(context); - } - - private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) { - if (DEBUG) Log.d(TAG, "inflateWidgetView: " + widgetInfo.contextPackage); - View widgetView = null; - Exception exception = null; - try { - Context cameraContext = context.createPackageContext( - widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED); - LayoutInflater cameraInflater = (LayoutInflater) - cameraContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - cameraInflater = cameraInflater.cloneInContext(cameraContext); - widgetView = cameraInflater.inflate(widgetInfo.layoutId, null, false); - } catch (NameNotFoundException e) { - exception = e; - } catch (RuntimeException e) { - exception = e; - } - if (exception != null) { - Log.w(TAG, "Error creating camera widget view", exception); - } - return widgetView; - } - - private static View inflateGenericWidgetView(Context context) { - if (DEBUG) Log.d(TAG, "inflateGenericWidgetView"); - ImageView iv = new ImageView(context); - iv.setImageResource(R.drawable.ic_lockscreen_camera); - iv.setScaleType(ScaleType.CENTER); - iv.setBackgroundColor(Color.argb(127, 0, 0, 0)); - return iv; - } - - private void render() { - final View root = getRootView(); - final int width = root.getWidth() - mInsets.right; // leave room - final int height = root.getHeight() - mInsets.bottom; // for bars - if (mRenderedSize.x == width && mRenderedSize.y == height) { - if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s %d%%", - width, height, (int)(100*mPreview.getScaleX()))); - return; - } - if (width == 0 || height == 0) { - return; - } - - mPreview.width = width; - mPreview.height = height; - mPreview.requestLayout(); - - final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight(); - final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - - final float pvScaleX = (float) thisWidth / width; - final float pvScaleY = (float) thisHeight / height; - final float pvScale = Math.min(pvScaleX, pvScaleY); - - final int pvWidth = (int) (pvScale * width); - final int pvHeight = (int) (pvScale * height); - - final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0; - final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0; - - final boolean isRtl = mPreview.getLayoutDirection() == LAYOUT_DIRECTION_RTL; - mPreview.setPivotX(isRtl ? mPreview.width : 0); - mPreview.setPivotY(0); - mPreview.setScaleX(pvScale); - mPreview.setScaleY(pvScale); - mPreview.setTranslationX((isRtl ? -1 : 1) * pvTransX); - mPreview.setTranslationY(pvTransY); - - mRenderedSize.set(width, height); - if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s %d%% instance=%s", - width, height, (int)(100*mPreview.getScaleX()), instanceId())); - } - - private void transitionToCamera() { - if (mTransitioning || mDown) return; - - mTransitioning = true; - - enableWindowExitAnimation(false); - - final int navHeight = mInsets.bottom; - final int navWidth = mInsets.right; - - mPreview.getLocationInWindow(mTmpLoc); - final float pvHeight = mPreview.getHeight() * mPreview.getScaleY(); - final float pvCenter = mTmpLoc[1] + pvHeight / 2f; - - final ViewGroup root = (ViewGroup) getRootView(); - - if (DEBUG) { - Log.d(TAG, "root = " + root.getLeft() + "," + root.getTop() + " " - + root.getWidth() + "x" + root.getHeight()); - } - - if (mFullscreenPreview == null) { - mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo); - mFullscreenPreview.setClickable(false); - root.addView(mFullscreenPreview, new FrameLayout.LayoutParams( - root.getWidth() - navWidth, - root.getHeight() - navHeight)); - } - - final float fsHeight = root.getHeight() - navHeight; - final float fsCenter = root.getTop() + fsHeight / 2; - - final float fsScaleY = mPreview.getScaleY(); - final float fsTransY = pvCenter - fsCenter; - final float fsScaleX = fsScaleY; - - mPreview.setVisibility(View.GONE); - mFullscreenPreview.setVisibility(View.VISIBLE); - mFullscreenPreview.setTranslationY(fsTransY); - mFullscreenPreview.setScaleX(fsScaleX); - mFullscreenPreview.setScaleY(fsScaleY); - mFullscreenPreview - .animate() - .scaleX(1) - .scaleY(1) - .translationX(0) - .translationY(0) - .setDuration(WIDGET_ANIMATION_DURATION) - .withEndAction(mPostTransitionToCameraEndAction) - .start(); - - if (navHeight > 0 || navWidth > 0) { - final boolean atBottom = navHeight > 0; - if (mFakeNavBar == null) { - mFakeNavBar = new View(mContext); - mFakeNavBar.setBackgroundColor(Color.BLACK); - root.addView(mFakeNavBar, new FrameLayout.LayoutParams( - atBottom ? FrameLayout.LayoutParams.MATCH_PARENT - : navWidth, - atBottom ? navHeight - : FrameLayout.LayoutParams.MATCH_PARENT, - atBottom ? Gravity.BOTTOM|Gravity.FILL_HORIZONTAL - : Gravity.RIGHT|Gravity.FILL_VERTICAL)); - mFakeNavBar.setPivotY(navHeight); - mFakeNavBar.setPivotX(navWidth); - } - mFakeNavBar.setAlpha(0f); - if (atBottom) { - mFakeNavBar.setScaleY(0.5f); - } else { - mFakeNavBar.setScaleX(0.5f); - } - mFakeNavBar.setVisibility(View.VISIBLE); - mFakeNavBar.animate() - .alpha(1f) - .scaleY(1f) - .scaleY(1f) - .setDuration(WIDGET_ANIMATION_DURATION) - .start(); - } - mCallbacks.onLaunchingCamera(); - } - - private void recover() { - if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis()); - mCallbacks.onCameraLaunchedUnsuccessfully(); - reset(); - } - - @Override - public void setOnLongClickListener(OnLongClickListener l) { - // ignore - } - - @Override - public void onClick(View v) { - if (DEBUG) Log.d(TAG, "clicked"); - if (mTransitioning) return; - if (mActive) { - cancelTransitionToCamera(); - transitionToCamera(); - } - } - - @Override - protected void onDetachedFromWindow() { - if (DEBUG) Log.d(TAG, "onDetachedFromWindow: instance " + instanceId() - + " at " + SystemClock.uptimeMillis()); - super.onDetachedFromWindow(); - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mCallback); - cancelTransitionToCamera(); - mHandler.removeCallbacks(mRecoverRunnable); - } - - @Override - public void onActive(boolean isActive) { - mActive = isActive; - if (mActive) { - rescheduleTransitionToCamera(); - } else { - reset(); - } - } - - @Override - public boolean onUserInteraction(MotionEvent event) { - if (mTransitioning) { - if (DEBUG) Log.d(TAG, "onUserInteraction eaten: mTransitioning"); - return true; - } - - getLocationOnScreen(mTmpLoc); - int rawBottom = mTmpLoc[1] + getHeight(); - if (event.getRawY() > rawBottom) { - if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget"); - return true; - } - - int action = event.getAction(); - mDown = action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE; - if (mActive) { - rescheduleTransitionToCamera(); - } - if (DEBUG) Log.d(TAG, "onUserInteraction observed, not eaten"); - return false; - } - - @Override - protected void onFocusLost() { - if (DEBUG) Log.d(TAG, "onFocusLost at " + SystemClock.uptimeMillis()); - cancelTransitionToCamera(); - super.onFocusLost(); - } - - public void onScreenTurnedOff() { - if (DEBUG) Log.d(TAG, "onScreenTurnedOff"); - reset(); - } - - private void rescheduleTransitionToCamera() { - if (DEBUG) Log.d(TAG, "rescheduleTransitionToCamera at " + SystemClock.uptimeMillis()); - mHandler.removeCallbacks(mTransitionToCameraRunnable); - final long duration = mUseFastTransition ? 0 : WIDGET_WAIT_DURATION; - mHandler.postDelayed(mTransitionToCameraRunnable, duration); - } - - private void cancelTransitionToCamera() { - if (DEBUG) Log.d(TAG, "cancelTransitionToCamera at " + SystemClock.uptimeMillis()); - mHandler.removeCallbacks(mTransitionToCameraRunnable); - } - - private void onCameraLaunched() { - mCallbacks.onCameraLaunchedSuccessfully(); - reset(); - } - - private void reset() { - if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis()); - mLaunchCameraStart = 0; - mTransitioning = false; - mDown = false; - cancelTransitionToCamera(); - mHandler.removeCallbacks(mRecoverRunnable); - mPreview.setVisibility(View.VISIBLE); - if (mFullscreenPreview != null) { - mFullscreenPreview.animate().cancel(); - mFullscreenPreview.setVisibility(View.GONE); - } - if (mFakeNavBar != null) { - mFakeNavBar.animate().cancel(); - mFakeNavBar.setVisibility(View.GONE); - } - enableWindowExitAnimation(true); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s", - w, h, oldw, oldh, SystemClock.uptimeMillis())); - if ((w != oldw && oldw > 0) || (h != oldh && oldh > 0)) { - // we can't trust the old geometry anymore; force a re-render - mRenderedSize.x = mRenderedSize.y = -1; - } - mHandler.post(mRenderRunnable); - super.onSizeChanged(w, h, oldw, oldh); - } - - @Override - public void onBouncerShowing(boolean showing) { - if (showing) { - mTransitioning = false; - mHandler.post(mRecoverRunnable); - } - } - - private void enableWindowExitAnimation(boolean isEnabled) { - View root = getRootView(); - ViewGroup.LayoutParams lp = root.getLayoutParams(); - if (!(lp instanceof WindowManager.LayoutParams)) - return; - WindowManager.LayoutParams wlp = (WindowManager.LayoutParams) lp; - int newWindowAnimations = isEnabled ? R.style.Animation_LockScreen : 0; - if (newWindowAnimations != wlp.windowAnimations) { - if (DEBUG) Log.d(TAG, "setting windowAnimations to: " + newWindowAnimations - + " at " + SystemClock.uptimeMillis()); - wlp.windowAnimations = newWindowAnimations; - mWindowManager.updateViewLayout(root, wlp); - } - } - - private void onKeyguardVisibilityChanged(boolean showing) { - if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing - + " at " + SystemClock.uptimeMillis()); - if (mTransitioning && !showing) { - mTransitioning = false; - mHandler.removeCallbacks(mRecoverRunnable); - if (mLaunchCameraStart > 0) { - long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart; - if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime)); - mLaunchCameraStart = 0; - onCameraLaunched(); - } - } - } - - private void onSecureCameraActivityStarted() { - if (DEBUG) Log.d(TAG, "onSecureCameraActivityStarted at " + SystemClock.uptimeMillis()); - mHandler.postDelayed(mRecoverRunnable, RECOVERY_DELAY); - } - - private String instanceId() { - return Integer.toHexString(hashCode()); - } - - public void setInsets(Rect insets) { - if (DEBUG) Log.d(TAG, "setInsets: " + insets); - mInsets.set(insets); - } - - public void setUseFastTransition(boolean useFastTransition) { - mUseFastTransition = useFastTransition; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java index b9263bf..e083c9c 100644 --- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java +++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java @@ -18,6 +18,7 @@ package com.android.keyguard; import java.util.List; import java.util.Locale; +import java.util.Objects; import android.content.Context; import android.content.Intent; @@ -25,7 +26,6 @@ import android.content.IntentFilter; import android.content.res.TypedArray; import android.net.ConnectivityManager; import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.text.method.SingleLineTransformationMethod; import android.util.AttributeSet; @@ -36,7 +36,7 @@ import android.widget.TextView; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.widget.LockPatternUtils; +import com.android.settingslib.WirelessUtils; public class CarrierText extends TextView { private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -44,7 +44,8 @@ public class CarrierText extends TextView { private static CharSequence mSeparator; - private LockPatternUtils mLockPatternUtils; + private final boolean mIsEmergencyCallCapable; + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { @@ -81,7 +82,8 @@ public class CarrierText extends TextView { public CarrierText(Context context, AttributeSet attrs) { super(context, attrs); - mLockPatternUtils = new LockPatternUtils(mContext); + mIsEmergencyCallCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); boolean useAllCaps; TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.CarrierText, 0, 0); @@ -140,12 +142,19 @@ public class CarrierText extends TextView { plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN); } if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn); - text = concatenate(plmn, spn); + if (Objects.equals(plmn, spn)) { + text = plmn; + } else { + text = concatenate(plmn, spn); + } } displayText = makeCarrierStringOnEmergencyCapable( getContext().getText(R.string.keyguard_missing_sim_message_short), text); } } + if (WirelessUtils.isAirplaneModeOn(mContext)) { + displayText = getContext().getString(R.string.airplane_mode); + } setText(displayText); } @@ -242,7 +251,7 @@ public class CarrierText extends TextView { */ private CharSequence makeCarrierStringOnEmergencyCapable( CharSequence simMessage, CharSequence emergencyCallMessage) { - if (mLockPatternUtils.isEmergencyCallCapable()) { + if (mIsEmergencyCallCapable) { return concatenate(simMessage, emergencyCallMessage); } return simMessage; @@ -289,11 +298,7 @@ public class CarrierText extends TextView { final boolean plmnValid = !TextUtils.isEmpty(plmn); final boolean spnValid = !TextUtils.isEmpty(spn); if (plmnValid && spnValid) { - if (plmn.equals(spn)) { - return plmn; - } else { - return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString(); - } + return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString(); } else if (plmnValid) { return plmn; } else if (spnValid) { diff --git a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java deleted file mode 100644 index 2ee21ac..0000000 --- a/packages/Keyguard/src/com/android/keyguard/ChallengeLayout.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -/** - * Interface implemented by ViewGroup-derived layouts that implement - * special logic for presenting security challenges to the user. - */ -public interface ChallengeLayout { - /** - * @return true if the security challenge area of this layout is currently visible - */ - boolean isChallengeShowing(); - - /** - * @return true if the challenge area significantly overlaps other content - */ - boolean isChallengeOverlapping(); - - /** - * Show or hide the challenge layout. - * - * If you want to show the challenge layout in bouncer mode where applicable, - * use {@link #showBouncer()} instead. - * - * @param b true to show, false to hide - */ - void showChallenge(boolean show); - - /** - * Show the bouncer challenge. This may block access to other child views. - */ - void showBouncer(); - - /** - * Hide the bouncer challenge if it is currently showing. - * This may restore previously blocked access to other child views. - */ - void hideBouncer(); - - /** - * Returns true if the challenge is currently in bouncer mode, - * potentially blocking access to other child views. - */ - boolean isBouncing(); - - /** - * Returns the duration of the bounce animation. - */ - int getBouncerAnimationDuration(); - - /** - * Set a listener that will respond to changes in bouncer state. - * - * @param listener listener to register - */ - void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener); - - /** - * Listener interface that reports changes in bouncer state. - * The bouncer is - */ - public interface OnBouncerStateChangedListener { - /** - * Called when the bouncer state changes. - * The bouncer is activated when the user must pass a security challenge - * to proceed with the requested action. - * - * <p>This differs from simply showing or hiding the security challenge - * as the bouncer will prevent interaction with other elements of the UI. - * If the user attempts to escape from the bouncer, it will be dismissed, - * this method will be called with false as the parameter, and the action - * should be canceled. If the security component reports a successful - * authentication and the containing code calls hideBouncer() as a result, - * this method will also be called with a false parameter. It is up to the - * caller of hideBouncer to be ready for this.</p> - * - * @param bouncerActive true if the bouncer is now active, - * false if the bouncer was dismissed. - */ - public void onBouncerStateChanged(boolean bouncerActive); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java b/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java deleted file mode 100644 index 52e7cd5..0000000 --- a/packages/Keyguard/src/com/android/keyguard/CheckLongPressHelper.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; - -public class CheckLongPressHelper { - private View mView; - private boolean mHasPerformedLongPress; - private CheckForLongPress mPendingCheckForLongPress; - private float mDownX, mDownY; - private int mLongPressTimeout; - private int mScaledTouchSlop; - - class CheckForLongPress implements Runnable { - public void run() { - if ((mView.getParent() != null) && mView.hasWindowFocus() - && !mHasPerformedLongPress) { - if (mView.performLongClick()) { - mView.setPressed(false); - mHasPerformedLongPress = true; - } - } - } - } - - public CheckLongPressHelper(View v) { - mScaledTouchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop(); - mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); - mView = v; - } - - public void postCheckForLongPress(MotionEvent ev) { - mDownX = ev.getX(); - mDownY = ev.getY(); - mHasPerformedLongPress = false; - - if (mPendingCheckForLongPress == null) { - mPendingCheckForLongPress = new CheckForLongPress(); - } - mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout); - } - - public void onMove(MotionEvent ev) { - float x = ev.getX(); - float y = ev.getY(); - boolean xMoved = Math.abs(mDownX - x) > mScaledTouchSlop; - boolean yMoved = Math.abs(mDownY - y) > mScaledTouchSlop; - - if (xMoved || yMoved) { - cancelLongPress(); - } - } - - public void cancelLongPress() { - mHasPerformedLongPress = false; - if (mPendingCheckForLongPress != null) { - mView.removeCallbacks(mPendingCheckForLongPress); - mPendingCheckForLongPress = null; - } - } - - public boolean hasPerformedLongPress() { - return mHasPerformedLongPress; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/DisappearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/DisappearAnimationUtils.java deleted file mode 100644 index 6fff0ba..0000000 --- a/packages/Keyguard/src/com/android/keyguard/DisappearAnimationUtils.java +++ /dev/null @@ -1,45 +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.keyguard; - -import android.content.Context; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -/** - * A class to make nice disappear transitions for views in a tabular layout. - */ -public class DisappearAnimationUtils extends AppearAnimationUtils { - - public DisappearAnimationUtils(Context ctx) { - this(ctx, DEFAULT_APPEAR_DURATION, - 1.0f, 1.0f, - AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in)); - } - - public DisappearAnimationUtils(Context ctx, long duration, float translationScaleFactor, - float delayScaleFactor, Interpolator interpolator) { - super(ctx, duration, translationScaleFactor, delayScaleFactor, interpolator); - mScaleTranslationWithRow = true; - mAppearing = false; - } - - protected long calculateDelay(int row, int col) { - return (long) ((row * 60 + col * (Math.pow(row, 0.4) + 0.4) * 10) * mDelayScale); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java index 50ac261..cbf22c0 100644 --- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java +++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java @@ -16,16 +16,19 @@ package com.android.keyguard; +import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.os.PowerManager; import android.os.SystemClock; import android.os.UserHandle; -import android.telephony.TelephonyManager; +import android.telecom.TelecomManager; import android.util.AttributeSet; import android.view.View; import android.widget.Button; +import com.android.internal.logging.MetricsLogger; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.widget.LockPatternUtils; @@ -36,7 +39,12 @@ import com.android.internal.widget.LockPatternUtils; * allows the user to return to the call. */ public class EmergencyButton extends Button { - private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; + private static final Intent INTENT_EMERGENCY_DIAL = new Intent() + .setAction("com.android.phone.EmergencyDialer.DIAL") + .setPackage("com.android.phone") + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_CLEAR_TOP); KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @@ -50,8 +58,17 @@ public class EmergencyButton extends Button { updateEmergencyCallButton(); } }; + + public interface EmergencyButtonCallback { + public void onEmergencyButtonClickedWhenInCall(); + } + private LockPatternUtils mLockPatternUtils; private PowerManager mPowerManager; + private EmergencyButtonCallback mEmergencyButtonCallback; + + private final boolean mIsVoiceCapable; + private final boolean mEnableEmergencyCallWhileSimLocked; public EmergencyButton(Context context) { this(context, null); @@ -59,6 +76,10 @@ public class EmergencyButton extends Button { public EmergencyButton(Context context, AttributeSet attrs) { super(context, attrs); + mIsVoiceCapable = context.getResources().getBoolean( + com.android.internal.R.bool.config_voice_capable); + mEnableEmergencyCallWhileSimLocked = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); } @Override @@ -86,42 +107,86 @@ public class EmergencyButton extends Button { updateEmergencyCallButton(); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateEmergencyCallButton(); + } + /** * Shows the emergency dialer or returns the user to the existing call. */ public void takeEmergencyCallAction() { + MetricsLogger.action(mContext, MetricsLogger.ACTION_EMERGENCY_CALL); // TODO: implement a shorter timeout once new PowerManager API is ready. // should be the equivalent to the old userActivity(EMERGENCY_CALL_TIMEOUT) mPowerManager.userActivity(SystemClock.uptimeMillis(), true); - if (mLockPatternUtils.isInCall()) { - mLockPatternUtils.resumeCall(); + if (isInCall()) { + resumeCall(); + if (mEmergencyButtonCallback != null) { + mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall(); + } } else { - final boolean bypassHandler = true; - KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler); - Intent intent = new Intent(ACTION_EMERGENCY_DIAL); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - getContext().startActivityAsUser(intent, - new UserHandle(mLockPatternUtils.getCurrentUser())); + KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction( + true /* bypassHandler */); + getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL, + ActivityOptions.makeCustomAnimation(getContext(), 0, 0).toBundle(), + new UserHandle(KeyguardUpdateMonitor.getCurrentUser())); } } private void updateEmergencyCallButton() { - boolean enabled = false; - if (mLockPatternUtils.isInCall()) { - enabled = true; // always show "return to call" if phone is off-hook - } else if (mLockPatternUtils.isEmergencyCallCapable()) { - final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimPinVoiceSecure(); - if (simLocked) { - // Some countries can't handle emergency calls while SIM is locked. - enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked(); + boolean visible = false; + if (mIsVoiceCapable) { + // Emergency calling requires voice capability. + if (isInCall()) { + visible = true; // always show "return to call" if phone is off-hook } else { - // True if we need to show a secure screen (pin/pattern/SIM pin/SIM puk); - // hides emergency button on "Slide" screen if device is not secure. - enabled = mLockPatternUtils.isSecure(); + final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext) + .isSimPinVoiceSecure(); + if (simLocked) { + // Some countries can't handle emergency calls while SIM is locked. + visible = mEnableEmergencyCallWhileSimLocked; + } else { + // Only show if there is a secure screen (pin/pattern/SIM pin/SIM puk); + visible = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser()); + } } } - mLockPatternUtils.updateEmergencyCallButtonState(this, enabled, false); + if (visible) { + setVisibility(View.VISIBLE); + + int textId; + if (isInCall()) { + textId = com.android.internal.R.string.lockscreen_return_to_call; + } else { + textId = com.android.internal.R.string.lockscreen_emergency_call; + } + setText(textId); + } else { + setVisibility(View.GONE); + } + } + + public void setCallback(EmergencyButtonCallback callback) { + mEmergencyButtonCallback = callback; + } + + /** + * Resumes a call in progress. + */ + private void resumeCall() { + getTelecommManager().showInCallScreen(false); + } + + /** + * @return {@code true} if there is a call currently in progress. + */ + private boolean isInCall() { + return getTelecommManager().isInCall(); } + private TelecomManager getTelecommManager() { + return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); + } } diff --git a/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java b/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java deleted file mode 100644 index 8d13ac2..0000000 --- a/packages/Keyguard/src/com/android/keyguard/FaceUnlock.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import com.android.internal.policy.IFaceLockCallback; -import com.android.internal.policy.IFaceLockInterface; -import com.android.internal.widget.LockPatternUtils; - -import android.app.admin.DevicePolicyManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.PowerManager; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.Log; -import android.view.View; - -public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback { - - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "FULLockscreen"; - private static final String FACE_LOCK_PACKAGE = "com.android.facelock"; - - private final Context mContext; - private final LockPatternUtils mLockPatternUtils; - - // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null? - private boolean mServiceRunning = false; - // TODO: now that the code has been restructure to do almost all operations from a handler, this - // lock may no longer be necessary. - private final Object mServiceRunningLock = new Object(); - private IFaceLockInterface mService; - private boolean mBoundToService = false; - private View mFaceUnlockView; - - private Handler mHandler; - private final int MSG_SERVICE_CONNECTED = 0; - private final int MSG_SERVICE_DISCONNECTED = 1; - private final int MSG_UNLOCK = 2; - private final int MSG_CANCEL = 3; - private final int MSG_REPORT_FAILED_ATTEMPT = 4; - private final int MSG_POKE_WAKELOCK = 5; - - // TODO: This was added for the purpose of adhering to what the biometric interface expects - // the isRunning() function to return. However, it is probably not necessary to have both - // mRunning and mServiceRunning. I'd just rather wait to change that logic. - private volatile boolean mIsRunning = false; - - KeyguardSecurityCallback mKeyguardScreenCallback; - - /** - * Stores some of the structures that Face Unlock will need to access and creates the handler - * will be used to execute messages on the UI thread. - */ - public FaceUnlock(Context context) { - mContext = context; - mLockPatternUtils = new LockPatternUtils(context); - mHandler = new Handler(this); - } - - public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) { - mKeyguardScreenCallback = keyguardScreenCallback; - } - - /** - * Stores and displays the view that Face Unlock is allowed to draw within. - * TODO: since the layout object will eventually be shared by multiple biometric unlock - * methods, we will have to add our other views (background, cancel button) here. - */ - public void initializeView(View biometricUnlockView) { - Log.d(TAG, "initializeView()"); - mFaceUnlockView = biometricUnlockView; - } - - /** - * Indicates whether Face Unlock is currently running. - */ - public boolean isRunning() { - return mIsRunning; - } - - /** - * Dismisses face unlock and goes to the backup lock - */ - public void stopAndShowBackup() { - if (DEBUG) Log.d(TAG, "stopAndShowBackup()"); - mHandler.sendEmptyMessage(MSG_CANCEL); - } - - /** - * Binds to the Face Unlock service. Face Unlock will be started when the bind completes. The - * Face Unlock view is displayed to hide the backup lock while the service is starting up. - * Called on the UI thread. - */ - public boolean start() { - if (DEBUG) Log.d(TAG, "start()"); - if (mHandler.getLooper() != Looper.myLooper()) { - Log.e(TAG, "start() called off of the UI thread"); - } - - if (mIsRunning) { - Log.w(TAG, "start() called when already running"); - } - - if (!mBoundToService) { - Log.d(TAG, "Binding to Face Unlock service for user=" - + mLockPatternUtils.getCurrentUser()); - mContext.bindServiceAsUser( - new Intent(IFaceLockInterface.class.getName()).setPackage(FACE_LOCK_PACKAGE), - mConnection, - Context.BIND_AUTO_CREATE, - new UserHandle(mLockPatternUtils.getCurrentUser())); - mBoundToService = true; - } else { - Log.w(TAG, "Attempt to bind to Face Unlock when already bound"); - } - - mIsRunning = true; - return true; - } - - /** - * Stops Face Unlock and unbinds from the service. Called on the UI thread. - */ - public boolean stop() { - if (DEBUG) Log.d(TAG, "stop()"); - if (mHandler.getLooper() != Looper.myLooper()) { - Log.e(TAG, "stop() called from non-UI thread"); - } - - // Clearing any old service connected messages. - mHandler.removeMessages(MSG_SERVICE_CONNECTED); - - boolean mWasRunning = mIsRunning; - - stopUi(); - - if (mBoundToService) { - if (mService != null) { - try { - mService.unregisterCallback(mFaceUnlockCallback); - } catch (RemoteException e) { - // Not much we can do - } - } - Log.d(TAG, "Unbinding from Face Unlock service"); - mContext.unbindService(mConnection); - mBoundToService = false; - } else { - // This is usually not an error when this happens. Sometimes we will tell it to - // unbind multiple times because it's called from both onWindowFocusChanged and - // onDetachedFromWindow. - if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound"); - } - mIsRunning = false; - return mWasRunning; - } - - /** - * Frees up resources used by Face Unlock and stops it if it is still running. - */ - public void cleanUp() { - if (DEBUG) Log.d(TAG, "cleanUp()"); - if (mService != null) { - try { - mService.unregisterCallback(mFaceUnlockCallback); - } catch (RemoteException e) { - // Not much we can do - } - stopUi(); - mService = null; - } - } - - /** - * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK. - */ - public int getQuality() { - return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; - } - - /** - * Handles messages such that everything happens on the UI thread in a deterministic order. - * Calls from the Face Unlock service come from binder threads. Calls from lockscreen typically - * come from the UI thread. This makes sure there are no race conditions between those calls. - */ - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_SERVICE_CONNECTED: - handleServiceConnected(); - break; - case MSG_SERVICE_DISCONNECTED: - handleServiceDisconnected(); - break; - case MSG_UNLOCK: - handleUnlock(msg.arg1); - break; - case MSG_CANCEL: - handleCancel(); - break; - case MSG_REPORT_FAILED_ATTEMPT: - handleReportFailedAttempt(); - break; - case MSG_POKE_WAKELOCK: - handlePokeWakelock(msg.arg1); - break; - default: - Log.e(TAG, "Unhandled message"); - return false; - } - return true; - } - - /** - * Tells the service to start its UI via an AIDL interface. Called when the - * onServiceConnected() callback is received. - */ - void handleServiceConnected() { - Log.d(TAG, "handleServiceConnected()"); - - // It is possible that an unbind has occurred in the time between the bind and when this - // function is reached. If an unbind has already occurred, proceeding on to call startUi() - // can result in a fatal error. Note that the onServiceConnected() callback is - // asynchronous, so this possibility would still exist if we executed this directly in - // onServiceConnected() rather than using a handler. - if (!mBoundToService) { - Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound"); - return; - } - - try { - mService.registerCallback(mFaceUnlockCallback); - } catch (RemoteException e) { - Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString()); - mService = null; - mBoundToService = false; - mIsRunning = false; - return; - } - - if (mFaceUnlockView != null) { - IBinder windowToken = mFaceUnlockView.getWindowToken(); - if (windowToken != null) { - // When switching between portrait and landscape view while Face Unlock is running, - // the screen will eventually go dark unless we poke the wakelock when Face Unlock - // is restarted. - mKeyguardScreenCallback.userActivity(); - - int[] position; - position = new int[2]; - mFaceUnlockView.getLocationInWindow(position); - startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(), - mFaceUnlockView.getHeight()); - } else { - Log.e(TAG, "windowToken is null in handleServiceConnected()"); - } - } - } - - /** - * Called when the onServiceDisconnected() callback is received. This should not happen during - * normal operation. It indicates an error has occurred. - */ - void handleServiceDisconnected() { - Log.e(TAG, "handleServiceDisconnected()"); - // TODO: this lock may no longer be needed now that everything is being called from a - // handler - synchronized (mServiceRunningLock) { - mService = null; - mServiceRunning = false; - } - mBoundToService = false; - mIsRunning = false; - } - - /** - * Stops the Face Unlock service and tells the device to grant access to the user. - */ - void handleUnlock(int authenticatedUserId) { - if (DEBUG) Log.d(TAG, "handleUnlock()"); - stop(); - int currentUserId = mLockPatternUtils.getCurrentUser(); - if (authenticatedUserId == currentUserId) { - if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId); - mKeyguardScreenCallback.reportUnlockAttempt(true); - mKeyguardScreenCallback.dismiss(true); - } else { - Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId + - ") because the current user is " + currentUserId); - } - } - - /** - * Stops the Face Unlock service and goes to the backup lock. - */ - void handleCancel() { - if (DEBUG) Log.d(TAG, "handleCancel()"); - // We are going to the backup method, so we don't want to see Face Unlock again until the - // next time the user visits keyguard. - KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false); - - mKeyguardScreenCallback.showBackupSecurity(); - stop(); - mKeyguardScreenCallback.userActivity(); - } - - /** - * Increments the number of failed Face Unlock attempts. - */ - void handleReportFailedAttempt() { - if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()"); - // We are going to the backup method, so we don't want to see Face Unlock again until the - // next time the user visits keyguard. - KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false); - - mKeyguardScreenCallback.reportUnlockAttempt(false); - } - - /** - * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific - * amount of time. - */ - void handlePokeWakelock(int millis) { - PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - if (powerManager.isScreenOn()) { - mKeyguardScreenCallback.userActivity(); - } - } - - /** - * Implements service connection methods. - */ - private ServiceConnection mConnection = new ServiceConnection() { - /** - * Called when the Face Unlock service connects after calling bind(). - */ - public void onServiceConnected(ComponentName className, IBinder iservice) { - Log.d(TAG, "Connected to Face Unlock service"); - mService = IFaceLockInterface.Stub.asInterface(iservice); - mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED); - } - - /** - * Called if the Face Unlock service unexpectedly disconnects. This indicates an error. - */ - public void onServiceDisconnected(ComponentName className) { - Log.e(TAG, "Unexpected disconnect from Face Unlock service"); - mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED); - } - }; - - /** - * Tells the Face Unlock service to start displaying its UI and start processing. - */ - private void startUi(IBinder windowToken, int x, int y, int w, int h) { - if (DEBUG) Log.d(TAG, "startUi()"); - synchronized (mServiceRunningLock) { - if (!mServiceRunning) { - Log.d(TAG, "Starting Face Unlock"); - try { - mService.startUi(windowToken, x, y, w, h, - mLockPatternUtils.isBiometricWeakLivelinessEnabled()); - } catch (RemoteException e) { - Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString()); - return; - } - mServiceRunning = true; - } else { - Log.w(TAG, "startUi() attempted while running"); - } - } - } - - /** - * Tells the Face Unlock service to stop displaying its UI and stop processing. - */ - private void stopUi() { - if (DEBUG) Log.d(TAG, "stopUi()"); - // Note that attempting to stop Face Unlock when it's not running is not an issue. - // Face Unlock can return, which stops it and then we try to stop it when the - // screen is turned off. That's why we check. - synchronized (mServiceRunningLock) { - if (mServiceRunning) { - Log.d(TAG, "Stopping Face Unlock"); - try { - mService.stopUi(); - } catch (RemoteException e) { - Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString()); - } - mServiceRunning = false; - } else { - // This is usually not an error when this happens. Sometimes we will tell it to - // stop multiple times because it's called from both onWindowFocusChanged and - // onDetachedFromWindow. - if (DEBUG) Log.d(TAG, "stopUi() attempted while not running"); - } - } - } - - /** - * Implements the AIDL biometric unlock service callback interface. - */ - private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() { - /** - * Called when Face Unlock wants to grant access to the user. - */ - public void unlock() { - if (DEBUG) Log.d(TAG, "unlock()"); - Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1); - mHandler.sendMessage(message); - } - - /** - * Called when Face Unlock wants to go to the backup. - */ - public void cancel() { - if (DEBUG) Log.d(TAG, "cancel()"); - mHandler.sendEmptyMessage(MSG_CANCEL); - } - - /** - * Called when Face Unlock wants to increment the number of failed attempts. - */ - public void reportFailedAttempt() { - if (DEBUG) Log.d(TAG, "reportFailedAttempt()"); - mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT); - } - - /** - * Called when Face Unlock wants to keep the screen alive and active for a specific amount - * of time. - */ - public void pokeWakelock(int millis) { - if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms"); - Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1); - mHandler.sendMessage(message); - } - - }; -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java index a411df3..0c6837f 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -17,7 +17,7 @@ package com.android.keyguard; import android.content.Context; -import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.CountDownTimer; import android.os.SystemClock; import android.util.AttributeSet; @@ -26,18 +26,19 @@ import android.view.KeyEvent; import android.view.View; import android.widget.LinearLayout; +import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; /** * Base class for PIN and password unlock screens. */ public abstract class KeyguardAbsKeyInputView extends LinearLayout - implements KeyguardSecurityView { + implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback { protected KeyguardSecurityCallback mCallback; protected LockPatternUtils mLockPatternUtils; + protected AsyncTask<?, ?, ?> mPendingLockCheck; protected SecurityMessageDisplay mSecurityMessageDisplay; protected View mEcaView; - private Drawable mBouncerFrame; protected boolean mEnableHaptics; // To avoid accidental lockout due to events while the device in in the pocket, ignore @@ -65,7 +66,8 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout // start fresh resetPasswordText(false /* animate */); // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); if (shouldLockout(deadline)) { handleAttemptLockout(deadline); } else { @@ -84,14 +86,19 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @Override protected void onFinishInflate() { mLockPatternUtils = new LockPatternUtils(mContext); - mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); + mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this); mEcaView = findViewById(R.id.keyguard_selector_fade_container); - View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); - if (bouncerFrameView != null) { - mBouncerFrame = bouncerFrameView.getBackground(); + + EmergencyButton button = (EmergencyButton) findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(this); } } + public void onEmergencyButtonClickedWhenInCall() { + mCallback.reset(); + } + /* * Override this if you have a different string for "wrong password" * @@ -102,20 +109,45 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } protected void verifyPasswordAndUnlock() { - String entry = getPasswordText(); - if (mLockPatternUtils.checkPassword(entry)) { - mCallback.reportUnlockAttempt(true); + final String entry = getPasswordText(); + setPasswordEntryInputEnabled(false); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + if (entry.length() < MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + // to avoid accidental lockout, only count attempts that are long enough to be a + // real password. This may require some tweaking. + setPasswordEntryInputEnabled(true); + onPasswordChecked(entry, false, 0); + return; + } + + mPendingLockCheck = LockPatternChecker.checkPassword( + mLockPatternUtils, + entry, + KeyguardUpdateMonitor.getCurrentUser(), + new LockPatternChecker.OnCheckCallback() { + @Override + public void onChecked(boolean matched, int timeoutMs) { + setPasswordEntryInputEnabled(true); + mPendingLockCheck = null; + onPasswordChecked(entry, matched, timeoutMs); + } + }); + } + + private void onPasswordChecked(String entry, boolean matched, int timeoutMs) { + if (matched) { + mCallback.reportUnlockAttempt(true, 0); mCallback.dismiss(true); } else { - if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) { - // to avoid accidental lockout, only count attempts that are long enough to be a - // real password. This may require some tweaking. - mCallback.reportUnlockAttempt(false); - int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); - if (0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); - handleAttemptLockout(deadline); - } + mCallback.reportUnlockAttempt(false, timeoutMs); + int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); + handleAttemptLockout(deadline); } mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } @@ -125,6 +157,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected abstract void resetPasswordText(boolean animate); protected abstract String getPasswordText(); protected abstract void setPasswordEntryEnabled(boolean enabled); + protected abstract void setPasswordEntryInputEnabled(boolean enabled); // Prevent user from using the PIN/Password entry until scheduled deadline. protected void handleAttemptLockout(long elapsedRealtimeDeadline) { @@ -147,9 +180,16 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout }.start(); } + protected void onUserInput() { + if (mCallback != null) { + mCallback.userActivity(); + } + mSecurityMessageDisplay.setMessage("", false); + } + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - mCallback.userActivity(); + onUserInput(); return false; } @@ -160,7 +200,10 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout @Override public void onPause() { - + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } } @Override @@ -173,6 +216,19 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout return mCallback; } + @Override + public void showPromptReason(int reason) { + if (reason != PROMPT_REASON_NONE) { + int promtReasonStringRes = getPromtReasonStringRes(reason); + if (promtReasonStringRes != 0) { + mSecurityMessageDisplay.setMessage(promtReasonStringRes, + true /* important */); + } + } + } + + protected abstract int getPromtReasonStringRes(int reason); + // Cause a VIRTUAL_KEY vibration public void doHapticKeyClick() { if (mEnableHaptics) { @@ -183,18 +239,6 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } @Override - public void showBouncer(int duration) { - KeyguardSecurityViewHelper. - showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); - } - - @Override - public void hideBouncer(int duration) { - KeyguardSecurityViewHelper. - hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); - } - - @Override public boolean startDisappearAnimation(Runnable finishRunnable) { return false; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java deleted file mode 100644 index 5cb3b9b..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAccountView.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.UserHandle; -import android.text.Editable; -import android.text.InputFilter; -import android.text.LoginFilter; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; - -import com.android.internal.widget.LockPatternUtils; - -import java.io.IOException; - -/** - * When the user forgets their password a bunch of times, we fall back on their - * account's login/password to unlock the phone (and reset their lock pattern). - */ -public class KeyguardAccountView extends LinearLayout implements KeyguardSecurityView, - View.OnClickListener, TextWatcher { - private static final String LOCK_PATTERN_PACKAGE = "com.android.settings"; - private static final String LOCK_PATTERN_CLASS = LOCK_PATTERN_PACKAGE + ".ChooseLockGeneric"; - - private KeyguardSecurityCallback mCallback; - private LockPatternUtils mLockPatternUtils; - private EditText mLogin; - private EditText mPassword; - private Button mOk; - public boolean mEnableFallback; - private SecurityMessageDisplay mSecurityMessageDisplay; - - /** - * Shown while making asynchronous check of password. - */ - private ProgressDialog mCheckingDialog; - - public KeyguardAccountView(Context context) { - this(context, null, 0); - } - - public KeyguardAccountView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardAccountView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mLockPatternUtils = new LockPatternUtils(getContext()); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - mLogin = (EditText) findViewById(R.id.login); - mLogin.setFilters(new InputFilter[] { new LoginFilter.UsernameFilterGeneric() } ); - mLogin.addTextChangedListener(this); - - mPassword = (EditText) findViewById(R.id.password); - mPassword.addTextChangedListener(this); - - mOk = (Button) findViewById(R.id.ok); - mOk.setOnClickListener(this); - - mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); - reset(); - } - - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mCallback = callback; - } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - } - - public KeyguardSecurityCallback getCallback() { - return mCallback; - } - - - public void afterTextChanged(Editable s) { - } - - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - public void onTextChanged(CharSequence s, int start, int before, int count) { - if (mCallback != null) { - mCallback.userActivity(); - } - } - - @Override - protected boolean onRequestFocusInDescendants(int direction, - Rect previouslyFocusedRect) { - // send focus to the login field - return mLogin.requestFocus(direction, previouslyFocusedRect); - } - - public boolean needsInput() { - return true; - } - - public void reset() { - // start fresh - mLogin.setText(""); - mPassword.setText(""); - mLogin.requestFocus(); - boolean permLocked = mLockPatternUtils.isPermanentlyLocked(); - mSecurityMessageDisplay.setMessage(permLocked ? R.string.kg_login_too_many_attempts : - R.string.kg_login_instructions, permLocked ? true : false); - } - - /** {@inheritDoc} */ - public void cleanUp() { - if (mCheckingDialog != null) { - mCheckingDialog.hide(); - } - mCallback = null; - mLockPatternUtils = null; - } - - public void onClick(View v) { - mCallback.userActivity(); - if (v == mOk) { - asyncCheckPassword(); - } - } - - private void postOnCheckPasswordResult(final boolean success) { - // ensure this runs on UI thread - mLogin.post(new Runnable() { - public void run() { - if (success) { - // clear out forgotten password - mLockPatternUtils.setPermanentlyLocked(false); - mLockPatternUtils.setLockPatternEnabled(false); - mLockPatternUtils.saveLockPattern(null); - - // launch the 'choose lock pattern' activity so - // the user can pick a new one if they want to - Intent intent = new Intent(); - intent.setClassName(LOCK_PATTERN_PACKAGE, LOCK_PATTERN_CLASS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivityAsUser(intent, - new UserHandle(mLockPatternUtils.getCurrentUser())); - mCallback.reportUnlockAttempt(true); - - // dismiss keyguard - mCallback.dismiss(true); - } else { - mSecurityMessageDisplay.setMessage(R.string.kg_login_invalid_input, true); - mPassword.setText(""); - mCallback.reportUnlockAttempt(false); - } - } - }); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - if (mLockPatternUtils.isPermanentlyLocked()) { - mCallback.dismiss(false); - } else { - // TODO: mCallback.forgotPattern(false); - } - return true; - } - return super.dispatchKeyEvent(event); - } - - /** - * Given the string the user entered in the 'username' field, find - * the stored account that they probably intended. Prefer, in order: - * - * - an exact match for what was typed, or - * - a case-insensitive match for what was typed, or - * - if they didn't include a domain, an exact match of the username, or - * - if they didn't include a domain, a case-insensitive - * match of the username. - * - * If there is a tie for the best match, choose neither -- - * the user needs to be more specific. - * - * @return an account name from the database, or null if we can't - * find a single best match. - */ - private Account findIntendedAccount(String username) { - Account[] accounts = AccountManager.get(mContext).getAccountsByTypeAsUser("com.google", - new UserHandle(mLockPatternUtils.getCurrentUser())); - - // Try to figure out which account they meant if they - // typed only the username (and not the domain), or got - // the case wrong. - - Account bestAccount = null; - int bestScore = 0; - for (Account a: accounts) { - int score = 0; - if (username.equals(a.name)) { - score = 4; - } else if (username.equalsIgnoreCase(a.name)) { - score = 3; - } else if (username.indexOf('@') < 0) { - int i = a.name.indexOf('@'); - if (i >= 0) { - String aUsername = a.name.substring(0, i); - if (username.equals(aUsername)) { - score = 2; - } else if (username.equalsIgnoreCase(aUsername)) { - score = 1; - } - } - } - if (score > bestScore) { - bestAccount = a; - bestScore = score; - } else if (score == bestScore) { - bestAccount = null; - } - } - return bestAccount; - } - - private void asyncCheckPassword() { - mCallback.userActivity(); - final String login = mLogin.getText().toString(); - final String password = mPassword.getText().toString(); - Account account = findIntendedAccount(login); - if (account == null) { - postOnCheckPasswordResult(false); - return; - } - getProgressDialog().show(); - Bundle options = new Bundle(); - options.putString(AccountManager.KEY_PASSWORD, password); - AccountManager.get(mContext).confirmCredentialsAsUser(account, options, null /* activity */, - new AccountManagerCallback<Bundle>() { - public void run(AccountManagerFuture<Bundle> future) { - try { - mCallback.userActivity(); - final Bundle result = future.getResult(); - final boolean verified = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT); - postOnCheckPasswordResult(verified); - } catch (OperationCanceledException e) { - postOnCheckPasswordResult(false); - } catch (IOException e) { - postOnCheckPasswordResult(false); - } catch (AuthenticatorException e) { - postOnCheckPasswordResult(false); - } finally { - mLogin.post(new Runnable() { - public void run() { - getProgressDialog().hide(); - } - }); - } - } - }, null /* handler */, new UserHandle(mLockPatternUtils.getCurrentUser())); - } - - private Dialog getProgressDialog() { - if (mCheckingDialog == null) { - mCheckingDialog = new ProgressDialog(mContext); - mCheckingDialog.setMessage( - mContext.getString(R.string.kg_login_checking_password)); - mCheckingDialog.setIndeterminate(true); - mCheckingDialog.setCancelable(false); - mCheckingDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - return mCheckingDialog; - } - - @Override - public void onPause() { - - } - - @Override - public void onResume(int reason) { - reset(); - } - - @Override - public void showUsabilityHint() { - } - - @Override - public void showBouncer(int duration) { - } - - @Override - public void hideBouncer(int duration) { - } - - @Override - public void startAppearAnimation() { - // TODO. - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return false; - } -} - diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java b/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java deleted file mode 100644 index 1978ded..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardActivityLauncher.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import com.android.internal.widget.LockPatternUtils; - -import android.app.ActivityManagerNative; -import android.app.ActivityOptions; -import android.app.IActivityManager.WaitResult; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Bundle; -import android.os.Handler; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.MediaStore; -import android.util.Log; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -import com.android.keyguard.KeyguardHostView.OnDismissAction; - -import java.util.List; - -public abstract class KeyguardActivityLauncher { - private static final String TAG = KeyguardActivityLauncher.class.getSimpleName(); - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout"; - private static final Intent SECURE_CAMERA_INTENT = - new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE) - .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - private static final Intent INSECURE_CAMERA_INTENT = - new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); - - abstract Context getContext(); - - abstract LockPatternUtils getLockPatternUtils(); - - abstract void setOnDismissAction(OnDismissAction action); - - abstract void requestDismissKeyguard(); - - public static class CameraWidgetInfo { - public String contextPackage; - public int layoutId; - } - - public CameraWidgetInfo getCameraWidgetInfo() { - CameraWidgetInfo info = new CameraWidgetInfo(); - Intent intent = getCameraIntent(); - PackageManager packageManager = getContext().getPackageManager(); - final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); - if (appList.size() == 0) { - if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found"); - return null; - } - ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, - PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, - getLockPatternUtils().getCurrentUser()); - if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved); - if (wouldLaunchResolverActivity(resolved, appList)) { - if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver"); - return info; - } - if (resolved == null || resolved.activityInfo == null) { - return null; - } - if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) { - if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found"); - return info; - } - int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT); - if (layoutId == 0) { - if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified"); - return info; - } - info.contextPackage = resolved.activityInfo.packageName; - info.layoutId = layoutId; - return info; - } - - public void launchCamera(Handler worker, Runnable onSecureCameraStarted) { - LockPatternUtils lockPatternUtils = getLockPatternUtils(); - - // Workaround to avoid camera release/acquisition race when resuming face unlock - // after showing lockscreen camera (bug 11063890). - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(getContext()); - updateMonitor.setAlternateUnlockEnabled(false); - - if (mustLaunchSecurely()) { - // Launch the secure version of the camera - if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) { - // TODO: Show disambiguation dialog instead. - // For now, we'll treat this like launching any other app from secure keyguard. - // When they do, user sees the system's ResolverActivity which lets them choose - // which secure camera to use. - launchActivity(SECURE_CAMERA_INTENT, false, false, null, null); - } else { - launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted); - } - } else { - // Launch the normal camera - launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null); - } - } - - private boolean mustLaunchSecurely() { - LockPatternUtils lockPatternUtils = getLockPatternUtils(); - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(getContext()); - int currentUser = lockPatternUtils.getCurrentUser(); - return lockPatternUtils.isSecure() && !updateMonitor.getUserHasTrust(currentUser); - } - - public void launchWidgetPicker(int appWidgetId) { - Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK); - - pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false); - pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER, - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); - - Bundle options = new Bundle(); - options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD); - pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); - pickIntent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_SINGLE_TOP - | Intent.FLAG_ACTIVITY_CLEAR_TOP - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - - launchActivity(pickIntent, false, false, null, null); - } - - /** - * Launches the said intent for the current foreground user. - * - * @param intent - * @param showsWhileLocked true if the activity can be run on top of keyguard. - * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED} - * @param useDefaultAnimations true if default transitions should be used, else suppressed. - * @param worker if supplied along with onStarted, used to launch the blocking activity call. - * @param onStarted if supplied along with worker, called after activity is started. - */ - public void launchActivity(final Intent intent, - boolean showsWhileLocked, - boolean useDefaultAnimations, - final Handler worker, - final Runnable onStarted) { - - final Context context = getContext(); - final Bundle animation = useDefaultAnimations ? null - : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle(); - launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted); - } - - public void launchActivityWithAnimation(final Intent intent, - boolean showsWhileLocked, - final Bundle animation, - final Handler worker, - final Runnable onStarted) { - - LockPatternUtils lockPatternUtils = getLockPatternUtils(); - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_SINGLE_TOP - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - boolean mustLaunchSecurely = mustLaunchSecurely(); - if (!mustLaunchSecurely || showsWhileLocked) { - if (!mustLaunchSecurely) { - dismissKeyguardOnNextActivity(); - } - try { - if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s", - intent, SystemClock.uptimeMillis())); - startActivityForCurrentUser(intent, animation, worker, onStarted); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "Activity not found for intent + " + intent.getAction()); - } - } else { - // Create a runnable to start the activity and ask the user to enter their - // credentials. - setOnDismissAction(new OnDismissAction() { - @Override - public boolean onDismiss() { - dismissKeyguardOnNextActivity(); - startActivityForCurrentUser(intent, animation, worker, onStarted); - return true; - } - }); - requestDismissKeyguard(); - } - } - - private void dismissKeyguardOnNextActivity() { - try { - WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); - } catch (RemoteException e) { - Log.w(TAG, "Error dismissing keyguard", e); - } - } - - private void startActivityForCurrentUser(final Intent intent, final Bundle options, - Handler worker, final Runnable onStarted) { - final UserHandle user = new UserHandle(UserHandle.USER_CURRENT); - if (worker == null || onStarted == null) { - getContext().startActivityAsUser(intent, options, user); - return; - } - // if worker + onStarted are supplied, run blocking activity launch call in the background - worker.post(new Runnable(){ - @Override - public void run() { - try { - WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait( - null /*caller*/, - null /*caller pkg*/, - intent, - intent.resolveTypeIfNeeded(getContext().getContentResolver()), - null /*resultTo*/, - null /*resultWho*/, - 0 /*requestCode*/, - Intent.FLAG_ACTIVITY_NEW_TASK, - null /*profilerInfo*/, - options, - user.getIdentifier()); - if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s", - result.result, result.thisTime, result.totalTime, result.who, - SystemClock.uptimeMillis())); - } catch (RemoteException e) { - Log.w(TAG, "Error starting activity", e); - return; - } - try { - onStarted.run(); - } catch (Throwable t) { - Log.w(TAG, "Error running onStarted callback", t); - } - }}); - } - - private Intent getCameraIntent() { - return mustLaunchSecurely() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; - } - - private boolean wouldLaunchResolverActivity(Intent intent) { - PackageManager packageManager = getContext().getPackageManager(); - ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, - PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); - List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( - intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser()); - return wouldLaunchResolverActivity(resolved, appList); - } - - private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) { - // If the list contains the above resolved activity, then it can't be - // ResolverActivity itself. - for (int i = 0; i < appList.size(); i++) { - ResolveInfo tmp = appList.get(i); - if (tmp.activityInfo.name.equals(resolved.activityInfo.name) - && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) { - return false; - } - } - return true; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java b/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java deleted file mode 100644 index 0e08cf4..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardCircleFramedDrawable.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; - -class KeyguardCircleFramedDrawable extends Drawable { - - private final Bitmap mBitmap; - private final int mSize; - private final Paint mPaint; - private final float mShadowRadius; - private final float mStrokeWidth; - private final int mFrameColor; - private final int mHighlightColor; - private final int mFrameShadowColor; - - private float mScale; - private Path mFramePath; - private Rect mSrcRect; - private RectF mDstRect; - private RectF mFrameRect; - private boolean mPressed; - - public KeyguardCircleFramedDrawable(Bitmap bitmap, int size, - int frameColor, float strokeWidth, - int frameShadowColor, float shadowRadius, - int highlightColor) { - super(); - mSize = size; - mShadowRadius = shadowRadius; - mFrameColor = frameColor; - mFrameShadowColor = frameShadowColor; - mStrokeWidth = strokeWidth; - mHighlightColor = highlightColor; - - mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(mBitmap); - - final int width = bitmap.getWidth(); - final int height = bitmap.getHeight(); - final int square = Math.min(width, height); - - final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square); - final RectF circleRect = new RectF(0f, 0f, mSize, mSize); - circleRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f); - circleRect.inset(mShadowRadius, mShadowRadius); - - final Path fillPath = new Path(); - fillPath.addArc(circleRect, 0f, 360f); - - canvas.drawColor(0, PorterDuff.Mode.CLEAR); - - // opaque circle matte - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setColor(Color.BLACK); - mPaint.setStyle(Paint.Style.FILL); - canvas.drawPath(fillPath, mPaint); - - // mask in the icon where the bitmap is opaque - mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); - canvas.drawBitmap(bitmap, cropRect, circleRect, mPaint); - - // prepare paint for frame drawing - mPaint.setXfermode(null); - - mScale = 1f; - - mSrcRect = new Rect(0, 0, mSize, mSize); - mDstRect = new RectF(0, 0, mSize, mSize); - mFrameRect = new RectF(mDstRect); - mFramePath = new Path(); - } - - public void reset() { - mScale = 1f; - mPressed = false; - } - - @Override - public void draw(Canvas canvas) { - // clear background - final float outside = Math.min(canvas.getWidth(), canvas.getHeight()); - final float inside = mScale * outside; - final float pad = (outside - inside) / 2f; - - mDstRect.set(pad, pad, outside - pad, outside - pad); - canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null); - - mFrameRect.set(mDstRect); - mFrameRect.inset(mStrokeWidth / 2f, mStrokeWidth / 2f); - mFrameRect.inset(mShadowRadius, mShadowRadius); - - mFramePath.reset(); - mFramePath.addArc(mFrameRect, 0f, 360f); - - // white frame - if (mPressed) { - mPaint.setStyle(Paint.Style.FILL); - mPaint.setColor(Color.argb((int) (0.33f * 255), - Color.red(mHighlightColor), - Color.green(mHighlightColor), - Color.blue(mHighlightColor))); - canvas.drawPath(mFramePath, mPaint); - } - mPaint.setStrokeWidth(mStrokeWidth); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setColor(mPressed ? mHighlightColor : mFrameColor); - mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mFrameShadowColor); - canvas.drawPath(mFramePath, mPaint); - } - - public void setScale(float scale) { - mScale = scale; - } - - public float getScale() { - return mScale; - } - - public void setPressed(boolean pressed) { - mPressed = pressed; - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - } - - @Override - public void setColorFilter(ColorFilter cf) { - } - - public boolean verifyParams(float iconSize, int frameColor, float stroke, - int frameShadowColor, float shadowRadius, int highlightColor) { - return mSize == iconSize - && mFrameColor == frameColor - && mStrokeWidth == stroke - && mFrameShadowColor == frameShadowColor - && mShadowRadius == shadowRadius - && mHighlightColor == highlightColor; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java index 05a5f66..e5f3dc9 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java @@ -26,5 +26,5 @@ public class KeyguardConstants { * be used temporarily for debugging. */ public static final boolean DEBUG = false; - public static final boolean DEBUG_SIM_STATES = true; + public static final boolean DEBUG_SIM_STATES = false; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java deleted file mode 100644 index b3e9f77..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardFaceUnlockView.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.telephony.TelephonyManager; -import android.util.AttributeSet; -import android.util.Log; -import android.view.IRotationWatcher; -import android.view.IWindowManager; -import android.view.View; -import android.widget.ImageButton; -import android.widget.LinearLayout; - -import com.android.internal.widget.LockPatternUtils; - -import java.lang.Math; - -public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView { - - private static final String TAG = "FULKeyguardFaceUnlockView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private KeyguardSecurityCallback mKeyguardSecurityCallback; - private LockPatternUtils mLockPatternUtils; - private BiometricSensorUnlock mBiometricUnlock; - private View mFaceUnlockAreaView; - private ImageButton mCancelButton; - private SecurityMessageDisplay mSecurityMessageDisplay; - private View mEcaView; - private Drawable mBouncerFrame; - - private boolean mIsBouncerVisibleToUser = false; - private final Object mIsBouncerVisibleToUserLock = new Object(); - - private int mLastRotation; - private boolean mWatchingRotation; - private final IWindowManager mWindowManager = - IWindowManager.Stub.asInterface(ServiceManager.getService("window")); - - private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { - public void onRotationChanged(int rotation) { - if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation); - - // If the difference between the new rotation value and the previous rotation value is - // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock - // and starts it in the new position. This is not performed for 90 degree rotations - // since a 90 degree rotation is a configuration change, which takes care of this for - // us. - if (Math.abs(rotation - mLastRotation) == 2) { - if (mBiometricUnlock != null) { - mBiometricUnlock.stop(); - maybeStartBiometricUnlock(); - } - } - mLastRotation = rotation; - } - }; - - public KeyguardFaceUnlockView(Context context) { - this(context, null); - } - - public KeyguardFaceUnlockView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - initializeBiometricUnlockView(); - - mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); - mEcaView = findViewById(R.id.keyguard_selector_fade_container); - View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); - if (bouncerFrameView != null) { - mBouncerFrame = bouncerFrameView.getBackground(); - } - } - - @Override - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mKeyguardSecurityCallback = callback; - // TODO: formalize this in the interface or factor it out - ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback); - } - - @Override - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - } - - @Override - public void reset() { - - } - - @Override - public void onDetachedFromWindow() { - if (DEBUG) Log.d(TAG, "onDetachedFromWindow()"); - if (mBiometricUnlock != null) { - mBiometricUnlock.stop(); - } - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); - if (mWatchingRotation) { - try { - mWindowManager.removeRotationWatcher(mRotationWatcher); - mWatchingRotation = false; - } catch (RemoteException e) { - Log.e(TAG, "Remote exception when removing rotation watcher"); - } - } - } - - @Override - public void onPause() { - if (DEBUG) Log.d(TAG, "onPause()"); - if (mBiometricUnlock != null) { - mBiometricUnlock.stop(); - } - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); - if (mWatchingRotation) { - try { - mWindowManager.removeRotationWatcher(mRotationWatcher); - mWatchingRotation = false; - } catch (RemoteException e) { - Log.e(TAG, "Remote exception when removing rotation watcher"); - } - } - } - - @Override - public void onResume(int reason) { - if (DEBUG) Log.d(TAG, "onResume()"); - synchronized (mIsBouncerVisibleToUserLock) { - mIsBouncerVisibleToUser = isBouncerVisibleToUser(); - } - KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); - - // Registers a callback which handles stopping the biometric unlock and restarting it in - // the new position for a 180 degree rotation change. - if (!mWatchingRotation) { - try { - mLastRotation = mWindowManager.watchRotation(mRotationWatcher); - mWatchingRotation = true; - } catch (RemoteException e) { - Log.e(TAG, "Remote exception when adding rotation watcher"); - } - } - } - - @Override - public boolean needsInput() { - return false; - } - - @Override - public KeyguardSecurityCallback getCallback() { - return mKeyguardSecurityCallback; - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - mBiometricUnlock.initializeView(mFaceUnlockAreaView); - } - - private void initializeBiometricUnlockView() { - if (DEBUG) Log.d(TAG, "initializeBiometricUnlockView()"); - mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view); - if (mFaceUnlockAreaView != null) { - mBiometricUnlock = new FaceUnlock(mContext); - - mCancelButton = (ImageButton) findViewById(R.id.face_unlock_cancel_button); - mCancelButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mBiometricUnlock.stopAndShowBackup(); - } - }); - } else { - Log.w(TAG, "Couldn't find biometric unlock view"); - } - } - - /** - * Starts the biometric unlock if it should be started based on a number of factors. If it - * should not be started, it either goes to the back up, or remains showing to prepare for - * it being started later. - */ - private void maybeStartBiometricUnlock() { - if (DEBUG) Log.d(TAG, "maybeStartBiometricUnlock()"); - if (mBiometricUnlock != null) { - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); - final boolean backupIsTimedOut = ( - monitor.getFailedUnlockAttempts() >= - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT); - - boolean isBouncerVisibleToUser; - synchronized(mIsBouncerVisibleToUserLock) { - isBouncerVisibleToUser = mIsBouncerVisibleToUser; - } - - // Don't start it if the bouncer is not showing, but keep this view up because we want - // it here and ready for when the bouncer does show. - if (!isBouncerVisibleToUser) { - mBiometricUnlock.stop(); // It shouldn't be running but calling this can't hurt. - return; - } - - // Although these same conditions are handled in KeyguardSecurityModel, they are still - // necessary here. When a tablet is rotated 90 degrees, a configuration change is - // triggered and everything is torn down and reconstructed. That means - // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even - // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed. - // However, for a 180 degree rotation, no configuration change is triggered, so only - // the logic here is capable of suppressing Face Unlock. - if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE - && monitor.isAlternateUnlockEnabled() - && !monitor.getMaxBiometricUnlockAttemptsReached() - && !backupIsTimedOut) { - mBiometricUnlock.start(); - } else { - mBiometricUnlock.stopAndShowBackup(); - } - } - } - - // Returns true if the device is currently in a state where the user is seeing the bouncer. - // This requires isKeyguardBouncer() to be true, but that doesn't imply that the screen is on or - // the keyguard visibility is set to true, so we must check those conditions as well. - private boolean isBouncerVisibleToUser() { - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - return updateMonitor.isKeyguardBouncer() && updateMonitor.isKeyguardVisible() && - updateMonitor.isScreenOn(); - } - - // Starts the biometric unlock if the bouncer was not previously visible to the user, but is now - // visibile to the user. Stops the biometric unlock if the bouncer was previously visible to - // the user, but is no longer visible to the user. - private void handleBouncerUserVisibilityChanged() { - boolean wasBouncerVisibleToUser; - synchronized(mIsBouncerVisibleToUserLock) { - wasBouncerVisibleToUser = mIsBouncerVisibleToUser; - mIsBouncerVisibleToUser = isBouncerVisibleToUser(); - } - - if (mBiometricUnlock != null) { - if (wasBouncerVisibleToUser && !mIsBouncerVisibleToUser) { - mBiometricUnlock.stop(); - } else if (!wasBouncerVisibleToUser && mIsBouncerVisibleToUser) { - maybeStartBiometricUnlock(); - } - } - } - - KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { - // We need to stop the biometric unlock when a phone call comes in - @Override - public void onPhoneStateChanged(int phoneState) { - if (DEBUG) Log.d(TAG, "onPhoneStateChanged(" + phoneState + ")"); - if (phoneState == TelephonyManager.CALL_STATE_RINGING) { - if (mBiometricUnlock != null) { - mBiometricUnlock.stopAndShowBackup(); - } - } - } - - @Override - public void onUserSwitching(int userId) { - if (DEBUG) Log.d(TAG, "onUserSwitched(" + userId + ")"); - if (mBiometricUnlock != null) { - mBiometricUnlock.stop(); - } - // No longer required; static value set by KeyguardViewMediator - // mLockPatternUtils.setCurrentUser(userId); - } - - @Override - public void onUserSwitchComplete(int userId) { - if (DEBUG) Log.d(TAG, "onUserSwitchComplete(" + userId + ")"); - if (mBiometricUnlock != null) { - maybeStartBiometricUnlock(); - } - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged(" + showing + ")"); - handleBouncerUserVisibilityChanged(); - } - - @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - if (DEBUG) Log.d(TAG, "onKeyguardBouncerChanged(" + bouncer + ")"); - handleBouncerUserVisibilityChanged(); - } - - @Override - public void onScreenTurnedOn() { - if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); - handleBouncerUserVisibilityChanged(); - } - - @Override - public void onScreenTurnedOff(int why) { - if (DEBUG) Log.d(TAG, "onScreenTurnedOff()"); - handleBouncerUserVisibilityChanged(); - } - - @Override - public void onEmergencyCallAction() { - if (mBiometricUnlock != null) { - mBiometricUnlock.stop(); - } - } - }; - - @Override - public void showUsabilityHint() { - } - - @Override - public void showBouncer(int duration) { - KeyguardSecurityViewHelper. - showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); - } - - @Override - public void hideBouncer(int duration) { - KeyguardSecurityViewHelper. - hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); - } - - @Override - public void startAppearAnimation() { - // TODO. - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return false; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java deleted file mode 100644 index 98a44a6..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardGlowStripView.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.widget.LinearLayout; - -/** - * A layout which animates a strip of horizontal, pulsing dots on request. This is used - * to indicate the presence of pages to the left / right. - */ -public class KeyguardGlowStripView extends LinearLayout { - private static final int DURATION = 500; - - private static final float SLIDING_WINDOW_SIZE = 0.4f; - private int mDotStripTop; - private int mHorizontalDotGap; - - private int mDotSize; - private int mNumDots; - private Drawable mDotDrawable; - private boolean mLeftToRight = true; - - private float mAnimationProgress = 0f; - private boolean mDrawDots = false; - private ValueAnimator mAnimator; - private Interpolator mDotAlphaInterpolator = new DecelerateInterpolator(0.5f); - - public KeyguardGlowStripView(Context context) { - this(context, null, 0); - } - - public KeyguardGlowStripView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardGlowStripView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyguardGlowStripView); - mDotSize = a.getDimensionPixelSize(R.styleable.KeyguardGlowStripView_dotSize, mDotSize); - mNumDots = a.getInt(R.styleable.KeyguardGlowStripView_numDots, mNumDots); - mDotDrawable = a.getDrawable(R.styleable.KeyguardGlowStripView_glowDot); - mLeftToRight = a.getBoolean(R.styleable.KeyguardGlowStripView_leftToRight, mLeftToRight); - } - - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - int availableWidth = w - getPaddingLeft() - getPaddingRight(); - mHorizontalDotGap = (availableWidth - mDotSize * mNumDots) / (mNumDots - 1); - mDotStripTop = getPaddingTop(); - invalidate(); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - - if (!mDrawDots) return; - - int xOffset = getPaddingLeft(); - mDotDrawable.setBounds(0, 0, mDotSize, mDotSize); - - for (int i = 0; i < mNumDots; i++) { - // We fudge the relative position to provide a fade in of the first dot and a fade - // out of the final dot. - float relativeDotPosition = SLIDING_WINDOW_SIZE / 2 + ((1.0f * i) / (mNumDots - 1)) * - (1 - SLIDING_WINDOW_SIZE); - float distance = Math.abs(relativeDotPosition - mAnimationProgress); - float alpha = Math.max(0, 1 - distance / (SLIDING_WINDOW_SIZE / 2)); - - alpha = mDotAlphaInterpolator.getInterpolation(alpha); - - canvas.save(); - canvas.translate(xOffset, mDotStripTop); - mDotDrawable.setAlpha((int) (alpha * 255)); - mDotDrawable.draw(canvas); - canvas.restore(); - xOffset += mDotSize + mHorizontalDotGap; - } - } - - public void makeEmGo() { - if (mAnimator != null) { - mAnimator.cancel(); - } - float from = mLeftToRight ? 0f : 1f; - float to = mLeftToRight ? 1f : 0f; - mAnimator = ValueAnimator.ofFloat(from, to); - mAnimator.setDuration(DURATION); - mAnimator.setInterpolator(new LinearInterpolator()); - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDrawDots = false; - // make sure we draw one frame at the end with everything gone. - invalidate(); - } - - @Override - public void onAnimationStart(Animator animation) { - mDrawDots = true; - } - }); - mAnimator.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mAnimationProgress = (Float) animation.getAnimatedValue(); - invalidate(); - } - }); - mAnimator.start(); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index ab18271..ff4e815 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2007 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. @@ -16,101 +16,37 @@ package com.android.keyguard; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState; - +import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.admin.DevicePolicyManager; -import android.appwidget.AppWidgetHost; -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; -import android.graphics.Rect; -import android.media.RemoteControlClient; -import android.os.Bundle; -import android.os.Looper; -import android.os.Parcel; -import android.os.Parcelable; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; +import android.graphics.Canvas; +import android.media.AudioManager; +import android.os.SystemClock; +import android.service.trust.TrustAgentService; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; -import android.util.Slog; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.widget.RemoteViews.OnClickHandler; - -import java.lang.ref.WeakReference; - -public class KeyguardHostView extends KeyguardViewBase { - private static final String TAG = "KeyguardHostView"; - public static boolean DEBUG = KeyguardConstants.DEBUG; - public static boolean DEBUGXPORT = true; // debug music transport control - - // Transport control states. - static final int TRANSPORT_GONE = 0; - static final int TRANSPORT_INVISIBLE = 1; - static final int TRANSPORT_VISIBLE = 2; - - private int mTransportState = TRANSPORT_GONE; - - // Found in KeyguardAppWidgetPickActivity.java - static final int APPWIDGET_HOST_ID = 0x4B455947; - private final int MAX_WIDGETS = 5; - - private AppWidgetHost mAppWidgetHost; - private AppWidgetManager mAppWidgetManager; - private KeyguardWidgetPager mAppWidgetContainer; - // TODO remove transport control references, these don't exist anymore - private KeyguardTransportControlView mTransportControl; - private int mAppWidgetToShow; - - protected int mFailedAttempts; - - private KeyguardViewStateManager mViewStateManager; +import android.view.KeyEvent; +import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; - private Rect mTempRect = new Rect(); - private int mDisabledFeatures; - private boolean mCameraDisabled; - private boolean mSafeModeEnabled; - private boolean mUserSetupCompleted; - - // User for whom this host view was created. Final because we should never change the - // id without reconstructing an instance of KeyguardHostView. See note below... - private final int mUserId; - - private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView; - - protected int mClientGeneration; - - protected boolean mShowSecurityWhenReturn; - - private final Rect mInsets = new Rect(); - - private MyOnClickHandler mOnClickHandler = new MyOnClickHandler(this); - - private Runnable mPostBootCompletedRunnable; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; - /*package*/ interface UserSwitcherCallback { - void hideSecurityView(int duration); - void showSecurityView(); - void showUnlockHint(); - void userActivity(); - } +import java.io.File; - interface TransportControlCallback { - void userActivity(); - } +/** + * Base class for keyguard view. {@link #reset} is where you should + * reset the state of your view. Use the {@link KeyguardViewCallback} via + * {@link #getCallback()} to send information back (such as poking the wake lock, + * or finishing the keyguard). + * + * Handles intercepting of media keys that still work when the keyguard is + * showing. + */ +public class KeyguardHostView extends FrameLayout implements SecurityCallback { public interface OnDismissAction { /** @@ -119,995 +55,415 @@ public class KeyguardHostView extends KeyguardViewBase { boolean onDismiss(); } - public KeyguardHostView(Context context) { - this(context, null); - } - - public KeyguardHostView(Context context, AttributeSet attrs) { - super(context, attrs); - - if (DEBUG) Log.e(TAG, "KeyguardHostView()"); - - mLockPatternUtils = new LockPatternUtils(context); - - // Note: This depends on KeyguardHostView getting reconstructed every time the - // user switches, since mUserId will be used for the entire session. - // Once created, keyguard should *never* re-use this instance with another user. - // In other words, mUserId should never change - hence it's marked final. - mUserId = mLockPatternUtils.getCurrentUser(); - - DevicePolicyManager dpm = - (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - if (dpm != null) { - mDisabledFeatures = getDisabledFeatures(dpm); - mCameraDisabled = dpm.getCameraDisabled(null); - } - - mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled(); - - // These need to be created with the user context... - Context userContext = null; - try { - final String packageName = "system"; - userContext = mContext.createPackageContextAsUser(packageName, 0, - new UserHandle(mUserId)); - - } catch (NameNotFoundException e) { - e.printStackTrace(); - // This should never happen, but it's better to have no widgets than to crash. - userContext = context; - } - - mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler, - Looper.myLooper()); - - mAppWidgetManager = AppWidgetManager.getInstance(userContext); - - mViewStateManager = new KeyguardViewStateManager(this); - - mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; - - // Ensure we have the current state *before* we call showAppropriateWidgetPage() - getInitialTransportState(); - - if (mSafeModeEnabled) { - Log.v(TAG, "Keyguard widgets disabled by safe mode"); - } - if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) { - Log.v(TAG, "Keyguard widgets disabled by DPM"); - } - if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) { - Log.v(TAG, "Keyguard secure camera disabled by DPM"); - } - } - - private void getInitialTransportState() { - DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext) - .getCachedDisplayClientState(); - mTransportState = (dcs.clearing ? TRANSPORT_GONE : - (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE)); - - if (DEBUGXPORT) Log.v(TAG, "Initial transport state: " - + mTransportState + ", pbstate=" + dcs.playbackState); - } - - private void cleanupAppWidgetIds() { - if (mSafeModeEnabled || widgetsDisabled()) return; + private AudioManager mAudioManager; + private TelephonyManager mTelephonyManager = null; + protected ViewMediatorCallback mViewMediatorCallback; + protected LockPatternUtils mLockPatternUtils; + private OnDismissAction mDismissAction; + private Runnable mCancelAction; - // Clean up appWidgetIds that are bound to lockscreen, but not actually used - // This is only to clean up after another bug: we used to not call - // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code - // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks - // that are triggered by deleteAppWidgetId, which is why we're doing this - int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets(); - int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds(); - for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) { - int appWidgetId = appWidgetIdsBoundToHost[i]; - if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) { - Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id " - + appWidgetId); - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - } - } - } - - private static boolean contains(int[] array, int target) { - for (int value : array) { - if (value == target) { - return true; - } - } - return false; - } - - private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks = + private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { + @Override - public void onBootCompleted() { - if (mPostBootCompletedRunnable != null) { - mPostBootCompletedRunnable.run(); - mPostBootCompletedRunnable = null; - } + public void onUserSwitchComplete(int userId) { + getSecurityContainer().showPrimarySecurityScreen(false /* turning off */); } + @Override - public void onUserSwitchComplete(int userId) { - if (mKeyguardMultiUserSelectorView != null) { - mKeyguardMultiUserSelectorView.finalizeActiveUserView(true); + public void onTrustGrantedWithFlags(int flags, int userId) { + if (userId != KeyguardUpdateMonitor.getCurrentUser()) return; + if (!isAttachedToWindow()) return; + boolean bouncerVisible = isVisibleToUser(); + boolean initiatedByUser = + (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0; + boolean dismissKeyguard = + (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0; + + if (initiatedByUser || dismissKeyguard) { + if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) { + if (!bouncerVisible) { + // The trust agent dismissed the keyguard without the user proving + // that they are present (by swiping up to show the bouncer). That's fine if + // the user proved presence via some other way to the trust agent. + Log.i(TAG, "TrustAgent dismissed Keyguard."); + } + dismiss(false /* authenticated */); + } else { + mViewMediatorCallback.playTrustedSound(); + } } } }; - private static final boolean isMusicPlaying(int playbackState) { - // This should agree with the list in AudioService.isPlaystateActive() - switch (playbackState) { - case RemoteControlClient.PLAYSTATE_PLAYING: - case RemoteControlClient.PLAYSTATE_BUFFERING: - case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: - case RemoteControlClient.PLAYSTATE_REWINDING: - case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: - case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: - return true; - default: - return false; - } - } + // Whether the volume keys should be handled by keyguard. If true, then + // they will be handled here for specific media types such as music, otherwise + // the audio service will bring up the volume dialog. + private static final boolean KEYGUARD_MANAGES_VOLUME = false; + public static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "KeyguardViewBase"; - private SlidingChallengeLayout mSlidingChallengeLayout; - private MultiPaneChallengeLayout mMultiPaneChallengeLayout; + private KeyguardSecurityContainer mSecurityContainer; - @Override - public boolean onTouchEvent(MotionEvent ev) { - boolean result = super.onTouchEvent(ev); - mTempRect.set(0, 0, 0, 0); - offsetRectIntoDescendantCoords(getSecurityContainer(), mTempRect); - ev.offsetLocation(mTempRect.left, mTempRect.top); - result = getSecurityContainer().dispatchTouchEvent(ev) || result; - ev.offsetLocation(-mTempRect.left, -mTempRect.top); - return result; + public KeyguardHostView(Context context) { + this(context, null); } - private int getWidgetPosition(int id) { - final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer; - final int children = appWidgetContainer.getChildCount(); - for (int i = 0; i < children; i++) { - final View content = appWidgetContainer.getWidgetPageAt(i).getContent(); - if (content != null && content.getId() == id) { - return i; - } else if (content == null) { - // Attempt to track down bug #8886916 - Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children); - } - } - return -1; + public KeyguardHostView(Context context, AttributeSet attrs) { + super(context, attrs); + KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback); } @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - // Grab instances of and make any necessary changes to the main layouts. Create - // view state manager and wire up necessary listeners / callbacks. - View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target); - mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); - mAppWidgetContainer.setVisibility(VISIBLE); - mAppWidgetContainer.setCallbacks(mWidgetCallbacks); - mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget); - mAppWidgetContainer.setMinScale(0.5f); - - mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout); - if (mSlidingChallengeLayout != null) { - mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager); - } - mAppWidgetContainer.setViewStateManager(mViewStateManager); - mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils); - - mMultiPaneChallengeLayout = - (MultiPaneChallengeLayout) findViewById(R.id.multi_pane_challenge); - ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout : - mMultiPaneChallengeLayout; - challenge.setOnBouncerStateChangedListener(mViewStateManager); - mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration()); - mViewStateManager.setPagedView(mAppWidgetContainer); - mViewStateManager.setChallengeLayout(challenge); - - mViewStateManager.setSecurityViewContainer(getSecurityContainer()); - - if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) { - updateAndAddWidgets(); - } else { - // We can't add widgets until after boot completes because AppWidgetHost may try - // to contact the providers. Do it later. - mPostBootCompletedRunnable = new Runnable() { - @Override - public void run() { - updateAndAddWidgets(); - } - }; + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (mViewMediatorCallback != null) { + mViewMediatorCallback.keyguardDoneDrawing(); } - - getSecurityContainer().updateSecurityViews(mViewStateManager.isBouncing()); - enableUserSelectorIfNecessary(); } - private void updateAndAddWidgets() { - cleanupAppWidgetIds(); - addDefaultWidgets(); - addWidgetsFromSettings(); - maybeEnableAddButton(); - checkAppWidgetConsistency(); - - // Don't let the user drag the challenge down if widgets are disabled. - if (mSlidingChallengeLayout != null) { - mSlidingChallengeLayout.setEnableChallengeDragging(!widgetsDisabled()); - } - - // Select the appropriate page - mSwitchPageRunnable.run(); - - // This needs to be called after the pages are all added. - mViewStateManager.showUsabilityHints(); - } - - private void maybeEnableAddButton() { - if (!shouldEnableAddWidget()) { - mAppWidgetContainer.setAddWidgetEnabled(false); + /** + * Sets an action to run when keyguard finishes. + * + * @param action + */ + public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) { + if (mCancelAction != null) { + mCancelAction.run(); + mCancelAction = null; } + mDismissAction = action; + mCancelAction = cancelAction; } - private boolean shouldEnableAddWidget() { - return numWidgets() < MAX_WIDGETS && mUserSetupCompleted; + public void cancelDismissAction() { + setOnDismissAction(null, null); } @Override - public boolean dismiss(boolean authenticated) { - boolean finished = super.dismiss(authenticated); - if (!finished) { - mViewStateManager.showBouncer(true); - - // Enter full screen mode if we're in SIM or Account screen - SecurityMode securityMode = getSecurityContainer().getSecurityMode(); - boolean isFullScreen = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen); - boolean isSimOrAccount = securityMode == SecurityMode.SimPin - || securityMode == SecurityMode.SimPuk - || securityMode == SecurityMode.Account; - mAppWidgetContainer.setVisibility( - isSimOrAccount && isFullScreen ? View.GONE : View.VISIBLE); - - // Don't show camera or search in navbar when SIM or Account screen is showing - setSystemUiVisibility(isSimOrAccount ? - (getSystemUiVisibility() | View.STATUS_BAR_DISABLE_SEARCH) - : (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_SEARCH)); - - if (mSlidingChallengeLayout != null) { - mSlidingChallengeLayout.setChallengeInteractive(!isFullScreen); - } - } - return finished; + protected void onFinishInflate() { + mSecurityContainer = + (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container); + mLockPatternUtils = new LockPatternUtils(mContext); + mSecurityContainer.setLockPatternUtils(mLockPatternUtils); + mSecurityContainer.setSecurityCallback(this); + mSecurityContainer.showPrimarySecurityScreen(false); + // mSecurityContainer.updateSecurityViews(false /* not bouncing */); } - private int getDisabledFeatures(DevicePolicyManager dpm) { - int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; - if (dpm != null) { - final int currentUser = mLockPatternUtils.getCurrentUser(); - disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser); - } - return disabledFeatures; + /** + * Called when the view needs to be shown. + */ + public void showPrimarySecurityScreen() { + if (DEBUG) Log.d(TAG, "show()"); + mSecurityContainer.showPrimarySecurityScreen(false); } - private boolean widgetsDisabled() { - boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic(); - boolean disabledByDpm = - (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0; - boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled(); - return disabledByLowRamDevice || disabledByDpm || disabledByUser; + /** + * Show a string explaining why the security view needs to be solved. + * + * @param reason a flag indicating which string should be shown, see + * {@link KeyguardSecurityView#PROMPT_REASON_NONE} + * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART} + */ + public void showPromptReason(int reason) { + mSecurityContainer.showPromptReason(reason); } - private boolean cameraDisabledByDpm() { - return mCameraDisabled - || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0; + /** + * Dismisses the keyguard by going to the next screen or making it gone. + * + * @return True if the keyguard is done. + */ + public boolean dismiss() { + return dismiss(false); } - @Override - public void setLockPatternUtils(LockPatternUtils utils) { - super.setLockPatternUtils(utils); - getSecurityContainer().updateSecurityViews(mViewStateManager.isBouncing()); + public boolean handleBackKey() { + if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { + mSecurityContainer.dismiss(false); + return true; + } + return false; } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mAppWidgetHost.startListening(); - KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks); + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription()); + return true; + } else { + return super.dispatchPopulateAccessibilityEvent(event); + } } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mAppWidgetHost.stopListening(); - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks); + protected KeyguardSecurityContainer getSecurityContainer() { + return mSecurityContainer; } - void addWidget(AppWidgetHostView view, int pageIndex) { - mAppWidgetContainer.addWidget(view, pageIndex); + @Override + public boolean dismiss(boolean authenticated) { + return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated); } - private KeyguardWidgetPager.Callbacks mWidgetCallbacks - = new KeyguardWidgetPager.Callbacks() { - @Override - public void userActivity() { - KeyguardHostView.this.userActivity(); - } - - @Override - public void onUserActivityTimeoutChanged() { - KeyguardHostView.this.onUserActivityTimeoutChanged(); - } - - @Override - public void onAddView(View v) { - if (!shouldEnableAddWidget()) { - mAppWidgetContainer.setAddWidgetEnabled(false); - } - } - - @Override - public void onRemoveView(View v, boolean deletePermanently) { - if (deletePermanently) { - final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); - if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && - appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) { - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - } - } + /** + * Authentication has happened and it's time to dismiss keyguard. This function + * should clean up and inform KeyguardViewMediator. + */ + @Override + public void finish() { + // If there's a pending runnable because the user interacted with a widget + // and we're leaving keyguard, then run it. + boolean deferKeyguardDone = false; + if (mDismissAction != null) { + deferKeyguardDone = mDismissAction.onDismiss(); + mDismissAction = null; + mCancelAction = null; } - - @Override - public void onRemoveViewAnimationCompleted() { - if (shouldEnableAddWidget()) { - mAppWidgetContainer.setAddWidgetEnabled(true); + if (mViewMediatorCallback != null) { + if (deferKeyguardDone) { + mViewMediatorCallback.keyguardDonePending(); + } else { + mViewMediatorCallback.keyguardDone(true); } } - }; + } @Override - public void onUserSwitching(boolean switching) { - if (!switching && mKeyguardMultiUserSelectorView != null) { - mKeyguardMultiUserSelectorView.finalizeActiveUserView(false); - } + public void reset() { + mViewMediatorCallback.resetKeyguard(); } - public void userActivity() { + @Override + public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { if (mViewMediatorCallback != null) { - mViewMediatorCallback.userActivity(); + mViewMediatorCallback.setNeedsInput(needsInput); } } - public void onUserActivityTimeoutChanged() { + public void userActivity() { if (mViewMediatorCallback != null) { - mViewMediatorCallback.onUserActivityTimeoutChanged(); + mViewMediatorCallback.userActivity(); } } - @Override - public long getUserActivityTimeout() { - // Currently only considering user activity timeouts needed by widgets. - // Could also take into account longer timeouts for certain security views. - if (mAppWidgetContainer != null) { - return mAppWidgetContainer.getUserActivityTimeout(); - } - return -1; + /** + * Called when the Keyguard is not actively shown anymore on the screen. + */ + public void onPause() { + if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", + Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); + mSecurityContainer.showPrimarySecurityScreen(true); + mSecurityContainer.onPause(); + clearFocus(); } - private static class MyOnClickHandler extends OnClickHandler { - - // weak reference to the hostView to avoid keeping a live reference - // due to Binder GC linkages to AppWidgetHost. By the same token, - // this click handler should not keep references to any large - // objects. - WeakReference<KeyguardHostView> mKeyguardHostView; - - MyOnClickHandler(KeyguardHostView hostView) { - mKeyguardHostView = new WeakReference<KeyguardHostView>(hostView); - } - - @Override - public boolean onClickHandler(final View view, - final android.app.PendingIntent pendingIntent, - final Intent fillInIntent) { - KeyguardHostView hostView = mKeyguardHostView.get(); - if (hostView == null) { - return false; - } - if (pendingIntent.isActivity()) { - hostView.setOnDismissAction(new OnDismissAction() { - public boolean onDismiss() { - try { - // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? - Context context = view.getContext(); - ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, - 0, 0, - view.getMeasuredWidth(), view.getMeasuredHeight()); - context.startIntentSender( - pendingIntent.getIntentSender(), fillInIntent, - Intent.FLAG_ACTIVITY_NEW_TASK, - Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); - } catch (IntentSender.SendIntentException e) { - android.util.Log.e(TAG, "Cannot send pending intent: ", e); - } catch (Exception e) { - android.util.Log.e(TAG, "Cannot send pending intent due to " + - "unknown exception: ", e); - } - return false; - } - }); - - if (hostView.mViewStateManager.isChallengeShowing()) { - hostView.mViewStateManager.showBouncer(true); - } else { - hostView.dismiss(); - } - return true; - } else { - return super.onClickHandler(view, pendingIntent, fillInIntent); - } - }; - }; - - @Override + /** + * Called when the Keyguard is actively shown on the screen. + */ public void onResume() { - super.onResume(); - if (mViewStateManager != null) { - mViewStateManager.showUsabilityHints(); - } - } - - @Override - public void onPause() { - super.onPause(); - // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen - // turns off we reset that behavior - clearAppWidgetToShow(); - if (KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) { - checkAppWidgetConsistency(); - } - CameraWidgetFrame cameraPage = findCameraPage(); - if (cameraPage != null) { - cameraPage.onScreenTurnedOff(); - } + if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); + mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); + requestFocus(); } - public void clearAppWidgetToShow() { - mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; + /** + * Starts the animation when the Keyguard gets shown. + */ + public void startAppearAnimation() { + mSecurityContainer.startAppearAnimation(); } - private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) { - AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId); - if (appWidgetInfo != null) { - AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo); - addWidget(view, pageIndex); - return true; - } else { - if (updateDbIfFailed) { - Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + " was null for user" - + mUserId + ", deleting"); - mAppWidgetHost.deleteAppWidgetId(appId); - mLockPatternUtils.removeAppWidget(appId); - } - return false; + public void startDisappearAnimation(Runnable finishRunnable) { + if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { + finishRunnable.run(); } } - private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks = - new CameraWidgetFrame.Callbacks() { - @Override - public void onLaunchingCamera() { - setSliderHandleAlpha(0); - } - - @Override - public void onCameraLaunchedSuccessfully() { - if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) { - mAppWidgetContainer.scrollLeft(); - } - setSliderHandleAlpha(1); - mShowSecurityWhenReturn = true; - } - - @Override - public void onCameraLaunchedUnsuccessfully() { - setSliderHandleAlpha(1); - } - - private void setSliderHandleAlpha(float alpha) { - SlidingChallengeLayout slider = - (SlidingChallengeLayout) findViewById(R.id.sliding_layout); - if (slider != null) { - slider.setHandleAlpha(alpha); - } - } - }; - - private int numWidgets() { - final int childCount = mAppWidgetContainer.getChildCount(); - int widgetCount = 0; - for (int i = 0; i < childCount; i++) { - if (mAppWidgetContainer.isWidgetPage(i)) { - widgetCount++; + /** + * Verify that the user can get past the keyguard securely. This is called, + * for example, when the phone disables the keyguard but then wants to launch + * something else that requires secure access. + * + * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)} + */ + public void verifyUnlock() { + SecurityMode securityMode = mSecurityContainer.getSecurityMode(); + if (securityMode == KeyguardSecurityModel.SecurityMode.None) { + if (mViewMediatorCallback != null) { + mViewMediatorCallback.keyguardDone(true); } - } - return widgetCount; - } - - private void addDefaultWidgets() { - if (!mSafeModeEnabled && !widgetsDisabled()) { - LayoutInflater inflater = LayoutInflater.from(mContext); - View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false); - mAppWidgetContainer.addWidget(addWidget, 0); - View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); - addWidgetButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // Pass in an invalid widget id... the picker will allocate an ID for us - getActivityLauncher().launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID); - } - }); - } - - // We currently disable cameras in safe mode because we support loading 3rd party - // cameras we can't trust. TODO: plumb safe mode into camera creation code and only - // inflate system-provided camera? - if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted - && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) { - View cameraWidget = CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, - getActivityLauncher()); - if (cameraWidget != null) { - mAppWidgetContainer.addWidget(cameraWidget); + } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern + && securityMode != KeyguardSecurityModel.SecurityMode.PIN + && securityMode != KeyguardSecurityModel.SecurityMode.Password) { + // can only verify unlock when in pattern/password mode + if (mViewMediatorCallback != null) { + mViewMediatorCallback.keyguardDone(false); } + } else { + // otherwise, go to the unlock screen, see if they can verify it + mSecurityContainer.verifyUnlock(); } } /** - * Create KeyguardTransportControlView on demand. - * @return + * Called before this view is being removed. */ - private KeyguardTransportControlView getOrCreateTransportControl() { - if (mTransportControl == null) { - LayoutInflater inflater = LayoutInflater.from(mContext); - mTransportControl = (KeyguardTransportControlView) - inflater.inflate(R.layout.keyguard_transport_control_view, this, false); - mTransportControl.setTransportControlCallback(new TransportControlCallback() { - public void userActivity() { - mViewMediatorCallback.userActivity(); - } - }); - } - return mTransportControl; - } - - private int getInsertPageIndex() { - View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget); - int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget); - if (insertionIndex < 0) { - insertionIndex = 0; // no add widget page found - } else { - insertionIndex++; // place after add widget - } - return insertionIndex; - } - - private void addDefaultStatusWidget(int index) { - LayoutInflater inflater = LayoutInflater.from(mContext); - View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true); - mAppWidgetContainer.addWidget(statusWidget, index); - } - - private void addWidgetsFromSettings() { - if (mSafeModeEnabled || widgetsDisabled()) { - addDefaultStatusWidget(0); - return; - } - - int insertionIndex = getInsertPageIndex(); - - // Add user-selected widget - final int[] widgets = mLockPatternUtils.getAppWidgets(); - - if (widgets == null) { - Log.d(TAG, "Problem reading widgets"); - } else { - for (int i = widgets.length -1; i >= 0; i--) { - if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) { - addDefaultStatusWidget(insertionIndex); - } else { - // We add the widgets from left to right, starting after the first page after - // the add page. We count down, since the order will be persisted from right - // to left, starting after camera. - addWidget(widgets[i], insertionIndex, true); - } - } - } + public void cleanUp() { + getSecurityContainer().onPause(); } - private int allocateIdForDefaultAppWidget() { - int appWidgetId; - Resources res = getContext().getResources(); - ComponentName defaultAppWidget = new ComponentName( - res.getString(R.string.widget_default_package_name), - res.getString(R.string.widget_default_class_name)); - - // Note: we don't support configuring the widget - appWidgetId = mAppWidgetHost.allocateAppWidgetId(); - - try { - mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Error when trying to bind default AppWidget: " + e); - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (interceptMediaKey(event)) { + return true; } - return appWidgetId; + return super.dispatchKeyEvent(event); } - public void checkAppWidgetConsistency() { - final int childCount = mAppWidgetContainer.getChildCount(); - boolean widgetPageExists = false; - for (int i = 0; i < childCount; i++) { - if (mAppWidgetContainer.isWidgetPage(i)) { - widgetPageExists = true; - break; - } - } - if (!widgetPageExists) { - final int insertPageIndex = getInsertPageIndex(); - - final boolean userAddedWidgetsEnabled = !widgetsDisabled(); - - boolean addedDefaultAppWidget = false; - - if (!mSafeModeEnabled) { - if (userAddedWidgetsEnabled) { - int appWidgetId = allocateIdForDefaultAppWidget(); - if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { - addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true); + /** + * Allows the media keys to work when the keyguard is showing. + * The media keys should be of no interest to the actual keyguard view(s), + * so intercepting them here should not be of any harm. + * @param event The key event + * @return whether the event was consumed as a media key. + */ + public boolean interceptMediaKey(KeyEvent event) { + final int keyCode = event.getKeyCode(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + /* Suppress PLAY/PAUSE toggle when phone is ringing or + * in-call to avoid music playback */ + if (mTelephonyManager == null) { + mTelephonyManager = (TelephonyManager) getContext().getSystemService( + Context.TELEPHONY_SERVICE); } - } else { - // note: even if widgetsDisabledByDpm() returns true, we still bind/create - // the default appwidget if possible - int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId(); - if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { - appWidgetId = allocateIdForDefaultAppWidget(); - if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { - mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId); - } + if (mTelephonyManager != null && + mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { + return true; // suppress key event } - if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { - addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false); - if (!addedDefaultAppWidget) { - mAppWidgetHost.deleteAppWidgetId(appWidgetId); - mLockPatternUtils.writeFallbackAppWidgetId( - AppWidgetManager.INVALID_APPWIDGET_ID); + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + handleMediaKeyEvent(event); + return true; + } + + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: { + if (KEYGUARD_MANAGES_VOLUME) { + synchronized (this) { + if (mAudioManager == null) { + mAudioManager = (AudioManager) getContext().getSystemService( + Context.AUDIO_SERVICE); + } } + // Volume buttons should only function for music (local or remote). + // TODO: Actually handle MUTE. + mAudioManager.adjustSuggestedStreamVolume( + keyCode == KeyEvent.KEYCODE_VOLUME_UP + ? AudioManager.ADJUST_RAISE + : AudioManager.ADJUST_LOWER /* direction */, + AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); + // Don't execute default volume behavior + return true; + } else { + return false; } } } - - // Use the built-in status/clock view if we can't inflate the default widget - if (!addedDefaultAppWidget) { - addDefaultStatusWidget(insertPageIndex); - } - - // trigger DB updates only if user-added widgets are enabled - if (!mSafeModeEnabled && userAddedWidgetsEnabled) { - mAppWidgetContainer.onAddView( - mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex); + } else if (event.getAction() == KeyEvent.ACTION_UP) { + switch (keyCode) { + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { + handleMediaKeyEvent(event); + return true; + } } } + return false; } - private final Runnable mSwitchPageRunnable = new Runnable() { - @Override - public void run() { - showAppropriateWidgetPage(); - } - }; - - static class SavedState extends BaseSavedState { - int transportState; - int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; - Rect insets = new Rect(); - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - this.transportState = in.readInt(); - this.appWidgetToShow = in.readInt(); - this.insets = in.readParcelable(null); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(this.transportState); - out.writeInt(this.appWidgetToShow); - out.writeParcelable(insets, 0); - } - - public static final Parcelable.Creator<SavedState> CREATOR - = new Parcelable.Creator<SavedState>() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; + private void handleMediaKeyEvent(KeyEvent keyEvent) { + synchronized (this) { + if (mAudioManager == null) { + mAudioManager = (AudioManager) getContext().getSystemService( + Context.AUDIO_SERVICE); } - }; - } - - @Override - public Parcelable onSaveInstanceState() { - if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState); - Parcelable superState = super.onSaveInstanceState(); - SavedState ss = new SavedState(superState); - // If the transport is showing, force it to show it on restore. - final boolean showing = mTransportControl != null - && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0; - ss.transportState = showing ? TRANSPORT_VISIBLE : mTransportState; - ss.appWidgetToShow = mAppWidgetToShow; - ss.insets.set(mInsets); - return ss; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - if (!(state instanceof SavedState)) { - super.onRestoreInstanceState(state); - return; } - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - mTransportState = (ss.transportState); - mAppWidgetToShow = ss.appWidgetToShow; - setInsets(ss.insets); - if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState); - mSwitchPageRunnable.run(); - } - - @Override - protected boolean fitSystemWindows(Rect insets) { - setInsets(insets); - return true; - } - - private void setInsets(Rect insets) { - mInsets.set(insets); - if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets); - if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets); - - final CameraWidgetFrame cameraWidget = findCameraPage(); - if (cameraWidget != null) cameraWidget.setInsets(mInsets); + mAudioManager.dispatchMediaKeyEvent(keyEvent); } @Override - public void onWindowFocusChanged(boolean hasWindowFocus) { - super.onWindowFocusChanged(hasWindowFocus); - if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused")); - if (hasWindowFocus && mShowSecurityWhenReturn) { - SlidingChallengeLayout slider = - (SlidingChallengeLayout) findViewById(R.id.sliding_layout); - if (slider != null) { - slider.setHandleAlpha(1); - slider.showChallenge(true); - } - mShowSecurityWhenReturn = false; - } - } + public void dispatchSystemUiVisibilityChanged(int visibility) { + super.dispatchSystemUiVisibilityChanged(visibility); - private void showAppropriateWidgetPage() { - final int state = mTransportState; - final boolean transportAdded = ensureTransportPresentOrRemoved(state); - final int pageToShow = getAppropriateWidgetPage(state); - if (!transportAdded) { - mAppWidgetContainer.setCurrentPage(pageToShow); - } else if (state == TRANSPORT_VISIBLE) { - // If the transport was just added, we need to wait for layout to happen before - // we can set the current page. - post(new Runnable() { - @Override - public void run() { - mAppWidgetContainer.setCurrentPage(pageToShow); - } - }); + if (!(mContext instanceof Activity)) { + setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); } } /** - * Examines the current state and adds the transport to the widget pager when the state changes. - * - * Showing the initial transport and keeping it around is a bit tricky because the signals - * coming from music players aren't always clear. Here's how the states are handled: - * - * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present. + * In general, we enable unlocking the insecure keyguard with the menu key. However, there are + * some cases where we wish to disable it, notably when the menu button placement or technology + * is prone to false positives. * - * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music - * player is registered but not currently playing music (or we don't know the state yet). The - * code adds it conditionally on play state. - * - * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing. - * - * Once the transport is showing, we always show it until keyguard is dismissed. This state is - * maintained by onSave/RestoreInstanceState(). This state is cleared in - * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be - * gone when keyguard is restarted until we get an update with the current state. - * - * @param state + * @return true if the menu key should be enabled */ - private boolean ensureTransportPresentOrRemoved(int state) { - final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1; - final boolean visible = state == TRANSPORT_VISIBLE; - final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state); - if (!showing && (visible || shouldBeVisible)) { - // insert to left of camera if it exists, otherwise after right-most widget - int lastWidget = mAppWidgetContainer.getChildCount() - 1; - int position = 0; // handle no widget case - if (lastWidget >= 0) { - position = mAppWidgetContainer.isCameraPage(lastWidget) ? - lastWidget : lastWidget + 1; - } - if (DEBUGXPORT) Log.v(TAG, "add transport at " + position); - mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position); + private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; + private boolean shouldEnableMenuKey() { + final Resources res = getResources(); + final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); + final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); + final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); + return !configDisabled || isTestHarness || fileOverride; + } + + public boolean handleMenuKey() { + // The following enables the MENU key to work for testing automation + if (shouldEnableMenuKey()) { + dismiss(); return true; - } else if (showing && state == TRANSPORT_GONE) { - if (DEBUGXPORT) Log.v(TAG, "remove transport"); - mAppWidgetContainer.removeWidget(getOrCreateTransportControl()); - mTransportControl = null; - KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null); } return false; } - private CameraWidgetFrame findCameraPage() { - for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) { - if (mAppWidgetContainer.isCameraPage(i)) { - return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i); - } - } - return null; - } - - boolean isMusicPage(int pageIndex) { - return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control); + public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { + mViewMediatorCallback = viewMediatorCallback; + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); } - private int getAppropriateWidgetPage(int musicTransportState) { - // assumes at least one widget (besides camera + add) - if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { - final int childCount = mAppWidgetContainer.getChildCount(); - for (int i = 0; i < childCount; i++) { - if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId() - == mAppWidgetToShow) { - return i; - } - } - mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; - } - // if music playing, show transport - if (musicTransportState == TRANSPORT_VISIBLE) { - if (DEBUG) Log.d(TAG, "Music playing, show transport"); - return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl()); - } - - // else show the right-most widget (except for camera) - int rightMost = mAppWidgetContainer.getChildCount() - 1; - if (mAppWidgetContainer.isCameraPage(rightMost)) { - rightMost--; - } - if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost); - return rightMost; - } - - private void enableUserSelectorIfNecessary() { - final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (um == null) { - Throwable t = new Throwable(); - t.fillInStackTrace(); - Log.e(TAG, "user service is null.", t); - return; - } - - // if there are multiple users, we need to enable to multi-user switcher - if (!um.isUserSwitcherEnabled()) { - return; - } - - final View multiUserView = findViewById(R.id.keyguard_user_selector); - if (multiUserView == null) { - if (DEBUG) Log.d(TAG, "can't find user_selector in layout."); - return; - } - - if (multiUserView instanceof KeyguardMultiUserSelectorView) { - mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView; - mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE); - mKeyguardMultiUserSelectorView.addUsers(um.getUsers(true)); - UserSwitcherCallback callback = new UserSwitcherCallback() { - @Override - public void hideSecurityView(int duration) { - getSecurityContainer().animate().alpha(0).setDuration(duration); - } - - @Override - public void showSecurityView() { - getSecurityContainer().setAlpha(1.0f); - } - - @Override - public void showUnlockHint() { - if (getSecurityContainer() != null) { - getSecurityContainer().showUsabilityHint(); - } - } - - @Override - public void userActivity() { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.userActivity(); - } - } - }; - mKeyguardMultiUserSelectorView.setCallback(callback); - } else { - Throwable t = new Throwable(); - t.fillInStackTrace(); - if (multiUserView == null) { - Log.e(TAG, "could not find the user_selector.", t); - } else { - Log.e(TAG, "user_selector is the wrong type.", t); - } - } - } - - @Override - public void cleanUp() { - // Make sure we let go of all widgets and their package contexts promptly. If we don't do - // this, and the associated application is uninstalled, it can cause a soft reboot. - int count = mAppWidgetContainer.getChildCount(); - for (int i = 0; i < count; i++) { - KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i); - frame.removeAllViews(); - } - getSecurityContainer().onPause(); // clean up any actions in progress - } - - public void goToWidget(int appWidgetId) { - mAppWidgetToShow = appWidgetId; - mSwitchPageRunnable.run(); - } - - @Override - protected void showBouncer(boolean show) { - super.showBouncer(show); - mViewStateManager.showBouncer(show); + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + mSecurityContainer.setLockPatternUtils(utils); } - @Override - public void onExternalMotionEvent(MotionEvent event) { - mAppWidgetContainer.handleExternalCameraEvent(event); + public SecurityMode getSecurityMode() { + return mSecurityContainer.getSecurityMode(); } - @Override - protected void onCreateOptions(Bundle options) { - if (options != null) { - int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET, - AppWidgetManager.INVALID_APPWIDGET_ID); - if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { - goToWidget(widgetToShow); - } - } + public SecurityMode getCurrentSecurityMode() { + return mSecurityContainer.getCurrentSecurityMode(); } - } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java b/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java deleted file mode 100644 index 343fdcb..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardLinearLayout.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.LinearLayout; - -/** - * A layout that arranges its children into a special type of grid. - */ -public class KeyguardLinearLayout extends LinearLayout { - int mTopChild = 0; - - public KeyguardLinearLayout(Context context) { - this(context, null, 0); - } - - public KeyguardLinearLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardLinearLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public void setTopChild(View child) { - int top = indexOfChild(child); - mTopChild = top; - invalidate(); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java index 236cbf6..301b171 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java @@ -16,29 +16,21 @@ package com.android.keyguard; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.content.ContentResolver; import android.content.Context; -import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.MutableInt; import android.view.View; import android.widget.TextView; import java.lang.ref.WeakReference; -import com.android.internal.widget.LockPatternUtils; - /*** * Manages a number of views inside of the given layout. See below for a list of widgets. */ -class KeyguardMessageArea extends TextView { +class KeyguardMessageArea extends TextView implements SecurityMessageDisplay { /** Handler token posted with accessibility announcement runnables. */ private static final Object ANNOUNCE_TOKEN = new Object(); @@ -48,87 +40,23 @@ class KeyguardMessageArea extends TextView { */ private static final long ANNOUNCEMENT_DELAY = 250; - static final int SECURITY_MESSAGE_DURATION = 5000; - protected static final int FADE_DURATION = 750; - - private static final String TAG = "KeyguardMessageArea"; - - // is the bouncer up? - boolean mShowingBouncer = false; + private static final int SECURITY_MESSAGE_DURATION = 5000; - KeyguardUpdateMonitor mUpdateMonitor; + private final KeyguardUpdateMonitor mUpdateMonitor; + private final Handler mHandler; // Timeout before we reset the message to show charging/owner info long mTimeout = SECURITY_MESSAGE_DURATION; - - private Handler mHandler; - CharSequence mMessage; - boolean mShowingMessage; - private CharSequence mSeparator; - private LockPatternUtils mLockPatternUtils; - Runnable mClearMessageRunnable = new Runnable() { + private final Runnable mClearMessageRunnable = new Runnable() { @Override public void run() { mMessage = null; - mShowingMessage = false; - if (mShowingBouncer) { - hideMessage(FADE_DURATION, true); - } else { - update(); - } + update(); } }; - public static class Helper implements SecurityMessageDisplay { - KeyguardMessageArea mMessageArea; - Helper(View v) { - mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area); - if (mMessageArea == null) { - throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass()); - } - } - - public void setMessage(CharSequence msg, boolean important) { - if (!TextUtils.isEmpty(msg) && important) { - mMessageArea.mMessage = msg; - mMessageArea.securityMessageChanged(); - } - } - - public void setMessage(int resId, boolean important) { - if (resId != 0 && important) { - mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId); - mMessageArea.securityMessageChanged(); - } - } - - public void setMessage(int resId, boolean important, Object... formatArgs) { - if (resId != 0 && important) { - mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs); - mMessageArea.securityMessageChanged(); - } - } - - @Override - public void showBouncer(int duration) { - mMessageArea.hideMessage(duration, false); - mMessageArea.mShowingBouncer = true; - } - - @Override - public void hideBouncer(int duration) { - mMessageArea.showMessage(duration); - mMessageArea.mShowingBouncer = false; - } - - @Override - public void setTimeout(int timeoutMs) { - mMessageArea.mTimeout = timeoutMs; - } - } - private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { public void onScreenTurnedOff(int why) { setSelected(false); @@ -146,28 +74,64 @@ class KeyguardMessageArea extends TextView { super(context, attrs); setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug - mLockPatternUtils = new LockPatternUtils(context); - - // Registering this callback immediately updates the battery state, among other things. mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext()); mUpdateMonitor.registerCallback(mInfoCallback); mHandler = new Handler(Looper.myLooper()); - mSeparator = getResources().getString( - com.android.internal.R.string.kg_text_message_separator); - update(); } @Override + public void setMessage(CharSequence msg, boolean important) { + if (!TextUtils.isEmpty(msg) && important) { + securityMessageChanged(msg); + } else { + clearMessage(); + } + } + + @Override + public void setMessage(int resId, boolean important) { + if (resId != 0 && important) { + CharSequence message = getContext().getResources().getText(resId); + securityMessageChanged(message); + } else { + clearMessage(); + } + } + + @Override + public void setMessage(int resId, boolean important, Object... formatArgs) { + if (resId != 0 && important) { + String message = getContext().getString(resId, formatArgs); + securityMessageChanged(message); + } else { + clearMessage(); + } + } + + @Override + public void setTimeout(int timeoutMs) { + mTimeout = timeoutMs; + } + + public static SecurityMessageDisplay findSecurityMessageDisplay(View v) { + KeyguardMessageArea messageArea = (KeyguardMessageArea) v.findViewById( + R.id.keyguard_message_area); + if (messageArea == null) { + throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass()); + } + return messageArea; + } + + @Override protected void onFinishInflate() { final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); setSelected(screenOn); // This is required to ensure marquee works } - public void securityMessageChanged() { - setAlpha(1f); - mShowingMessage = true; + private void securityMessageChanged(CharSequence message) { + mMessage = message; update(); mHandler.removeCallbacks(mClearMessageRunnable); if (mTimeout > 0) { @@ -178,55 +142,17 @@ class KeyguardMessageArea extends TextView { (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY)); } - /** - * Update the status lines based on these rules: - * AlarmStatus: Alarm state always gets it's own line. - * Status1 is shared between help, battery status and generic unlock instructions, - * prioritized in that order. - * @param showStatusLines status lines are shown if true - */ - void update() { - MutableInt icon = new MutableInt(0); - CharSequence status = getCurrentMessage(); - setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0); - setText(status); - } - - - CharSequence getCurrentMessage() { - return mShowingMessage ? mMessage : null; + private void clearMessage() { + mHandler.removeCallbacks(mClearMessageRunnable); + mHandler.post(mClearMessageRunnable); } - private void hideMessage(int duration, boolean thenUpdate) { - if (duration > 0) { - Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f); - anim.setDuration(duration); - if (thenUpdate) { - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - update(); - } - }); - } - anim.start(); - } else { - setAlpha(0f); - if (thenUpdate) { - update(); - } - } + private void update() { + CharSequence status = mMessage; + setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE); + setText(status); } - private void showMessage(int duration) { - if (duration > 0) { - Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f); - anim.setDuration(duration); - anim.start(); - } else { - setAlpha(1f); - } - } /** * Runnable used to delay accessibility announcements. @@ -235,7 +161,7 @@ class KeyguardMessageArea extends TextView { private final WeakReference<View> mHost; private final CharSequence mTextToAnnounce; - public AnnounceRunnable(View host, CharSequence textToAnnounce) { + AnnounceRunnable(View host, CharSequence textToAnnounce) { mHost = new WeakReference<View>(host); mTextToAnnounce = textToAnnounce; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java deleted file mode 100644 index 3aec55c..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserAvatar.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.os.UserManager; -import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.TextView; - -class KeyguardMultiUserAvatar extends FrameLayout { - private static final String TAG = KeyguardMultiUserAvatar.class.getSimpleName(); - private static final boolean DEBUG = KeyguardConstants.DEBUG; - - private ImageView mUserImage; - private TextView mUserName; - private UserInfo mUserInfo; - private static final float ACTIVE_ALPHA = 1.0f; - private static final float INACTIVE_ALPHA = 1.0f; - private static final float ACTIVE_SCALE = 1.5f; - private static final float ACTIVE_TEXT_ALPHA = 0f; - private static final float INACTIVE_TEXT_ALPHA = 0.5f; - private static final int SWITCH_ANIMATION_DURATION = 150; - - private final float mActiveAlpha; - private final float mActiveScale; - private final float mActiveTextAlpha; - private final float mInactiveAlpha; - private final float mInactiveTextAlpha; - private final float mShadowRadius; - private final float mStroke; - private final float mIconSize; - private final int mFrameColor; - private final int mFrameShadowColor; - private final int mTextColor; - private final int mHighlightColor; - - private boolean mTouched; - - private boolean mActive; - private boolean mInit = true; - private KeyguardMultiUserSelectorView mUserSelector; - private KeyguardCircleFramedDrawable mFramed; - private boolean mPressLock; - private UserManager mUserManager; - - public static KeyguardMultiUserAvatar fromXml(int resId, Context context, - KeyguardMultiUserSelectorView userSelector, UserInfo info) { - KeyguardMultiUserAvatar icon = (KeyguardMultiUserAvatar) - LayoutInflater.from(context).inflate(resId, userSelector, false); - - icon.init(info, userSelector); - return icon; - } - - public KeyguardMultiUserAvatar(Context context) { - this(context, null, 0); - } - - public KeyguardMultiUserAvatar(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardMultiUserAvatar(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - Resources res = mContext.getResources(); - mTextColor = res.getColor(R.color.keyguard_avatar_nick_color); - mIconSize = res.getDimension(R.dimen.keyguard_avatar_size); - mStroke = res.getDimension(R.dimen.keyguard_avatar_frame_stroke_width); - mShadowRadius = res.getDimension(R.dimen.keyguard_avatar_frame_shadow_radius); - mFrameColor = res.getColor(R.color.keyguard_avatar_frame_color); - mFrameShadowColor = res.getColor(R.color.keyguard_avatar_frame_shadow_color); - mHighlightColor = res.getColor(R.color.keyguard_avatar_frame_pressed_color); - mActiveTextAlpha = ACTIVE_TEXT_ALPHA; - mInactiveTextAlpha = INACTIVE_TEXT_ALPHA; - mActiveScale = ACTIVE_SCALE; - mActiveAlpha = ACTIVE_ALPHA; - mInactiveAlpha = INACTIVE_ALPHA; - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - - mTouched = false; - - setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - - protected String rewriteIconPath(String path) { - return path; - } - - public void init(UserInfo user, KeyguardMultiUserSelectorView userSelector) { - mUserInfo = user; - mUserSelector = userSelector; - - mUserImage = (ImageView) findViewById(R.id.keyguard_user_avatar); - mUserName = (TextView) findViewById(R.id.keyguard_user_name); - - mFramed = (KeyguardCircleFramedDrawable) - MultiUserAvatarCache.getInstance().get(user.id); - - // If we can't find it or the params don't match, create the drawable again - if (mFramed == null - || !mFramed.verifyParams(mIconSize, mFrameColor, mStroke, mFrameShadowColor, - mShadowRadius, mHighlightColor)) { - Bitmap icon = null; - try { - icon = mUserManager.getUserIcon(user.id); - } catch (Exception e) { - if (DEBUG) Log.d(TAG, "failed to get profile icon " + user, e); - } - - if (icon == null) { - icon = BitmapFactory.decodeResource(mContext.getResources(), - com.android.internal.R.drawable.ic_contact_picture); - } - - mFramed = new KeyguardCircleFramedDrawable(icon, (int) mIconSize, mFrameColor, mStroke, - mFrameShadowColor, mShadowRadius, mHighlightColor); - MultiUserAvatarCache.getInstance().put(user.id, mFramed); - } - - mFramed.reset(); - - mUserImage.setImageDrawable(mFramed); - mUserName.setText(mUserInfo.name); - setOnClickListener(mUserSelector); - mInit = false; - } - - public void setActive(boolean active, boolean animate, final Runnable onComplete) { - if (mActive != active || mInit) { - mActive = active; - - if (active) { - KeyguardLinearLayout parent = (KeyguardLinearLayout) getParent(); - parent.setTopChild(this); - // TODO: Create an appropriate asset when string changes are possible. - setContentDescription(mUserName.getText() - + ". " + mContext.getString(R.string.user_switched, "")); - } else { - setContentDescription(mUserName.getText()); - } - } - updateVisualsForActive(mActive, animate, SWITCH_ANIMATION_DURATION, onComplete); - } - - void updateVisualsForActive(boolean active, boolean animate, int duration, - final Runnable onComplete) { - final float finalAlpha = active ? mActiveAlpha : mInactiveAlpha; - final float initAlpha = active ? mInactiveAlpha : mActiveAlpha; - final float finalScale = active ? 1f : 1f / mActiveScale; - final float initScale = mFramed.getScale(); - final int finalTextAlpha = active ? (int) (mActiveTextAlpha * 255) : - (int) (mInactiveTextAlpha * 255); - final int initTextAlpha = active ? (int) (mInactiveTextAlpha * 255) : - (int) (mActiveTextAlpha * 255); - int textColor = mTextColor; - mUserName.setTextColor(textColor); - - if (animate && mTouched) { - ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); - va.addUpdateListener(new AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float r = animation.getAnimatedFraction(); - float scale = (1 - r) * initScale + r * finalScale; - float alpha = (1 - r) * initAlpha + r * finalAlpha; - int textAlpha = (int) ((1 - r) * initTextAlpha + r * finalTextAlpha); - mFramed.setScale(scale); - mUserImage.setAlpha(alpha); - mUserName.setTextColor(Color.argb(textAlpha, 255, 255, 255)); - mUserImage.invalidate(); - } - }); - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (onComplete != null) { - onComplete.run(); - } - } - }); - va.setDuration(duration); - va.start(); - } else { - mFramed.setScale(finalScale); - mUserImage.setAlpha(finalAlpha); - mUserName.setTextColor(Color.argb(finalTextAlpha, 255, 255, 255)); - if (onComplete != null) { - post(onComplete); - } - } - - mTouched = true; - } - - @Override - public void setPressed(boolean pressed) { - if (mPressLock && !pressed) { - return; - } - - if (mPressLock || !pressed || isClickable()) { - super.setPressed(pressed); - mFramed.setPressed(pressed); - mUserImage.invalidate(); - } - } - - public void lockPressed(boolean pressed) { - mPressLock = pressed; - setPressed(pressed); - } - - public UserInfo getUserInfo() { - return mUserInfo; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java deleted file mode 100644 index 06815e1..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardMultiUserSelectorView.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.app.ActivityManagerNative; -import android.content.Context; -import android.content.pm.UserInfo; -import android.os.RemoteException; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; - -public class KeyguardMultiUserSelectorView extends FrameLayout implements View.OnClickListener { - private static final String TAG = "KeyguardMultiUserSelectorView"; - - private ViewGroup mUsersGrid; - private KeyguardMultiUserAvatar mActiveUserAvatar; - private KeyguardHostView.UserSwitcherCallback mCallback; - private static final int FADE_OUT_ANIMATION_DURATION = 100; - - public KeyguardMultiUserSelectorView(Context context) { - this(context, null, 0); - } - - public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardMultiUserSelectorView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - protected void onFinishInflate () { - mUsersGrid = (ViewGroup) findViewById(R.id.keyguard_users_grid); - mUsersGrid.removeAllViews(); - setClipChildren(false); - setClipToPadding(false); - - } - - public void setCallback(KeyguardHostView.UserSwitcherCallback callback) { - mCallback = callback; - } - - public void addUsers(Collection<UserInfo> userList) { - UserInfo activeUser; - try { - activeUser = ActivityManagerNative.getDefault().getCurrentUser(); - } catch (RemoteException re) { - activeUser = null; - } - - ArrayList<UserInfo> users = new ArrayList<UserInfo>(userList); - Collections.sort(users, mOrderAddedComparator); - - for (UserInfo user: users) { - if (user.supportsSwitchTo()) { - KeyguardMultiUserAvatar uv = createAndAddUser(user); - if (user.id == activeUser.id) { - mActiveUserAvatar = uv; - } - uv.setActive(false, false, null); - } - } - mActiveUserAvatar.lockPressed(true); - } - - public void finalizeActiveUserView(boolean animate) { - if (animate) { - getHandler().postDelayed(new Runnable() { - @Override - public void run() { - finalizeActiveUserNow(true); - } - }, 500); - } else { - finalizeActiveUserNow(animate); - } - } - - void finalizeActiveUserNow(boolean animate) { - mActiveUserAvatar.lockPressed(false); - mActiveUserAvatar.setActive(true, animate, null); - } - - Comparator<UserInfo> mOrderAddedComparator = new Comparator<UserInfo>() { - @Override - public int compare(UserInfo lhs, UserInfo rhs) { - return (lhs.serialNumber - rhs.serialNumber); - } - }; - - private KeyguardMultiUserAvatar createAndAddUser(UserInfo user) { - KeyguardMultiUserAvatar uv = KeyguardMultiUserAvatar.fromXml( - R.layout.keyguard_multi_user_avatar, mContext, this, user); - mUsersGrid.addView(uv); - return uv; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent event) { - if(event.getActionMasked() != MotionEvent.ACTION_CANCEL && mCallback != null) { - mCallback.userActivity(); - } - return false; - } - - private void setAllClickable(boolean clickable) - { - for(int i = 0; i < mUsersGrid.getChildCount(); i++) { - View v = mUsersGrid.getChildAt(i); - v.setClickable(clickable); - v.setPressed(false); - } - } - - @Override - public void onClick(View v) { - if (!(v instanceof KeyguardMultiUserAvatar)) return; - final KeyguardMultiUserAvatar avatar = (KeyguardMultiUserAvatar) v; - if (avatar.isClickable()) { // catch race conditions - if (mActiveUserAvatar == avatar) { - // If they click the currently active user, show the unlock hint - mCallback.showUnlockHint(); - return; - } else { - // Reset the previously active user to appear inactive - mCallback.hideSecurityView(FADE_OUT_ANIMATION_DURATION); - setAllClickable(false); - avatar.lockPressed(true); - mActiveUserAvatar.setActive(false, true, new Runnable() { - @Override - public void run() { - mActiveUserAvatar = avatar; - try { - ActivityManagerNative.getDefault() - .switchUser(avatar.getUserInfo().id); - } catch (RemoteException re) { - Log.e(TAG, "Couldn't switch user " + re); - } - } - }); - } - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java index 04ef57e..d265e0d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java @@ -22,6 +22,9 @@ import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; +import com.android.settingslib.animation.AppearAnimationUtils; +import com.android.settingslib.animation.DisappearAnimationUtils; + /** * Displays a PIN pad for unlocking. */ @@ -29,7 +32,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; - private ViewGroup mKeyguardBouncerFrame; + private ViewGroup mContainer; private ViewGroup mRow0; private ViewGroup mRow1; private ViewGroup mRow2; @@ -47,7 +50,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mAppearAnimationUtils = new AppearAnimationUtils(context); mDisappearAnimationUtils = new DisappearAnimationUtils(context, 125, 0.6f /* translationScale */, - 0.6f /* delayScale */, AnimationUtils.loadInterpolator( + 0.45f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); @@ -55,11 +58,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { protected void resetState() { super.resetState(); - if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) { - mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true); - } else { - mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false); - } + mSecurityMessageDisplay.setMessage(R.string.kg_pin_instructions, false); } @Override @@ -71,7 +70,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { protected void onFinishInflate() { super.onFinishInflate(); - mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame); + mContainer = (ViewGroup) findViewById(R.id.container); mRow0 = (ViewGroup) findViewById(R.id.row0); mRow1 = (ViewGroup) findViewById(R.id.row1); mRow2 = (ViewGroup) findViewById(R.id.row2); @@ -119,7 +118,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { .setDuration(500) .setInterpolator(mAppearAnimationUtils.getInterpolator()) .translationY(0); - mAppearAnimationUtils.startAnimation(mViews, + mAppearAnimationUtils.startAnimation2d(mViews, new Runnable() { @Override public void run() { @@ -136,7 +135,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { .setDuration(280) .setInterpolator(mDisappearAnimationUtils.getInterpolator()) .translationY(mDisappearYTranslation); - mDisappearAnimationUtils.startAnimation(mViews, + mDisappearAnimationUtils.startAnimation2d(mViews, new Runnable() { @Override public void run() { @@ -150,8 +149,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { } private void enableClipping(boolean enable) { - mKeyguardBouncerFrame.setClipToPadding(enable); - mKeyguardBouncerFrame.setClipChildren(enable); + mContainer.setClipToPadding(enable); + mContainer.setClipChildren(enable); mRow1.setClipToPadding(enable); mRow2.setClipToPadding(enable); mRow3.setClipToPadding(enable); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java index 845d53a..2db87b3 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Rect; import android.text.Editable; import android.text.InputType; +import android.text.TextUtils; import android.text.TextWatcher; import android.text.method.TextKeyListener; import android.util.AttributeSet; @@ -34,6 +35,8 @@ import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.widget.TextViewInputDisabler; + import java.util.List; /** * Displays an alphanumeric (latin-1) key entry for the user to enter @@ -48,6 +51,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView InputMethodManager mImm; private TextView mPasswordEntry; + private TextViewInputDisabler mPasswordEntryDisabler; + private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -69,7 +74,12 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView protected void resetState() { mSecurityMessageDisplay.setMessage(R.string.kg_password_instructions, false); - mPasswordEntry.setEnabled(true); + final boolean wasDisabled = mPasswordEntry.isEnabled(); + setPasswordEntryEnabled(true); + setPasswordEntryInputEnabled(true); + if (wasDisabled) { + mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } } @Override @@ -90,7 +100,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView post(new Runnable() { @Override public void run() { - if (isShown()) { + if (isShown() && mPasswordEntry.isEnabled()) { mPasswordEntry.requestFocus(); if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); @@ -101,6 +111,16 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override + protected int getPromtReasonStringRes(int reason) { + switch (reason) { + case PROMPT_REASON_RESTART: + return R.string.kg_prompt_reason_restart_password; + default: + return 0; + } + } + + @Override public void onPause() { super.onPause(); mImm.hideSoftInputFromWindow(getWindowToken(), 0); @@ -122,6 +142,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView Context.INPUT_METHOD_SERVICE); mPasswordEntry = (TextView) findViewById(getPasswordTextViewId()); + mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); @@ -138,20 +159,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); - mPasswordEntry.addTextChangedListener(new TextWatcher() { - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - public void afterTextChanged(Editable s) { - if (mCallback != null) { - mCallback.userActivity(); - } - } - }); - mPasswordEntry.requestFocus(); // If there's more than one IME, enable the IME switcher button @@ -162,7 +169,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView switchImeButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mCallback.userActivity(); // Leave the screen on a bit longer - mImm.showInputMethodPicker(); + // Do not show auxiliary subtypes in password lock screen. + mImm.showInputMethodPicker(false /* showAuxiliarySubtypes */); } }); } @@ -200,6 +208,11 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntry.setEnabled(enabled); } + @Override + protected void setPasswordEntryInputEnabled(boolean enabled) { + mPasswordEntryDisabler.setInputEnabled(enabled); + } + /** * Method adapted from com.android.inputmethod.latin.Utils * @@ -292,6 +305,11 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override public void afterTextChanged(Editable s) { + // Poor man's user edit detection, assuming empty text is programmatic and everything else + // is from the user. + if (!TextUtils.isEmpty(s)) { + onUserInput(); + } } @Override diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index 0dfe1dc..3568429 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -20,7 +20,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Rect; -import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.CountDownTimer; import android.os.SystemClock; import android.text.TextUtils; @@ -33,13 +33,18 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.LinearLayout; +import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.settingslib.animation.AppearAnimationCreator; +import com.android.settingslib.animation.AppearAnimationUtils; +import com.android.settingslib.animation.DisappearAnimationUtils; import java.util.List; public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView, - AppearAnimationCreator<LockPatternView.CellState> { + AppearAnimationCreator<LockPatternView.CellState>, + EmergencyButton.EmergencyButtonCallback { private static final String TAG = "SecurityPatternView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -59,6 +64,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private CountDownTimer mCountdownTimer = null; private LockPatternUtils mLockPatternUtils; + private AsyncTask<?, ?, ?> mPendingLockCheck; private LockPatternView mLockPatternView; private KeyguardSecurityCallback mCallback; @@ -79,11 +85,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } }; private Rect mTempRect = new Rect(); - private SecurityMessageDisplay mSecurityMessageDisplay; + private KeyguardMessageArea mSecurityMessageDisplay; private View mEcaView; - private Drawable mBouncerFrame; - private ViewGroup mKeyguardBouncerFrame; - private KeyguardMessageArea mHelpMessage; + private ViewGroup mContainer; private int mDisappearYTranslation; enum FooterMode { @@ -105,7 +109,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mContext, android.R.interpolator.linear_out_slow_in)); mDisappearAnimationUtils = new DisappearAnimationUtils(context, 125, 1.2f /* translationScale */, - 0.8f /* delayScale */, AnimationUtils.loadInterpolator( + 0.6f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); @@ -127,26 +131,28 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView = (LockPatternView) findViewById(R.id.lockPatternView); mLockPatternView.setSaveEnabled(false); - mLockPatternView.setFocusable(false); mLockPatternView.setOnPatternListener(new UnlockPatternListener()); // stealth mode will be the same for the life of this screen - mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled()); + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( + KeyguardUpdateMonitor.getCurrentUser())); // vibrate mode will be the same for the life of this screen mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); - setFocusableInTouchMode(true); - - mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); + mSecurityMessageDisplay = + (KeyguardMessageArea) KeyguardMessageArea.findSecurityMessageDisplay(this); mEcaView = findViewById(R.id.keyguard_selector_fade_container); - View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); - if (bouncerFrameView != null) { - mBouncerFrame = bouncerFrameView.getBackground(); + mContainer = (ViewGroup) findViewById(R.id.container); + + EmergencyButton button = (EmergencyButton) findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(this); } + } - mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame); - mHelpMessage = (KeyguardMessageArea) findViewById(R.id.keyguard_message_area); + public void onEmergencyButtonClickedWhenInCall() { + mCallback.reset(); } @Override @@ -173,7 +179,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mLockPatternView.clearPattern(); // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); if (deadline != 0) { handleAttemptLockout(deadline); } else { @@ -182,11 +189,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } private void displayDefaultSecurityMessage() { - if (mKeyguardUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) { - mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true); - } else { - mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false); - } + mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false); } @Override @@ -204,6 +207,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public void onPatternStart() { mLockPatternView.removeCallbacks(mCancelPatternRunnable); + mSecurityMessageDisplay.setMessage("", false); } public void onPatternCleared() { @@ -213,9 +217,36 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mCallback.userActivity(); } - public void onPatternDetected(List<LockPatternView.Cell> pattern) { - if (mLockPatternUtils.checkPattern(pattern)) { - mCallback.reportUnlockAttempt(true); + public void onPatternDetected(final List<LockPatternView.Cell> pattern) { + mLockPatternView.disableInput(); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { + mLockPatternView.enableInput(); + onPatternChecked(pattern, false, 0); + return; + } + + mPendingLockCheck = LockPatternChecker.checkPattern( + mLockPatternUtils, + pattern, + KeyguardUpdateMonitor.getCurrentUser(), + new LockPatternChecker.OnCheckCallback() { + @Override + public void onChecked(boolean matched, int timeoutMs) { + mLockPatternView.enableInput(); + mPendingLockCheck = null; + onPatternChecked(pattern, matched, timeoutMs); + } + }); + } + + private void onPatternChecked(List<LockPatternView.Cell> pattern, boolean matched, + int timeoutMs) { + if (matched) { + mCallback.reportUnlockAttempt(true, 0); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); mCallback.dismiss(true); } else { @@ -223,15 +254,11 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mCallback.userActivity(); } mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - boolean registeredAttempt = - pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL; - if (registeredAttempt) { - mCallback.reportUnlockAttempt(false); - } + mCallback.reportUnlockAttempt(false, timeoutMs); int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts(); - if (registeredAttempt && - 0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); handleAttemptLockout(deadline); } else { mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true); @@ -275,6 +302,10 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit mCountdownTimer.cancel(); mCountdownTimer = null; } + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } } @Override @@ -288,15 +319,14 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @Override - public void showBouncer(int duration) { - KeyguardSecurityViewHelper. - showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); - } - - @Override - public void hideBouncer(int duration) { - KeyguardSecurityViewHelper. - hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); + public void showPromptReason(int reason) { + switch (reason) { + case PROMPT_REASON_RESTART: + mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern, + true /* important */); + break; + default: + } } @Override @@ -308,7 +338,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit .setDuration(500) .setInterpolator(mAppearAnimationUtils.getInterpolator()) .translationY(0); - mAppearAnimationUtils.startAnimation( + mAppearAnimationUtils.startAnimation2d( mLockPatternView.getCellStates(), new Runnable() { @Override @@ -317,8 +347,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } }, this); - if (!TextUtils.isEmpty(mHelpMessage.getText())) { - mAppearAnimationUtils.createAnimation(mHelpMessage, 0, + if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { + mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0, AppearAnimationUtils.DEFAULT_APPEAR_DURATION, mAppearAnimationUtils.getStartTranslation(), true /* appearing */, @@ -336,7 +366,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit .setDuration(300) .setInterpolator(mDisappearAnimationUtils.getInterpolator()) .translationY(-mDisappearAnimationUtils.getStartTranslation()); - mDisappearAnimationUtils.startAnimation(mLockPatternView.getCellStates(), + mDisappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(), new Runnable() { @Override public void run() { @@ -346,8 +376,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } }, KeyguardPatternView.this); - if (!TextUtils.isEmpty(mHelpMessage.getText())) { - mDisappearAnimationUtils.createAnimation(mHelpMessage, 0, + if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { + mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0, 200, - mDisappearAnimationUtils.getStartTranslation() * 3, false /* appearing */, @@ -359,8 +389,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private void enableClipping(boolean enable) { setClipChildren(enable); - mKeyguardBouncerFrame.setClipToPadding(enable); - mKeyguardBouncerFrame.setClipChildren(enable); + mContainer.setClipToPadding(enable); + mContainer.setClipChildren(enable); } @Override diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java index bca0305..07947b1 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -72,6 +72,11 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } @Override + protected void setPasswordEntryInputEnabled(boolean enabled) { + mPasswordEntry.setEnabled(enabled); + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (KeyEvent.isConfirmKey(keyCode)) { performClick(mOkButton); @@ -88,6 +93,16 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return super.onKeyDown(keyCode, event); } + @Override + protected int getPromtReasonStringRes(int reason) { + switch (reason) { + case PROMPT_REASON_RESTART: + return R.string.kg_prompt_reason_restart_pin; + default: + return 0; + } + } + private void performClick(View view) { view.performClick(); } @@ -145,10 +160,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); - // Poke the wakelock any time the text is selected or modified - mPasswordEntry.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mCallback.userActivity(); + mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() { + @Override + public void onUserActivity() { + onUserInput(); } }); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java index f361b5c..836c195 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -37,12 +37,13 @@ public interface KeyguardSecurityCallback { /** * Call to report an unlock attempt. * @param success set to 'true' if user correctly entered security credentials. + * @param timeoutMs timeout in milliseconds to wait before reattempting an unlock. + * Only nonzero if 'success' is false */ - void reportUnlockAttempt(boolean success); + void reportUnlockAttempt(boolean success, int timeoutMs); /** - * Shows the backup security for the current method. If none available, this call is a no-op. + * Resets the keyguard view. */ - void showBackupSecurity(); - + void reset(); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java index 559b112..23bd238 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -20,7 +20,6 @@ import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.UserHandle; -import android.os.UserManager; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; @@ -41,13 +40,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe private static final int USER_TYPE_SECONDARY_USER = 3; private KeyguardSecurityModel mSecurityModel; - private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView private LockPatternUtils mLockPatternUtils; private KeyguardSecurityViewFlipper mSecurityViewFlipper; private boolean mIsVerifyUnlockOnly; private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; - private boolean mIsBouncing; private SecurityCallback mSecurityCallback; private final KeyguardUpdateMonitor mUpdateMonitor; @@ -58,6 +55,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe public void userActivity(); public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); public void finish(); + public void reset(); } public KeyguardSecurityContainer(Context context, AttributeSet attrs) { @@ -107,13 +105,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return false; } - void updateSecurityViews(boolean isBouncing) { - int children = mSecurityViewFlipper.getChildCount(); - for (int i = 0; i < children; i++) { - updateSecurityView(mSecurityViewFlipper.getChildAt(i), isBouncing); - } - } - public void announceCurrentSecurityMethod() { View v = (View) getSecurityView(mCurrentSecuritySelection); if (v != null) { @@ -145,24 +136,18 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); View v = inflater.inflate(layoutId, mSecurityViewFlipper, false); mSecurityViewFlipper.addView(v); - updateSecurityView(v, mIsBouncing); + updateSecurityView(v); view = (KeyguardSecurityView)v; } return view; } - private void updateSecurityView(View view, boolean isBouncing) { - mIsBouncing = isBouncing; + private void updateSecurityView(View view) { if (view instanceof KeyguardSecurityView) { KeyguardSecurityView ksv = (KeyguardSecurityView) view; ksv.setKeyguardCallback(mCallback); ksv.setLockPatternUtils(mLockPatternUtils); - if (isBouncing) { - ksv.showBouncer(0); - } else { - ksv.hideBouncer(0); - } } else { Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); } @@ -191,8 +176,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe dialog.show(); } - private void showTimeoutDialog() { - int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + private void showTimeoutDialog(int timeoutMs) { + int timeoutInSeconds = (int) timeoutMs / 1000; int messageId = 0; switch (mSecurityModel.getSecurityMode()) { @@ -206,8 +191,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; break; // These don't have timeout dialogs. - case Account: - case Biometric: case Invalid: case None: case SimPin: @@ -261,31 +244,18 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe showDialog(null, message); } - private void showAlmostAtAccountLoginDialog() { - final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; - final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; - String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login, - count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); - showDialog(null, message); - } - - private void reportFailedUnlockAttempt() { + private void reportFailedUnlockAttempt(int timeoutMs) { final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); SecurityMode mode = mSecurityModel.getSecurityMode(); - final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern; - final int currentUser = mLockPatternUtils.getCurrentUser(); + final int currentUser = KeyguardUpdateMonitor.getCurrentUser(); final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); final int failedAttemptsBeforeWipe = dpm.getMaximumFailedPasswordsForWipe(null, currentUser); - final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; - final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? (failedAttemptsBeforeWipe - failedAttempts) : Integer.MAX_VALUE; // because DPM returns 0 if no restriction @@ -311,25 +281,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); showWipeDialog(failedAttempts, userType); } - } else { - showTimeout = - (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; - if (usingPattern && mEnableFallback) { - if (failedAttempts == failedAttemptWarning) { - showAlmostAtAccountLoginDialog(); - showTimeout = false; // don't show both dialogs - } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { - mLockPatternUtils.setPermanentlyLocked(true); - showSecurityScreen(SecurityMode.Account); - // don't show timeout dialog because we show account unlock screen next - showTimeout = false; - } - } } monitor.reportFailedUnlockAttempt(); - mLockPatternUtils.reportFailedPasswordAttempt(); - if (showTimeout) { - showTimeoutDialog(); + mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser()); + if (timeoutMs > 0) { + showTimeoutDialog(timeoutMs); } } @@ -341,41 +297,21 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe void showPrimarySecurityScreen(boolean turningOff) { SecurityMode securityMode = mSecurityModel.getSecurityMode(); if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); - if (!turningOff && - KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) { - // If we're not turning off, then allow biometric alternate. - // We'll reload it when the device comes back on. - securityMode = mSecurityModel.getAlternateFor(securityMode); - } showSecurityScreen(securityMode); } /** - * Shows the backup security screen for the current security mode. This could be used for - * password recovery screens but is currently only used for pattern unlock to show the - * account unlock screen and biometric unlock to show the user's normal unlock. - */ - private void showBackupSecurityScreen() { - if (DEBUG) Log.d(TAG, "showBackupSecurity()"); - SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection); - showSecurityScreen(backup); - } - - /** * Shows the next security screen if there is one. * @param authenticated true if the user entered the correct authentication - * @param authenticated * @return true if keyguard is done */ boolean showNextSecurityScreenOrFinish(boolean authenticated) { if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); boolean finish = false; - if (mUpdateMonitor.getUserHasTrust(mLockPatternUtils.getCurrentUser())) { + if (mUpdateMonitor.getUserHasTrust(KeyguardUpdateMonitor.getCurrentUser())) { finish = true; } else if (SecurityMode.None == mCurrentSecuritySelection) { SecurityMode securityMode = mSecurityModel.getSecurityMode(); - // Allow an alternate, such as biometric unlock - securityMode = mSecurityModel.getAlternateFor(securityMode); if (SecurityMode.None == securityMode) { finish = true; // no security required } else { @@ -386,8 +322,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case Pattern: case Password: case PIN: - case Account: - case Biometric: finish = true; break; @@ -395,7 +329,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(); - if (securityMode != SecurityMode.None) { + if (securityMode != SecurityMode.None + || !mLockPatternUtils.isLockScreenDisabled( + KeyguardUpdateMonitor.getCurrentUser())) { showSecurityScreen(securityMode); } else { finish = true; @@ -464,22 +400,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return null; } - public void showBouncer(int duration) { - KeyguardSecurityViewFlipper flipper = getFlipper(); - if (flipper != null) { - flipper.showBouncer(duration); - } - } - - public void hideBouncer(int duration) { - KeyguardSecurityViewFlipper flipper = getFlipper(); - if (flipper != null) { - flipper.hideBouncer(duration); - } - } - private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { - public void userActivity() { if (mSecurityCallback != null) { mSecurityCallback.userActivity(); @@ -494,25 +415,20 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return mIsVerifyUnlockOnly; } - public void reportUnlockAttempt(boolean success) { + public void reportUnlockAttempt(boolean success, int timeoutMs) { KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); if (success) { monitor.clearFailedUnlockAttempts(); - mLockPatternUtils.reportSuccessfulPasswordAttempt(); + mLockPatternUtils.reportSuccessfulPasswordAttempt( + KeyguardUpdateMonitor.getCurrentUser()); } else { - if (mCurrentSecuritySelection == SecurityMode.Biometric) { - monitor.reportFailedBiometricUnlockAttempt(); - } else { - KeyguardSecurityContainer.this.reportFailedUnlockAttempt(); - } + KeyguardSecurityContainer.this.reportFailedUnlockAttempt(timeoutMs); } } - @Override - public void showBackupSecurity() { - KeyguardSecurityContainer.this.showBackupSecurityScreen(); + public void reset() { + mSecurityCallback.reset(); } - }; // The following is used to ignore callbacks from SecurityViews that are no longer current @@ -522,13 +438,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe @Override public void userActivity() { } @Override - public void showBackupSecurity() { } - @Override - public void reportUnlockAttempt(boolean success) { } + public void reportUnlockAttempt(boolean success, int timeoutMs) { } @Override public boolean isVerifyUnlockOnly() { return false; } @Override public void dismiss(boolean securityVerified) { } + @Override + public void reset() {} }; private int getSecurityViewIdForMode(SecurityMode securityMode) { @@ -536,8 +452,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case Pattern: return R.id.keyguard_pattern_view; case PIN: return R.id.keyguard_pin_view; case Password: return R.id.keyguard_password_view; - case Biometric: return R.id.keyguard_face_unlock_view; - case Account: return R.id.keyguard_account_view; case SimPin: return R.id.keyguard_sim_pin_view; case SimPuk: return R.id.keyguard_sim_puk_view; } @@ -549,8 +463,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case Pattern: return R.layout.keyguard_pattern_view; case PIN: return R.layout.keyguard_pin_view; case Password: return R.layout.keyguard_password_view; - case Biometric: return R.layout.keyguard_face_unlock_view; - case Account: return R.layout.keyguard_account_view; case SimPin: return R.layout.keyguard_sim_pin_view; case SimPuk: return R.layout.keyguard_sim_puk_view; default: @@ -599,6 +511,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } @Override + public void showPromptReason(int reason) { + if (mCurrentSecuritySelection != SecurityMode.None) { + getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); + } + } + + @Override public void showUsabilityHint() { mSecurityViewFlipper.showUsabilityHint(); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java index 1e2a233..454221a 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java @@ -17,20 +17,16 @@ package com.android.keyguard; import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; import com.android.internal.telephony.IccCardConstants; import com.android.internal.widget.LockPatternUtils; -import java.util.List; - public class KeyguardSecurityModel { /** - * The different types of security available for {@link Mode#UnlockScreen}. - * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode() + * The different types of security available. + * @see KeyguardSecurityContainer#showSecurityScreen */ public enum SecurityMode { Invalid, // NULL state @@ -38,117 +34,58 @@ public class KeyguardSecurityModel { Pattern, // Unlock by drawing a pattern. Password, // Unlock by entering an alphanumeric password PIN, // Strictly numeric password - Biometric, // Unlock with a biometric key (e.g. finger print or face unlock) - Account, // Unlock by entering an account's login and password. SimPin, // Unlock by entering a sim pin. SimPuk // Unlock by entering a sim puk } - private Context mContext; + private final Context mContext; + private final boolean mIsPukScreenAvailable; + private LockPatternUtils mLockPatternUtils; KeyguardSecurityModel(Context context) { mContext = context; mLockPatternUtils = new LockPatternUtils(context); + mIsPukScreenAvailable = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enable_puk_unlock_screen); } void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; } - /** - * Returns true if biometric unlock is installed and selected. If this returns false there is - * no need to even construct the biometric unlock. - */ - boolean isBiometricUnlockEnabled() { - return mLockPatternUtils.usingBiometricWeak() - && mLockPatternUtils.isBiometricWeakInstalled(); - } - - /** - * Returns true if a condition is currently suppressing the biometric unlock. If this returns - * true there is no need to even construct the biometric unlock. - */ - private boolean isBiometricUnlockSuppressed() { - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); - final boolean backupIsTimedOut = monitor.getFailedUnlockAttempts() >= - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; - return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut - || !monitor.isAlternateUnlockEnabled() - || monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE; - } - SecurityMode getSecurityMode() { KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); - SecurityMode mode = SecurityMode.None; + if (SubscriptionManager.isValidSubscriptionId( monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED))) { - mode = SecurityMode.SimPin; - } else if (SubscriptionManager.isValidSubscriptionId( - monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED)) - && mLockPatternUtils.isPukUnlockScreenEnable()) { - mode = SecurityMode.SimPuk; - } else { - final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality(); - switch (security) { - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: - mode = mLockPatternUtils.isLockPasswordEnabled() ? - SecurityMode.PIN : SecurityMode.None; - break; - case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: - case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: - case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: - mode = mLockPatternUtils.isLockPasswordEnabled() ? - SecurityMode.Password : SecurityMode.None; - break; - - case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: - case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: - if (mLockPatternUtils.isLockPatternEnabled()) { - mode = mLockPatternUtils.isPermanentlyLocked() ? - SecurityMode.Account : SecurityMode.Pattern; - } - break; - - default: - throw new IllegalStateException("Unknown security quality:" + security); - } + return SecurityMode.SimPin; } - return mode; - } - /** - * Some unlock methods can have an alternate, such as biometric unlocks (e.g. face unlock). - * This function decides if an alternate unlock is available and returns it. Otherwise, - * returns @param mode. - * - * @param mode the mode we want the alternate for - * @return alternate or the given mode - */ - SecurityMode getAlternateFor(SecurityMode mode) { - if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed() - && (mode == SecurityMode.Password - || mode == SecurityMode.PIN - || mode == SecurityMode.Pattern)) { - return SecurityMode.Biometric; + if (mIsPukScreenAvailable && SubscriptionManager.isValidSubscriptionId( + monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED))) { + return SecurityMode.SimPuk; } - return mode; // no alternate, return what was given - } - /** - * Some unlock methods can have a backup which gives the user another way to get into - * the device. This is currently only supported for Biometric and Pattern unlock. - * - * @return backup method or current security mode - */ - SecurityMode getBackupSecurityMode(SecurityMode mode) { - switch(mode) { - case Biometric: - return getSecurityMode(); - case Pattern: - return SecurityMode.Account; + final int security = mLockPatternUtils.getActivePasswordQuality( + KeyguardUpdateMonitor.getCurrentUser()); + switch (security) { + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: + return SecurityMode.PIN; + + case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: + case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: + case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: + return SecurityMode.Password; + + case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + return SecurityMode.Pattern; + case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: + return SecurityMode.None; + + default: + throw new IllegalStateException("Unknown security quality:" + security); } - return mode; // no backup, return current security mode } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java index 78fcb9f..5658a74 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java @@ -21,6 +21,9 @@ public interface KeyguardSecurityView { static public final int SCREEN_ON = 1; static public final int VIEW_REVEALED = 2; + int PROMPT_REASON_NONE = 0; + int PROMPT_REASON_RESTART = 1; + /** * Interface back to keyguard to tell it when security * @param callback @@ -66,24 +69,18 @@ public interface KeyguardSecurityView { KeyguardSecurityCallback getCallback(); /** - * Instruct the view to show usability hints, if any. + * Show a string explaining why the security view needs to be solved. * + * @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE} + * and {@link #PROMPT_REASON_RESTART} */ - void showUsabilityHint(); + void showPromptReason(int reason); /** - * Place the security view into bouncer mode. - * Animate transisiton if duration is non-zero. - * @param duration millisends for the transisiton animation. - */ - void showBouncer(int duration); - - /** - * Place the security view into non-bouncer mode. - * Animate transisiton if duration is non-zero. - * @param duration millisends for the transisiton animation. + * Instruct the view to show usability hints, if any. + * */ - void hideBouncer(int duration); + void showUsabilityHint(); /** * Starts the animation which should run when the security view appears. diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java index ea5c304..a0ff21b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.annotation.NonNull; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; @@ -25,12 +26,15 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewHierarchyEncoder; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ViewFlipper; import com.android.internal.widget.LockPatternUtils; +import java.lang.Override; + /** * Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so * we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy. @@ -127,34 +131,18 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard } @Override - public void showUsabilityHint() { + public void showPromptReason(int reason) { KeyguardSecurityView ksv = getSecurityView(); if (ksv != null) { - ksv.showUsabilityHint(); - } - } - - @Override - public void showBouncer(int duration) { - KeyguardSecurityView active = getSecurityView(); - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof KeyguardSecurityView) { - KeyguardSecurityView ksv = (KeyguardSecurityView) child; - ksv.showBouncer(ksv == active ? duration : 0); - } + ksv.showPromptReason(reason); } } @Override - public void hideBouncer(int duration) { - KeyguardSecurityView active = getSecurityView(); - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof KeyguardSecurityView) { - KeyguardSecurityView ksv = (KeyguardSecurityView) child; - ksv.hideBouncer(ksv == active ? duration : 0); - } + public void showUsabilityHint() { + KeyguardSecurityView ksv = getSecurityView(); + if (ksv != null) { + ksv.showUsabilityHint(); } } @@ -223,8 +211,8 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard final int wPadding = getPaddingLeft() + getPaddingRight(); final int hPadding = getPaddingTop() + getPaddingBottom(); - maxWidth -= wPadding; - maxHeight -= hPadding; + maxWidth = Math.max(0, maxWidth - wPadding); + maxHeight = Math.max(0, maxHeight - hPadding); int width = widthMode == MeasureSpec.EXACTLY ? widthSize : 0; int height = heightMode == MeasureSpec.EXACTLY ? heightSize : 0; @@ -292,5 +280,14 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0); a.recycle(); } + + /** @hide */ + @Override + protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) { + super.encodeProperties(encoder); + + encoder.addProperty("layout:maxWidth", maxWidth); + encoder.addProperty("layout:maxHeight", maxHeight); + } } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java deleted file mode 100644 index 67a6f52..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewHelper.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.graphics.drawable.Drawable; -import android.view.View; - -/** - * Some common functions that are useful for KeyguardSecurityViews. - */ -public class KeyguardSecurityViewHelper { - - public static void showBouncer(SecurityMessageDisplay securityMessageDisplay, - final View ecaView, Drawable bouncerFrame, int duration) { - if (securityMessageDisplay != null) { - securityMessageDisplay.showBouncer(duration); - } - if (ecaView != null) { - if (duration > 0) { - Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 0f); - anim.setDuration(duration); - anim.addListener(new AnimatorListenerAdapter() { - private boolean mCanceled; - @Override - public void onAnimationCancel(Animator animation) { - // Fail safe and show the emergency button in onAnimationEnd() - mCanceled = true; - ecaView.setAlpha(1f); - } - @Override - public void onAnimationEnd(Animator animation) { - ecaView.setVisibility(mCanceled ? View.VISIBLE : View.INVISIBLE); - } - }); - anim.start(); - } else { - ecaView.setAlpha(0f); - ecaView.setVisibility(View.INVISIBLE); - } - } - if (bouncerFrame != null) { - if (duration > 0) { - Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 0, 255); - anim.setDuration(duration); - anim.start(); - } else { - bouncerFrame.setAlpha(255); - } - } - } - - public static void hideBouncer(SecurityMessageDisplay securityMessageDisplay, - View ecaView, Drawable bouncerFrame, int duration) { - if (securityMessageDisplay != null) { - securityMessageDisplay.hideBouncer(duration); - } - if (ecaView != null) { - ecaView.setVisibility(View.VISIBLE); - if (duration > 0) { - Animator anim = ObjectAnimator.ofFloat(ecaView, "alpha", 1f); - anim.setDuration(duration); - anim.start(); - } else { - ecaView.setAlpha(1f); - } - } - if (bouncerFrame != null) { - if (duration > 0) { - Animator anim = ObjectAnimator.ofInt(bouncerFrame, "alpha", 255, 0); - anim.setDuration(duration); - anim.start(); - } else { - bouncerFrame.setAlpha(0); - } - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java index f4acff8..aeac912 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java @@ -95,6 +95,12 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } } + @Override + protected int getPromtReasonStringRes(int reason) { + // No message on SIM Pin + return 0; + } + private String getPinPasswordErrorMessage(int attemptsRemaining) { String displayMessage; diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java index b85d966..af88239 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java @@ -140,6 +140,12 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } } + @Override + protected int getPromtReasonStringRes(int reason) { + // No message on SIM Puk + return 0; + } + private String getPukPasswordErrorMessage(int attemptsRemaining) { String displayMessage; diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java deleted file mode 100644 index 828c921..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java +++ /dev/null @@ -1,84 +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.keyguard; - -import android.content.Context; -import android.os.Bundle; -import android.util.AttributeSet; -import android.view.MotionEvent; - -public class KeyguardSimpleHostView extends KeyguardViewBase { - - public KeyguardSimpleHostView(Context context, AttributeSet attrs) { - super(context, attrs); - KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback); - } - - @Override - protected void showBouncer(boolean show) { - super.showBouncer(show); - if (show) { - getSecurityContainer().showBouncer(250); - } else { - getSecurityContainer().hideBouncer(250); - } - } - - @Override - public void cleanUp() { - getSecurityContainer().onPause(); - } - - @Override - public long getUserActivityTimeout() { - return -1; // not used - } - - @Override - protected void onUserSwitching(boolean switching) { - // TODO Auto-generated method stub - } - - @Override - protected void onCreateOptions(Bundle options) { - // TODO Auto-generated method stub - } - - @Override - protected void onExternalMotionEvent(MotionEvent event) { - // TODO Auto-generated method stub - } - - private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onUserSwitchComplete(int userId) { - getSecurityContainer().showPrimarySecurityScreen(false /* turning off */); - } - - @Override - public void onTrustInitiatedByUser(int userId) { - if (userId != mLockPatternUtils.getCurrentUser()) return; - if (!isAttachedToWindow()) return; - - if (isVisibleToUser()) { - dismiss(false /* authenticated */); - } else { - mViewMediatorCallback.playTrustedSound(); - } - } - }; -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java index c8d9fe2..4e9621a 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java @@ -22,7 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.provider.AlarmClock; +import android.os.UserHandle; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.AttributeSet; @@ -42,7 +42,8 @@ public class KeyguardStatusView extends GridLayout { private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardStatusView"; - private LockPatternUtils mLockPatternUtils; + private final LockPatternUtils mLockPatternUtils; + private final AlarmManager mAlarmManager; private TextView mAlarmStatusView; private TextClock mDateView; @@ -92,6 +93,8 @@ public class KeyguardStatusView extends GridLayout { public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + mLockPatternUtils = new LockPatternUtils(getContext()); } private void setEnableMarquee(boolean enabled) { @@ -109,7 +112,7 @@ public class KeyguardStatusView extends GridLayout { mDateView.setShowCurrentUserTime(true); mClockView.setShowCurrentUserTime(true); mOwnerInfo = (TextView) findViewById(R.id.owner_info); - mLockPatternUtils = new LockPatternUtils(getContext()); + final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); setEnableMarquee(screenOn); refresh(); @@ -140,7 +143,8 @@ public class KeyguardStatusView extends GridLayout { } private void refresh() { - AlarmManager.AlarmClockInfo nextAlarm = mLockPatternUtils.getNextAlarm(); + AlarmManager.AlarmClockInfo nextAlarm = + mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); Patterns.update(mContext, nextAlarm != null); refreshTime(); @@ -193,16 +197,13 @@ public class KeyguardStatusView extends GridLayout { KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mInfoCallback); } - public int getAppWidgetId() { - return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET; - } - private String getOwnerInfo() { ContentResolver res = getContext().getContentResolver(); String info = null; - final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(); + final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled( + KeyguardUpdateMonitor.getCurrentUser()); if (ownerInfoEnabled) { - info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser()); + info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser()); } return info; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusViewManager.java deleted file mode 100644 index e69de29..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusViewManager.java +++ /dev/null diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java deleted file mode 100644 index 0d472ae..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardTransportControlView.java +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Copyright (C) 2011 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.keyguard; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.drawable.Drawable; -import android.media.AudioManager; -import android.media.MediaMetadataEditor; -import android.media.MediaMetadataRetriever; -import android.media.RemoteControlClient; -import android.media.RemoteController; -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.transition.ChangeBounds; -import android.transition.ChangeText; -import android.transition.Fade; -import android.transition.TransitionManager; -import android.transition.TransitionSet; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.SeekBar; -import android.widget.TextView; - -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; - -/** - * This is the widget responsible for showing music controls in keyguard. - */ -public class KeyguardTransportControlView extends FrameLayout { - - private static final int RESET_TO_METADATA_DELAY = 5000; - protected static final boolean DEBUG = KeyguardConstants.DEBUG; - protected static final String TAG = "TransportControlView"; - - private static final boolean ANIMATE_TRANSITIONS = true; - protected static final long QUIESCENT_PLAYBACK_FACTOR = 1000; - - private ViewGroup mMetadataContainer; - private ViewGroup mInfoContainer; - private TextView mTrackTitle; - private TextView mTrackArtistAlbum; - - private View mTransientSeek; - private SeekBar mTransientSeekBar; - private TextView mTransientSeekTimeElapsed; - private TextView mTransientSeekTimeTotal; - - private ImageView mBtnPrev; - private ImageView mBtnPlay; - private ImageView mBtnNext; - private Metadata mMetadata = new Metadata(); - private int mTransportControlFlags; - private int mCurrentPlayState; - private AudioManager mAudioManager; - private RemoteController mRemoteController; - - private ImageView mBadge; - - private boolean mSeekEnabled; - private java.text.DateFormat mFormat; - - private Date mTempDate = new Date(); - - /** - * The metadata which should be populated into the view once we've been attached - */ - private RemoteController.MetadataEditor mPopulateMetadataWhenAttached = null; - - private RemoteController.OnClientUpdateListener mRCClientUpdateListener = - new RemoteController.OnClientUpdateListener() { - @Override - public void onClientChange(boolean clearing) { - if (clearing) { - clearMetadata(); - } - } - - @Override - public void onClientPlaybackStateUpdate(int state) { - updatePlayPauseState(state); - } - - @Override - public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs, - long currentPosMs, float speed) { - updatePlayPauseState(state); - if (DEBUG) Log.d(TAG, "onClientPlaybackStateUpdate(state=" + state + - ", stateChangeTimeMs=" + stateChangeTimeMs + ", currentPosMs=" + currentPosMs + - ", speed=" + speed + ")"); - - removeCallbacks(mUpdateSeekBars); - // Since the music client may be responding to historical events that cause the - // playback state to change dramatically, wait until things become quiescent before - // resuming automatic scrub position update. - if (mTransientSeek.getVisibility() == View.VISIBLE - && playbackPositionShouldMove(mCurrentPlayState)) { - postDelayed(mUpdateSeekBars, QUIESCENT_PLAYBACK_FACTOR); - } - } - - @Override - public void onClientTransportControlUpdate(int transportControlFlags) { - updateTransportControls(transportControlFlags); - } - - @Override - public void onClientMetadataUpdate(RemoteController.MetadataEditor metadataEditor) { - updateMetadata(metadataEditor); - } - }; - - private class UpdateSeekBarRunnable implements Runnable { - public void run() { - boolean seekAble = updateOnce(); - if (seekAble) { - removeCallbacks(this); - postDelayed(this, 1000); - } - } - public boolean updateOnce() { - return updateSeekBars(); - } - }; - - private final UpdateSeekBarRunnable mUpdateSeekBars = new UpdateSeekBarRunnable(); - - private final Runnable mResetToMetadata = new Runnable() { - public void run() { - resetToMetadata(); - } - }; - - private final OnClickListener mTransportCommandListener = new OnClickListener() { - public void onClick(View v) { - int keyCode = -1; - if (v == mBtnPrev) { - keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; - } else if (v == mBtnNext) { - keyCode = KeyEvent.KEYCODE_MEDIA_NEXT; - } else if (v == mBtnPlay) { - keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; - } - if (keyCode != -1) { - sendMediaButtonClick(keyCode); - delayResetToMetadata(); // if the scrub bar is showing, keep showing it. - } - } - }; - - private final OnLongClickListener mTransportShowSeekBarListener = new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mSeekEnabled) { - return tryToggleSeekBar(); - } - return false; - } - }; - - // This class is here to throttle scrub position updates to the music client - class FutureSeekRunnable implements Runnable { - private int mProgress; - private boolean mPending; - - public void run() { - scrubTo(mProgress); - mPending = false; - } - - void setProgress(int progress) { - mProgress = progress; - if (!mPending) { - mPending = true; - postDelayed(this, 30); - } - } - }; - - // This is here because RemoteControlClient's method isn't visible :/ - private final static boolean playbackPositionShouldMove(int playstate) { - switch(playstate) { - case RemoteControlClient.PLAYSTATE_STOPPED: - case RemoteControlClient.PLAYSTATE_PAUSED: - case RemoteControlClient.PLAYSTATE_BUFFERING: - case RemoteControlClient.PLAYSTATE_ERROR: - case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: - case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: - return false; - case RemoteControlClient.PLAYSTATE_PLAYING: - case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: - case RemoteControlClient.PLAYSTATE_REWINDING: - default: - return true; - } - } - - private final FutureSeekRunnable mFutureSeekRunnable = new FutureSeekRunnable(); - - private final SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = - new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - mFutureSeekRunnable.setProgress(progress); - delayResetToMetadata(); - mTempDate.setTime(progress); - mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate)); - } else { - updateSeekDisplay(); - } - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - delayResetToMetadata(); - removeCallbacks(mUpdateSeekBars); // don't update during user interaction - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }; - - private static final int TRANSITION_DURATION = 200; - private final TransitionSet mMetadataChangeTransition; - - KeyguardHostView.TransportControlCallback mTransportControlCallback; - - private final KeyguardUpdateMonitorCallback mUpdateMonitor - = new KeyguardUpdateMonitorCallback() { - public void onScreenTurnedOff(int why) { - setEnableMarquee(false); - } - public void onScreenTurnedOn() { - setEnableMarquee(true); - } - }; - - public KeyguardTransportControlView(Context context, AttributeSet attrs) { - super(context, attrs); - if (DEBUG) Log.v(TAG, "Create TCV " + this); - mAudioManager = new AudioManager(mContext); - mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE; // until we get a callback - mRemoteController = new RemoteController(context, mRCClientUpdateListener); - - final DisplayMetrics dm = context.getResources().getDisplayMetrics(); - final int dim = Math.max(dm.widthPixels, dm.heightPixels); - mRemoteController.setArtworkConfiguration(true, dim, dim); - - final ChangeText tc = new ChangeText(); - tc.setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN); - final TransitionSet inner = new TransitionSet(); - inner.addTransition(tc).addTransition(new ChangeBounds()); - final TransitionSet tg = new TransitionSet(); - tg.addTransition(new Fade(Fade.OUT)).addTransition(inner). - addTransition(new Fade(Fade.IN)); - tg.setOrdering(TransitionSet.ORDERING_SEQUENTIAL); - tg.setDuration(TRANSITION_DURATION); - mMetadataChangeTransition = tg; - } - - private void updateTransportControls(int transportControlFlags) { - mTransportControlFlags = transportControlFlags; - setSeekBarsEnabled( - (transportControlFlags & RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE) != 0); - } - - void setSeekBarsEnabled(boolean enabled) { - if (enabled == mSeekEnabled) return; - - mSeekEnabled = enabled; - if (mTransientSeek.getVisibility() == VISIBLE && !enabled) { - mTransientSeek.setVisibility(INVISIBLE); - mMetadataContainer.setVisibility(VISIBLE); - cancelResetToMetadata(); - } - } - - public void setTransportControlCallback(KeyguardHostView.TransportControlCallback - transportControlCallback) { - mTransportControlCallback = transportControlCallback; - } - - private void setEnableMarquee(boolean enabled) { - if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee"); - if (mTrackTitle != null) mTrackTitle.setSelected(enabled); - if (mTrackArtistAlbum != null) mTrackTitle.setSelected(enabled); - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - mInfoContainer = (ViewGroup) findViewById(R.id.info_container); - mMetadataContainer = (ViewGroup) findViewById(R.id.metadata_container); - mBadge = (ImageView) findViewById(R.id.badge); - mTrackTitle = (TextView) findViewById(R.id.title); - mTrackArtistAlbum = (TextView) findViewById(R.id.artist_album); - mTransientSeek = findViewById(R.id.transient_seek); - mTransientSeekBar = (SeekBar) findViewById(R.id.transient_seek_bar); - mTransientSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener); - mTransientSeekTimeElapsed = (TextView) findViewById(R.id.transient_seek_time_elapsed); - mTransientSeekTimeTotal = (TextView) findViewById(R.id.transient_seek_time_remaining); - mBtnPrev = (ImageView) findViewById(R.id.btn_prev); - mBtnPlay = (ImageView) findViewById(R.id.btn_play); - mBtnNext = (ImageView) findViewById(R.id.btn_next); - final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext }; - for (View view : buttons) { - view.setOnClickListener(mTransportCommandListener); - view.setOnLongClickListener(mTransportShowSeekBarListener); - } - final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn(); - setEnableMarquee(screenOn); - // Allow long-press anywhere else in this view to show the seek bar - setOnLongClickListener(mTransportShowSeekBarListener); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - if (DEBUG) Log.v(TAG, "onAttachToWindow()"); - if (mPopulateMetadataWhenAttached != null) { - updateMetadata(mPopulateMetadataWhenAttached); - mPopulateMetadataWhenAttached = null; - } - if (DEBUG) Log.v(TAG, "Registering TCV " + this); - mMetadata.clear(); - mAudioManager.registerRemoteController(mRemoteController); - KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitor); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - final DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); - final int dim = Math.max(dm.widthPixels, dm.heightPixels); - mRemoteController.setArtworkConfiguration(true, dim, dim); - } - - @Override - public void onDetachedFromWindow() { - if (DEBUG) Log.v(TAG, "onDetachFromWindow()"); - super.onDetachedFromWindow(); - if (DEBUG) Log.v(TAG, "Unregistering TCV " + this); - mAudioManager.unregisterRemoteController(mRemoteController); - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitor); - mMetadata.clear(); - removeCallbacks(mUpdateSeekBars); - } - - @Override - protected Parcelable onSaveInstanceState() { - SavedState ss = new SavedState(super.onSaveInstanceState()); - ss.artist = mMetadata.artist; - ss.trackTitle = mMetadata.trackTitle; - ss.albumTitle = mMetadata.albumTitle; - ss.duration = mMetadata.duration; - ss.bitmap = mMetadata.bitmap; - return ss; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - if (!(state instanceof SavedState)) { - super.onRestoreInstanceState(state); - return; - } - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - mMetadata.artist = ss.artist; - mMetadata.trackTitle = ss.trackTitle; - mMetadata.albumTitle = ss.albumTitle; - mMetadata.duration = ss.duration; - mMetadata.bitmap = ss.bitmap; - populateMetadata(); - } - - void setBadgeIcon(Drawable bmp) { - mBadge.setImageDrawable(bmp); - - final ColorMatrix cm = new ColorMatrix(); - cm.setSaturation(0); - mBadge.setColorFilter(new ColorMatrixColorFilter(cm)); - mBadge.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); - mBadge.setImageAlpha(0xef); - } - - class Metadata { - private String artist; - private String trackTitle; - private String albumTitle; - private Bitmap bitmap; - private long duration; - - public void clear() { - artist = null; - trackTitle = null; - albumTitle = null; - bitmap = null; - duration = -1; - } - - public String toString() { - return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + - " albumTitle=" + albumTitle + " duration=" + duration + "]"; - } - } - - void clearMetadata() { - mPopulateMetadataWhenAttached = null; - mMetadata.clear(); - populateMetadata(); - } - - void updateMetadata(RemoteController.MetadataEditor data) { - if (isAttachedToWindow()) { - mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, - mMetadata.artist); - mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, - mMetadata.trackTitle); - mMetadata.albumTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_ALBUM, - mMetadata.albumTitle); - mMetadata.duration = data.getLong(MediaMetadataRetriever.METADATA_KEY_DURATION, -1); - mMetadata.bitmap = data.getBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK, - mMetadata.bitmap); - populateMetadata(); - } else { - mPopulateMetadataWhenAttached = data; - } - } - - /** - * Populates the given metadata into the view - */ - private void populateMetadata() { - if (ANIMATE_TRANSITIONS && isLaidOut() && mMetadataContainer.getVisibility() == VISIBLE) { - TransitionManager.beginDelayedTransition(mMetadataContainer, mMetadataChangeTransition); - } - - final String remoteClientPackage = mRemoteController.getRemoteControlClientPackageName(); - Drawable badgeIcon = null; - try { - badgeIcon = getContext().getPackageManager().getApplicationIcon(remoteClientPackage); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Couldn't get remote control client package icon", e); - } - setBadgeIcon(badgeIcon); - mTrackTitle.setText(!TextUtils.isEmpty(mMetadata.trackTitle) - ? mMetadata.trackTitle : null); - - final StringBuilder sb = new StringBuilder(); - if (!TextUtils.isEmpty(mMetadata.artist)) { - if (sb.length() != 0) { - sb.append(" - "); - } - sb.append(mMetadata.artist); - } - if (!TextUtils.isEmpty(mMetadata.albumTitle)) { - if (sb.length() != 0) { - sb.append(" - "); - } - sb.append(mMetadata.albumTitle); - } - - final String trackArtistAlbum = sb.toString(); - mTrackArtistAlbum.setText(!TextUtils.isEmpty(trackArtistAlbum) ? - trackArtistAlbum : null); - - if (mMetadata.duration >= 0) { - setSeekBarsEnabled(true); - setSeekBarDuration(mMetadata.duration); - - final String skeleton; - - if (mMetadata.duration >= 86400000) { - skeleton = "DDD kk mm ss"; - } else if (mMetadata.duration >= 3600000) { - skeleton = "kk mm ss"; - } else { - skeleton = "mm ss"; - } - mFormat = new SimpleDateFormat(DateFormat.getBestDateTimePattern( - getContext().getResources().getConfiguration().locale, - skeleton)); - mFormat.setTimeZone(TimeZone.getTimeZone("GMT+0")); - } else { - setSeekBarsEnabled(false); - } - - KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(mMetadata.bitmap); - final int flags = mTransportControlFlags; - setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS); - setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT); - setVisibilityBasedOnFlag(mBtnPlay, flags, - RemoteControlClient.FLAG_KEY_MEDIA_PLAY - | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE - | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE - | RemoteControlClient.FLAG_KEY_MEDIA_STOP); - - updatePlayPauseState(mCurrentPlayState); - } - - void updateSeekDisplay() { - if (mMetadata != null && mRemoteController != null && mFormat != null) { - mTempDate.setTime(mRemoteController.getEstimatedMediaPosition()); - mTransientSeekTimeElapsed.setText(mFormat.format(mTempDate)); - mTempDate.setTime(mMetadata.duration); - mTransientSeekTimeTotal.setText(mFormat.format(mTempDate)); - - if (DEBUG) Log.d(TAG, "updateSeekDisplay timeElapsed=" + mTempDate + - " duration=" + mMetadata.duration); - } - } - - boolean tryToggleSeekBar() { - if (ANIMATE_TRANSITIONS) { - TransitionManager.beginDelayedTransition(mInfoContainer); - } - if (mTransientSeek.getVisibility() == VISIBLE) { - mTransientSeek.setVisibility(INVISIBLE); - mMetadataContainer.setVisibility(VISIBLE); - cancelResetToMetadata(); - removeCallbacks(mUpdateSeekBars); // don't update if scrubber isn't visible - } else { - mTransientSeek.setVisibility(VISIBLE); - mMetadataContainer.setVisibility(INVISIBLE); - delayResetToMetadata(); - if (playbackPositionShouldMove(mCurrentPlayState)) { - mUpdateSeekBars.run(); - } else { - mUpdateSeekBars.updateOnce(); - } - } - mTransportControlCallback.userActivity(); - return true; - } - - void resetToMetadata() { - if (ANIMATE_TRANSITIONS) { - TransitionManager.beginDelayedTransition(mInfoContainer); - } - if (mTransientSeek.getVisibility() == VISIBLE) { - mTransientSeek.setVisibility(INVISIBLE); - mMetadataContainer.setVisibility(VISIBLE); - } - // TODO Also hide ratings, if applicable - } - - void delayResetToMetadata() { - removeCallbacks(mResetToMetadata); - postDelayed(mResetToMetadata, RESET_TO_METADATA_DELAY); - } - - void cancelResetToMetadata() { - removeCallbacks(mResetToMetadata); - } - - void setSeekBarDuration(long duration) { - mTransientSeekBar.setMax((int) duration); - } - - void scrubTo(int progress) { - mRemoteController.seekTo(progress); - mTransportControlCallback.userActivity(); - } - - private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { - if ((flags & flag) != 0) { - view.setVisibility(View.VISIBLE); - } else { - view.setVisibility(View.INVISIBLE); - } - } - - private void updatePlayPauseState(int state) { - if (DEBUG) Log.v(TAG, - "updatePlayPauseState(), old=" + mCurrentPlayState + ", state=" + state); - if (state == mCurrentPlayState) { - return; - } - final int imageResId; - final int imageDescId; - switch (state) { - case RemoteControlClient.PLAYSTATE_ERROR: - imageResId = R.drawable.stat_sys_warning; - // TODO use more specific image description string for warning, but here the "play" - // message is still valid because this button triggers a play command. - imageDescId = R.string.keyguard_transport_play_description; - break; - - case RemoteControlClient.PLAYSTATE_PLAYING: - imageResId = R.drawable.ic_media_pause; - imageDescId = R.string.keyguard_transport_pause_description; - break; - - case RemoteControlClient.PLAYSTATE_BUFFERING: - imageResId = R.drawable.ic_media_stop; - imageDescId = R.string.keyguard_transport_stop_description; - break; - - case RemoteControlClient.PLAYSTATE_PAUSED: - default: - imageResId = R.drawable.ic_media_play; - imageDescId = R.string.keyguard_transport_play_description; - break; - } - - boolean clientSupportsSeek = mMetadata != null && mMetadata.duration > 0; - setSeekBarsEnabled(clientSupportsSeek); - - mBtnPlay.setImageResource(imageResId); - mBtnPlay.setContentDescription(getResources().getString(imageDescId)); - mCurrentPlayState = state; - } - - boolean updateSeekBars() { - final int position = (int) mRemoteController.getEstimatedMediaPosition(); - if (DEBUG) Log.v(TAG, "Estimated time:" + position); - if (position >= 0) { - mTransientSeekBar.setProgress(position); - return true; - } - Log.w(TAG, "Updating seek bars; received invalid estimated media position (" + - position + "). Disabling seek."); - setSeekBarsEnabled(false); - return false; - } - - static class SavedState extends BaseSavedState { - boolean clientPresent; - String artist; - String trackTitle; - String albumTitle; - long duration; - Bitmap bitmap; - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - clientPresent = in.readInt() != 0; - artist = in.readString(); - trackTitle = in.readString(); - albumTitle = in.readString(); - duration = in.readLong(); - bitmap = Bitmap.CREATOR.createFromParcel(in); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(clientPresent ? 1 : 0); - out.writeString(artist); - out.writeString(trackTitle); - out.writeString(albumTitle); - out.writeLong(duration); - bitmap.writeToParcel(out, flags); - } - - public static final Parcelable.Creator<SavedState> CREATOR - = new Parcelable.Creator<SavedState>() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - private void sendMediaButtonClick(int keyCode) { - // TODO We should think about sending these up/down events accurately with touch up/down - // on the buttons, but in the near term this will interfere with the long press behavior. - mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); - mRemoteController.sendMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); - - mTransportControlCallback.userActivity(); - } - - public boolean providesClock() { - return false; - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 64fb24b..022338d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.IUserSwitchObserver; @@ -23,7 +24,6 @@ import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -40,6 +40,7 @@ import static android.os.BatteryManager.EXTRA_HEALTH; import android.media.AudioManager; import android.os.BatteryManager; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Message; @@ -51,17 +52,17 @@ import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.TelephonyProperties; -import android.service.fingerprint.FingerprintManager; -import android.service.fingerprint.FingerprintManagerReceiver; -import android.service.fingerprint.FingerprintUtils; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; +import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import com.google.android.collect.Lists; @@ -86,7 +87,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final String TAG = "KeyguardUpdateMonitor"; private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; - private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3; private static final int LOW_BATTERY_THRESHOLD = 20; private static final String ACTION_FACE_UNLOCK_STARTED @@ -100,25 +100,24 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_SIM_STATE_CHANGE = 304; private static final int MSG_RINGER_MODE_CHANGED = 305; private static final int MSG_PHONE_STATE_CHANGED = 306; - private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307; private static final int MSG_DEVICE_PROVISIONED = 308; private static final int MSG_DPM_STATE_CHANGED = 309; private static final int MSG_USER_SWITCHING = 310; - private static final int MSG_USER_REMOVED = 311; private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312; private static final int MSG_BOOT_COMPLETED = 313; private static final int MSG_USER_SWITCH_COMPLETE = 314; - private static final int MSG_SET_CURRENT_CLIENT_ID = 315; - private static final int MSG_SET_PLAYBACK_STATE = 316; private static final int MSG_USER_INFO_CHANGED = 317; private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318; private static final int MSG_SCREEN_TURNED_ON = 319; private static final int MSG_SCREEN_TURNED_OFF = 320; private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; - private static final int MSG_FINGERPRINT_PROCESSED = 323; - private static final int MSG_FINGERPRINT_ACQUIRED = 324; - private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 325; - private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 326; + private static final int MSG_FINGERPRINT_AUTHENTICATED = 323; + private static final int MSG_FINGERPRINT_ERROR = 324; + private static final int MSG_FINGERPRINT_HELP = 325; + private static final int MSG_FINGERPRINT_AUTH_FAILED = 326; + private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327; + private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328; + private static final int MSG_AIRPLANE_MODE_CHANGED = 329; private static KeyguardUpdateMonitor sInstance; @@ -138,10 +137,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private BatteryStatus mBatteryStatus; // Password attempts - private int mFailedAttempts = 0; - private int mFailedBiometricUnlockAttempts = 0; - - private boolean mAlternateUnlockEnabled; + private SparseIntArray mFailedAttempts = new SparseIntArray(); private boolean mClockVisible; @@ -154,6 +150,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mScreenOn; private SubscriptionManager mSubscriptionManager; private List<SubscriptionInfo> mSubscriptionInfo; + private boolean mFingerprintDetectionRunning; + private TrustManager mTrustManager; private final Handler mHandler = new Handler() { @Override @@ -174,9 +172,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_PHONE_STATE_CHANGED: handlePhoneStateChanged((String) msg.obj); break; - case MSG_CLOCK_VISIBILITY_CHANGED: - handleClockVisibilityChanged(); - break; case MSG_DEVICE_PROVISIONED: handleDeviceProvisioned(); break; @@ -189,9 +184,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); break; - case MSG_USER_REMOVED: - handleUserRemoved(msg.arg1); - break; case MSG_KEYGUARD_VISIBILITY_CHANGED: handleKeyguardVisibilityChanged(msg.arg1); break; @@ -213,11 +205,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_SCREEN_TURNED_ON: handleScreenTurnedOn(); break; - case MSG_FINGERPRINT_ACQUIRED: - handleFingerprintAcquired(msg.arg1); + case MSG_FINGERPRINT_AUTHENTICATED: + handleFingerprintAuthenticated(); + break; + case MSG_FINGERPRINT_HELP: + handleFingerprintHelp(msg.arg1 /* msgId */, (String) msg.obj /* errString */); break; - case MSG_FINGERPRINT_PROCESSED: - handleFingerprintProcessed(msg.arg1); + case MSG_FINGERPRINT_ERROR: + handleFingerprintError(msg.arg1 /* msgId */, (String) msg.obj /* errString */); + break; + case MSG_FINGERPRINT_AUTH_FAILED: + handleFingerprintAuthFailed(); break; case MSG_FACE_UNLOCK_STATE_CHANGED: handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2); @@ -225,6 +223,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_SIM_SUBSCRIPTION_INFO_CHANGED: handleSimSubscriptionInfoChanged(); break; + case MSG_AIRPLANE_MODE_CHANGED: + handleAirplaneModeChanged(); + break; } } }; @@ -239,19 +240,28 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); - private SparseBooleanArray mUserFingerprintRecognized = new SparseBooleanArray(); + private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); + private static int sCurrentUser; + + public synchronized static void setCurrentUser(int currentUser) { + sCurrentUser = currentUser; + } + + public synchronized static int getCurrentUser() { + return sCurrentUser; + } + @Override - public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) { + public void onTrustChanged(boolean enabled, int userId, int flags) { mUserHasTrust.put(userId, enabled); - for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onTrustChanged(userId); - if (enabled && initiatedByUser) { - cb.onTrustInitiatedByUser(userId); + if (enabled && flags != 0) { + cb.onTrustGrantedWithFlags(flags, userId); } } } @@ -299,6 +309,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } + private void handleAirplaneModeChanged() { + for (int j = 0; j < mCallbacks.size(); j++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + if (cb != null) { + cb.onRefreshCarrierInfo(); + } + } + } + /** @return List of SubscriptionInfo records, maybe empty but never null */ List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) { List<SubscriptionInfo> sil = mSubscriptionInfo; @@ -326,48 +345,75 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - private void onFingerprintRecognized(int userId) { - mUserFingerprintRecognized.put(userId, true); + private void onFingerprintAuthenticated(int userId) { + mUserFingerprintAuthenticated.put(userId, true); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onFingerprintRecognized(userId); + cb.onFingerprintAuthenticated(userId); } } } - private void handleFingerprintProcessed(int fingerprintId) { - if (fingerprintId == 0) return; // not a valid fingerprint + private void handleFingerprintAuthFailed() { + // FingerprintManager will allow us to retry a few times before finally giving up. + // TODO: Figure out the proper logic to stop this call when max tries is reached. + handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized)); + } - final int userId; + private void handleFingerprintAuthenticated() { try { - userId = ActivityManagerNative.getDefault().getCurrentUser().id; - } catch (RemoteException e) { - Log.e(TAG, "Failed to get current user id: ", e); - return; - } - if (isFingerprintDisabled(userId)) { - Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId); - return; + final int userId; + try { + userId = ActivityManagerNative.getDefault().getCurrentUser().id; + } catch (RemoteException e) { + Log.e(TAG, "Failed to get current user id: ", e); + return; + } + if (isFingerprintDisabled(userId)) { + Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId); + return; + } + onFingerprintAuthenticated(userId); + } finally { + setFingerprintRunningDetectionRunning(false); } - final ContentResolver res = mContext.getContentResolver(); - final int ids[] = FingerprintUtils.getFingerprintIdsForUser(res, userId); - for (int i = 0; i < ids.length; i++) { - if (ids[i] == fingerprintId) { - onFingerprintRecognized(userId); + } + + private void handleFingerprintHelp(int msgId, String helpString) { + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onFingerprintHelp(msgId, helpString); } } } - private void handleFingerprintAcquired(int info) { + private void handleFingerprintError(int msgId, String errString) { + setFingerprintRunningDetectionRunning(false); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onFingerprintAcquired(info); + cb.onFingerprintError(msgId, errString); } } } + private void setFingerprintRunningDetectionRunning(boolean running) { + if (running != mFingerprintDetectionRunning) { + mFingerprintDetectionRunning = running; + notifyFingerprintRunningStateChanged(); + } + } + + private void notifyFingerprintRunningStateChanged() { + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onFingerprintRunningStateChanged(mFingerprintDetectionRunning); + } + } + } private void handleFaceUnlockStateChanged(boolean running, int userId) { mUserFaceUnlockRunning.put(userId, running); for (int i = 0; i < mCallbacks.size(); i++) { @@ -382,25 +428,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return mUserFaceUnlockRunning.get(userId); } - private boolean isTrustDisabled(int userId) { - final DevicePolicyManager dpm = - (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - if (dpm != null) { - // TODO once UI is finalized - final boolean disabledByGlobalActions = false; - final boolean disabledBySettings = false; - - // Don't allow trust agent if device is secured with a SIM PIN. This is here - // mainly because there's no other way to prompt the user to enter their SIM PIN - // once they get past the keyguard screen. - final boolean disabledBySimPin = isSimPinSecure(); + public boolean isFingerprintDetectionRunning() { + return mFingerprintDetectionRunning; + } - final boolean disabledByDpm = (dpm.getKeyguardDisabledFeatures(null, userId) - & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0; - return disabledByDpm || disabledByGlobalActions || disabledBySettings - || disabledBySimPin; - } - return false; + private boolean isTrustDisabled(int userId) { + // Don't allow trust agent if device is secured with a SIM PIN. This is here + // mainly because there's no other way to prompt the user to enter their SIM PIN + // once they get past the keyguard screen. + final boolean disabledBySimPin = isSimPinSecure(); + return disabledBySimPin; } private boolean isFingerprintDisabled(int userId) { @@ -412,7 +449,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public boolean getUserHasTrust(int userId) { return !isTrustDisabled(userId) && mUserHasTrust.get(userId) - || mUserFingerprintRecognized.get(userId); + || mUserFingerprintAuthenticated.get(userId); } public boolean getUserTrustIsManaged(int userId) { @@ -462,9 +499,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); - } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); + } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { + mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED); } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { dispatchBootCompleted(); } @@ -492,23 +528,32 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } }; - private FingerprintManagerReceiver mFingerprintManagerReceiver = - new FingerprintManagerReceiver() { + + private FingerprintManager.AuthenticationCallback mAuthenticationCallback + = new AuthenticationCallback() { + @Override - public void onProcessed(int fingerprintId) { - mHandler.obtainMessage(MSG_FINGERPRINT_PROCESSED, fingerprintId, 0).sendToTarget(); + public void onAuthenticationFailed() { + mHandler.obtainMessage(MSG_FINGERPRINT_AUTH_FAILED).sendToTarget(); }; @Override - public void onAcquired(int info) { - mHandler.obtainMessage(MSG_FINGERPRINT_ACQUIRED, info, 0).sendToTarget(); + public void onAuthenticationSucceeded(AuthenticationResult result) { + mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget(); } @Override - public void onError(int error) { - if (DEBUG) Log.w(TAG, "FingerprintManager reported error: " + error); + public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) { + mHandler.obtainMessage(MSG_FINGERPRINT_HELP, helpMsgId, 0, helpString).sendToTarget(); + } + + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { + mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString).sendToTarget(); } }; + private CancellationSignal mFingerprintCancelSignal; + private FingerprintManager mFpm; /** * When we receive a @@ -627,6 +672,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } protected void handleScreenTurnedOn() { + updateFingerprintListeningState(); final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -645,6 +691,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onScreenTurnedOff(arg1); } } + updateFingerprintListeningState(); } /** @@ -689,10 +736,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - filter.addAction(Intent.ACTION_USER_REMOVED); context.registerReceiver(mBroadcastReceiver, filter); final IntentFilter bootCompleteFilter = new IntentFilter(); @@ -717,13 +764,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public void onUserSwitching(int newUserId, IRemoteCallback reply) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0, reply)); - mSwitchingUser = true; } @Override public void onUserSwitchComplete(int newUserId) throws RemoteException { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE, newUserId, 0)); - mSwitchingUser = false; + } + @Override + public void onForegroundProfileSwitch(int newProfileId) { + // Ignore. } }); } catch (RemoteException e) { @@ -731,12 +780,52 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { e.printStackTrace(); } - TrustManager trustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); - trustManager.registerTrustListener(this); + mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); + mTrustManager.registerTrustListener(this); + + mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); + updateFingerprintListeningState(); + } + + private void updateFingerprintListeningState() { + boolean shouldListenForFingerprint = shouldListenForFingerprint(); + if (mFingerprintDetectionRunning && !shouldListenForFingerprint) { + stopListeningForFingerprint(); + } else if (!mFingerprintDetectionRunning && shouldListenForFingerprint) { + startListeningForFingerprint(); + } + } + + private boolean shouldListenForFingerprint() { + return mScreenOn && mKeyguardIsVisible && !mSwitchingUser + && mTrustManager.hasUserAuthenticatedSinceBoot(ActivityManager.getCurrentUser()); + } + + private void startListeningForFingerprint() { + if (DEBUG) Log.v(TAG, "startListeningForFingerprint()"); + int userId = ActivityManager.getCurrentUser(); + if (isUnlockWithFingerPrintPossible(userId)) { + if (mFingerprintCancelSignal != null) { + mFingerprintCancelSignal.cancel(); + } + mFingerprintCancelSignal = new CancellationSignal(); + mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId); + setFingerprintRunningDetectionRunning(true); + } + } + + public boolean isUnlockWithFingerPrintPossible(int userId) { + return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId) + && mFpm.getEnrolledFingerprints(userId).size() > 0; + } - FingerprintManager fpm; - fpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); - fpm.startListening(mFingerprintManagerReceiver); + private void stopListeningForFingerprint() { + if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()"); + if (isFingerprintDetectionRunning()) { + mFingerprintCancelSignal.cancel(); + mFingerprintCancelSignal = null; + } + setFingerprintRunningDetectionRunning(false); } private boolean isDeviceProvisionedInSettingsDb() { @@ -788,6 +877,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * Handle {@link #MSG_USER_SWITCHING} */ protected void handleUserSwitching(int userId, IRemoteCallback reply) { + mSwitchingUser = true; + updateFingerprintListeningState(); + for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -804,6 +896,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * Handle {@link #MSG_USER_SWITCH_COMPLETE} */ protected void handleUserSwitchComplete(int userId) { + mSwitchingUser = false; + updateFingerprintListeningState(); + for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -845,18 +940,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** - * Handle {@link #MSG_USER_REMOVED} - */ - protected void handleUserRemoved(int userId) { - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onUserRemoved(userId); - } - } - } - - /** * Handle {@link #MSG_DEVICE_PROVISIONED} */ protected void handleDeviceProvisioned() { @@ -975,19 +1058,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** - * Handle {@link #MSG_CLOCK_VISIBILITY_CHANGED} - */ - private void handleClockVisibilityChanged() { - if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()"); - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onClockVisibilityChanged(); - } - } - } - - /** * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED} */ private void handleKeyguardVisibilityChanged(int showing) { @@ -1000,6 +1070,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onKeyguardVisibilityChangedRaw(isShowing); } } + updateFingerprintListeningState(); } /** @@ -1030,21 +1101,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - public boolean isKeyguardVisible() { - return mKeyguardIsVisible; - } - - /** - * @return if the keyguard is currently in bouncer mode. - */ - public boolean isKeyguardBouncer() { - return mBouncer; - } - - public boolean isSwitchingUser() { - return mSwitchingUser; - } - private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) { final boolean nowPluggedIn = current.isPluggedIn(); final boolean wasPluggedIn = old.isPluggedIn(); @@ -1070,13 +1126,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** - * @return The default plmn (no service) - */ - private CharSequence getDefaultPlmn() { - return mContext.getResources().getText(R.string.keyguard_carrier_default); - } - - /** * Remove the given observer's callback. * * @param callback The callback to remove @@ -1141,11 +1190,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { message.sendToTarget(); } - public void reportClockVisible(boolean visible) { - mClockVisible = visible; - mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget(); - } - /** * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we * have the information earlier than waiting for the intent @@ -1184,45 +1228,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return mDeviceProvisioned; } - public int getFailedUnlockAttempts() { - return mFailedAttempts; - } - public void clearFailedUnlockAttempts() { - mFailedAttempts = 0; - mFailedBiometricUnlockAttempts = 0; + mFailedAttempts.delete(sCurrentUser); } - public void clearFingerprintRecognized() { - mUserFingerprintRecognized.clear(); + public int getFailedUnlockAttempts() { + return mFailedAttempts.get(sCurrentUser, 0); } public void reportFailedUnlockAttempt() { - mFailedAttempts++; - } - - public boolean isClockVisible() { - return mClockVisible; - } - - public int getPhoneState() { - return mPhoneState; + mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1); } - public void reportFailedBiometricUnlockAttempt() { - mFailedBiometricUnlockAttempts++; - } - - public boolean getMaxBiometricUnlockAttemptsReached() { - return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP; - } - - public boolean isAlternateUnlockEnabled() { - return mAlternateUnlockEnabled; - } - - public void setAlternateUnlockEnabled(boolean enabled) { - mAlternateUnlockEnabled = enabled; + public void clearFingerprintRecognized() { + mUserFingerprintAuthenticated.clear(); } public boolean isSimPinVoiceSecure() { @@ -1297,7 +1316,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON); } - public void dispatchScreenTurndOff(int why) { + public void dispatchScreenTurnedOff(int why) { synchronized(this) { mScreenOn = false; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index c2f355a..26e6973 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -15,11 +15,11 @@ */ package com.android.keyguard; -import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.graphics.Bitmap; import android.media.AudioManager; import android.os.SystemClock; +import android.hardware.fingerprint.FingerprintManager; import android.telephony.TelephonyManager; import android.view.WindowManagerPolicy; @@ -123,11 +123,6 @@ public class KeyguardUpdateMonitorCallback { public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) { } /** - * Called when a user is removed. - */ - public void onUserRemoved(int userId) { } - - /** * Called when the user's info changed. */ public void onUserInfoChanged(int userId) { } @@ -176,23 +171,38 @@ public class KeyguardUpdateMonitorCallback { public void onTrustManagedChanged(int userId) { } /** - * Called when the user has proved to a trust agent that they want to use the device. + * Called after trust was granted with non-zero flags. */ - public void onTrustInitiatedByUser(int userId) { } + public void onTrustGrantedWithFlags(int flags, int userId) { } /** * Called when a fingerprint is recognized. - * @param userId + * @param userId the user id for which the fingerprint was authenticated */ - public void onFingerprintRecognized(int userId) { } + public void onFingerprintAuthenticated(int userId) { } /** - * Called when fingerprint is acquired but not yet recognized + * Called when fingerprint provides help string (e.g. "Try again") + * @param msgId + * @param helpString */ - public void onFingerprintAcquired(int info) { } + public void onFingerprintHelp(int msgId, String helpString) { } + + /** + * Called when fingerprint provides an semi-permanent error message + * (e.g. "Hardware not available"). + * @param msgId one of the error messages listed in {@link FingerprintManager} + * @param errString + */ + public void onFingerprintError(int msgId, String errString) { } /** * Called when the state of face unlock changed. */ public void onFaceUnlockStateChanged(boolean running, int userId) { } + + /** + * Called when the fingerprint running state changed. + */ + public void onFingerprintRunningStateChanged(boolean running) { } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java deleted file mode 100644 index fc17909..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2007 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.keyguard; - -import android.app.Activity; -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.SearchManager; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.media.AudioManager; -import android.media.IAudioService; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.os.UserHandle; -import android.telephony.TelephonyManager; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Slog; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.widget.FrameLayout; - -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardHostView.OnDismissAction; -import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; - -import java.io.File; - -/** - * Base class for keyguard view. {@link #reset} is where you should - * reset the state of your view. Use the {@link KeyguardViewCallback} via - * {@link #getCallback()} to send information back (such as poking the wake lock, - * or finishing the keyguard). - * - * Handles intercepting of media keys that still work when the keyguard is - * showing. - */ -public abstract class KeyguardViewBase extends FrameLayout implements SecurityCallback { - - private AudioManager mAudioManager; - private TelephonyManager mTelephonyManager = null; - protected ViewMediatorCallback mViewMediatorCallback; - protected LockPatternUtils mLockPatternUtils; - private OnDismissAction mDismissAction; - - // Whether the volume keys should be handled by keyguard. If true, then - // they will be handled here for specific media types such as music, otherwise - // the audio service will bring up the volume dialog. - private static final boolean KEYGUARD_MANAGES_VOLUME = false; - public static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "KeyguardViewBase"; - - private KeyguardSecurityContainer mSecurityContainer; - - public KeyguardViewBase(Context context) { - this(context, null); - } - - public KeyguardViewBase(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void dispatchDraw(Canvas canvas) { - super.dispatchDraw(canvas); - if (mViewMediatorCallback != null) { - mViewMediatorCallback.keyguardDoneDrawing(); - } - } - - /** - * Sets an action to run when keyguard finishes. - * - * @param action - */ - public void setOnDismissAction(OnDismissAction action) { - mDismissAction = action; - } - - @Override - protected void onFinishInflate() { - mSecurityContainer = - (KeyguardSecurityContainer) findViewById(R.id.keyguard_security_container); - mLockPatternUtils = new LockPatternUtils(mContext); - mSecurityContainer.setLockPatternUtils(mLockPatternUtils); - mSecurityContainer.setSecurityCallback(this); - mSecurityContainer.showPrimarySecurityScreen(false); - // mSecurityContainer.updateSecurityViews(false /* not bouncing */); - } - - /** - * Called when the view needs to be shown. - */ - public void showPrimarySecurityScreen() { - if (DEBUG) Log.d(TAG, "show()"); - mSecurityContainer.showPrimarySecurityScreen(false); - } - - /** - * Dismisses the keyguard by going to the next screen or making it gone. - * - * @return True if the keyguard is done. - */ - public boolean dismiss() { - return dismiss(false); - } - - protected void showBouncer(boolean show) { - CharSequence what = getContext().getResources().getText( - show ? R.string.keyguard_accessibility_show_bouncer - : R.string.keyguard_accessibility_hide_bouncer); - announceForAccessibility(what); - announceCurrentSecurityMethod(); - } - - public boolean handleBackKey() { - if (mSecurityContainer.getCurrentSecuritySelection() == SecurityMode.Account) { - // go back to primary screen - mSecurityContainer.showPrimarySecurityScreen(false /*turningOff*/); - return true; - } - if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) { - mSecurityContainer.dismiss(false); - return true; - } - return false; - } - - protected void announceCurrentSecurityMethod() { - mSecurityContainer.announceCurrentSecurityMethod(); - } - - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { - event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription()); - return true; - } else { - return super.dispatchPopulateAccessibilityEvent(event); - } - } - - protected KeyguardSecurityContainer getSecurityContainer() { - return mSecurityContainer; - } - - @Override - public boolean dismiss(boolean authenticated) { - return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated); - } - - /** - * Authentication has happened and it's time to dismiss keyguard. This function - * should clean up and inform KeyguardViewMediator. - */ - @Override - public void finish() { - // If the alternate unlock was suppressed, it can now be safely - // enabled because the user has left keyguard. - KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); - - // If there's a pending runnable because the user interacted with a widget - // and we're leaving keyguard, then run it. - boolean deferKeyguardDone = false; - if (mDismissAction != null) { - deferKeyguardDone = mDismissAction.onDismiss(); - mDismissAction = null; - } - if (mViewMediatorCallback != null) { - if (deferKeyguardDone) { - mViewMediatorCallback.keyguardDonePending(); - } else { - mViewMediatorCallback.keyguardDone(true); - } - } - } - - @Override - public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.setNeedsInput(needsInput); - } - } - - public void userActivity() { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.userActivity(); - } - } - - protected void onUserActivityTimeoutChanged() { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.onUserActivityTimeoutChanged(); - } - } - - /** - * Called when the Keyguard is not actively shown anymore on the screen. - */ - public void onPause() { - if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", - Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); - // Once the screen turns off, we no longer consider this to be first boot and we want the - // biometric unlock to start next time keyguard is shown. - KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); - mSecurityContainer.showPrimarySecurityScreen(true); - mSecurityContainer.onPause(); - clearFocus(); - } - - /** - * Called when the Keyguard is actively shown on the screen. - */ - public void onResume() { - if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); - mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON); - requestFocus(); - } - - /** - * Starts the animation when the Keyguard gets shown. - */ - public void startAppearAnimation() { - mSecurityContainer.startAppearAnimation(); - } - - public void startDisappearAnimation(Runnable finishRunnable) { - if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) { - finishRunnable.run(); - } - } - - /** - * Verify that the user can get past the keyguard securely. This is called, - * for example, when the phone disables the keyguard but then wants to launch - * something else that requires secure access. - * - * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)} - */ - public void verifyUnlock() { - SecurityMode securityMode = mSecurityContainer.getSecurityMode(); - if (securityMode == KeyguardSecurityModel.SecurityMode.None) { - if (mViewMediatorCallback != null) { - mViewMediatorCallback.keyguardDone(true); - } - } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern - && securityMode != KeyguardSecurityModel.SecurityMode.PIN - && securityMode != KeyguardSecurityModel.SecurityMode.Password) { - // can only verify unlock when in pattern/password mode - if (mViewMediatorCallback != null) { - mViewMediatorCallback.keyguardDone(false); - } - } else { - // otherwise, go to the unlock screen, see if they can verify it - mSecurityContainer.verifyUnlock(); - } - } - - /** - * Called before this view is being removed. - */ - abstract public void cleanUp(); - - /** - * Gets the desired user activity timeout in milliseconds, or -1 if the - * default should be used. - */ - abstract public long getUserActivityTimeout(); - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (interceptMediaKey(event)) { - return true; - } - return super.dispatchKeyEvent(event); - } - - /** - * Allows the media keys to work when the keyguard is showing. - * The media keys should be of no interest to the actual keyguard view(s), - * so intercepting them here should not be of any harm. - * @param event The key event - * @return whether the event was consumed as a media key. - */ - public boolean interceptMediaKey(KeyEvent event) { - final int keyCode = event.getKeyCode(); - if (event.getAction() == KeyEvent.ACTION_DOWN) { - switch (keyCode) { - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAY/PAUSE toggle when phone is ringing or - * in-call to avoid music playback */ - if (mTelephonyManager == null) { - mTelephonyManager = (TelephonyManager) getContext().getSystemService( - Context.TELEPHONY_SERVICE); - } - if (mTelephonyManager != null && - mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { - return true; // suppress key event - } - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { - handleMediaKeyEvent(event); - return true; - } - - case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_MUTE: { - if (KEYGUARD_MANAGES_VOLUME) { - synchronized (this) { - if (mAudioManager == null) { - mAudioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - } - } - // Volume buttons should only function for music (local or remote). - // TODO: Actually handle MUTE. - mAudioManager.adjustSuggestedStreamVolume( - keyCode == KeyEvent.KEYCODE_VOLUME_UP - ? AudioManager.ADJUST_RAISE - : AudioManager.ADJUST_LOWER /* direction */, - AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */); - // Don't execute default volume behavior - return true; - } else { - return false; - } - } - } - } else if (event.getAction() == KeyEvent.ACTION_UP) { - switch (keyCode) { - case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY: - case KeyEvent.KEYCODE_MEDIA_PAUSE: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: - case KeyEvent.KEYCODE_MEDIA_RECORD: - case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: - case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: { - handleMediaKeyEvent(event); - return true; - } - } - } - return false; - } - - private void handleMediaKeyEvent(KeyEvent keyEvent) { - synchronized (this) { - if (mAudioManager == null) { - mAudioManager = (AudioManager) getContext().getSystemService( - Context.AUDIO_SERVICE); - } - } - mAudioManager.dispatchMediaKeyEvent(keyEvent); - } - - @Override - public void dispatchSystemUiVisibilityChanged(int visibility) { - super.dispatchSystemUiVisibilityChanged(visibility); - - if (!(mContext instanceof Activity)) { - setSystemUiVisibility(STATUS_BAR_DISABLE_BACK); - } - } - - /** - * In general, we enable unlocking the insecure keyguard with the menu key. However, there are - * some cases where we wish to disable it, notably when the menu button placement or technology - * is prone to false positives. - * - * @return true if the menu key should be enabled - */ - private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; - private boolean shouldEnableMenuKey() { - final Resources res = getResources(); - final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen); - final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); - final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); - return !configDisabled || isTestHarness || fileOverride; - } - - public boolean handleMenuKey() { - // The following enables the MENU key to work for testing automation - if (shouldEnableMenuKey()) { - dismiss(); - return true; - } - return false; - } - - public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) { - mViewMediatorCallback = viewMediatorCallback; - // Update ViewMediator with the current input method requirements - mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput()); - } - - protected KeyguardActivityLauncher getActivityLauncher() { - return mActivityLauncher; - } - - private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { - @Override - Context getContext() { - return mContext; - } - - @Override - void setOnDismissAction(OnDismissAction action) { - KeyguardViewBase.this.setOnDismissAction(action); - } - - @Override - LockPatternUtils getLockPatternUtils() { - return mLockPatternUtils; - } - - @Override - void requestDismissKeyguard() { - KeyguardViewBase.this.dismiss(false); - } - }; - - public void showAssistant() { - final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) - .getAssistIntent(mContext, true, UserHandle.USER_CURRENT); - - if (intent == null) return; - - final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, - R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit, - getHandler(), null); - - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - mActivityLauncher.launchActivityWithAnimation(intent, false, opts.toBundle(), null, null); - } - - public void launchCamera() { - mActivityLauncher.launchCamera(getHandler(), null); - } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - mSecurityContainer.setLockPatternUtils(utils); - } - - public SecurityMode getSecurityMode() { - return mSecurityContainer.getSecurityMode(); - } - - public SecurityMode getCurrentSecurityMode() { - return mSecurityContainer.getCurrentSecurityMode(); - } - - protected abstract void onUserSwitching(boolean switching); - - protected abstract void onCreateOptions(Bundle options); - - protected abstract void onExternalMotionEvent(MotionEvent event); - -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java deleted file mode 100644 index e47edf3..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewStateManager.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.animation.AnimatorListenerAdapter; -import android.os.Handler; -import android.os.Looper; -import android.view.View; - -public class KeyguardViewStateManager implements - SlidingChallengeLayout.OnChallengeScrolledListener, - ChallengeLayout.OnBouncerStateChangedListener { - - private static final String TAG = "KeyguardViewStateManager"; - private KeyguardWidgetPager mKeyguardWidgetPager; - private ChallengeLayout mChallengeLayout; - private KeyguardHostView mKeyguardHostView; - private int[] mTmpPoint = new int[2]; - private int[] mTmpLoc = new int[2]; - - private KeyguardSecurityView mKeyguardSecurityContainer; - private static final int SCREEN_ON_HINT_DURATION = 1000; - private static final int SCREEN_ON_RING_HINT_DELAY = 300; - private static final boolean SHOW_INITIAL_PAGE_HINTS = false; - Handler mMainQueue = new Handler(Looper.myLooper()); - - int mLastScrollState = SlidingChallengeLayout.SCROLL_STATE_IDLE; - - // Paged view state - private int mPageListeningToSlider = -1; - private int mCurrentPage = -1; - private int mPageIndexOnPageBeginMoving = -1; - - int mChallengeTop = 0; - - private final AnimatorListener mPauseListener = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - mKeyguardSecurityContainer.onPause(); - } - }; - - private final AnimatorListener mResumeListener = new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - if (((View)mKeyguardSecurityContainer).isShown()) { - mKeyguardSecurityContainer.onResume(0); - } - } - }; - - public KeyguardViewStateManager(KeyguardHostView hostView) { - mKeyguardHostView = hostView; - } - - public void setPagedView(KeyguardWidgetPager pagedView) { - mKeyguardWidgetPager = pagedView; - updateEdgeSwiping(); - } - - public void setChallengeLayout(ChallengeLayout layout) { - mChallengeLayout = layout; - updateEdgeSwiping(); - } - - private void updateEdgeSwiping() { - if (mChallengeLayout != null && mKeyguardWidgetPager != null) { - if (mChallengeLayout.isChallengeOverlapping()) { - mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(true); - } else { - mKeyguardWidgetPager.setOnlyAllowEdgeSwipes(false); - } - } - } - - public boolean isChallengeShowing() { - if (mChallengeLayout != null) { - return mChallengeLayout.isChallengeShowing(); - } - return false; - } - - public boolean isChallengeOverlapping() { - if (mChallengeLayout != null) { - return mChallengeLayout.isChallengeOverlapping(); - } - return false; - } - - public void setSecurityViewContainer(KeyguardSecurityView container) { - mKeyguardSecurityContainer = container; - } - - public void showBouncer(boolean show) { - mChallengeLayout.showBouncer(); - } - - public boolean isBouncing() { - return mChallengeLayout.isBouncing(); - } - - public void fadeOutSecurity(int duration) { - ((View) mKeyguardSecurityContainer).animate().alpha(0f).setDuration(duration) - .setListener(mPauseListener); - } - - public void fadeInSecurity(int duration) { - ((View) mKeyguardSecurityContainer).animate().alpha(1f).setDuration(duration) - .setListener(mResumeListener); - } - - public void onPageBeginMoving() { - if (mChallengeLayout.isChallengeOverlapping() && - mChallengeLayout instanceof SlidingChallengeLayout) { - SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout; - scl.fadeOutChallenge(); - mPageIndexOnPageBeginMoving = mKeyguardWidgetPager.getCurrentPage(); - } - // We use mAppWidgetToShow to show a particular widget after you add it-- - // once the user swipes a page we clear that behavior - if (mKeyguardHostView != null) { - mKeyguardHostView.clearAppWidgetToShow(); - mKeyguardHostView.setOnDismissAction(null); - } - if (mHideHintsRunnable != null) { - mMainQueue.removeCallbacks(mHideHintsRunnable); - mHideHintsRunnable = null; - } - } - - public void onPageEndMoving() { - mPageIndexOnPageBeginMoving = -1; - } - - public void onPageSwitching(View newPage, int newPageIndex) { - if (mKeyguardWidgetPager != null && mChallengeLayout instanceof SlidingChallengeLayout) { - boolean isCameraPage = newPage instanceof CameraWidgetFrame; - if (isCameraPage) { - CameraWidgetFrame camera = (CameraWidgetFrame) newPage; - camera.setUseFastTransition(mKeyguardWidgetPager.isWarping()); - } - SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout; - scl.setChallengeInteractive(!isCameraPage); - final int currentFlags = mKeyguardWidgetPager.getSystemUiVisibility(); - final int newFlags = isCameraPage ? (currentFlags | View.STATUS_BAR_DISABLE_SEARCH) - : (currentFlags & ~View.STATUS_BAR_DISABLE_SEARCH); - mKeyguardWidgetPager.setSystemUiVisibility(newFlags); - } - - // If the page we're settling to is the same as we started on, and the action of - // moving the page hid the security, we restore it immediately. - if (mPageIndexOnPageBeginMoving == mKeyguardWidgetPager.getNextPage() && - mChallengeLayout instanceof SlidingChallengeLayout) { - SlidingChallengeLayout scl = (SlidingChallengeLayout) mChallengeLayout; - scl.fadeInChallenge(); - mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(-1); - } - mPageIndexOnPageBeginMoving = -1; - } - - public void onPageSwitched(View newPage, int newPageIndex) { - // Reset the previous page size and ensure the current page is sized appropriately. - // We only modify the page state if it is not currently under control by the slider. - // This prevents conflicts. - - // If the page hasn't switched, don't bother with any of this - if (mCurrentPage == newPageIndex) return; - - if (mKeyguardWidgetPager != null && mChallengeLayout != null) { - KeyguardWidgetFrame prevPage = mKeyguardWidgetPager.getWidgetPageAt(mCurrentPage); - if (prevPage != null && mCurrentPage != mPageListeningToSlider && mCurrentPage - != mKeyguardWidgetPager.getWidgetToResetOnPageFadeOut()) { - prevPage.resetSize(); - } - - KeyguardWidgetFrame newCurPage = mKeyguardWidgetPager.getWidgetPageAt(newPageIndex); - boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping(); - if (challengeOverlapping && !newCurPage.isSmall() - && mPageListeningToSlider != newPageIndex) { - newCurPage.shrinkWidget(true); - } - } - - mCurrentPage = newPageIndex; - } - - public void onPageBeginWarp() { - fadeOutSecurity(SlidingChallengeLayout.CHALLENGE_FADE_OUT_DURATION); - View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex()); - ((KeyguardWidgetFrame)frame).showFrame(this); - } - - public void onPageEndWarp() { - fadeInSecurity(SlidingChallengeLayout.CHALLENGE_FADE_IN_DURATION); - View frame = mKeyguardWidgetPager.getPageAt(mKeyguardWidgetPager.getPageWarpIndex()); - ((KeyguardWidgetFrame)frame).hideFrame(this); - } - - private int getChallengeTopRelativeToFrame(KeyguardWidgetFrame frame, int top) { - mTmpPoint[0] = 0; - mTmpPoint[1] = top; - mapPoint((View) mChallengeLayout, frame, mTmpPoint); - return mTmpPoint[1]; - } - - /** - * Simple method to map a point from one view's coordinates to another's. Note: this method - * doesn't account for transforms, so if the views will be transformed, this should not be used. - * - * @param fromView The view to which the point is relative - * @param toView The view into which the point should be mapped - * @param pt The point - */ - private void mapPoint(View fromView, View toView, int pt[]) { - fromView.getLocationInWindow(mTmpLoc); - - int x = mTmpLoc[0]; - int y = mTmpLoc[1]; - - toView.getLocationInWindow(mTmpLoc); - int vX = mTmpLoc[0]; - int vY = mTmpLoc[1]; - - pt[0] += x - vX; - pt[1] += y - vY; - } - - private void userActivity() { - if (mKeyguardHostView != null) { - mKeyguardHostView.onUserActivityTimeoutChanged(); - mKeyguardHostView.userActivity(); - } - } - - @Override - public void onScrollStateChanged(int scrollState) { - if (mKeyguardWidgetPager == null || mChallengeLayout == null) return; - - boolean challengeOverlapping = mChallengeLayout.isChallengeOverlapping(); - - if (scrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) { - KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider); - if (frame == null) return; - - if (!challengeOverlapping) { - if (!mKeyguardWidgetPager.isPageMoving()) { - frame.resetSize(); - userActivity(); - } else { - mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider); - } - } - if (frame.isSmall()) { - // This is to make sure that if the scroller animation gets cut off midway - // that the frame doesn't stay in a partial down position. - frame.setFrameHeight(frame.getSmallFrameHeight()); - } - if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) { - frame.hideFrame(this); - } - updateEdgeSwiping(); - - if (mChallengeLayout.isChallengeShowing()) { - mKeyguardSecurityContainer.onResume(KeyguardSecurityView.VIEW_REVEALED); - } else { - mKeyguardSecurityContainer.onPause(); - } - mPageListeningToSlider = -1; - } else if (mLastScrollState == SlidingChallengeLayout.SCROLL_STATE_IDLE) { - // Whether dragging or settling, if the last state was idle, we use this signal - // to update the current page who will receive events from the sliding challenge. - // We resize the frame as appropriate. - mPageListeningToSlider = mKeyguardWidgetPager.getNextPage(); - KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider); - if (frame == null) return; - - // Skip showing the frame and shrinking the widget if we are - if (!mChallengeLayout.isBouncing()) { - if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) { - frame.showFrame(this); - } - - // As soon as the security begins sliding, the widget becomes small (if it wasn't - // small to begin with). - if (!frame.isSmall()) { - // We need to fetch the final page, in case the pages are in motion. - mPageListeningToSlider = mKeyguardWidgetPager.getNextPage(); - frame.shrinkWidget(false); - } - } else { - if (!frame.isSmall()) { - // We need to fetch the final page, in case the pages are in motion. - mPageListeningToSlider = mKeyguardWidgetPager.getNextPage(); - } - } - - // View is on the move. Pause the security view until it completes. - mKeyguardSecurityContainer.onPause(); - } - mLastScrollState = scrollState; - } - - @Override - public void onScrollPositionChanged(float scrollPosition, int challengeTop) { - mChallengeTop = challengeTop; - KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider); - if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) { - frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop)); - } - } - - private Runnable mHideHintsRunnable = new Runnable() { - @Override - public void run() { - if (mKeyguardWidgetPager != null) { - mKeyguardWidgetPager.hideOutlinesAndSidePages(); - } - } - }; - - public void showUsabilityHints() { - mMainQueue.postDelayed( new Runnable() { - @Override - public void run() { - mKeyguardSecurityContainer.showUsabilityHint(); - } - } , SCREEN_ON_RING_HINT_DELAY); - if (SHOW_INITIAL_PAGE_HINTS) { - mKeyguardWidgetPager.showInitialPageHints(); - } - if (mHideHintsRunnable != null) { - mMainQueue.postDelayed(mHideHintsRunnable, SCREEN_ON_HINT_DURATION); - } - } - - // ChallengeLayout.OnBouncerStateChangedListener - @Override - public void onBouncerStateChanged(boolean bouncerActive) { - if (bouncerActive) { - mKeyguardWidgetPager.zoomOutToBouncer(); - } else { - mKeyguardWidgetPager.zoomInFromBouncer(); - if (mKeyguardHostView != null) { - mKeyguardHostView.setOnDismissAction(null); - } - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java deleted file mode 100644 index 98b31b7..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetCarousel.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; - -import java.util.ArrayList; - -public class KeyguardWidgetCarousel extends KeyguardWidgetPager { - - private float mAdjacentPagesAngle; - private static float MAX_SCROLL_PROGRESS = 1.3f; - private static float CAMERA_DISTANCE = 10000; - protected AnimatorSet mChildrenTransformsAnimator; - float[] mTmpTransform = new float[3]; - - public KeyguardWidgetCarousel(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardWidgetCarousel(Context context) { - this(context, null, 0); - } - - public KeyguardWidgetCarousel(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mAdjacentPagesAngle = context.getResources().getInteger(R.integer.kg_carousel_angle); - } - - protected float getMaxScrollProgress() { - return MAX_SCROLL_PROGRESS; - } - - public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { - View child = getChildAt(index); - if (child == null) return 0f; - - boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; - float scrollProgress = getScrollProgress(screenCenter, child, index); - - if (isOverScrollChild(index, scrollProgress)) { - return 1.0f; - } else if ((showSidePages && inVisibleRange) || index == getNextPage()) { - scrollProgress = getBoundedScrollProgress(screenCenter, child, index); - float alpha = 1.0f - 1.0f * Math.abs(scrollProgress / MAX_SCROLL_PROGRESS); - return alpha; - } else { - return 0f; - } - } - - public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { - boolean inVisibleRange = index >= getNextPage() - 1 && index <= getNextPage() + 1; - if (inVisibleRange) { - return super.getOutlineAlphaForPage(screenCenter, index, showSidePages); - } else { - return 0f; - } - } - - private void updatePageAlphaValues(int screenCenter) { - if (mChildrenOutlineFadeAnimation != null) { - mChildrenOutlineFadeAnimation.cancel(); - mChildrenOutlineFadeAnimation = null; - } - boolean showSidePages = mShowingInitialHints || isPageMoving(); - if (!isReordering(false)) { - for (int i = 0; i < getChildCount(); i++) { - KeyguardWidgetFrame child = getWidgetPageAt(i); - if (child != null) { - float outlineAlpha = getOutlineAlphaForPage(screenCenter, i, showSidePages); - float contentAlpha = getAlphaForPage(screenCenter, i,showSidePages); - child.setBackgroundAlpha(outlineAlpha); - child.setContentAlpha(contentAlpha); - } - } - } - } - - public void showInitialPageHints() { - mShowingInitialHints = true; - int count = getChildCount(); - for (int i = 0; i < count; i++) { - boolean inVisibleRange = i >= getNextPage() - 1 && i <= getNextPage() + 1; - KeyguardWidgetFrame child = getWidgetPageAt(i); - if (inVisibleRange) { - child.setBackgroundAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); - child.setContentAlpha(1f); - } else { - child.setBackgroundAlpha(0f); - child.setContentAlpha(0f); - } - } - } - - @Override - protected void screenScrolled(int screenCenter) { - mScreenCenter = screenCenter; - updatePageAlphaValues(screenCenter); - if (isReordering(false)) return; - for (int i = 0; i < getChildCount(); i++) { - KeyguardWidgetFrame v = getWidgetPageAt(i); - float scrollProgress = getScrollProgress(screenCenter, v, i); - float boundedProgress = getBoundedScrollProgress(screenCenter, v, i); - if (v == mDragView || v == null) continue; - v.setCameraDistance(CAMERA_DISTANCE); - - if (isOverScrollChild(i, scrollProgress)) { - v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); - v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); - } else { - int width = v.getMeasuredWidth(); - float pivotX = (width / 2f) + boundedProgress * (width / 2f); - float pivotY = v.getMeasuredHeight() / 2; - float rotationY = - mAdjacentPagesAngle * boundedProgress; - v.setPivotX(pivotX); - v.setPivotY(pivotY); - v.setRotationY(rotationY); - v.setOverScrollAmount(0f, false); - } - float alpha = v.getAlpha(); - // If the view has 0 alpha, we set it to be invisible so as to prevent - // it from accepting touches - if (alpha == 0) { - v.setVisibility(INVISIBLE); - } else if (v.getVisibility() != VISIBLE) { - v.setVisibility(VISIBLE); - } - } - } - - void animatePagesToNeutral() { - if (mChildrenTransformsAnimator != null) { - mChildrenTransformsAnimator.cancel(); - mChildrenTransformsAnimator = null; - } - - int count = getChildCount(); - PropertyValuesHolder alpha; - PropertyValuesHolder outlineAlpha; - PropertyValuesHolder rotationY; - ArrayList<Animator> anims = new ArrayList<Animator>(); - - for (int i = 0; i < count; i++) { - KeyguardWidgetFrame child = getWidgetPageAt(i); - boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); - if (!inVisibleRange) { - child.setRotationY(0f); - } - alpha = PropertyValuesHolder.ofFloat("contentAlpha", 1.0f); - outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", - KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); - rotationY = PropertyValuesHolder.ofFloat("rotationY", 0f); - ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, rotationY); - child.setVisibility(VISIBLE); - if (!inVisibleRange) { - a.setInterpolator(mSlowFadeInterpolator); - } - anims.add(a); - } - - int duration = REORDERING_ZOOM_IN_OUT_DURATION; - mChildrenTransformsAnimator = new AnimatorSet(); - mChildrenTransformsAnimator.playTogether(anims); - - mChildrenTransformsAnimator.setDuration(duration); - mChildrenTransformsAnimator.start(); - } - - private void getTransformForPage(int screenCenter, int index, float[] transform) { - View child = getChildAt(index); - float boundedProgress = getBoundedScrollProgress(screenCenter, child, index); - float rotationY = - mAdjacentPagesAngle * boundedProgress; - int width = child.getMeasuredWidth(); - float pivotX = (width / 2f) + boundedProgress * (width / 2f); - float pivotY = child.getMeasuredHeight() / 2; - - transform[0] = pivotX; - transform[1] = pivotY; - transform[2] = rotationY; - } - - Interpolator mFastFadeInterpolator = new Interpolator() { - Interpolator mInternal = new DecelerateInterpolator(1.5f); - float mFactor = 2.5f; - @Override - public float getInterpolation(float input) { - return mInternal.getInterpolation(Math.min(mFactor * input, 1f)); - } - }; - - Interpolator mSlowFadeInterpolator = new Interpolator() { - Interpolator mInternal = new AccelerateInterpolator(1.5f); - float mFactor = 1.3f; - @Override - public float getInterpolation(float input) { - input -= (1 - 1 / mFactor); - input = mFactor * Math.max(input, 0f); - return mInternal.getInterpolation(input); - } - }; - - void animatePagesToCarousel() { - if (mChildrenTransformsAnimator != null) { - mChildrenTransformsAnimator.cancel(); - mChildrenTransformsAnimator = null; - } - - int count = getChildCount(); - PropertyValuesHolder alpha; - PropertyValuesHolder outlineAlpha; - PropertyValuesHolder rotationY; - PropertyValuesHolder pivotX; - PropertyValuesHolder pivotY; - ArrayList<Animator> anims = new ArrayList<Animator>(); - - for (int i = 0; i < count; i++) { - KeyguardWidgetFrame child = getWidgetPageAt(i); - float finalAlpha = getAlphaForPage(mScreenCenter, i, true); - float finalOutlineAlpha = getOutlineAlphaForPage(mScreenCenter, i, true); - getTransformForPage(mScreenCenter, i, mTmpTransform); - - boolean inVisibleRange = (i >= mCurrentPage - 1 && i <= mCurrentPage + 1); - - ObjectAnimator a; - alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalAlpha); - outlineAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", finalOutlineAlpha); - pivotX = PropertyValuesHolder.ofFloat("pivotX", mTmpTransform[0]); - pivotY = PropertyValuesHolder.ofFloat("pivotY", mTmpTransform[1]); - rotationY = PropertyValuesHolder.ofFloat("rotationY", mTmpTransform[2]); - - if (inVisibleRange) { - // for the central pages we animate into a rotated state - a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha, - pivotX, pivotY, rotationY); - } else { - a = ObjectAnimator.ofPropertyValuesHolder(child, alpha, outlineAlpha); - a.setInterpolator(mFastFadeInterpolator); - } - anims.add(a); - } - - int duration = REORDERING_ZOOM_IN_OUT_DURATION; - mChildrenTransformsAnimator = new AnimatorSet(); - mChildrenTransformsAnimator.playTogether(anims); - - mChildrenTransformsAnimator.setDuration(duration); - mChildrenTransformsAnimator.start(); - } - - protected void reorderStarting() { - mViewStateManager.fadeOutSecurity(REORDERING_ZOOM_IN_OUT_DURATION); - animatePagesToNeutral(); - } - - protected boolean zoomIn(final Runnable onCompleteRunnable) { - animatePagesToCarousel(); - return super.zoomIn(onCompleteRunnable); - } - - @Override - protected void onEndReordering() { - super.onEndReordering(); - mViewStateManager.fadeInSecurity(REORDERING_ZOOM_IN_OUT_DURATION); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java deleted file mode 100644 index 8ee9b61..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetFrame.java +++ /dev/null @@ -1,519 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.LinearGradient; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.Shader; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; - -public class KeyguardWidgetFrame extends FrameLayout { - private final static PorterDuffXfermode sAddBlendMode = - new PorterDuffXfermode(PorterDuff.Mode.ADD); - - static final float OUTLINE_ALPHA_MULTIPLIER = 0.6f; - static final int HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR = 0x99FF0000; - - // Temporarily disable this for the time being until we know why the gfx is messing up - static final boolean ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY = true; - - private int mGradientColor; - private LinearGradient mForegroundGradient; - private LinearGradient mLeftToRightGradient; - private LinearGradient mRightToLeftGradient; - private Paint mGradientPaint = new Paint(); - boolean mLeftToRight = true; - - private float mOverScrollAmount = 0f; - private final Rect mForegroundRect = new Rect(); - private int mForegroundAlpha = 0; - private CheckLongPressHelper mLongPressHelper; - private Animator mFrameFade; - private boolean mIsSmall = false; - private Handler mWorkerHandler; - - private float mBackgroundAlpha; - private float mContentAlpha; - private float mBackgroundAlphaMultiplier = 1.0f; - private Drawable mBackgroundDrawable; - private Rect mBackgroundRect = new Rect(); - - // These variables are all needed in order to size things properly before we're actually - // measured. - private int mSmallWidgetHeight; - private int mSmallFrameHeight; - private boolean mWidgetLockedSmall = false; - private int mMaxChallengeTop = -1; - private int mFrameStrokeAdjustment; - private boolean mPerformAppWidgetSizeUpdateOnBootComplete; - - // This will hold the width value before we've actually been measured - private int mFrameHeight; - - private boolean mIsHoveringOverDeleteDropTarget; - - // Multiple callers may try and adjust the alpha of the frame. When a caller shows - // the outlines, we give that caller control, and nobody else can fade them out. - // This prevents animation conflicts. - private Object mBgAlphaController; - - public KeyguardWidgetFrame(Context context) { - this(context, null, 0); - } - - public KeyguardWidgetFrame(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mLongPressHelper = new CheckLongPressHelper(this); - - Resources res = context.getResources(); - // TODO: this padding should really correspond to the padding embedded in the background - // drawable (ie. outlines). - float density = res.getDisplayMetrics().density; - int padding = (int) (res.getDisplayMetrics().density * 8); - setPadding(padding, padding, padding, padding); - - mFrameStrokeAdjustment = 2 + (int) (2 * density); - - // This will be overriden on phones based on the current security mode, however on tablets - // we need to specify a height. - mSmallWidgetHeight = - res.getDimensionPixelSize(R.dimen.kg_small_widget_height); - mBackgroundDrawable = res.getDrawable(R.drawable.kg_widget_bg_padded); - mGradientColor = res.getColor(R.color.kg_widget_pager_gradient); - mGradientPaint.setXfermode(sAddBlendMode); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - cancelLongPress(); - KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks); - - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks); - } - - private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks = - new KeyguardUpdateMonitorCallback() { - @Override - public void onBootCompleted() { - if (mPerformAppWidgetSizeUpdateOnBootComplete) { - performAppWidgetSizeCallbacksIfNecessary(); - mPerformAppWidgetSizeUpdateOnBootComplete = false; - } - } - }; - - void setIsHoveringOverDeleteDropTarget(boolean isHovering) { - if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { - if (mIsHoveringOverDeleteDropTarget != isHovering) { - mIsHoveringOverDeleteDropTarget = isHovering; - int resId = isHovering ? R.string.keyguard_accessibility_delete_widget_start - : R.string.keyguard_accessibility_delete_widget_end; - String text = getContext().getResources().getString(resId, getContentDescription()); - announceForAccessibility(text); - invalidate(); - } - } - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - // Watch for longpress events at this level to make sure - // users can always pick up this widget - switch (ev.getAction()) { - case MotionEvent.ACTION_DOWN: - mLongPressHelper.postCheckForLongPress(ev); - break; - case MotionEvent.ACTION_MOVE: - mLongPressHelper.onMove(ev); - break; - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mLongPressHelper.cancelLongPress(); - break; - } - - // Otherwise continue letting touch events fall through to children - return false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - // Watch for longpress events at this level to make sure - // users can always pick up this widget - switch (ev.getAction()) { - case MotionEvent.ACTION_MOVE: - mLongPressHelper.onMove(ev); - break; - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mLongPressHelper.cancelLongPress(); - break; - } - - // We return true here to ensure that we will get cancel / up signal - // even if none of our children have requested touch. - return true; - } - - @Override - public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { - super.requestDisallowInterceptTouchEvent(disallowIntercept); - cancelLongPress(); - } - - @Override - public void cancelLongPress() { - super.cancelLongPress(); - mLongPressHelper.cancelLongPress(); - } - - - private void drawGradientOverlay(Canvas c) { - mGradientPaint.setShader(mForegroundGradient); - mGradientPaint.setAlpha(mForegroundAlpha); - c.drawRect(mForegroundRect, mGradientPaint); - } - - private void drawHoveringOverDeleteOverlay(Canvas c) { - if (mIsHoveringOverDeleteDropTarget) { - c.drawColor(HOVER_OVER_DELETE_DROP_TARGET_OVERLAY_COLOR); - } - } - - protected void drawBg(Canvas canvas) { - if (mBackgroundAlpha > 0.0f) { - Drawable bg = mBackgroundDrawable; - - bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255)); - bg.setBounds(mBackgroundRect); - bg.draw(canvas); - } - } - - @Override - protected void dispatchDraw(Canvas canvas) { - if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { - canvas.save(); - } - drawBg(canvas); - super.dispatchDraw(canvas); - drawGradientOverlay(canvas); - if (ENABLE_HOVER_OVER_DELETE_DROP_TARGET_OVERLAY) { - drawHoveringOverDeleteOverlay(canvas); - canvas.restore(); - } - } - - /** - * Because this view has fading outlines, it is essential that we enable hardware - * layers on the content (child) so that updating the alpha of the outlines doesn't - * result in the content layer being recreated. - */ - public void enableHardwareLayersForContent() { - View widget = getContent(); - if (widget != null && widget.isHardwareAccelerated()) { - widget.setLayerType(LAYER_TYPE_HARDWARE, null); - } - } - - /** - * Because this view has fading outlines, it is essential that we enable hardware - * layers on the content (child) so that updating the alpha of the outlines doesn't - * result in the content layer being recreated. - */ - public void disableHardwareLayersForContent() { - View widget = getContent(); - if (widget != null) { - widget.setLayerType(LAYER_TYPE_NONE, null); - } - } - - public View getContent() { - return getChildAt(0); - } - - public int getContentAppWidgetId() { - View content = getContent(); - if (content instanceof AppWidgetHostView) { - return ((AppWidgetHostView) content).getAppWidgetId(); - } else if (content instanceof KeyguardStatusView) { - return ((KeyguardStatusView) content).getAppWidgetId(); - } else { - return AppWidgetManager.INVALID_APPWIDGET_ID; - } - } - - public float getBackgroundAlpha() { - return mBackgroundAlpha; - } - - public void setBackgroundAlphaMultiplier(float multiplier) { - if (Float.compare(mBackgroundAlphaMultiplier, multiplier) != 0) { - mBackgroundAlphaMultiplier = multiplier; - invalidate(); - } - } - - public float getBackgroundAlphaMultiplier() { - return mBackgroundAlphaMultiplier; - } - - public void setBackgroundAlpha(float alpha) { - if (Float.compare(mBackgroundAlpha, alpha) != 0) { - mBackgroundAlpha = alpha; - invalidate(); - } - } - - public float getContentAlpha() { - return mContentAlpha; - } - - public void setContentAlpha(float alpha) { - mContentAlpha = alpha; - View content = getContent(); - if (content != null) { - content.setAlpha(alpha); - } - } - - /** - * Depending on whether the security is up, the widget size needs to change - * - * @param height The height of the widget, -1 for full height - */ - private void setWidgetHeight(int height) { - boolean needLayout = false; - View widget = getContent(); - if (widget != null) { - LayoutParams lp = (LayoutParams) widget.getLayoutParams(); - if (lp.height != height) { - needLayout = true; - lp.height = height; - } - } - if (needLayout) { - requestLayout(); - } - } - - public void setMaxChallengeTop(int top) { - boolean dirty = mMaxChallengeTop != top; - mMaxChallengeTop = top; - mSmallWidgetHeight = top - getPaddingTop(); - mSmallFrameHeight = top + getPaddingBottom(); - if (dirty && mIsSmall) { - setWidgetHeight(mSmallWidgetHeight); - setFrameHeight(mSmallFrameHeight); - } else if (dirty && mWidgetLockedSmall) { - setWidgetHeight(mSmallWidgetHeight); - } - } - - public boolean isSmall() { - return mIsSmall; - } - - public void adjustFrame(int challengeTop) { - int frameHeight = challengeTop + getPaddingBottom(); - setFrameHeight(frameHeight); - } - - public void shrinkWidget(boolean alsoShrinkFrame) { - mIsSmall = true; - setWidgetHeight(mSmallWidgetHeight); - - if (alsoShrinkFrame) { - setFrameHeight(mSmallFrameHeight); - } - } - - public int getSmallFrameHeight() { - return mSmallFrameHeight; - } - - public void setWidgetLockedSmall(boolean locked) { - if (locked) { - setWidgetHeight(mSmallWidgetHeight); - } - mWidgetLockedSmall = locked; - } - - public void resetSize() { - mIsSmall = false; - if (!mWidgetLockedSmall) { - setWidgetHeight(LayoutParams.MATCH_PARENT); - } - setFrameHeight(getMeasuredHeight()); - } - - public void setFrameHeight(int height) { - mFrameHeight = height; - mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(mFrameHeight, getMeasuredHeight())); - mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment,getMeasuredWidth() - - mFrameStrokeAdjustment, Math.min(getMeasuredHeight(), mFrameHeight) - - mFrameStrokeAdjustment); - updateGradient(); - invalidate(); - } - - public void hideFrame(Object caller) { - fadeFrame(caller, false, 0f, KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_OUT_DURATION); - } - - public void showFrame(Object caller) { - fadeFrame(caller, true, OUTLINE_ALPHA_MULTIPLIER, - KeyguardWidgetPager.CHILDREN_OUTLINE_FADE_IN_DURATION); - } - - public void fadeFrame(Object caller, boolean takeControl, float alpha, int duration) { - if (takeControl) { - mBgAlphaController = caller; - } - - if (mBgAlphaController != caller && mBgAlphaController != null) { - return; - } - - if (mFrameFade != null) { - mFrameFade.cancel(); - mFrameFade = null; - } - PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", alpha); - mFrameFade = ObjectAnimator.ofPropertyValuesHolder(this, bgAlpha); - mFrameFade.setDuration(duration); - mFrameFade.start(); - } - - private void updateGradient() { - float x0 = mLeftToRight ? 0 : mForegroundRect.width(); - float x1 = mLeftToRight ? mForegroundRect.width(): 0; - mLeftToRightGradient = new LinearGradient(x0, 0f, x1, 0f, - mGradientColor, 0, Shader.TileMode.CLAMP); - mRightToLeftGradient = new LinearGradient(x1, 0f, x0, 0f, - mGradientColor, 0, Shader.TileMode.CLAMP); - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - if (!mIsSmall) { - mFrameHeight = h; - } - - // mFrameStrokeAdjustment is a cludge to prevent the overlay from drawing outside the - // rounded rect background. - mForegroundRect.set(mFrameStrokeAdjustment, mFrameStrokeAdjustment, - w - mFrameStrokeAdjustment, Math.min(h, mFrameHeight) - mFrameStrokeAdjustment); - - mBackgroundRect.set(0, 0, getMeasuredWidth(), Math.min(h, mFrameHeight)); - updateGradient(); - invalidate(); - } - - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - performAppWidgetSizeCallbacksIfNecessary(); - } - - private void performAppWidgetSizeCallbacksIfNecessary() { - View content = getContent(); - if (!(content instanceof AppWidgetHostView)) return; - - if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) { - mPerformAppWidgetSizeUpdateOnBootComplete = true; - return; - } - - // TODO: there's no reason to force the AppWidgetHostView to catch duplicate size calls. - // We can do that even more cheaply here. It's not an issue right now since we're in the - // system process and hence no binder calls. - AppWidgetHostView awhv = (AppWidgetHostView) content; - float density = getResources().getDisplayMetrics().density; - - int width = (int) (content.getMeasuredWidth() / density); - int height = (int) (content.getMeasuredHeight() / density); - awhv.updateAppWidgetSize(null, width, height, width, height, true); - } - - void setOverScrollAmount(float r, boolean left) { - if (Float.compare(mOverScrollAmount, r) != 0) { - mOverScrollAmount = r; - mForegroundGradient = left ? mLeftToRightGradient : mRightToLeftGradient; - mForegroundAlpha = (int) Math.round((0.5f * r * 255)); - - // We bump up the alpha of the outline to hide the fact that the overlay is drawing - // over the rounded part of the frame. - float bgAlpha = Math.min(OUTLINE_ALPHA_MULTIPLIER + r * (1 - OUTLINE_ALPHA_MULTIPLIER), - 1f); - setBackgroundAlpha(bgAlpha); - invalidate(); - } - } - - public void onActive(boolean isActive) { - // hook for subclasses - } - - public boolean onUserInteraction(MotionEvent event) { - // hook for subclasses - return false; - } - - public void onBouncerShowing(boolean showing) { - // hook for subclasses - } - - public void setWorkerHandler(Handler workerHandler) { - mWorkerHandler = workerHandler; - } - - public Handler getWorkerHandler() { - return mWorkerHandler; - } - -} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java deleted file mode 100644 index 6120127..0000000 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardWidgetPager.java +++ /dev/null @@ -1,967 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.animation.TimeInterpolator; -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.Context; -import android.os.Handler; -import android.os.HandlerThread; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.util.Slog; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnLongClickListener; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.DecelerateInterpolator; -import android.widget.FrameLayout; -import android.widget.TextClock; - -import com.android.internal.widget.LockPatternUtils; - -import java.util.ArrayList; -import java.util.TimeZone; - -public class KeyguardWidgetPager extends PagedView implements PagedView.PageSwitchListener, - OnLongClickListener, ChallengeLayout.OnBouncerStateChangedListener { - - ZInterpolator mZInterpolator = new ZInterpolator(0.5f); - private static float CAMERA_DISTANCE = 10000; - protected static float OVERSCROLL_MAX_ROTATION = 30; - private static final boolean PERFORM_OVERSCROLL_ROTATION = true; - - private static final int FLAG_HAS_LOCAL_HOUR = 0x1; - private static final int FLAG_HAS_LOCAL_MINUTE = 0x2; - - protected KeyguardViewStateManager mViewStateManager; - private LockPatternUtils mLockPatternUtils; - - // Related to the fading in / out background outlines - public static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; - public static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; - protected AnimatorSet mChildrenOutlineFadeAnimation; - protected int mScreenCenter; - private boolean mHasMeasure = false; - boolean showHintsAfterLayout = false; - - private static final long CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT = 30000; - private static final String TAG = "KeyguardWidgetPager"; - private boolean mCenterSmallWidgetsVertically; - - private int mPage = 0; - private Callbacks mCallbacks; - - private int mWidgetToResetAfterFadeOut; - protected boolean mShowingInitialHints = false; - - // A temporary handle to the Add-Widget view - private View mAddWidgetView; - private int mLastWidthMeasureSpec; - private int mLastHeightMeasureSpec; - - // Bouncer - private int mBouncerZoomInOutDuration = 250; - private float BOUNCER_SCALE_FACTOR = 0.67f; - - // Background worker thread: used here for persistence, also made available to widget frames - private final HandlerThread mBackgroundWorkerThread; - private final Handler mBackgroundWorkerHandler; - private boolean mCameraEventInProgress; - - public KeyguardWidgetPager(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public KeyguardWidgetPager(Context context) { - this(null, null, 0); - } - - public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - setPageSwitchListener(this); - - mBackgroundWorkerThread = new HandlerThread("KeyguardWidgetPager Worker"); - mBackgroundWorkerThread.start(); - mBackgroundWorkerHandler = new Handler(mBackgroundWorkerThread.getLooper()); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - // Clean up the worker thread - mBackgroundWorkerThread.quit(); - } - - public void setViewStateManager(KeyguardViewStateManager viewStateManager) { - mViewStateManager = viewStateManager; - } - - public void setLockPatternUtils(LockPatternUtils l) { - mLockPatternUtils = l; - } - - @Override - public void onPageSwitching(View newPage, int newPageIndex) { - if (mViewStateManager != null) { - mViewStateManager.onPageSwitching(newPage, newPageIndex); - } - } - - @Override - public void onPageSwitched(View newPage, int newPageIndex) { - boolean showingClock = false; - if (newPage instanceof ViewGroup) { - ViewGroup vg = (ViewGroup) newPage; - if (vg.getChildAt(0) instanceof KeyguardStatusView) { - showingClock = true; - } - } - - if (newPage != null && - findClockInHierarchy(newPage) == (FLAG_HAS_LOCAL_HOUR | FLAG_HAS_LOCAL_MINUTE)) { - showingClock = true; - } - - // Disable the status bar clock if we're showing the default status widget - if (showingClock) { - setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_CLOCK); - } else { - setSystemUiVisibility(getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK); - } - - // Extend the display timeout if the user switches pages - if (mPage != newPageIndex) { - int oldPageIndex = mPage; - mPage = newPageIndex; - userActivity(); - KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex); - if (oldWidgetPage != null) { - oldWidgetPage.onActive(false); - } - KeyguardWidgetFrame newWidgetPage = getWidgetPageAt(newPageIndex); - if (newWidgetPage != null) { - newWidgetPage.onActive(true); - newWidgetPage.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - newWidgetPage.requestAccessibilityFocus(); - } - if (mParent != null && AccessibilityManager.getInstance(mContext).isEnabled()) { - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_VIEW_SCROLLED); - onInitializeAccessibilityEvent(event); - onPopulateAccessibilityEvent(event); - mParent.requestSendAccessibilityEvent(this, event); - } - } - if (mViewStateManager != null) { - mViewStateManager.onPageSwitched(newPage, newPageIndex); - } - } - - @Override - public void onPageBeginWarp() { - showOutlinesAndSidePages(); - mViewStateManager.onPageBeginWarp(); - } - - @Override - public void onPageEndWarp() { - // if we're moving to the warp page, then immediately hide the other widgets. - int duration = getPageWarpIndex() == getNextPage() ? 0 : -1; - animateOutlinesAndSidePages(false, duration); - mViewStateManager.onPageEndWarp(); - } - - @Override - public void sendAccessibilityEvent(int eventType) { - if (eventType != AccessibilityEvent.TYPE_VIEW_SCROLLED || isPageMoving()) { - super.sendAccessibilityEvent(eventType); - } - } - - private void updateWidgetFramesImportantForAccessibility() { - final int pageCount = getPageCount(); - for (int i = 0; i < pageCount; i++) { - KeyguardWidgetFrame frame = getWidgetPageAt(i); - updateWidgetFrameImportantForAccessibility(frame); - } - } - - private void updateWidgetFrameImportantForAccessibility(KeyguardWidgetFrame frame) { - if (frame.getContentAlpha() <= 0) { - frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } else { - frame.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - } - - private void userActivity() { - if (mCallbacks != null) { - mCallbacks.onUserActivityTimeoutChanged(); - mCallbacks.userActivity(); - } - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return captureUserInteraction(ev) || super.onTouchEvent(ev); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return captureUserInteraction(ev) || super.onInterceptTouchEvent(ev); - } - - private boolean captureUserInteraction(MotionEvent ev) { - KeyguardWidgetFrame currentWidgetPage = getWidgetPageAt(getCurrentPage()); - return currentWidgetPage != null && currentWidgetPage.onUserInteraction(ev); - } - - public void showPagingFeedback() { - // Nothing yet. - } - - public long getUserActivityTimeout() { - View page = getPageAt(mPage); - if (page instanceof ViewGroup) { - ViewGroup vg = (ViewGroup) page; - View view = vg.getChildAt(0); - if (!(view instanceof KeyguardStatusView) - && !(view instanceof KeyguardMultiUserSelectorView)) { - return CUSTOM_WIDGET_USER_ACTIVITY_TIMEOUT; - } - } - return -1; - } - - public void setCallbacks(Callbacks callbacks) { - mCallbacks = callbacks; - } - - public interface Callbacks { - public void userActivity(); - public void onUserActivityTimeoutChanged(); - public void onAddView(View v); - public void onRemoveView(View v, boolean deletePermanently); - public void onRemoveViewAnimationCompleted(); - } - - public void addWidget(View widget) { - addWidget(widget, -1); - } - - public void onRemoveView(View v, final boolean deletePermanently) { - final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); - if (mCallbacks != null) { - mCallbacks.onRemoveView(v, deletePermanently); - } - mBackgroundWorkerHandler.post(new Runnable() { - @Override - public void run() { - mLockPatternUtils.removeAppWidget(appWidgetId); - } - }); - } - - @Override - public void onRemoveViewAnimationCompleted() { - if (mCallbacks != null) { - mCallbacks.onRemoveViewAnimationCompleted(); - } - } - - public void onAddView(View v, final int index) { - final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId(); - final int[] pagesRange = new int[mTempVisiblePagesRange.length]; - getVisiblePages(pagesRange); - boundByReorderablePages(true, pagesRange); - if (mCallbacks != null) { - mCallbacks.onAddView(v); - } - // Subtract from the index to take into account pages before the reorderable - // pages (e.g. the "add widget" page) - mBackgroundWorkerHandler.post(new Runnable() { - @Override - public void run() { - mLockPatternUtils.addAppWidget(appWidgetId, index - pagesRange[0]); - } - }); - } - - /* - * We wrap widgets in a special frame which handles drawing the over scroll foreground. - */ - public void addWidget(View widget, int pageIndex) { - KeyguardWidgetFrame frame; - // All views contained herein should be wrapped in a KeyguardWidgetFrame - if (!(widget instanceof KeyguardWidgetFrame)) { - frame = new KeyguardWidgetFrame(getContext()); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - lp.gravity = Gravity.TOP; - - // The framework adds a default padding to AppWidgetHostView. We don't need this padding - // for the Keyguard, so we override it to be 0. - widget.setPadding(0, 0, 0, 0); - frame.addView(widget, lp); - - // We set whether or not this widget supports vertical resizing. - if (widget instanceof AppWidgetHostView) { - AppWidgetHostView awhv = (AppWidgetHostView) widget; - AppWidgetProviderInfo info = awhv.getAppWidgetInfo(); - if ((info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) { - frame.setWidgetLockedSmall(false); - } else { - // Lock the widget to be small. - frame.setWidgetLockedSmall(true); - if (mCenterSmallWidgetsVertically) { - lp.gravity = Gravity.CENTER; - } - } - } - } else { - frame = (KeyguardWidgetFrame) widget; - } - - ViewGroup.LayoutParams pageLp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - frame.setOnLongClickListener(this); - frame.setWorkerHandler(mBackgroundWorkerHandler); - - if (pageIndex == -1) { - addView(frame, pageLp); - } else { - addView(frame, pageIndex, pageLp); - } - - // Update the frame content description. - View content = (widget == frame) ? frame.getContent() : widget; - if (content != null) { - String contentDescription = mContext.getString( - R.string.keyguard_accessibility_widget, - content.getContentDescription()); - frame.setContentDescription(contentDescription); - } - updateWidgetFrameImportantForAccessibility(frame); - } - - /** - * Use addWidget() instead. - * @deprecated - */ - @Override - public void addView(View child, int index) { - enforceKeyguardWidgetFrame(child); - super.addView(child, index); - } - - /** - * Use addWidget() instead. - * @deprecated - */ - @Override - public void addView(View child, int width, int height) { - enforceKeyguardWidgetFrame(child); - super.addView(child, width, height); - } - - /** - * Use addWidget() instead. - * @deprecated - */ - @Override - public void addView(View child, LayoutParams params) { - enforceKeyguardWidgetFrame(child); - super.addView(child, params); - } - - /** - * Use addWidget() instead. - * @deprecated - */ - @Override - public void addView(View child, int index, LayoutParams params) { - enforceKeyguardWidgetFrame(child); - super.addView(child, index, params); - } - - private void enforceKeyguardWidgetFrame(View child) { - if (!(child instanceof KeyguardWidgetFrame)) { - throw new IllegalArgumentException( - "KeyguardWidgetPager children must be KeyguardWidgetFrames"); - } - } - - public KeyguardWidgetFrame getWidgetPageAt(int index) { - // This is always a valid cast as we've guarded the ability to - return (KeyguardWidgetFrame) getChildAt(index); - } - - protected void onUnhandledTap(MotionEvent ev) { - showPagingFeedback(); - } - - @Override - protected void onPageBeginMoving() { - if (mViewStateManager != null) { - mViewStateManager.onPageBeginMoving(); - } - if (!isReordering(false)) { - showOutlinesAndSidePages(); - } - userActivity(); - } - - @Override - protected void onPageEndMoving() { - if (mViewStateManager != null) { - mViewStateManager.onPageEndMoving(); - } - - // In the reordering case, the pages will be faded appropriately on completion - // of the zoom in animation. - if (!isReordering(false)) { - hideOutlinesAndSidePages(); - } - } - - protected void enablePageContentLayers() { - int children = getChildCount(); - for (int i = 0; i < children; i++) { - getWidgetPageAt(i).enableHardwareLayersForContent(); - } - } - - protected void disablePageContentLayers() { - int children = getChildCount(); - for (int i = 0; i < children; i++) { - getWidgetPageAt(i).disableHardwareLayersForContent(); - } - } - - /* - * This interpolator emulates the rate at which the perceived scale of an object changes - * as its distance from a camera increases. When this interpolator is applied to a scale - * animation on a view, it evokes the sense that the object is shrinking due to moving away - * from the camera. - */ - static class ZInterpolator implements TimeInterpolator { - private float focalLength; - - public ZInterpolator(float foc) { - focalLength = foc; - } - - public float getInterpolation(float input) { - return (1.0f - focalLength / (focalLength + input)) / - (1.0f - focalLength / (focalLength + 1.0f)); - } - } - - @Override - protected void overScroll(float amount) { - acceleratedOverScroll(amount); - } - - float backgroundAlphaInterpolator(float r) { - return Math.min(1f, r); - } - - private void updatePageAlphaValues(int screenCenter) { - } - - public float getAlphaForPage(int screenCenter, int index, boolean showSidePages) { - if (isWarping()) { - return index == getPageWarpIndex() ? 1.0f : 0.0f; - } - if (showSidePages) { - return 1f; - } else { - return index == mCurrentPage ? 1.0f : 0f; - } - } - - public float getOutlineAlphaForPage(int screenCenter, int index, boolean showSidePages) { - if (showSidePages) { - return getAlphaForPage(screenCenter, index, showSidePages) - * KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER; - } else { - return 0f; - } - } - - protected boolean isOverScrollChild(int index, float scrollProgress) { - boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX; - return (isInOverscroll && (index == 0 && scrollProgress < 0 || - index == getChildCount() - 1 && scrollProgress > 0)); - } - - @Override - protected void screenScrolled(int screenCenter) { - mScreenCenter = screenCenter; - updatePageAlphaValues(screenCenter); - for (int i = 0; i < getChildCount(); i++) { - KeyguardWidgetFrame v = getWidgetPageAt(i); - if (v == mDragView) continue; - if (v != null) { - float scrollProgress = getScrollProgress(screenCenter, v, i); - - v.setCameraDistance(mDensity * CAMERA_DISTANCE); - - if (isOverScrollChild(i, scrollProgress) && PERFORM_OVERSCROLL_ROTATION) { - float pivotX = v.getMeasuredWidth() / 2; - float pivotY = v.getMeasuredHeight() / 2; - v.setPivotX(pivotX); - v.setPivotY(pivotY); - v.setRotationY(- OVERSCROLL_MAX_ROTATION * scrollProgress); - v.setOverScrollAmount(Math.abs(scrollProgress), scrollProgress < 0); - } else { - v.setRotationY(0f); - v.setOverScrollAmount(0, false); - } - - float alpha = v.getAlpha(); - // If the view has 0 alpha, we set it to be invisible so as to prevent - // it from accepting touches - if (alpha == 0) { - v.setVisibility(INVISIBLE); - } else if (v.getVisibility() != VISIBLE) { - v.setVisibility(VISIBLE); - } - } - } - } - - public boolean isWidgetPage(int pageIndex) { - if (pageIndex < 0 || pageIndex >= getChildCount()) { - return false; - } - View v = getChildAt(pageIndex); - if (v != null && v instanceof KeyguardWidgetFrame) { - KeyguardWidgetFrame kwf = (KeyguardWidgetFrame) v; - return kwf.getContentAppWidgetId() != AppWidgetManager.INVALID_APPWIDGET_ID; - } - return false; - } - - /** - * Returns the bounded set of pages that are re-orderable. The range is fully inclusive. - */ - @Override - void boundByReorderablePages(boolean isReordering, int[] range) { - if (isReordering) { - // Remove non-widget pages from the range - while (range[1] >= range[0] && !isWidgetPage(range[1])) { - range[1]--; - } - while (range[0] <= range[1] && !isWidgetPage(range[0])) { - range[0]++; - } - } - } - - protected void reorderStarting() { - showOutlinesAndSidePages(); - } - - @Override - protected void onStartReordering() { - super.onStartReordering(); - enablePageContentLayers(); - reorderStarting(); - } - - @Override - protected void onEndReordering() { - super.onEndReordering(); - hideOutlinesAndSidePages(); - } - - void showOutlinesAndSidePages() { - animateOutlinesAndSidePages(true); - } - - void hideOutlinesAndSidePages() { - animateOutlinesAndSidePages(false); - } - - void updateChildrenContentAlpha(float sidePageAlpha) { - int count = getChildCount(); - for (int i = 0; i < count; i++) { - KeyguardWidgetFrame child = getWidgetPageAt(i); - if (i != mCurrentPage) { - child.setBackgroundAlpha(sidePageAlpha); - child.setContentAlpha(0f); - } else { - child.setBackgroundAlpha(0f); - child.setContentAlpha(1f); - } - } - } - - public void showInitialPageHints() { - mShowingInitialHints = true; - updateChildrenContentAlpha(KeyguardWidgetFrame.OUTLINE_ALPHA_MULTIPLIER); - } - - @Override - void setCurrentPage(int currentPage) { - super.setCurrentPage(currentPage); - updateChildrenContentAlpha(0.0f); - updateWidgetFramesImportantForAccessibility(); - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mHasMeasure = false; - } - - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - mLastWidthMeasureSpec = widthMeasureSpec; - mLastHeightMeasureSpec = heightMeasureSpec; - - int maxChallengeTop = -1; - View parent = (View) getParent(); - boolean challengeShowing = false; - // Widget pages need to know where the top of the sliding challenge is so that they - // now how big the widget should be when the challenge is up. We compute it here and - // then propagate it to each of our children. - if (parent.getParent() instanceof SlidingChallengeLayout) { - SlidingChallengeLayout scl = (SlidingChallengeLayout) parent.getParent(); - int top = scl.getMaxChallengeTop(); - - // This is a bit evil, but we need to map a coordinate relative to the SCL into a - // coordinate relative to our children, hence we subtract the top padding.s - maxChallengeTop = top - getPaddingTop(); - challengeShowing = scl.isChallengeShowing(); - - int count = getChildCount(); - for (int i = 0; i < count; i++) { - KeyguardWidgetFrame frame = getWidgetPageAt(i); - frame.setMaxChallengeTop(maxChallengeTop); - // On the very first measure pass, if the challenge is showing, we need to make sure - // that the widget on the current page is small. - if (challengeShowing && i == mCurrentPage && !mHasMeasure) { - frame.shrinkWidget(true); - } - } - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - mHasMeasure = true; - } - - void animateOutlinesAndSidePages(final boolean show) { - animateOutlinesAndSidePages(show, -1); - } - - public void setWidgetToResetOnPageFadeOut(int widget) { - mWidgetToResetAfterFadeOut = widget; - } - - public int getWidgetToResetOnPageFadeOut() { - return mWidgetToResetAfterFadeOut; - } - - void animateOutlinesAndSidePages(final boolean show, int duration) { - if (mChildrenOutlineFadeAnimation != null) { - mChildrenOutlineFadeAnimation.cancel(); - mChildrenOutlineFadeAnimation = null; - } - int count = getChildCount(); - PropertyValuesHolder alpha; - ArrayList<Animator> anims = new ArrayList<Animator>(); - - if (duration == -1) { - duration = show ? CHILDREN_OUTLINE_FADE_IN_DURATION : - CHILDREN_OUTLINE_FADE_OUT_DURATION; - } - - int curPage = getNextPage(); - for (int i = 0; i < count; i++) { - float finalContentAlpha; - if (show) { - finalContentAlpha = getAlphaForPage(mScreenCenter, i, true); - } else if (!show && i == curPage) { - finalContentAlpha = 1f; - } else { - finalContentAlpha = 0f; - } - KeyguardWidgetFrame child = getWidgetPageAt(i); - - alpha = PropertyValuesHolder.ofFloat("contentAlpha", finalContentAlpha); - ObjectAnimator a = ObjectAnimator.ofPropertyValuesHolder(child, alpha); - anims.add(a); - - float finalOutlineAlpha = show ? getOutlineAlphaForPage(mScreenCenter, i, true) : 0f; - child.fadeFrame(this, show, finalOutlineAlpha, duration); - } - - mChildrenOutlineFadeAnimation = new AnimatorSet(); - mChildrenOutlineFadeAnimation.playTogether(anims); - - mChildrenOutlineFadeAnimation.setDuration(duration); - mChildrenOutlineFadeAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (show) { - enablePageContentLayers(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (!show) { - disablePageContentLayers(); - KeyguardWidgetFrame frame = getWidgetPageAt(mWidgetToResetAfterFadeOut); - if (frame != null && !(frame == getWidgetPageAt(mCurrentPage) && - mViewStateManager.isChallengeOverlapping())) { - frame.resetSize(); - } - mWidgetToResetAfterFadeOut = -1; - mShowingInitialHints = false; - } - updateWidgetFramesImportantForAccessibility(); - } - }); - mChildrenOutlineFadeAnimation.start(); - } - - @Override - public boolean onLongClick(View v) { - // Disallow long pressing to reorder if the challenge is showing - boolean isChallengeOverlapping = mViewStateManager.isChallengeShowing() && - mViewStateManager.isChallengeOverlapping(); - if (!isChallengeOverlapping && startReordering()) { - return true; - } - return false; - } - - public void removeWidget(View view) { - if (view instanceof KeyguardWidgetFrame) { - removeView(view); - } else { - // Assume view was wrapped by a KeyguardWidgetFrame in KeyguardWidgetPager#addWidget(). - // This supports legacy hard-coded "widgets" like KeyguardTransportControlView. - int pos = getWidgetPageIndex(view); - if (pos != -1) { - KeyguardWidgetFrame frame = (KeyguardWidgetFrame) getChildAt(pos); - frame.removeView(view); - removeView(frame); - } else { - Slog.w(TAG, "removeWidget() can't find:" + view); - } - } - } - - public int getWidgetPageIndex(View view) { - if (view instanceof KeyguardWidgetFrame) { - return indexOfChild(view); - } else { - // View was wrapped by a KeyguardWidgetFrame by KeyguardWidgetPager#addWidget() - return indexOfChild((KeyguardWidgetFrame)view.getParent()); - } - } - - @Override - protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) { - KeyguardWidgetFrame child = getWidgetPageAt(viewIndex); - child.setIsHoveringOverDeleteDropTarget(isHovering); - } - - // ChallengeLayout.OnBouncerStateChangedListener - @Override - public void onBouncerStateChanged(boolean bouncerActive) { - if (bouncerActive) { - zoomOutToBouncer(); - } else { - zoomInFromBouncer(); - } - } - - void setBouncerAnimationDuration(int duration) { - mBouncerZoomInOutDuration = duration; - } - - // Zoom in after the bouncer is dismissed - void zoomInFromBouncer() { - if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { - mZoomInOutAnim.cancel(); - } - final View currentPage = getPageAt(getCurrentPage()); - if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) { - mZoomInOutAnim = new AnimatorSet(); - mZoomInOutAnim.playTogether( - ObjectAnimator.ofFloat(currentPage, "scaleX", 1f), - ObjectAnimator.ofFloat(currentPage , "scaleY", 1f)); - mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration); - mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); - mZoomInOutAnim.start(); - } - if (currentPage instanceof KeyguardWidgetFrame) { - ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false); - } - } - - // Zoom out after the bouncer is initiated - void zoomOutToBouncer() { - if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { - mZoomInOutAnim.cancel(); - } - int curPage = getCurrentPage(); - View currentPage = getPageAt(curPage); - if (shouldSetTopAlignedPivotForWidget(curPage)) { - currentPage.setPivotY(0); - // Note: we are working around the issue that setting the x-pivot to the same value as it - // was does not actually work. - currentPage.setPivotX(0); - currentPage.setPivotX(currentPage.getMeasuredWidth() / 2); - } - if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) { - mZoomInOutAnim = new AnimatorSet(); - mZoomInOutAnim.playTogether( - ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR), - ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR)); - mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration); - mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f)); - mZoomInOutAnim.start(); - } - if (currentPage instanceof KeyguardWidgetFrame) { - ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true); - } - } - - void setAddWidgetEnabled(boolean enabled) { - if (mAddWidgetView != null && enabled) { - addView(mAddWidgetView, 0); - // We need to force measure the PagedView so that the calls to update the scroll - // position below work - measure(mLastWidthMeasureSpec, mLastHeightMeasureSpec); - // Bump up the current page to account for the addition of the new page - setCurrentPage(mCurrentPage + 1); - mAddWidgetView = null; - } else if (mAddWidgetView == null && !enabled) { - View addWidget = findViewById(R.id.keyguard_add_widget); - if (addWidget != null) { - mAddWidgetView = addWidget; - removeView(addWidget); - } - } - } - - boolean isAddPage(int pageIndex) { - View v = getChildAt(pageIndex); - return v != null && v.getId() == R.id.keyguard_add_widget; - } - - boolean isCameraPage(int pageIndex) { - View v = getChildAt(pageIndex); - return v != null && v instanceof CameraWidgetFrame; - } - - @Override - protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) { - return !isCameraPage(childIndex) && super.shouldSetTopAlignedPivotForWidget(childIndex); - } - - /** - * Search given {@link View} hierarchy for {@link TextClock} instances that - * show various time components. Returns combination of - * {@link #FLAG_HAS_LOCAL_HOUR} and {@link #FLAG_HAS_LOCAL_MINUTE}. - */ - private static int findClockInHierarchy(View view) { - if (view instanceof TextClock) { - return getClockFlags((TextClock) view); - } else if (view instanceof ViewGroup) { - int flags = 0; - final ViewGroup group = (ViewGroup) view; - final int size = group.getChildCount(); - for (int i = 0; i < size; i++) { - flags |= findClockInHierarchy(group.getChildAt(i)); - } - return flags; - } else { - return 0; - } - } - - /** - * Return combination of {@link #FLAG_HAS_LOCAL_HOUR} and - * {@link #FLAG_HAS_LOCAL_MINUTE} describing the time represented described - * by the given {@link TextClock}. - */ - private static int getClockFlags(TextClock clock) { - int flags = 0; - - final String timeZone = clock.getTimeZone(); - if (timeZone != null && !TimeZone.getDefault().equals(TimeZone.getTimeZone(timeZone))) { - // Ignore clocks showing another timezone - return 0; - } - - final CharSequence format = clock.getFormat(); - final char hour = clock.is24HourModeEnabled() ? DateFormat.HOUR_OF_DAY - : DateFormat.HOUR; - - if (DateFormat.hasDesignator(format, hour)) { - flags |= FLAG_HAS_LOCAL_HOUR; - } - if (DateFormat.hasDesignator(format, DateFormat.MINUTE)) { - flags |= FLAG_HAS_LOCAL_MINUTE; - } - - return flags; - } - - public void handleExternalCameraEvent(MotionEvent event) { - beginCameraEvent(); - int cameraPage = getPageCount() - 1; - boolean endWarp = false; - if (isCameraPage(cameraPage) || mCameraEventInProgress) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - // Once we start dispatching camera events, we must continue to do so - // to keep event dispatch happy. - mCameraEventInProgress = true; - userActivity(); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mCameraEventInProgress = false; - break; - } - dispatchTouchEvent(event); - } - endCameraEvent(); - } - -} diff --git a/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java deleted file mode 100644 index 340a4d5..0000000 --- a/packages/Keyguard/src/com/android/keyguard/MultiPaneChallengeLayout.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -public class MultiPaneChallengeLayout extends ViewGroup implements ChallengeLayout { - private static final String TAG = "MultiPaneChallengeLayout"; - - final int mOrientation; - private boolean mIsBouncing; - - public static final int HORIZONTAL = LinearLayout.HORIZONTAL; - public static final int VERTICAL = LinearLayout.VERTICAL; - public static final int ANIMATE_BOUNCE_DURATION = 350; - - private KeyguardSecurityContainer mChallengeView; - private View mUserSwitcherView; - private View mScrimView; - private OnBouncerStateChangedListener mBouncerListener; - - private final Rect mTempRect = new Rect(); - private final Rect mZeroPadding = new Rect(); - private final Rect mInsets = new Rect(); - - private final DisplayMetrics mDisplayMetrics; - - private final OnClickListener mScrimClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - hideBouncer(); - } - }; - - public MultiPaneChallengeLayout(Context context) { - this(context, null); - } - - public MultiPaneChallengeLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MultiPaneChallengeLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.MultiPaneChallengeLayout, defStyleAttr, 0); - mOrientation = a.getInt(R.styleable.MultiPaneChallengeLayout_android_orientation, - HORIZONTAL); - a.recycle(); - - final Resources res = getResources(); - mDisplayMetrics = res.getDisplayMetrics(); - - setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } - - public void setInsets(Rect insets) { - mInsets.set(insets); - } - - @Override - public boolean isChallengeShowing() { - return true; - } - - @Override - public boolean isChallengeOverlapping() { - return false; - } - - @Override - public void showChallenge(boolean b) { - } - - @Override - public int getBouncerAnimationDuration() { - return ANIMATE_BOUNCE_DURATION; - } - - @Override - public void showBouncer() { - if (mIsBouncing) return; - mIsBouncing = true; - if (mScrimView != null) { - if (mChallengeView != null) { - mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION); - } - - Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f); - anim.setDuration(ANIMATE_BOUNCE_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mScrimView.setVisibility(VISIBLE); - } - }); - anim.start(); - } - if (mBouncerListener != null) { - mBouncerListener.onBouncerStateChanged(true); - } - } - - @Override - public void hideBouncer() { - if (!mIsBouncing) return; - mIsBouncing = false; - if (mScrimView != null) { - if (mChallengeView != null) { - mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION); - } - - Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f); - anim.setDuration(ANIMATE_BOUNCE_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mScrimView.setVisibility(INVISIBLE); - } - }); - anim.start(); - } - if (mBouncerListener != null) { - mBouncerListener.onBouncerStateChanged(false); - } - } - - @Override - public boolean isBouncing() { - return mIsBouncing; - } - - @Override - public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) { - mBouncerListener = listener; - } - - @Override - public void requestChildFocus(View child, View focused) { - if (mIsBouncing && child != mChallengeView) { - // Clear out of the bouncer if the user tries to move focus outside of - // the security challenge view. - hideBouncer(); - } - super.requestChildFocus(child, focused); - } - - void setScrimView(View scrim) { - if (mScrimView != null) { - mScrimView.setOnClickListener(null); - } - mScrimView = scrim; - if (mScrimView != null) { - mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f); - mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE); - mScrimView.setFocusable(true); - mScrimView.setOnClickListener(mScrimClickListener); - } - } - - private int getVirtualHeight(LayoutParams lp, int height, int heightUsed) { - int virtualHeight = height; - final View root = getRootView(); - if (root != null) { - // This calculation is super dodgy and relies on several assumptions. - // Specifically that the root of the window will be padded in for insets - // and that the window is LAYOUT_IN_SCREEN. - virtualHeight = mDisplayMetrics.heightPixels - root.getPaddingTop() - mInsets.top; - } - if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) { - // Always measure the user switcher as if there were no IME insets - // on the window. - return virtualHeight - heightUsed; - } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) { - return height; - } - return Math.min(virtualHeight - heightUsed, height); - } - - @Override - protected void onMeasure(final int widthSpec, final int heightSpec) { - if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY || - MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) { - throw new IllegalArgumentException( - "MultiPaneChallengeLayout must be measured with an exact size"); - } - - final int width = MeasureSpec.getSize(widthSpec); - final int height = MeasureSpec.getSize(heightSpec); - setMeasuredDimension(width, height); - - final int insetHeight = height - mInsets.top - mInsets.bottom; - final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY); - - int widthUsed = 0; - int heightUsed = 0; - - // First pass. Find the challenge view and measure the user switcher, - // which consumes space in the layout. - mChallengeView = null; - mUserSwitcherView = null; - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) { - if (mChallengeView != null) { - throw new IllegalStateException( - "There may only be one child of type challenge"); - } - if (!(child instanceof KeyguardSecurityContainer)) { - throw new IllegalArgumentException( - "Challenge must be a KeyguardSecurityContainer"); - } - mChallengeView = (KeyguardSecurityContainer) child; - } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) { - if (mUserSwitcherView != null) { - throw new IllegalStateException( - "There may only be one child of type userSwitcher"); - } - mUserSwitcherView = child; - - if (child.getVisibility() == GONE) continue; - - int adjustedWidthSpec = widthSpec; - int adjustedHeightSpec = insetHeightSpec; - if (lp.maxWidth >= 0) { - adjustedWidthSpec = MeasureSpec.makeMeasureSpec( - Math.min(lp.maxWidth, width), MeasureSpec.EXACTLY); - } - if (lp.maxHeight >= 0) { - adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - Math.min(lp.maxHeight, insetHeight), MeasureSpec.EXACTLY); - } - // measureChildWithMargins will resolve layout direction for the LayoutParams - measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0); - - // Only subtract out space from one dimension. Favor vertical. - // Offset by 1.5x to add some balance along the other edge. - if (Gravity.isVertical(lp.gravity)) { - heightUsed += child.getMeasuredHeight() * 1.5f; - } else if (Gravity.isHorizontal(lp.gravity)) { - widthUsed += child.getMeasuredWidth() * 1.5f; - } - } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { - setScrimView(child); - child.measure(widthSpec, heightSpec); - } - } - - // Second pass. Measure everything that's left. - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER || - lp.childType == LayoutParams.CHILD_TYPE_SCRIM || - child.getVisibility() == GONE) { - // Don't need to measure GONE children, and the user switcher was already measured. - continue; - } - - final int virtualHeight = getVirtualHeight(lp, insetHeight, heightUsed); - - int adjustedWidthSpec; - int adjustedHeightSpec; - if (lp.centerWithinArea > 0) { - if (mOrientation == HORIZONTAL) { - adjustedWidthSpec = MeasureSpec.makeMeasureSpec( - (int) ((width - widthUsed) * lp.centerWithinArea + 0.5f), - MeasureSpec.EXACTLY); - adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - virtualHeight, MeasureSpec.EXACTLY); - } else { - adjustedWidthSpec = MeasureSpec.makeMeasureSpec( - width - widthUsed, MeasureSpec.EXACTLY); - adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - (int) (virtualHeight * lp.centerWithinArea + 0.5f), - MeasureSpec.EXACTLY); - } - } else { - adjustedWidthSpec = MeasureSpec.makeMeasureSpec( - width - widthUsed, MeasureSpec.EXACTLY); - adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - virtualHeight, MeasureSpec.EXACTLY); - } - if (lp.maxWidth >= 0) { - adjustedWidthSpec = MeasureSpec.makeMeasureSpec( - Math.min(lp.maxWidth, MeasureSpec.getSize(adjustedWidthSpec)), - MeasureSpec.EXACTLY); - } - if (lp.maxHeight >= 0) { - adjustedHeightSpec = MeasureSpec.makeMeasureSpec( - Math.min(lp.maxHeight, MeasureSpec.getSize(adjustedHeightSpec)), - MeasureSpec.EXACTLY); - } - - measureChildWithMargins(child, adjustedWidthSpec, 0, adjustedHeightSpec, 0); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final Rect padding = mTempRect; - padding.left = getPaddingLeft(); - padding.top = getPaddingTop(); - padding.right = getPaddingRight(); - padding.bottom = getPaddingBottom(); - final int width = r - l; - final int height = b - t; - final int insetHeight = height - mInsets.top - mInsets.bottom; - - // Reserve extra space in layout for the user switcher by modifying - // local padding during this layout pass - if (mUserSwitcherView != null && mUserSwitcherView.getVisibility() != GONE) { - layoutWithGravity(width, insetHeight, mUserSwitcherView, padding, true); - } - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - // We did the user switcher above if we have one. - if (child == mUserSwitcherView || child.getVisibility() == GONE) continue; - - if (child == mScrimView) { - child.layout(0, 0, width, height); - continue; - } else if (lp.childType == LayoutParams.CHILD_TYPE_PAGE_DELETE_DROP_TARGET) { - layoutWithGravity(width, insetHeight, child, mZeroPadding, false); - continue; - } - - layoutWithGravity(width, insetHeight, child, padding, false); - } - } - - private void layoutWithGravity(int width, int height, View child, Rect padding, - boolean adjustPadding) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - final int heightUsed = padding.top + padding.bottom - getPaddingTop() - getPaddingBottom(); - height = getVirtualHeight(lp, height, heightUsed); - - final int gravity = Gravity.getAbsoluteGravity(lp.gravity, getLayoutDirection()); - - final boolean fixedLayoutSize = lp.centerWithinArea > 0; - final boolean fixedLayoutHorizontal = fixedLayoutSize && mOrientation == HORIZONTAL; - final boolean fixedLayoutVertical = fixedLayoutSize && mOrientation == VERTICAL; - - final int adjustedWidth; - final int adjustedHeight; - if (fixedLayoutHorizontal) { - final int paddedWidth = width - padding.left - padding.right; - adjustedWidth = (int) (paddedWidth * lp.centerWithinArea + 0.5f); - adjustedHeight = height; - } else if (fixedLayoutVertical) { - final int paddedHeight = height - getPaddingTop() - getPaddingBottom(); - adjustedWidth = width; - adjustedHeight = (int) (paddedHeight * lp.centerWithinArea + 0.5f); - } else { - adjustedWidth = width; - adjustedHeight = height; - } - - final boolean isVertical = Gravity.isVertical(gravity); - final boolean isHorizontal = Gravity.isHorizontal(gravity); - final int childWidth = child.getMeasuredWidth(); - final int childHeight = child.getMeasuredHeight(); - - int left = padding.left; - int top = padding.top; - int right = left + childWidth; - int bottom = top + childHeight; - switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - top = fixedLayoutVertical ? - padding.top + (adjustedHeight - childHeight) / 2 : padding.top; - bottom = top + childHeight; - if (adjustPadding && isVertical) { - padding.top = bottom; - padding.bottom += childHeight / 2; - } - break; - case Gravity.BOTTOM: - bottom = fixedLayoutVertical - ? padding.top + height - (adjustedHeight - childHeight) / 2 - : padding.top + height; - top = bottom - childHeight; - if (adjustPadding && isVertical) { - padding.bottom = height - top; - padding.top += childHeight / 2; - } - break; - case Gravity.CENTER_VERTICAL: - top = padding.top + (height - childHeight) / 2; - bottom = top + childHeight; - break; - } - switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.LEFT: - left = fixedLayoutHorizontal ? - padding.left + (adjustedWidth - childWidth) / 2 : padding.left; - right = left + childWidth; - if (adjustPadding && isHorizontal && !isVertical) { - padding.left = right; - padding.right += childWidth / 2; - } - break; - case Gravity.RIGHT: - right = fixedLayoutHorizontal - ? width - padding.right - (adjustedWidth - childWidth) / 2 - : width - padding.right; - left = right - childWidth; - if (adjustPadding && isHorizontal && !isVertical) { - padding.right = width - left; - padding.left += childWidth / 2; - } - break; - case Gravity.CENTER_HORIZONTAL: - final int paddedWidth = width - padding.left - padding.right; - left = (paddedWidth - childWidth) / 2; - right = left + childWidth; - break; - } - top += mInsets.top; - bottom += mInsets.top; - child.layout(left, top, right, bottom); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs, this); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : - p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) : - new LayoutParams(p); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - public static class LayoutParams extends MarginLayoutParams { - - public float centerWithinArea = 0; - - public int childType = 0; - - public static final int CHILD_TYPE_NONE = 0; - public static final int CHILD_TYPE_WIDGET = 1; - public static final int CHILD_TYPE_CHALLENGE = 2; - public static final int CHILD_TYPE_USER_SWITCHER = 3; - public static final int CHILD_TYPE_SCRIM = 4; - public static final int CHILD_TYPE_PAGE_DELETE_DROP_TARGET = 7; - - public int gravity = Gravity.NO_GRAVITY; - - public int maxWidth = -1; - public int maxHeight = -1; - - public LayoutParams() { - this(WRAP_CONTENT, WRAP_CONTENT); - } - - LayoutParams(Context c, AttributeSet attrs, MultiPaneChallengeLayout parent) { - super(c, attrs); - - final TypedArray a = c.obtainStyledAttributes(attrs, - R.styleable.MultiPaneChallengeLayout_Layout); - - centerWithinArea = a.getFloat( - R.styleable.MultiPaneChallengeLayout_Layout_layout_centerWithinArea, 0); - childType = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_childType, - CHILD_TYPE_NONE); - gravity = a.getInt(R.styleable.MultiPaneChallengeLayout_Layout_layout_gravity, - Gravity.NO_GRAVITY); - maxWidth = a.getDimensionPixelSize( - R.styleable.MultiPaneChallengeLayout_Layout_layout_maxWidth, -1); - maxHeight = a.getDimensionPixelSize( - R.styleable.MultiPaneChallengeLayout_Layout_layout_maxHeight, -1); - - // Default gravity settings based on type and parent orientation - if (gravity == Gravity.NO_GRAVITY) { - if (parent.mOrientation == HORIZONTAL) { - switch (childType) { - case CHILD_TYPE_WIDGET: - gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; - break; - case CHILD_TYPE_CHALLENGE: - gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; - break; - case CHILD_TYPE_USER_SWITCHER: - gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - break; - } - } else { - switch (childType) { - case CHILD_TYPE_WIDGET: - gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; - break; - case CHILD_TYPE_CHALLENGE: - gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - break; - case CHILD_TYPE_USER_SWITCHER: - gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - break; - } - } - } - - a.recycle(); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(ViewGroup.LayoutParams source) { - super(source); - } - - public LayoutParams(MarginLayoutParams source) { - super(source); - } - - public LayoutParams(LayoutParams source) { - this((MarginLayoutParams) source); - - centerWithinArea = source.centerWithinArea; - childType = source.childType; - gravity = source.gravity; - maxWidth = source.maxWidth; - maxHeight = source.maxHeight; - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java b/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java deleted file mode 100644 index 7128211..0000000 --- a/packages/Keyguard/src/com/android/keyguard/MultiUserAvatarCache.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2013 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.keyguard; - -import android.graphics.drawable.Drawable; - -import java.util.HashMap; - -public class MultiUserAvatarCache { - - private static MultiUserAvatarCache sInstance; - - private final HashMap<Integer, Drawable> mCache; - - private MultiUserAvatarCache() { - mCache = new HashMap<Integer, Drawable>(); - } - - public static MultiUserAvatarCache getInstance() { - if (sInstance == null) { - sInstance = new MultiUserAvatarCache(); - } - return sInstance; - } - - public void clear(int userId) { - mCache.remove(userId); - } - - public Drawable get(int userId) { - return mCache.get(userId); - } - - public void put(int userId, Drawable image) { - mCache.put(userId, image); - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java index 681db80..ef8bb0b 100644 --- a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java +++ b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java @@ -18,17 +18,13 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.os.Debug; import android.os.PowerManager; import android.os.SystemClock; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; diff --git a/packages/Keyguard/src/com/android/keyguard/PagedView.java b/packages/Keyguard/src/com/android/keyguard/PagedView.java deleted file mode 100644 index b42a085..0000000 --- a/packages/Keyguard/src/com/android/keyguard/PagedView.java +++ /dev/null @@ -1,2845 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.*; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.AnimationUtils; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; -import android.widget.Scroller; - -import java.util.ArrayList; - -/** - * An abstraction of the original Workspace which supports browsing through a - * sequential list of "pages" - */ -public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener { - private static final int WARP_SNAP_DURATION = 160; - private static final String TAG = "WidgetPagedView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final boolean DEBUG_WARP = false; - protected static final int INVALID_PAGE = -1; - private static final int WARP_PEEK_ANIMATION_DURATION = 150; - private static final float WARP_ANIMATE_AMOUNT = -75.0f; // in dip - - // the min drag distance for a fling to register, to prevent random page shifts - private static final int MIN_LENGTH_FOR_FLING = 25; - - protected static final int PAGE_SNAP_ANIMATION_DURATION = 750; - protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; - protected static final float NANOTIME_DIV = 1000000000.0f; - - private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; - private static final float OVERSCROLL_DAMP_FACTOR = 0.14f; - - private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f; - // The page is moved more than halfway, automatically move to the next page on touch up. - private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.5f; - - // The following constants need to be scaled based on density. The scaled versions will be - // assigned to the corresponding member variables below. - private static final int FLING_THRESHOLD_VELOCITY = 1500; - private static final int MIN_SNAP_VELOCITY = 1500; - private static final int MIN_FLING_VELOCITY = 500; - - // We are disabling touch interaction of the widget region for factory ROM. - private static final boolean DISABLE_TOUCH_INTERACTION = false; - private static final boolean DISABLE_TOUCH_SIDE_PAGES = true; - private static final boolean DISABLE_FLING_TO_DELETE = false; - - static final int AUTOMATIC_PAGE_SPACING = -1; - - protected int mFlingThresholdVelocity; - protected int mMinFlingVelocity; - protected int mMinSnapVelocity; - - protected float mDensity; - protected float mSmoothingTime; - protected float mTouchX; - - protected boolean mFirstLayout = true; - - protected int mCurrentPage; - protected int mChildCountOnLastMeasure; - - protected int mNextPage = INVALID_PAGE; - protected int mMaxScrollX; - protected Scroller mScroller; - private VelocityTracker mVelocityTracker; - - private float mParentDownMotionX; - private float mParentDownMotionY; - private float mDownMotionX; - private float mDownMotionY; - private float mDownScrollX; - protected float mLastMotionX; - protected float mLastMotionXRemainder; - protected float mLastMotionY; - protected float mTotalMotionX; - private int mLastScreenCenter = -1; - private int[] mChildOffsets; - private int[] mChildRelativeOffsets; - private int[] mChildOffsetsWithLayoutScale; - private String mDeleteString; // Accessibility announcement when widget is deleted - - protected final static int TOUCH_STATE_REST = 0; - protected final static int TOUCH_STATE_SCROLLING = 1; - protected final static int TOUCH_STATE_PREV_PAGE = 2; - protected final static int TOUCH_STATE_NEXT_PAGE = 3; - protected final static int TOUCH_STATE_REORDERING = 4; - protected final static int TOUCH_STATE_READY = 5; // when finger is down - - protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f; - protected final static float TOUCH_SLOP_SCALE = 1.0f; - - protected int mTouchState = TOUCH_STATE_REST; - protected boolean mForceScreenScrolled = false; - - protected OnLongClickListener mLongClickListener; - - protected int mTouchSlop; - private int mPagingTouchSlop; - private int mMaximumVelocity; - private int mMinimumWidth; - protected int mPageSpacing; - protected int mCellCountX = 0; - protected int mCellCountY = 0; - protected boolean mAllowOverScroll = true; - protected int mUnboundedScrollX; - protected int[] mTempVisiblePagesRange = new int[2]; - protected boolean mForceDrawAllChildrenNextFrame; - - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise - // it is equal to the scaled overscroll position. We use a separate value so as to prevent - // the screens from continuing to translate beyond the normal bounds. - protected int mOverScrollX; - - // parameter that adjusts the layout to be optimized for pages with that scale factor - protected float mLayoutScale = 1.0f; - - protected static final int INVALID_POINTER = -1; - - protected int mActivePointerId = INVALID_POINTER; - - private PageSwitchListener mPageSwitchListener; - - protected ArrayList<Boolean> mDirtyPageContent; - - // If true, syncPages and syncPageItems will be called to refresh pages - protected boolean mContentIsRefreshable = true; - - // If true, modify alpha of neighboring pages as user scrolls left/right - protected boolean mFadeInAdjacentScreens = false; - - // It true, use a different slop parameter (pagingTouchSlop = 2 * touchSlop) for deciding - // to switch to a new page - protected boolean mUsePagingTouchSlop = true; - - // If true, the subclass should directly update scrollX itself in its computeScroll method - // (SmoothPagedView does this) - protected boolean mDeferScrollUpdate = false; - - protected boolean mIsPageMoving = false; - - // All syncs and layout passes are deferred until data is ready. - protected boolean mIsDataReady = true; - - // Scrolling indicator - private ValueAnimator mScrollIndicatorAnimator; - private View mScrollIndicator; - private int mScrollIndicatorPaddingLeft; - private int mScrollIndicatorPaddingRight; - private boolean mShouldShowScrollIndicator = false; - private boolean mShouldShowScrollIndicatorImmediately = false; - protected static final int sScrollIndicatorFadeInDuration = 150; - protected static final int sScrollIndicatorFadeOutDuration = 650; - protected static final int sScrollIndicatorFlashDuration = 650; - - // The viewport whether the pages are to be contained (the actual view may be larger than the - // viewport) - private Rect mViewport = new Rect(); - - // Reordering - // We use the min scale to determine how much to expand the actually PagedView measured - // dimensions such that when we are zoomed out, the view is not clipped - private int REORDERING_DROP_REPOSITION_DURATION = 200; - protected int REORDERING_REORDER_REPOSITION_DURATION = 300; - protected int REORDERING_ZOOM_IN_OUT_DURATION = 250; - private int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 300; - private float REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE = 0.1f; - private long REORDERING_DELETE_DROP_TARGET_FADE_DURATION = 150; - private float mMinScale = 1f; - protected View mDragView; - protected AnimatorSet mZoomInOutAnim; - private Runnable mSidePageHoverRunnable; - private int mSidePageHoverIndex = -1; - // This variable's scope is only for the duration of startReordering() and endReordering() - private boolean mReorderingStarted = false; - // This variable's scope is for the duration of startReordering() and after the zoomIn() - // animation after endReordering() - private boolean mIsReordering; - // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition - private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2; - private int mPostReorderingPreZoomInRemainingAnimationCount; - private Runnable mPostReorderingPreZoomInRunnable; - - // Edge swiping - private boolean mOnlyAllowEdgeSwipes = false; - private boolean mDownEventOnEdge = false; - private int mEdgeSwipeRegionSize = 0; - - // Convenience/caching - private Matrix mTmpInvMatrix = new Matrix(); - private float[] mTmpPoint = new float[2]; - private Rect mTmpRect = new Rect(); - private Rect mAltTmpRect = new Rect(); - - // Fling to delete - private int FLING_TO_DELETE_FADE_OUT_DURATION = 350; - private float FLING_TO_DELETE_FRICTION = 0.035f; - // The degrees specifies how much deviation from the up vector to still consider a fling "up" - private float FLING_TO_DELETE_MAX_FLING_DEGREES = 65f; - protected int mFlingToDeleteThresholdVelocity = -1400; - // Drag to delete - private boolean mDeferringForDelete = false; - private int DELETE_SLIDE_IN_SIDE_PAGE_DURATION = 250; - private int DRAG_TO_DELETE_FADE_OUT_DURATION = 350; - - // Drop to delete - private View mDeleteDropTarget; - - // Bouncer - private boolean mTopAlignPageWhenShrinkingForBouncer = false; - - // Page warping - private int mPageSwapIndex = -1; // the page we swapped out if needed - private int mPageWarpIndex = -1; // the page we intend to warp - - private boolean mWarpPageExposed; - private ViewPropertyAnimator mWarpAnimation; - - private boolean mIsCameraEvent; - private float mWarpPeekAmount; - private boolean mOnPageEndWarpCalled; - private boolean mOnPageBeginWarpCalled; - - public interface PageSwitchListener { - void onPageSwitching(View newPage, int newPageIndex); - void onPageSwitched(View newPage, int newPageIndex); - } - - public PagedView(Context context) { - this(context, null); - } - - public PagedView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PagedView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.PagedView, defStyle, 0); - setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0)); - mScrollIndicatorPaddingLeft = - a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingLeft, 0); - mScrollIndicatorPaddingRight = - a.getDimensionPixelSize(R.styleable.PagedView_scrollIndicatorPaddingRight, 0); - a.recycle(); - - Resources r = getResources(); - mEdgeSwipeRegionSize = r.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size); - mTopAlignPageWhenShrinkingForBouncer = - r.getBoolean(R.bool.kg_top_align_page_shrink_on_bouncer_visible); - - setHapticFeedbackEnabled(false); - init(); - } - - /** - * Initializes various states for this workspace. - */ - protected void init() { - mDirtyPageContent = new ArrayList<Boolean>(); - mDirtyPageContent.ensureCapacity(32); - mScroller = new Scroller(getContext(), new ScrollInterpolator()); - mCurrentPage = 0; - - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); - mTouchSlop = configuration.getScaledTouchSlop(); - mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); - mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); - mDensity = getResources().getDisplayMetrics().density; - mWarpPeekAmount = mDensity * WARP_ANIMATE_AMOUNT; - - // Scale the fling-to-delete threshold by the density - mFlingToDeleteThresholdVelocity = (int) (mFlingToDeleteThresholdVelocity * mDensity); - - mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity); - mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity); - mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity); - setOnHierarchyChangeListener(this); - } - - void setDeleteDropTarget(View v) { - mDeleteDropTarget = v; - } - - // Convenience methods to map points from self to parent and vice versa - float[] mapPointFromViewToParent(View v, float x, float y) { - mTmpPoint[0] = x; - mTmpPoint[1] = y; - v.getMatrix().mapPoints(mTmpPoint); - mTmpPoint[0] += v.getLeft(); - mTmpPoint[1] += v.getTop(); - return mTmpPoint; - } - float[] mapPointFromParentToView(View v, float x, float y) { - mTmpPoint[0] = x - v.getLeft(); - mTmpPoint[1] = y - v.getTop(); - v.getMatrix().invert(mTmpInvMatrix); - mTmpInvMatrix.mapPoints(mTmpPoint); - return mTmpPoint; - } - - void updateDragViewTranslationDuringDrag() { - float x = mLastMotionX - mDownMotionX + getScrollX() - mDownScrollX; - float y = mLastMotionY - mDownMotionY; - mDragView.setTranslationX(x); - mDragView.setTranslationY(y); - - if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): " + x + ", " + y); - } - - public void setMinScale(float f) { - mMinScale = f; - requestLayout(); - } - - @Override - public void setScaleX(float scaleX) { - super.setScaleX(scaleX); - if (isReordering(true)) { - float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY); - mLastMotionX = p[0]; - mLastMotionY = p[1]; - updateDragViewTranslationDuringDrag(); - } - } - - // Convenience methods to get the actual width/height of the PagedView (since it is measured - // to be larger to account for the minimum possible scale) - int getViewportWidth() { - return mViewport.width(); - } - int getViewportHeight() { - return mViewport.height(); - } - - // Convenience methods to get the offset ASSUMING that we are centering the pages in the - // PagedView both horizontally and vertically - int getViewportOffsetX() { - return (getMeasuredWidth() - getViewportWidth()) / 2; - } - int getViewportOffsetY() { - return (getMeasuredHeight() - getViewportHeight()) / 2; - } - - public void setPageSwitchListener(PageSwitchListener pageSwitchListener) { - mPageSwitchListener = pageSwitchListener; - if (mPageSwitchListener != null) { - mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage); - } - } - - /** - * Called by subclasses to mark that data is ready, and that we can begin loading and laying - * out pages. - */ - protected void setDataIsReady() { - mIsDataReady = true; - } - - protected boolean isDataReady() { - return mIsDataReady; - } - - /** - * Returns the index of the currently displayed page. - * - * @return The index of the currently displayed page. - */ - int getCurrentPage() { - return mCurrentPage; - } - - int getNextPage() { - return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage; - } - - int getPageCount() { - return getChildCount(); - } - - View getPageAt(int index) { - return getChildAt(index); - } - - protected int indexToPage(int index) { - return index; - } - - /** - * Updates the scroll of the current page immediately to its final scroll position. We use this - * in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of - * the previous tab page. - */ - protected void updateCurrentPageScroll() { - int offset = getChildOffset(mCurrentPage); - int relOffset = getRelativeChildOffset(mCurrentPage); - int newX = offset - relOffset; - scrollTo(newX, 0); - mScroller.setFinalX(newX); - mScroller.forceFinished(true); - } - - /** - * Sets the current page. - */ - void setCurrentPage(int currentPage) { - notifyPageSwitching(currentPage); - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - // don't introduce any checks like mCurrentPage == currentPage here-- if we change the - // the default - if (getChildCount() == 0) { - return; - } - - mForceScreenScrolled = true; - mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1)); - updateCurrentPageScroll(); - updateScrollingIndicator(); - notifyPageSwitched(); - invalidate(); - } - - public void setOnlyAllowEdgeSwipes(boolean enable) { - mOnlyAllowEdgeSwipes = enable; - } - - protected void notifyPageSwitching(int whichPage) { - if (mPageSwitchListener != null) { - mPageSwitchListener.onPageSwitching(getPageAt(whichPage), whichPage); - } - } - - protected void notifyPageSwitched() { - if (mPageSwitchListener != null) { - mPageSwitchListener.onPageSwitched(getPageAt(mCurrentPage), mCurrentPage); - } - } - - protected void pageBeginMoving() { - if (DEBUG_WARP) Log.v(TAG, "pageBeginMoving(" + mIsPageMoving + ")"); - if (!mIsPageMoving) { - mIsPageMoving = true; - if (isWarping()) { - dispatchOnPageBeginWarp(); - } - onPageBeginMoving(); - } - } - - private void dispatchOnPageBeginWarp() { - if (!mOnPageBeginWarpCalled) { - onPageBeginWarp(); - mOnPageBeginWarpCalled = true; - } - mOnPageEndWarpCalled = false; - } - - private void dispatchOnPageEndWarp() { - if (!mOnPageEndWarpCalled) { - onPageEndWarp(); - mOnPageEndWarpCalled = true; - } - mOnPageBeginWarpCalled = false; - } - - protected void pageEndMoving() { - if (DEBUG_WARP) Log.v(TAG, "pageEndMoving(" + mIsPageMoving + ")"); - if (mIsPageMoving) { - mIsPageMoving = false; - if (isWarping()) { - dispatchOnPageEndWarp(); - mWarpPageExposed = false; - } - onPageEndMoving(); - } - } - - protected boolean isPageMoving() { - return mIsPageMoving; - } - - // a method that subclasses can override to add behavior - protected void onPageBeginMoving() { - } - - // a method that subclasses can override to add behavior - protected void onPageEndMoving() { - } - - /** - * Registers the specified listener on each page contained in this workspace. - * - * @param l The listener used to respond to long clicks. - */ - @Override - public void setOnLongClickListener(OnLongClickListener l) { - mLongClickListener = l; - final int count = getPageCount(); - for (int i = 0; i < count; i++) { - getPageAt(i).setOnLongClickListener(l); - } - } - - @Override - public void scrollBy(int x, int y) { - scrollTo(mUnboundedScrollX + x, getScrollY() + y); - } - - @Override - public void scrollTo(int x, int y) { - mUnboundedScrollX = x; - - if (x < 0) { - super.scrollTo(0, y); - if (mAllowOverScroll) { - overScroll(x); - } - } else if (x > mMaxScrollX) { - super.scrollTo(mMaxScrollX, y); - if (mAllowOverScroll) { - overScroll(x - mMaxScrollX); - } - } else { - mOverScrollX = x; - super.scrollTo(x, y); - } - - mTouchX = x; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - - // Update the last motion events when scrolling - if (isReordering(true)) { - float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY); - mLastMotionX = p[0]; - mLastMotionY = p[1]; - updateDragViewTranslationDuringDrag(); - } - } - - // we moved this functionality to a helper function so SmoothPagedView can reuse it - protected boolean computeScrollHelper() { - if (mScroller.computeScrollOffset()) { - // Don't bother scrolling if the page does not need to be moved - if (getScrollX() != mScroller.getCurrX() - || getScrollY() != mScroller.getCurrY() - || mOverScrollX != mScroller.getCurrX()) { - scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); - } - invalidate(); - return true; - } else if (mNextPage != INVALID_PAGE) { - mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1)); - mNextPage = INVALID_PAGE; - notifyPageSwitched(); - - // We don't want to trigger a page end moving unless the page has settled - // and the user has stopped scrolling - if (mTouchState == TOUCH_STATE_REST) { - pageEndMoving(); - } - - onPostReorderingAnimationCompleted(); - return true; - } - return false; - } - - @Override - public void computeScroll() { - computeScrollHelper(); - } - - protected boolean shouldSetTopAlignedPivotForWidget(int childIndex) { - return mTopAlignPageWhenShrinkingForBouncer; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (!mIsDataReady || getChildCount() == 0) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - return; - } - - // We measure the dimensions of the PagedView to be larger than the pages so that when we - // zoom out (and scale down), the view is still contained in the parent - View parent = (View) getParent(); - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); - // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the - // viewport, we can be at most one and a half screens offset once we scale down - DisplayMetrics dm = getResources().getDisplayMetrics(); - int maxSize = Math.max(dm.widthPixels, dm.heightPixels); - int parentWidthSize = (int) (1.5f * maxSize); - int parentHeightSize = maxSize; - int scaledWidthSize = (int) (parentWidthSize / mMinScale); - int scaledHeightSize = (int) (parentHeightSize / mMinScale); - mViewport.set(0, 0, widthSize, heightSize); - - if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - return; - } - - // Return early if we aren't given a proper dimension - if (widthSize <= 0 || heightSize <= 0) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - return; - } - - /* Allow the height to be set as WRAP_CONTENT. This allows the particular case - * of the All apps view on XLarge displays to not take up more space then it needs. Width - * is still not allowed to be set as WRAP_CONTENT since many parts of the code expect - * each page to have the same width. - */ - final int verticalPadding = getPaddingTop() + getPaddingBottom(); - final int horizontalPadding = getPaddingLeft() + getPaddingRight(); - - // The children are given the same width and height as the workspace - // unless they were set to WRAP_CONTENT - if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize); - if (DEBUG) Log.d(TAG, "PagedView.scaledSize: " + scaledWidthSize + ", " + scaledHeightSize); - if (DEBUG) Log.d(TAG, "PagedView.parentSize: " + parentWidthSize + ", " + parentHeightSize); - if (DEBUG) Log.d(TAG, "PagedView.horizontalPadding: " + horizontalPadding); - if (DEBUG) Log.d(TAG, "PagedView.verticalPadding: " + verticalPadding); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - // disallowing padding in paged view (just pass 0) - final View child = getPageAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - int childWidthMode; - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthMode = MeasureSpec.AT_MOST; - } else { - childWidthMode = MeasureSpec.EXACTLY; - } - - int childHeightMode; - if (lp.height == LayoutParams.WRAP_CONTENT) { - childHeightMode = MeasureSpec.AT_MOST; - } else { - childHeightMode = MeasureSpec.EXACTLY; - } - - final int childWidthMeasureSpec = - MeasureSpec.makeMeasureSpec(widthSize - horizontalPadding, childWidthMode); - final int childHeightMeasureSpec = - MeasureSpec.makeMeasureSpec(heightSize - verticalPadding, childHeightMode); - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - setMeasuredDimension(scaledWidthSize, scaledHeightSize); - - // We can't call getChildOffset/getRelativeChildOffset until we set the measured dimensions. - // We also wait until we set the measured dimensions before flushing the cache as well, to - // ensure that the cache is filled with good values. - invalidateCachedOffsets(); - - if (mChildCountOnLastMeasure != getChildCount() && !mDeferringForDelete) { - setCurrentPage(mCurrentPage); - } - mChildCountOnLastMeasure = getChildCount(); - - if (childCount > 0) { - if (DEBUG) Log.d(TAG, "getRelativeChildOffset(): " + getViewportWidth() + ", " - + getChildWidth(0)); - - // Calculate the variable page spacing if necessary - if (mPageSpacing == AUTOMATIC_PAGE_SPACING) { - // The gap between pages in the PagedView should be equal to the gap from the page - // to the edge of the screen (so it is not visible in the current screen). To - // account for unequal padding on each side of the paged view, we take the maximum - // of the left/right gap and use that as the gap between each page. - int offset = getRelativeChildOffset(0); - int spacing = Math.max(offset, widthSize - offset - - getChildAt(0).getMeasuredWidth()); - setPageSpacing(spacing); - } - } - - updateScrollingIndicatorPosition(); - - if (childCount > 0) { - mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1); - } else { - mMaxScrollX = 0; - } - } - - public void setPageSpacing(int pageSpacing) { - mPageSpacing = pageSpacing; - invalidateCachedOffsets(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (!mIsDataReady || getChildCount() == 0) { - return; - } - - if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); - final int childCount = getChildCount(); - - int offsetX = getViewportOffsetX(); - int offsetY = getViewportOffsetY(); - - // Update the viewport offsets - mViewport.offset(offsetX, offsetY); - - int childLeft = offsetX + getRelativeChildOffset(0); - for (int i = 0; i < childCount; i++) { - final View child = getPageAt(i); - int childTop = offsetY + getPaddingTop(); - if (child.getVisibility() != View.GONE) { - final int childWidth = getScaledMeasuredWidth(child); - final int childHeight = child.getMeasuredHeight(); - - if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop); - child.layout(childLeft, childTop, - childLeft + child.getMeasuredWidth(), childTop + childHeight); - childLeft += childWidth + mPageSpacing; - } - } - - if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) { - setHorizontalScrollBarEnabled(false); - updateCurrentPageScroll(); - setHorizontalScrollBarEnabled(true); - mFirstLayout = false; - } - } - - protected void screenScrolled(int screenCenter) { - } - - @Override - public void onChildViewAdded(View parent, View child) { - // This ensures that when children are added, they get the correct transforms / alphas - // in accordance with any scroll effects. - mForceScreenScrolled = true; - invalidate(); - invalidateCachedOffsets(); - } - - @Override - public void onChildViewRemoved(View parent, View child) { - mForceScreenScrolled = true; - invalidate(); - invalidateCachedOffsets(); - } - - protected void invalidateCachedOffsets() { - int count = getChildCount(); - if (count == 0) { - mChildOffsets = null; - mChildRelativeOffsets = null; - mChildOffsetsWithLayoutScale = null; - return; - } - - mChildOffsets = new int[count]; - mChildRelativeOffsets = new int[count]; - mChildOffsetsWithLayoutScale = new int[count]; - for (int i = 0; i < count; i++) { - mChildOffsets[i] = -1; - mChildRelativeOffsets[i] = -1; - mChildOffsetsWithLayoutScale[i] = -1; - } - } - - protected int getChildOffset(int index) { - if (index < 0 || index > getChildCount() - 1) return 0; - - int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ? - mChildOffsets : mChildOffsetsWithLayoutScale; - - if (childOffsets != null && childOffsets[index] != -1) { - return childOffsets[index]; - } else { - if (getChildCount() == 0) - return 0; - - int offset = getRelativeChildOffset(0); - for (int i = 0; i < index; ++i) { - offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing; - } - if (childOffsets != null) { - childOffsets[index] = offset; - } - return offset; - } - } - - protected int getRelativeChildOffset(int index) { - if (index < 0 || index > getChildCount() - 1) return 0; - - if (mChildRelativeOffsets != null && mChildRelativeOffsets[index] != -1) { - return mChildRelativeOffsets[index]; - } else { - final int padding = getPaddingLeft() + getPaddingRight(); - final int offset = getPaddingLeft() + - (getViewportWidth() - padding - getChildWidth(index)) / 2; - if (mChildRelativeOffsets != null) { - mChildRelativeOffsets[index] = offset; - } - return offset; - } - } - - protected int getScaledMeasuredWidth(View child) { - // This functions are called enough times that it actually makes a difference in the - // profiler -- so just inline the max() here - final int measuredWidth = child.getMeasuredWidth(); - final int minWidth = mMinimumWidth; - final int maxWidth = (minWidth > measuredWidth) ? minWidth : measuredWidth; - return (int) (maxWidth * mLayoutScale + 0.5f); - } - - void boundByReorderablePages(boolean isReordering, int[] range) { - // Do nothing - } - - // TODO: Fix this - protected void getVisiblePages(int[] range) { - range[0] = 0; - range[1] = getPageCount() - 1; - - /* - final int pageCount = getChildCount(); - - if (pageCount > 0) { - final int screenWidth = getViewportWidth(); - int leftScreen = 0; - int rightScreen = 0; - int offsetX = getViewportOffsetX() + getScrollX(); - View currPage = getPageAt(leftScreen); - while (leftScreen < pageCount - 1 && - currPage.getX() + currPage.getWidth() - - currPage.getPaddingRight() < offsetX) { - leftScreen++; - currPage = getPageAt(leftScreen); - } - rightScreen = leftScreen; - currPage = getPageAt(rightScreen + 1); - while (rightScreen < pageCount - 1 && - currPage.getX() - currPage.getPaddingLeft() < offsetX + screenWidth) { - rightScreen++; - currPage = getPageAt(rightScreen + 1); - } - - // TEMP: this is a hacky way to ensure that animations to new pages are not clipped - // because we don't draw them while scrolling? - range[0] = Math.max(0, leftScreen - 1); - range[1] = Math.min(rightScreen + 1, getChildCount() - 1); - } else { - range[0] = -1; - range[1] = -1; - } - */ - } - - protected boolean shouldDrawChild(View child) { - return child.getAlpha() > 0; - } - - @Override - protected void dispatchDraw(Canvas canvas) { - int halfScreenSize = getViewportWidth() / 2; - // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. - // Otherwise it is equal to the scaled overscroll position. - int screenCenter = mOverScrollX + halfScreenSize; - - if (screenCenter != mLastScreenCenter || mForceScreenScrolled) { - // set mForceScreenScrolled before calling screenScrolled so that screenScrolled can - // set it for the next frame - mForceScreenScrolled = false; - screenScrolled(screenCenter); - mLastScreenCenter = screenCenter; - } - - // Find out which screens are visible; as an optimization we only call draw on them - final int pageCount = getChildCount(); - if (pageCount > 0) { - getVisiblePages(mTempVisiblePagesRange); - final int leftScreen = mTempVisiblePagesRange[0]; - final int rightScreen = mTempVisiblePagesRange[1]; - if (leftScreen != -1 && rightScreen != -1) { - final long drawingTime = getDrawingTime(); - // Clip to the bounds - canvas.save(); - canvas.clipRect(getScrollX(), getScrollY(), getScrollX() + getRight() - getLeft(), - getScrollY() + getBottom() - getTop()); - - // Draw all the children, leaving the drag view for last - for (int i = pageCount - 1; i >= 0; i--) { - final View v = getPageAt(i); - if (v == mDragView) continue; - if (mForceDrawAllChildrenNextFrame || - (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) { - drawChild(canvas, v, drawingTime); - } - } - // Draw the drag view on top (if there is one) - if (mDragView != null) { - drawChild(canvas, mDragView, drawingTime); - } - - mForceDrawAllChildrenNextFrame = false; - canvas.restore(); - } - } - } - - @Override - public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { - int page = indexToPage(indexOfChild(child)); - if (page != mCurrentPage || !mScroller.isFinished()) { - snapToPage(page); - return true; - } - return false; - } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - int focusablePage; - if (mNextPage != INVALID_PAGE) { - focusablePage = mNextPage; - } else { - focusablePage = mCurrentPage; - } - View v = getPageAt(focusablePage); - if (v != null) { - return v.requestFocus(direction, previouslyFocusedRect); - } - return false; - } - - @Override - public boolean dispatchUnhandledMove(View focused, int direction) { - if (direction == View.FOCUS_LEFT) { - if (getCurrentPage() > 0) { - snapToPage(getCurrentPage() - 1); - return true; - } - } else if (direction == View.FOCUS_RIGHT) { - if (getCurrentPage() < getPageCount() - 1) { - snapToPage(getCurrentPage() + 1); - return true; - } - } - return super.dispatchUnhandledMove(focused, direction); - } - - @Override - public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { - if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) { - getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode); - } - if (direction == View.FOCUS_LEFT) { - if (mCurrentPage > 0) { - getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode); - } - } else if (direction == View.FOCUS_RIGHT){ - if (mCurrentPage < getPageCount() - 1) { - getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode); - } - } - } - - /** - * If one of our descendant views decides that it could be focused now, only - * pass that along if it's on the current page. - * - * This happens when live folders requery, and if they're off page, they - * end up calling requestFocus, which pulls it on page. - */ - @Override - public void focusableViewAvailable(View focused) { - View current = getPageAt(mCurrentPage); - View v = focused; - while (true) { - if (v == current) { - super.focusableViewAvailable(focused); - return; - } - if (v == this) { - return; - } - ViewParent parent = v.getParent(); - if (parent instanceof View) { - v = (View)v.getParent(); - } else { - return; - } - } - } - - /** - * Return true if a tap at (x, y) should trigger a flip to the previous page. - */ - protected boolean hitsPreviousPage(float x, float y) { - return (x < getViewportOffsetX() + getRelativeChildOffset(mCurrentPage) - mPageSpacing); - } - - /** - * Return true if a tap at (x, y) should trigger a flip to the next page. - */ - protected boolean hitsNextPage(float x, float y) { - return (x > (getViewportOffsetX() + getViewportWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing)); - } - - /** Returns whether x and y originated within the buffered viewport */ - private boolean isTouchPointInViewportWithBuffer(int x, int y) { - mTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top, - mViewport.right + mViewport.width() / 2, mViewport.bottom); - return mTmpRect.contains(x, y); - } - - /** Returns whether x and y originated within the current page view bounds */ - private boolean isTouchPointInCurrentPage(int x, int y) { - View v = getPageAt(getCurrentPage()); - if (v != null) { - mTmpRect.set((v.getLeft() - getScrollX()), 0, (v.getRight() - getScrollX()), - v.getBottom()); - return mTmpRect.contains(x, y); - } - return false; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - - /* - * This method JUST determines whether we want to intercept the motion. - * If we return true, onTouchEvent will be called and we do the actual - * scrolling there. - */ - acquireVelocityTrackerAndAddMovement(ev); - - // Skip touch handling if there are no pages to swipe - if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); - - /* - * Shortcut the most recurring case: the user is in the dragging - * state and he is moving his finger. We want to intercept this - * motion. - */ - final int action = ev.getAction(); - if ((action == MotionEvent.ACTION_MOVE) && - (mTouchState == TOUCH_STATE_SCROLLING)) { - return true; - } - - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_MOVE: { - /* - * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check - * whether the user has moved far enough from his original down touch. - */ - if (mActivePointerId != INVALID_POINTER) { - if (mIsCameraEvent || determineScrollingStart(ev)) { - startScrolling(ev); - } - break; - } - // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN - // event. in that case, treat the first occurence of a move event as a ACTION_DOWN - // i.e. fall through to the next case (don't break) - // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events - // while it's small- this was causing a crash before we checked for INVALID_POINTER) - - break; - } - - case MotionEvent.ACTION_DOWN: { - if (mIsCameraEvent) { - animateWarpPageOnScreen("interceptTouch(): DOWN"); - } - // Remember where the motion event started - saveDownState(ev); - - /* - * If being flinged and user touches the screen, initiate drag; - * otherwise don't. mScroller.isFinished should be false when - * being flinged. - */ - final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); - final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); - if (finishedScrolling) { - setTouchState(TOUCH_STATE_REST); - mScroller.abortAnimation(); - } else { - if (mIsCameraEvent || isTouchPointInViewportWithBuffer( - (int) mDownMotionX, (int) mDownMotionY)) { - setTouchState(TOUCH_STATE_SCROLLING); - } else { - setTouchState(TOUCH_STATE_REST); - } - } - - // check if this can be the beginning of a tap on the side of the pages - // to scroll the current page - if (!DISABLE_TOUCH_SIDE_PAGES) { - if (mTouchState != TOUCH_STATE_PREV_PAGE - && mTouchState != TOUCH_STATE_NEXT_PAGE) { - if (getChildCount() > 0) { - float x = ev.getX(); - float y = ev.getY(); - if (hitsPreviousPage(x, y)) { - setTouchState(TOUCH_STATE_PREV_PAGE); - } else if (hitsNextPage(x, y)) { - setTouchState(TOUCH_STATE_NEXT_PAGE); - } - } - } - } - break; - } - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - resetTouchState(); - // Just intercept the touch event on up if we tap outside the strict viewport - if (!isTouchPointInCurrentPage((int) mLastMotionX, (int) mLastMotionY)) { - return true; - } - break; - - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - releaseVelocityTracker(); - break; - } - - /* - * The only time we want to intercept motion events is if we are in the - * drag mode. - */ - return mTouchState != TOUCH_STATE_REST; - } - - private void setTouchState(int touchState) { - if (mTouchState != touchState) { - if (DEBUG_WARP) Log.v(TAG, "mTouchState changing to " + touchState); - onTouchStateChanged(touchState); - mTouchState = touchState; - } - } - - void onTouchStateChanged(int newTouchState) { - if (DEBUG) { - Log.v(TAG, "onTouchStateChanged(old="+ mTouchState + ", new=" + newTouchState + ")"); - } - } - - /** - * Save the state when we get {@link MotionEvent#ACTION_DOWN} - * @param ev - */ - private void saveDownState(MotionEvent ev) { - // Remember where the motion event started - mDownMotionX = mLastMotionX = ev.getX(); - mDownMotionY = mLastMotionY = ev.getY(); - mDownScrollX = getScrollX(); - float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); - mParentDownMotionX = p[0]; - mParentDownMotionY = p[1]; - mLastMotionXRemainder = 0; - mTotalMotionX = 0; - mActivePointerId = ev.getPointerId(0); - - // Determine if the down event is within the threshold to be an edge swipe - int leftEdgeBoundary = getViewportOffsetX() + mEdgeSwipeRegionSize; - int rightEdgeBoundary = getMeasuredWidth() - getViewportOffsetX() - mEdgeSwipeRegionSize; - if ((mDownMotionX <= leftEdgeBoundary || mDownMotionX >= rightEdgeBoundary)) { - mDownEventOnEdge = true; - } - } - - private boolean isHorizontalCameraScroll(MotionEvent ev) { - // Disallow scrolling if we don't have a valid pointer index - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) return false; - - // If we're only allowing edge swipes, we break out early if the down event wasn't - // at the edge. - if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return false; - - final float x = ev.getX(pointerIndex); - final int xDiff = (int) Math.abs(x - mDownMotionX); - - final int touchSlop = Math.round(TOUCH_SLOP_SCALE * mTouchSlop); - boolean xPaged = xDiff > mPagingTouchSlop; - boolean xMoved = xDiff > touchSlop; - - return mIsCameraEvent && (mUsePagingTouchSlop ? xPaged : xMoved); - } - - /* - * Determines if we should change the touch state to start scrolling after the - * user moves their touch point too far. - */ - protected boolean determineScrollingStart(MotionEvent ev) { - // Disallow scrolling if we don't have a valid pointer index - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) return false; - - // Disallow scrolling if we started the gesture from outside the viewport - final float x = ev.getX(pointerIndex); - final float y = ev.getY(pointerIndex); - if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return false; - - // If we're only allowing edge swipes, we break out early if the down event wasn't - // at the edge. - if (mOnlyAllowEdgeSwipes && !mDownEventOnEdge) return false; - - final int xDiff = (int) Math.abs(x - mLastMotionX); - - final int touchSlop = Math.round(TOUCH_SLOP_SCALE * mTouchSlop); - boolean xPaged = xDiff > mPagingTouchSlop; - boolean xMoved = xDiff > touchSlop; - - return mUsePagingTouchSlop ? xPaged : xMoved; - } - - private void startScrolling(MotionEvent ev) { - // Ignore if we don't have a valid pointer index - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) return; - - final float x = ev.getX(pointerIndex); - setTouchState(TOUCH_STATE_SCROLLING); - mTotalMotionX += Math.abs(mLastMotionX - x); - mLastMotionX = x; - mLastMotionXRemainder = 0; - mTouchX = getViewportOffsetX() + getScrollX(); - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - pageBeginMoving(); - } - - protected float getMaxScrollProgress() { - return 1.0f; - } - - protected float getBoundedScrollProgress(int screenCenter, View v, int page) { - final int halfScreenSize = getViewportWidth() / 2; - - screenCenter = Math.min(mScrollX + halfScreenSize, screenCenter); - screenCenter = Math.max(halfScreenSize, screenCenter); - - return getScrollProgress(screenCenter, v, page); - } - - protected float getScrollProgress(int screenCenter, View v, int page) { - final int halfScreenSize = getViewportWidth() / 2; - - int totalDistance = getScaledMeasuredWidth(v) + mPageSpacing; - int delta = screenCenter - (getChildOffset(page) - - getRelativeChildOffset(page) + halfScreenSize); - - float scrollProgress = delta / (totalDistance * 1.0f); - scrollProgress = Math.min(scrollProgress, getMaxScrollProgress()); - scrollProgress = Math.max(scrollProgress, - getMaxScrollProgress()); - return scrollProgress; - } - - // This curve determines how the effect of scrolling over the limits of the page dimishes - // as the user pulls further and further from the bounds - private float overScrollInfluenceCurve(float f) { - f -= 1.0f; - return f * f * f + 1.0f; - } - - protected void acceleratedOverScroll(float amount) { - int screenSize = getViewportWidth(); - - // We want to reach the max over scroll effect when the user has - // over scrolled half the size of the screen - float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize); - - if (f == 0) return; - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - int overScrollAmount = (int) Math.round(f * screenSize); - if (amount < 0) { - mOverScrollX = overScrollAmount; - super.scrollTo(0, getScrollY()); - } else { - mOverScrollX = mMaxScrollX + overScrollAmount; - super.scrollTo(mMaxScrollX, getScrollY()); - } - invalidate(); - } - - protected void dampedOverScroll(float amount) { - int screenSize = getViewportWidth(); - - float f = (amount / screenSize); - - if (f == 0) return; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - - // Clamp this factor, f, to -1 < f < 1 - if (Math.abs(f) >= 1) { - f /= Math.abs(f); - } - - int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize); - if (amount < 0) { - mOverScrollX = overScrollAmount; - super.scrollTo(0, getScrollY()); - } else { - mOverScrollX = mMaxScrollX + overScrollAmount; - super.scrollTo(mMaxScrollX, getScrollY()); - } - invalidate(); - } - - protected void overScroll(float amount) { - dampedOverScroll(amount); - } - - protected float maxOverScroll() { - // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not - // exceed). Used to find out how much extra wallpaper we need for the over scroll effect - float f = 1.0f; - f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); - return OVERSCROLL_DAMP_FACTOR * f; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (DISABLE_TOUCH_INTERACTION) { - return false; - } - - // Skip touch handling if there are no pages to swipe - if (getChildCount() <= 0) return super.onTouchEvent(ev); - - acquireVelocityTrackerAndAddMovement(ev); - - final int action = ev.getAction(); - - switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - /* - * If being flinged and user touches, stop the fling. isFinished - * will be false if being flinged. - */ - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - } - - // Remember where the motion event started - saveDownState(ev); - - if (mTouchState == TOUCH_STATE_SCROLLING) { - pageBeginMoving(); - } else { - setTouchState(TOUCH_STATE_READY); - } - - if (mIsCameraEvent) { - animateWarpPageOnScreen("onTouch(): DOWN"); - } - break; - - case MotionEvent.ACTION_MOVE: - if (mTouchState == TOUCH_STATE_SCROLLING) { - // Scroll to follow the motion event - final int pointerIndex = ev.findPointerIndex(mActivePointerId); - - if (pointerIndex == -1) return true; - - final float x = ev.getX(pointerIndex); - final float deltaX = mLastMotionX + mLastMotionXRemainder - x; - - mTotalMotionX += Math.abs(deltaX); - - // Only scroll and update mLastMotionX if we have moved some discrete amount. We - // keep the remainder because we are actually testing if we've moved from the last - // scrolled position (which is discrete). - if (Math.abs(deltaX) >= 1.0f) { - mTouchX += deltaX; - mSmoothingTime = System.nanoTime() / NANOTIME_DIV; - if (isWarping()) { - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - v.setTranslationX(v.getTranslationX() - deltaX); - } else if (!mDeferScrollUpdate) { - scrollBy((int) deltaX, 0); - if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); - } else { - invalidate(); - } - mLastMotionX = x; - mLastMotionXRemainder = deltaX - (int) deltaX; - } else { - awakenScrollBars(); - } - } else if (mTouchState == TOUCH_STATE_REORDERING) { - // Update the last motion position - mLastMotionX = ev.getX(); - mLastMotionY = ev.getY(); - - // Update the parent down so that our zoom animations take this new movement into - // account - float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); - mParentDownMotionX = pt[0]; - mParentDownMotionY = pt[1]; - updateDragViewTranslationDuringDrag(); - - // Find the closest page to the touch point - final int dragViewIndex = indexOfChild(mDragView); - int bufferSize = (int) (REORDERING_SIDE_PAGE_BUFFER_PERCENTAGE * - getViewportWidth()); - int leftBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.left, 0)[0] - + bufferSize); - int rightBufferEdge = (int) (mapPointFromViewToParent(this, mViewport.right, 0)[0] - - bufferSize); - - // Change the drag view if we are hovering over the drop target - boolean isHoveringOverDelete = isHoveringOverDeleteDropTarget( - (int) mParentDownMotionX, (int) mParentDownMotionY); - setPageHoveringOverDeleteDropTarget(dragViewIndex, isHoveringOverDelete); - - if (DEBUG) Log.d(TAG, "leftBufferEdge: " + leftBufferEdge); - if (DEBUG) Log.d(TAG, "rightBufferEdge: " + rightBufferEdge); - if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX); - if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY); - if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX); - if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY); - - float parentX = mParentDownMotionX; - int pageIndexToSnapTo = -1; - if (parentX < leftBufferEdge && dragViewIndex > 0) { - pageIndexToSnapTo = dragViewIndex - 1; - } else if (parentX > rightBufferEdge && dragViewIndex < getChildCount() - 1) { - pageIndexToSnapTo = dragViewIndex + 1; - } - - final int pageUnderPointIndex = pageIndexToSnapTo; - if (pageUnderPointIndex > -1 && !isHoveringOverDelete) { - mTempVisiblePagesRange[0] = 0; - mTempVisiblePagesRange[1] = getPageCount() - 1; - boundByReorderablePages(true, mTempVisiblePagesRange); - if (mTempVisiblePagesRange[0] <= pageUnderPointIndex && - pageUnderPointIndex <= mTempVisiblePagesRange[1] && - pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) { - mSidePageHoverIndex = pageUnderPointIndex; - mSidePageHoverRunnable = new Runnable() { - @Override - public void run() { - // Update the down scroll position to account for the fact that the - // current page is moved - mDownScrollX = getChildOffset(pageUnderPointIndex) - - getRelativeChildOffset(pageUnderPointIndex); - - // Setup the scroll to the correct page before we swap the views - snapToPage(pageUnderPointIndex); - - // For each of the pages between the paged view and the drag view, - // animate them from the previous position to the new position in - // the layout (as a result of the drag view moving in the layout) - int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1; - int lowerIndex = (dragViewIndex < pageUnderPointIndex) ? - dragViewIndex + 1 : pageUnderPointIndex; - int upperIndex = (dragViewIndex > pageUnderPointIndex) ? - dragViewIndex - 1 : pageUnderPointIndex; - for (int i = lowerIndex; i <= upperIndex; ++i) { - View v = getChildAt(i); - // dragViewIndex < pageUnderPointIndex, so after we remove the - // drag view all subsequent views to pageUnderPointIndex will - // shift down. - int oldX = getViewportOffsetX() + getChildOffset(i); - int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta); - - // Animate the view translation from its old position to its new - // position - AnimatorSet anim = (AnimatorSet) v.getTag(); - if (anim != null) { - anim.cancel(); - } - - v.setTranslationX(oldX - newX); - anim = new AnimatorSet(); - anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION); - anim.playTogether( - ObjectAnimator.ofFloat(v, "translationX", 0f)); - anim.start(); - v.setTag(anim); - } - - removeView(mDragView); - onRemoveView(mDragView, false); - addView(mDragView, pageUnderPointIndex); - onAddView(mDragView, pageUnderPointIndex); - mSidePageHoverIndex = -1; - } - }; - postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT); - } - } else { - removeCallbacks(mSidePageHoverRunnable); - mSidePageHoverIndex = -1; - } - } else if (determineScrollingStart(ev)) { - startScrolling(ev); - } else if (isHorizontalCameraScroll(ev)) { - startScrolling(ev); - // we need to cancel the camera animation - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - v.animate().cancel(); - } - break; - - case MotionEvent.ACTION_UP: - if (mTouchState == TOUCH_STATE_SCROLLING) { - final int activePointerId = mActivePointerId; - final int pointerIndex = ev.findPointerIndex(activePointerId); - - if (pointerIndex == -1) return true; - - final float x = ev.getX(pointerIndex); - final VelocityTracker velocityTracker = mVelocityTracker; - velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - int velocityX = (int) velocityTracker.getXVelocity(activePointerId); - final int deltaX = (int) (x - mDownMotionX); - final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage)); - boolean isSignificantMove = Math.abs(deltaX) > pageWidth * - SIGNIFICANT_MOVE_THRESHOLD; - - mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); - - boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && - Math.abs(velocityX) > mFlingThresholdVelocity; - - // In the case that the page is moved far to one direction and then is flung - // in the opposite direction, we use a threshold to determine whether we should - // just return to the starting page, or if we should skip one further. - boolean returnToOriginalPage = false; - if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && - Math.signum(velocityX) != Math.signum(deltaX) && isFling) { - returnToOriginalPage = true; - } - - int finalPage; - // We give flings precedence over large moves, which is why we short-circuit our - // test for a large move if a fling has been registered. That is, a large - // move to the left and fling to the right will register as a fling to the right. - if (((isSignificantMove && deltaX > 0 && !isFling) || - (isFling && velocityX > 0)) && mCurrentPage > 0) { - finalPage = returnToOriginalPage || isWarping() - ? mCurrentPage : mCurrentPage - 1; - snapToPageWithVelocity(finalPage, velocityX); - } else if (((isSignificantMove && deltaX < 0 && !isFling) || - (isFling && velocityX < 0)) && - mCurrentPage < getChildCount() - 1) { - finalPage = returnToOriginalPage ? mCurrentPage : - isWarping() ? getPageWarpIndex() : mCurrentPage + 1; - snapToPageWithVelocity(finalPage, velocityX); - } else { - snapToDestination(); - } - } else if (mTouchState == TOUCH_STATE_PREV_PAGE) { - // at this point we have not moved beyond the touch slop - // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so - // we can just page - int nextPage = Math.max(0, mCurrentPage - 1); - if (nextPage != mCurrentPage) { - snapToPage(nextPage); - } else { - snapToDestination(); - } - } else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { - // at this point we have not moved beyond the touch slop - // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so - // we can just page - int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); - if (nextPage != mCurrentPage) { - snapToPage(nextPage); - } else { - snapToDestination(); - } - } else if (mTouchState == TOUCH_STATE_REORDERING) { - // Update the last motion position - mLastMotionX = ev.getX(); - mLastMotionY = ev.getY(); - - // Update the parent down so that our zoom animations take this new movement into - // account - float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY); - mParentDownMotionX = pt[0]; - mParentDownMotionY = pt[1]; - updateDragViewTranslationDuringDrag(); - boolean handledFling = false; - if (!DISABLE_FLING_TO_DELETE) { - // Check the velocity and see if we are flinging-to-delete - PointF flingToDeleteVector = isFlingingToDelete(); - if (flingToDeleteVector != null) { - onFlingToDelete(flingToDeleteVector); - handledFling = true; - } - } - if (!handledFling && isHoveringOverDeleteDropTarget((int) mParentDownMotionX, - (int) mParentDownMotionY)) { - onDropToDelete(); - } - } else { - if (DEBUG_WARP) Log.v(TAG, "calling onUnhandledTap()"); - if (mWarpPageExposed && !isAnimatingWarpPage()) { - animateWarpPageOffScreen("unhandled tap", true); - } - onUnhandledTap(ev); - } - - // Remove the callback to wait for the side page hover timeout - removeCallbacks(mSidePageHoverRunnable); - // End any intermediate reordering states - resetTouchState(); - break; - - case MotionEvent.ACTION_CANCEL: - if (mTouchState == TOUCH_STATE_SCROLLING) { - snapToDestination(); - } - resetTouchState(); - break; - - case MotionEvent.ACTION_POINTER_UP: - onSecondaryPointerUp(ev); - break; - } - - return true; - } - - //public abstract void onFlingToDelete(View v); - public abstract void onRemoveView(View v, boolean deletePermanently); - public abstract void onRemoveViewAnimationCompleted(); - public abstract void onAddView(View v, int index); - - private void resetTouchState() { - releaseVelocityTracker(); - endReordering(); - setTouchState(TOUCH_STATE_REST); - mActivePointerId = INVALID_POINTER; - mDownEventOnEdge = false; - } - - protected void onUnhandledTap(MotionEvent ev) {} - - @Override - public boolean onGenericMotionEvent(MotionEvent event) { - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { - switch (event.getAction()) { - case MotionEvent.ACTION_SCROLL: { - // Handle mouse (or ext. device) by shifting the page depending on the scroll - final float vscroll; - final float hscroll; - if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { - vscroll = 0; - hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - } else { - vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); - hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); - } - if (hscroll != 0 || vscroll != 0) { - if (hscroll > 0 || vscroll > 0) { - scrollRight(); - } else { - scrollLeft(); - } - return true; - } - } - } - } - return super.onGenericMotionEvent(event); - } - - private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - } - - private void releaseVelocityTracker() { - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - - private void onSecondaryPointerUp(MotionEvent ev) { - final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> - MotionEvent.ACTION_POINTER_INDEX_SHIFT; - final int pointerId = ev.getPointerId(pointerIndex); - if (pointerId == mActivePointerId) { - // This was our active pointer going up. Choose a new - // active pointer and adjust accordingly. - // TODO: Make this decision more intelligent. - final int newPointerIndex = pointerIndex == 0 ? 1 : 0; - mLastMotionX = mDownMotionX = ev.getX(newPointerIndex); - mLastMotionY = ev.getY(newPointerIndex); - mLastMotionXRemainder = 0; - mActivePointerId = ev.getPointerId(newPointerIndex); - if (mVelocityTracker != null) { - mVelocityTracker.clear(); - } - } - } - - @Override - public void requestChildFocus(View child, View focused) { - super.requestChildFocus(child, focused); - int page = indexToPage(indexOfChild(child)); - if (page >= 0 && page != getCurrentPage() && !isInTouchMode()) { - snapToPage(page); - } - } - - protected int getChildIndexForRelativeOffset(int relativeOffset) { - final int childCount = getChildCount(); - int left; - int right; - for (int i = 0; i < childCount; ++i) { - left = getRelativeChildOffset(i); - right = (left + getScaledMeasuredWidth(getPageAt(i))); - if (left <= relativeOffset && relativeOffset <= right) { - return i; - } - } - return -1; - } - - protected int getChildWidth(int index) { - // This functions are called enough times that it actually makes a difference in the - // profiler -- so just inline the max() here - final int measuredWidth = getPageAt(index).getMeasuredWidth(); - final int minWidth = mMinimumWidth; - return (minWidth > measuredWidth) ? minWidth : measuredWidth; - } - - int getPageNearestToPoint(float x) { - int index = 0; - for (int i = 0; i < getChildCount(); ++i) { - if (x < getChildAt(i).getRight() - getScrollX()) { - return index; - } else { - index++; - } - } - return Math.min(index, getChildCount() - 1); - } - - int getPageNearestToCenterOfScreen() { - int minDistanceFromScreenCenter = Integer.MAX_VALUE; - int minDistanceFromScreenCenterIndex = -1; - int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; ++i) { - View layout = (View) getPageAt(i); - int childWidth = getScaledMeasuredWidth(layout); - int halfChildWidth = (childWidth / 2); - int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth; - int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); - if (distanceFromScreenCenter < minDistanceFromScreenCenter) { - minDistanceFromScreenCenter = distanceFromScreenCenter; - minDistanceFromScreenCenterIndex = i; - } - } - return minDistanceFromScreenCenterIndex; - } - - protected void snapToDestination() { - final int newPage = getPageNearestToCenterOfScreen(); - if (isWarping()) { - cancelWarpAnimation("snapToDestination", mCurrentPage != newPage); - } - snapToPage(newPage, getPageSnapDuration()); - } - - private int getPageSnapDuration() { - return isWarping() ? WARP_SNAP_DURATION : PAGE_SNAP_ANIMATION_DURATION; - } - - private static class ScrollInterpolator implements Interpolator { - public ScrollInterpolator() { - } - - public float getInterpolation(float t) { - t -= 1.0f; - return t*t*t*t*t + 1; - } - } - - // We want the duration of the page snap animation to be influenced by the distance that - // the screen has to travel, however, we don't want this duration to be effected in a - // purely linear fashion. Instead, we use this method to moderate the effect that the distance - // of travel has on the overall snap duration. - float distanceInfluenceForSnapDuration(float f) { - f -= 0.5f; // center the values about 0. - f *= 0.3f * Math.PI / 2.0f; - return (float) Math.sin(f); - } - - protected void snapToPageWithVelocity(int whichPage, int velocity) { - whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1)); - int halfScreenSize = getViewportWidth() / 2; - - if (isWarping()) { - cancelWarpAnimation("snapToPageWithVelocity", mCurrentPage != whichPage); - } - - if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); - if (DEBUG) Log.d(TAG, "snapToPageWithVelocity.getRelativeChildOffset(): " - + getViewportWidth() + ", " + getChildWidth(whichPage)); - final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); - int delta = newX - mUnboundedScrollX; - int duration = 0; - - if (Math.abs(velocity) < mMinFlingVelocity) { - // If the velocity is low enough, then treat this more as an automatic page advance - // as opposed to an apparent physical response to flinging - snapToPage(whichPage, getPageSnapDuration()); - return; - } - - // Here we compute a "distance" that will be used in the computation of the overall - // snap duration. This is a function of the actual distance that needs to be traveled; - // we keep this value close to half screen size in order to reduce the variance in snap - // duration as a function of the distance the page needs to travel. - float distanceRatio = Math.min(1f, 1.0f * Math.abs(delta) / (2 * halfScreenSize)); - float distance = halfScreenSize + halfScreenSize * - distanceInfluenceForSnapDuration(distanceRatio); - - velocity = Math.abs(velocity); - velocity = Math.max(mMinSnapVelocity, velocity); - - // we want the page's snap velocity to approximately match the velocity at which the - // user flings, so we scale the duration by a value near to the derivative of the scroll - // interpolator at zero, ie. 5. We use 4 to make it a little slower. - duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); - - snapToPage(whichPage, delta, duration); - } - - protected void snapToPage(int whichPage) { - snapToPage(whichPage, getPageSnapDuration()); - } - protected void snapToPageImmediately(int whichPage) { - snapToPage(whichPage, getPageSnapDuration(), true); - } - - protected void snapToPage(int whichPage, int duration) { - snapToPage(whichPage, duration, false); - } - protected void snapToPage(int whichPage, int duration, boolean immediate) { - whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1)); - - if (DEBUG) Log.d(TAG, "snapToPage.getChildOffset(): " + getChildOffset(whichPage)); - if (DEBUG) Log.d(TAG, "snapToPage.getRelativeChildOffset(): " + getViewportWidth() + ", " - + getChildWidth(whichPage)); - int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage); - final int sX = mUnboundedScrollX; - final int delta = newX - sX; - snapToPage(whichPage, delta, duration, immediate); - } - - protected void snapToPage(int whichPage, int delta, int duration) { - snapToPage(whichPage, delta, duration, false); - } - - protected void snapToPage(int whichPage, int delta, int duration, boolean immediate) { - if (isWarping() && whichPage == mCurrentPage+1) { - mNextPage = getPageWarpIndex(); // jump to the warp page - if (DEBUG_WARP) Log.v(TAG, "snapToPage(" + whichPage + ") : reset mPageSwapIndex"); - } else { - mNextPage = whichPage; - } - - if(mWarpPageExposed) { - dispatchOnPageEndWarp(); - mWarpPageExposed = false; - } - notifyPageSwitching(whichPage); - - - View focusedChild = getFocusedChild(); - if (focusedChild != null && whichPage != mCurrentPage && - focusedChild == getPageAt(mCurrentPage)) { - focusedChild.clearFocus(); - } - - pageBeginMoving(); - awakenScrollBars(duration); - if (immediate) { - duration = 0; - } else if (duration == 0) { - duration = Math.abs(delta); - } - - if (!mScroller.isFinished()) mScroller.abortAnimation(); - mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); - - notifyPageSwitched(); - - // Trigger a compute() to finish switching pages if necessary - if (immediate) { - computeScroll(); - } - - mForceScreenScrolled = true; - invalidate(); - } - - protected boolean isWarping() { - return mWarpPageExposed; - } - - public void scrollLeft() { - if (mScroller.isFinished()) { - if (mCurrentPage > 0) snapToPage(mCurrentPage - 1); - } else { - if (mNextPage > 0) snapToPage(mNextPage - 1); - } - } - - public void scrollRight() { - if (mScroller.isFinished()) { - if (mCurrentPage < getChildCount() -1) snapToPage(mCurrentPage + 1); - } else { - if (mNextPage < getChildCount() -1) snapToPage(mNextPage + 1); - } - } - - public int getPageForView(View v) { - int result = -1; - if (v != null) { - ViewParent vp = v.getParent(); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - if (vp == getPageAt(i)) { - return i; - } - } - } - return result; - } - - public static class SavedState extends BaseSavedState { - int currentPage = -1; - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - currentPage = in.readInt(); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(currentPage); - } - - public static final Parcelable.Creator<SavedState> CREATOR = - new Parcelable.Creator<SavedState>() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - protected View getScrollingIndicator() { - return null; - } - - protected boolean isScrollingIndicatorEnabled() { - return false; - } - - Runnable hideScrollingIndicatorRunnable = new Runnable() { - @Override - public void run() { - hideScrollingIndicator(false); - } - }; - - protected void flashScrollingIndicator(boolean animated) { - removeCallbacks(hideScrollingIndicatorRunnable); - showScrollingIndicator(!animated); - postDelayed(hideScrollingIndicatorRunnable, sScrollIndicatorFlashDuration); - } - - protected void showScrollingIndicator(boolean immediately) { - mShouldShowScrollIndicator = true; - mShouldShowScrollIndicatorImmediately = true; - if (getChildCount() <= 1) return; - if (!isScrollingIndicatorEnabled()) return; - - mShouldShowScrollIndicator = false; - getScrollingIndicator(); - if (mScrollIndicator != null) { - // Fade the indicator in - updateScrollingIndicatorPosition(); - mScrollIndicator.setVisibility(View.VISIBLE); - cancelScrollingIndicatorAnimations(); - if (immediately) { - mScrollIndicator.setAlpha(1f); - } else { - mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 1f); - mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeInDuration); - mScrollIndicatorAnimator.start(); - } - } - } - - protected void cancelScrollingIndicatorAnimations() { - if (mScrollIndicatorAnimator != null) { - mScrollIndicatorAnimator.cancel(); - } - } - - protected void hideScrollingIndicator(boolean immediately) { - if (getChildCount() <= 1) return; - if (!isScrollingIndicatorEnabled()) return; - - getScrollingIndicator(); - if (mScrollIndicator != null) { - // Fade the indicator out - updateScrollingIndicatorPosition(); - cancelScrollingIndicatorAnimations(); - if (immediately) { - mScrollIndicator.setVisibility(View.INVISIBLE); - mScrollIndicator.setAlpha(0f); - } else { - mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f); - mScrollIndicatorAnimator.setDuration(sScrollIndicatorFadeOutDuration); - mScrollIndicatorAnimator.addListener(new AnimatorListenerAdapter() { - private boolean cancelled = false; - @Override - public void onAnimationCancel(android.animation.Animator animation) { - cancelled = true; - } - @Override - public void onAnimationEnd(Animator animation) { - if (!cancelled) { - mScrollIndicator.setVisibility(View.INVISIBLE); - } - } - }); - mScrollIndicatorAnimator.start(); - } - } - } - - /** - * To be overridden by subclasses to determine whether the scroll indicator should stretch to - * fill its space on the track or not. - */ - protected boolean hasElasticScrollIndicator() { - return true; - } - - private void updateScrollingIndicator() { - if (getChildCount() <= 1) return; - if (!isScrollingIndicatorEnabled()) return; - - getScrollingIndicator(); - if (mScrollIndicator != null) { - updateScrollingIndicatorPosition(); - } - if (mShouldShowScrollIndicator) { - showScrollingIndicator(mShouldShowScrollIndicatorImmediately); - } - } - - private void updateScrollingIndicatorPosition() { - if (!isScrollingIndicatorEnabled()) return; - if (mScrollIndicator == null) return; - int numPages = getChildCount(); - int pageWidth = getViewportWidth(); - int lastChildIndex = Math.max(0, getChildCount() - 1); - int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex); - int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight; - int indicatorWidth = mScrollIndicator.getMeasuredWidth() - - mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight(); - - float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX)); - int indicatorSpace = trackWidth / numPages; - int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft; - if (hasElasticScrollIndicator()) { - if (mScrollIndicator.getMeasuredWidth() != indicatorSpace) { - mScrollIndicator.getLayoutParams().width = indicatorSpace; - mScrollIndicator.requestLayout(); - } - } else { - int indicatorCenterOffset = indicatorSpace / 2 - indicatorWidth / 2; - indicatorPos += indicatorCenterOffset; - } - mScrollIndicator.setTranslationX(indicatorPos); - } - - // Animate the drag view back to the original position - void animateDragViewToOriginalPosition() { - if (mDragView != null) { - AnimatorSet anim = new AnimatorSet(); - anim.setDuration(REORDERING_DROP_REPOSITION_DURATION); - anim.playTogether( - ObjectAnimator.ofFloat(mDragView, "translationX", 0f), - ObjectAnimator.ofFloat(mDragView, "translationY", 0f)); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - onPostReorderingAnimationCompleted(); - } - }); - anim.start(); - } - } - - // "Zooms out" the PagedView to reveal more side pages - protected boolean zoomOut() { - if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { - mZoomInOutAnim.cancel(); - } - - if (!(getScaleX() < 1f || getScaleY() < 1f)) { - mZoomInOutAnim = new AnimatorSet(); - mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION); - mZoomInOutAnim.playTogether( - ObjectAnimator.ofFloat(this, "scaleX", mMinScale), - ObjectAnimator.ofFloat(this, "scaleY", mMinScale)); - mZoomInOutAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - // Show the delete drop target - if (mDeleteDropTarget != null) { - mDeleteDropTarget.setVisibility(View.VISIBLE); - mDeleteDropTarget.animate().alpha(1f) - .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mDeleteDropTarget.setAlpha(0f); - } - }); - } - } - }); - mZoomInOutAnim.start(); - return true; - } - return false; - } - - protected void onStartReordering() { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - announceForAccessibility(mContext.getString( - R.string.keyguard_accessibility_widget_reorder_start)); - } - - // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.) - setTouchState(TOUCH_STATE_REORDERING); - mIsReordering = true; - - // Mark all the non-widget pages as invisible - getVisiblePages(mTempVisiblePagesRange); - boundByReorderablePages(true, mTempVisiblePagesRange); - for (int i = 0; i < getPageCount(); ++i) { - if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) { - getPageAt(i).setAlpha(0f); - } - } - - // We must invalidate to trigger a redraw to update the layers such that the drag view - // is always drawn on top - invalidate(); - } - - private void onPostReorderingAnimationCompleted() { - // Trigger the callback when reordering has settled - --mPostReorderingPreZoomInRemainingAnimationCount; - if (mPostReorderingPreZoomInRunnable != null && - mPostReorderingPreZoomInRemainingAnimationCount == 0) { - mPostReorderingPreZoomInRunnable.run(); - mPostReorderingPreZoomInRunnable = null; - } - } - - protected void onEndReordering() { - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - if (mDeleteString != null) { - announceForAccessibility(mDeleteString); - mDeleteString = null; - } else { - announceForAccessibility(mContext.getString( - R.string.keyguard_accessibility_widget_reorder_end)); - } - } - mIsReordering = false; - - // Mark all the non-widget pages as visible again - getVisiblePages(mTempVisiblePagesRange); - boundByReorderablePages(true, mTempVisiblePagesRange); - for (int i = 0; i < getPageCount(); ++i) { - if (i < mTempVisiblePagesRange[0] || i > mTempVisiblePagesRange[1]) { - getPageAt(i).setAlpha(1f); - } - } - } - - public boolean startReordering() { - int dragViewIndex = getPageNearestToCenterOfScreen(); - mTempVisiblePagesRange[0] = 0; - mTempVisiblePagesRange[1] = getPageCount() - 1; - boundByReorderablePages(true, mTempVisiblePagesRange); - - // Check if we are within the reordering range - if (mTempVisiblePagesRange[0] <= dragViewIndex && - dragViewIndex <= mTempVisiblePagesRange[1]) { - mReorderingStarted = true; - if (zoomOut()) { - // Find the drag view under the pointer - mDragView = getChildAt(dragViewIndex); - - onStartReordering(); - } - return true; - } - return false; - } - - boolean isReordering(boolean testTouchState) { - boolean state = mIsReordering; - if (testTouchState) { - state &= (mTouchState == TOUCH_STATE_REORDERING); - } - return state; - } - void endReordering() { - // For simplicity, we call endReordering sometimes even if reordering was never started. - // In that case, we don't want to do anything. - if (!mReorderingStarted) return; - mReorderingStarted = false; - - // If we haven't flung-to-delete the current child, then we just animate the drag view - // back into position - final Runnable onCompleteRunnable = new Runnable() { - @Override - public void run() { - onEndReordering(); - } - }; - if (!mDeferringForDelete) { - mPostReorderingPreZoomInRunnable = new Runnable() { - public void run() { - zoomIn(onCompleteRunnable); - }; - }; - - mPostReorderingPreZoomInRemainingAnimationCount = - NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT; - // Snap to the current page - snapToPage(indexOfChild(mDragView), 0); - // Animate the drag view back to the front position - animateDragViewToOriginalPosition(); - } else { - // Handled in post-delete-animation-callbacks - } - } - - // "Zooms in" the PagedView to highlight the current page - protected boolean zoomIn(final Runnable onCompleteRunnable) { - if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) { - mZoomInOutAnim.cancel(); - } - if (getScaleX() < 1f || getScaleY() < 1f) { - mZoomInOutAnim = new AnimatorSet(); - mZoomInOutAnim.setDuration(REORDERING_ZOOM_IN_OUT_DURATION); - mZoomInOutAnim.playTogether( - ObjectAnimator.ofFloat(this, "scaleX", 1f), - ObjectAnimator.ofFloat(this, "scaleY", 1f)); - mZoomInOutAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - // Hide the delete drop target - if (mDeleteDropTarget != null) { - mDeleteDropTarget.animate().alpha(0f) - .setDuration(REORDERING_DELETE_DROP_TARGET_FADE_DURATION) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDeleteDropTarget.setVisibility(View.GONE); - } - }); - } - } - @Override - public void onAnimationCancel(Animator animation) { - mDragView = null; - } - @Override - public void onAnimationEnd(Animator animation) { - mDragView = null; - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - } - }); - mZoomInOutAnim.start(); - return true; - } else { - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } - } - return false; - } - - /* - * Flinging to delete - IN PROGRESS - */ - private PointF isFlingingToDelete() { - ViewConfiguration config = ViewConfiguration.get(getContext()); - mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity()); - - if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) { - // Do a quick dot product test to ensure that we are flinging upwards - PointF vel = new PointF(mVelocityTracker.getXVelocity(), - mVelocityTracker.getYVelocity()); - PointF upVec = new PointF(0f, -1f); - float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) / - (vel.length() * upVec.length())); - if (theta <= Math.toRadians(FLING_TO_DELETE_MAX_FLING_DEGREES)) { - return vel; - } - } - return null; - } - - /** - * Creates an animation from the current drag view along its current velocity vector. - * For this animation, the alpha runs for a fixed duration and we update the position - * progressively. - */ - private static class FlingAlongVectorAnimatorUpdateListener implements AnimatorUpdateListener { - private View mDragView; - private PointF mVelocity; - private Rect mFrom; - private long mPrevTime; - private float mFriction; - - private final TimeInterpolator mAlphaInterpolator = new DecelerateInterpolator(0.75f); - - public FlingAlongVectorAnimatorUpdateListener(View dragView, PointF vel, Rect from, - long startTime, float friction) { - mDragView = dragView; - mVelocity = vel; - mFrom = from; - mPrevTime = startTime; - mFriction = 1f - (mDragView.getResources().getDisplayMetrics().density * friction); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); - long curTime = AnimationUtils.currentAnimationTimeMillis(); - - mFrom.left += (mVelocity.x * (curTime - mPrevTime) / 1000f); - mFrom.top += (mVelocity.y * (curTime - mPrevTime) / 1000f); - - mDragView.setTranslationX(mFrom.left); - mDragView.setTranslationY(mFrom.top); - mDragView.setAlpha(1f - mAlphaInterpolator.getInterpolation(t)); - - mVelocity.x *= mFriction; - mVelocity.y *= mFriction; - mPrevTime = curTime; - } - }; - - private Runnable createPostDeleteAnimationRunnable(final View dragView) { - return new Runnable() { - @Override - public void run() { - int dragViewIndex = indexOfChild(dragView); - - // For each of the pages around the drag view, animate them from the previous - // position to the new position in the layout (as a result of the drag view moving - // in the layout) - // NOTE: We can make an assumption here because we have side-bound pages that we - // will always have pages to animate in from the left - getVisiblePages(mTempVisiblePagesRange); - boundByReorderablePages(true, mTempVisiblePagesRange); - boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]); - boolean slideFromLeft = (isLastWidgetPage || - dragViewIndex > mTempVisiblePagesRange[0]); - - // Setup the scroll to the correct page before we swap the views - if (slideFromLeft) { - snapToPageImmediately(dragViewIndex - 1); - } - - int firstIndex = (isLastWidgetPage ? 0 : mTempVisiblePagesRange[0]); - int lastIndex = Math.min(mTempVisiblePagesRange[1], getPageCount() - 1); - int lowerIndex = (slideFromLeft ? firstIndex : dragViewIndex + 1 ); - int upperIndex = (slideFromLeft ? dragViewIndex - 1 : lastIndex); - ArrayList<Animator> animations = new ArrayList<Animator>(); - for (int i = lowerIndex; i <= upperIndex; ++i) { - View v = getChildAt(i); - // dragViewIndex < pageUnderPointIndex, so after we remove the - // drag view all subsequent views to pageUnderPointIndex will - // shift down. - int oldX = 0; - int newX = 0; - if (slideFromLeft) { - if (i == 0) { - // Simulate the page being offscreen with the page spacing - oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i) - - mPageSpacing; - } else { - oldX = getViewportOffsetX() + getChildOffset(i - 1); - } - newX = getViewportOffsetX() + getChildOffset(i); - } else { - oldX = getChildOffset(i) - getChildOffset(i - 1); - newX = 0; - } - - // Animate the view translation from its old position to its new - // position - AnimatorSet anim = (AnimatorSet) v.getTag(); - if (anim != null) { - anim.cancel(); - } - - // Note: Hacky, but we want to skip any optimizations to not draw completely - // hidden views - v.setAlpha(Math.max(v.getAlpha(), 0.01f)); - v.setTranslationX(oldX - newX); - anim = new AnimatorSet(); - anim.playTogether( - ObjectAnimator.ofFloat(v, "translationX", 0f), - ObjectAnimator.ofFloat(v, "alpha", 1f)); - animations.add(anim); - v.setTag(anim); - } - - AnimatorSet slideAnimations = new AnimatorSet(); - slideAnimations.playTogether(animations); - slideAnimations.setDuration(DELETE_SLIDE_IN_SIDE_PAGE_DURATION); - slideAnimations.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - final Runnable onCompleteRunnable = new Runnable() { - @Override - public void run() { - mDeferringForDelete = false; - onEndReordering(); - onRemoveViewAnimationCompleted(); - } - }; - zoomIn(onCompleteRunnable); - } - }); - slideAnimations.start(); - - removeView(dragView); - onRemoveView(dragView, true); - } - }; - } - - public void onFlingToDelete(PointF vel) { - final long startTime = AnimationUtils.currentAnimationTimeMillis(); - - // NOTE: Because it takes time for the first frame of animation to actually be - // called and we expect the animation to be a continuation of the fling, we have - // to account for the time that has elapsed since the fling finished. And since - // we don't have a startDelay, we will always get call to update when we call - // start() (which we want to ignore). - final TimeInterpolator tInterpolator = new TimeInterpolator() { - private int mCount = -1; - private long mStartTime; - private float mOffset; - /* Anonymous inner class ctor */ { - mStartTime = startTime; - } - - @Override - public float getInterpolation(float t) { - if (mCount < 0) { - mCount++; - } else if (mCount == 0) { - mOffset = Math.min(0.5f, (float) (AnimationUtils.currentAnimationTimeMillis() - - mStartTime) / FLING_TO_DELETE_FADE_OUT_DURATION); - mCount++; - } - return Math.min(1f, mOffset + t); - } - }; - - final Rect from = new Rect(); - final View dragView = mDragView; - from.left = (int) dragView.getTranslationX(); - from.top = (int) dragView.getTranslationY(); - AnimatorUpdateListener updateCb = new FlingAlongVectorAnimatorUpdateListener(dragView, vel, - from, startTime, FLING_TO_DELETE_FRICTION); - - mDeleteString = getContext().getResources() - .getString(R.string.keyguard_accessibility_widget_deleted, - mDragView.getContentDescription()); - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - // Create and start the animation - ValueAnimator mDropAnim = new ValueAnimator(); - mDropAnim.setInterpolator(tInterpolator); - mDropAnim.setDuration(FLING_TO_DELETE_FADE_OUT_DURATION); - mDropAnim.setFloatValues(0f, 1f); - mDropAnim.addUpdateListener(updateCb); - mDropAnim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - mDropAnim.start(); - mDeferringForDelete = true; - } - - /* Drag to delete */ - private boolean isHoveringOverDeleteDropTarget(int x, int y) { - if (mDeleteDropTarget != null) { - mAltTmpRect.set(0, 0, 0, 0); - View parent = (View) mDeleteDropTarget.getParent(); - if (parent != null) { - parent.getGlobalVisibleRect(mAltTmpRect); - } - mDeleteDropTarget.getGlobalVisibleRect(mTmpRect); - mTmpRect.offset(-mAltTmpRect.left, -mAltTmpRect.top); - return mTmpRect.contains(x, y); - } - return false; - } - - protected void setPageHoveringOverDeleteDropTarget(int viewIndex, boolean isHovering) {} - - private void onDropToDelete() { - final View dragView = mDragView; - - final float toScale = 0f; - final float toAlpha = 0f; - - // Create and start the complex animation - ArrayList<Animator> animations = new ArrayList<Animator>(); - AnimatorSet motionAnim = new AnimatorSet(); - motionAnim.setInterpolator(new DecelerateInterpolator(2)); - motionAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "scaleX", toScale), - ObjectAnimator.ofFloat(dragView, "scaleY", toScale)); - animations.add(motionAnim); - - AnimatorSet alphaAnim = new AnimatorSet(); - alphaAnim.setInterpolator(new LinearInterpolator()); - alphaAnim.playTogether( - ObjectAnimator.ofFloat(dragView, "alpha", toAlpha)); - animations.add(alphaAnim); - - mDeleteString = getContext().getResources() - .getString(R.string.keyguard_accessibility_widget_deleted, - mDragView.getContentDescription()); - final Runnable onAnimationEndRunnable = createPostDeleteAnimationRunnable(dragView); - - AnimatorSet anim = new AnimatorSet(); - anim.playTogether(animations); - anim.setDuration(DRAG_TO_DELETE_FADE_OUT_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - onAnimationEndRunnable.run(); - } - }); - anim.start(); - - mDeferringForDelete = true; - } - - /* Accessibility */ - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setScrollable(getPageCount() > 1); - if (getCurrentPage() < getPageCount() - 1) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); - } - if (getCurrentPage() > 0) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); - } - } - - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); - event.setScrollable(true); - if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { - event.setFromIndex(mCurrentPage); - event.setToIndex(mCurrentPage); - event.setItemCount(getChildCount()); - } - } - - @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (super.performAccessibilityAction(action, arguments)) { - return true; - } - switch (action) { - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { - if (getCurrentPage() < getPageCount() - 1) { - scrollRight(); - return true; - } - } break; - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { - if (getCurrentPage() > 0) { - scrollLeft(); - return true; - } - } break; - } - return false; - } - - @Override - public boolean onHoverEvent(android.view.MotionEvent event) { - return true; - } - - void beginCameraEvent() { - mIsCameraEvent = true; - } - - void endCameraEvent() { - mIsCameraEvent = false; - } - - AnimatorListenerAdapter mOnScreenAnimationListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mWarpAnimation = null; - if (mTouchState != TOUCH_STATE_SCROLLING && mTouchState != TOUCH_STATE_READY) { - animateWarpPageOffScreen("onScreen end", true); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - super.onAnimationCancel(animation); - mWarpAnimation = null; - } - }; - - AnimatorListenerAdapter mOffScreenAnimationListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mWarpAnimation = null; - mWarpPageExposed = false; - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - v.setTranslationX(0.0f); - } - - @Override - public void onAnimationCancel(Animator animation) { - super.onAnimationCancel(animation); - mWarpAnimation = null; - } - }; - - private void cancelWarpAnimation(String msg, boolean abortAnimation) { - if (DEBUG_WARP) Log.v(TAG, "cancelWarpAnimation(" + msg + ",abort=" + abortAnimation + ")"); - if (abortAnimation) { - // We're done with the animation and moving to a new page. Let the scroller - // take over the animation. - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - v.animate().cancel(); - // Make the scroll amount match the current warp position. - scrollBy(Math.round(-v.getTranslationX()), 0); - v.setTranslationX(0); - } else { - animateWarpPageOffScreen("canceled", true); - } - } - - private boolean isAnimatingWarpPage() { - return mWarpAnimation != null; - } - - private void animateWarpPageOnScreen(String reason) { - if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOnScreen(" + reason + ")"); - if (!mWarpPageExposed) { - mWarpPageExposed = true; - dispatchOnPageBeginWarp(); - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - if (DEBUG_WARP) Log.v(TAG, "moving page on screen: Tx=" + v.getTranslationX()); - DecelerateInterpolator interp = new DecelerateInterpolator(1.5f); - int totalOffset = getCurrentWarpOffset(); - v.setTranslationX(totalOffset); - mWarpAnimation = v.animate(); - mWarpAnimation.translationX(mWarpPeekAmount+totalOffset) - .setInterpolator(interp) - .setDuration(WARP_PEEK_ANIMATION_DURATION) - .setListener(mOnScreenAnimationListener); - } - } - - private int getCurrentWarpOffset() { - if (mCurrentPage == getPageWarpIndex()) { - return 0; - } - View viewRight = getPageAt(mCurrentPage + 1); - View warpView = getPageAt(getPageWarpIndex()); - if (viewRight != warpView && viewRight != null && warpView != null) { - return viewRight.getLeft() - warpView.getLeft(); - } - return 0; - } - - private void animateWarpPageOffScreen(String reason, boolean animate) { - if (DEBUG_WARP) Log.v(TAG, "animateWarpPageOffScreen(" + reason + " anim:" + animate + ")"); - if (mWarpPageExposed) { - dispatchOnPageEndWarp(); - KeyguardWidgetFrame v = (KeyguardWidgetFrame) getPageAt(getPageWarpIndex()); - if (DEBUG_WARP) Log.v(TAG, "moving page off screen: Tx=" + v.getTranslationX()); - AccelerateInterpolator interp = new AccelerateInterpolator(1.5f); - int totalOffset = getCurrentWarpOffset(); - v.animate().translationX(totalOffset) - .setInterpolator(interp) - .setDuration(animate ? WARP_PEEK_ANIMATION_DURATION : 0) - .setListener(mOffScreenAnimationListener); - } else { - if (DEBUG_WARP) Log.e(TAG, "animateWarpPageOffScreen(): not warping", new Exception()); - } - } - - /** - * Swaps the position of the views by setting the left and right edges appropriately. - */ - void swapPages(int indexA, int indexB) { - View viewA = getPageAt(indexA); - View viewB = getPageAt(indexB); - if (viewA != viewB && viewA != null && viewB != null) { - int deltaX = viewA.getLeft() - viewB.getLeft(); - viewA.offsetLeftAndRight(-deltaX); - viewB.offsetLeftAndRight(deltaX); - } - } - - public void startPageWarp(int pageIndex) { - if (DEBUG_WARP) Log.v(TAG, "START WARP"); - if (pageIndex != mCurrentPage + 1) { - mPageSwapIndex = mCurrentPage + 1; - } - mPageWarpIndex = pageIndex; - } - - protected int getPageWarpIndex() { - return getPageCount() - 1; - } - - public void stopPageWarp() { - if (DEBUG_WARP) Log.v(TAG, "END WARP"); - // mPageSwapIndex is reset in snapToPage() after the scroll animation completes - } - - public void onPageBeginWarp() { - - } - - public void onPageEndWarp() { - - } - -} diff --git a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java index 67ddcfa..50e7ecb 100644 --- a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java +++ b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java @@ -92,6 +92,11 @@ public class PasswordTextView extends View { private Interpolator mDisappearInterpolator; private Interpolator mFastOutSlowInInterpolator; private boolean mShowPassword; + private UserActivityListener mUserActivityListener; + + public interface UserActivityListener { + void onUserActivity(); + } public PasswordTextView(Context context) { this(context, null); @@ -206,8 +211,15 @@ public class PasswordTextView extends View { sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1); } + public void setUserActivityListener(UserActivityListener userActivitiListener) { + mUserActivityListener = userActivitiListener; + } + private void userActivity() { mPM.userActivity(SystemClock.uptimeMillis(), false); + if (mUserActivityListener != null) { + mUserActivityListener.onUserActivity(); + } } public void deleteLastChar() { diff --git a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java index e2f91e3..b38cfd5 100644 --- a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java +++ b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java @@ -24,8 +24,4 @@ public interface SecurityMessageDisplay { public void setMessage(int resId, boolean important, Object... formatArgs); public void setTimeout(int timeout_ms); - - public void showBouncer(int animationDuration); - - public void hideBouncer(int animationDuration); } diff --git a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java b/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java deleted file mode 100644 index ab9286b..0000000 --- a/packages/Keyguard/src/com/android/keyguard/SlidingChallengeLayout.java +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * Copyright (C) 2012 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.keyguard; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.util.FloatProperty; -import android.util.Log; -import android.util.Property; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.Interpolator; -import android.widget.Scroller; - -/** - * This layout handles interaction with the sliding security challenge views - * that overlay/resize other keyguard contents. - */ -public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout { - private static final String TAG = "SlidingChallengeLayout"; - private static final boolean DEBUG = KeyguardConstants.DEBUG; - - // The drag handle is measured in dp above & below the top edge of the - // challenge view; these parameters change based on whether the challenge - // is open or closed. - private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp - private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp - private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp - private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp - - private static final int HANDLE_ANIMATE_DURATION = 250; // ms - - // Drawn to show the drag handle in closed state; crossfades to the challenge view - // when challenge is fully visible - private boolean mEdgeCaptured; - - private DisplayMetrics mDisplayMetrics; - - // Initialized during measurement from child layoutparams - private View mExpandChallengeView; - private KeyguardSecurityContainer mChallengeView; - private View mScrimView; - private View mWidgetsView; - - // Range: 0 (fully hidden) to 1 (fully visible) - private float mChallengeOffset = 1.f; - private boolean mChallengeShowing = true; - private boolean mChallengeShowingTargetState = true; - private boolean mWasChallengeShowing = true; - private boolean mIsBouncing = false; - - private final Scroller mScroller; - private ObjectAnimator mFader; - private int mScrollState; - private OnChallengeScrolledListener mScrollListener; - private OnBouncerStateChangedListener mBouncerListener; - private boolean mEnableChallengeDragging; - - public static final int SCROLL_STATE_IDLE = 0; - public static final int SCROLL_STATE_DRAGGING = 1; - public static final int SCROLL_STATE_SETTLING = 2; - public static final int SCROLL_STATE_FADING = 3; - - public static final int CHALLENGE_FADE_OUT_DURATION = 100; - public static final int CHALLENGE_FADE_IN_DURATION = 160; - - private static final int MAX_SETTLE_DURATION = 600; // ms - - // ID of the pointer in charge of a current drag - private int mActivePointerId = INVALID_POINTER; - private static final int INVALID_POINTER = -1; - - // True if the user is currently dragging the slider - private boolean mDragging; - // True if the user may not drag until a new gesture begins - private boolean mBlockDrag; - - private VelocityTracker mVelocityTracker; - private int mMinVelocity; - private int mMaxVelocity; - private float mGestureStartX, mGestureStartY; // where did you first touch the screen? - private int mGestureStartChallengeBottom; // where was the challenge at that time? - - private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view - private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line - private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view - private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line - - private int mDragHandleEdgeSlop; - private int mChallengeBottomBound; // Number of pixels from the top of the challenge view - // that should remain on-screen - - private int mTouchSlop; - private int mTouchSlopSquare; - - float mHandleAlpha; - float mFrameAlpha; - float mFrameAnimationTarget = Float.MIN_VALUE; - private ObjectAnimator mHandleAnimation; - private ObjectAnimator mFrameAnimation; - - private boolean mHasGlowpad; - private final Rect mInsets = new Rect(); - - // We have an internal and external version, and we and them together. - private boolean mChallengeInteractiveExternal = true; - private boolean mChallengeInteractiveInternal = true; - - static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA = - new FloatProperty<SlidingChallengeLayout>("handleAlpha") { - @Override - public void setValue(SlidingChallengeLayout view, float value) { - view.mHandleAlpha = value; - view.invalidate(); - } - - @Override - public Float get(SlidingChallengeLayout view) { - return view.mHandleAlpha; - } - }; - - // True if at least one layout pass has happened since the view was attached. - private boolean mHasLayout; - - private static final Interpolator sMotionInterpolator = new Interpolator() { - public float getInterpolation(float t) { - t -= 1.0f; - return t * t * t * t * t + 1.0f; - } - }; - - private static final Interpolator sHandleFadeInterpolator = new Interpolator() { - public float getInterpolation(float t) { - return t * t; - } - }; - - private final Runnable mEndScrollRunnable = new Runnable () { - public void run() { - completeChallengeScroll(); - } - }; - - private final OnClickListener mScrimClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - hideBouncer(); - } - }; - - private final OnClickListener mExpandChallengeClickListener = new OnClickListener() { - @Override - public void onClick(View v) { - if (!isChallengeShowing()) { - showChallenge(true); - } - } - }; - - /** - * Listener interface that reports changes in scroll state of the challenge area. - */ - public interface OnChallengeScrolledListener { - /** - * The scroll state itself changed. - * - * <p>scrollState will be one of the following:</p> - * - * <ul> - * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li> - * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging - * the challenge area.</li> - * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating - * into place.</li> - * </ul> - * - * <p>Do not perform expensive operations (e.g. layout) - * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p> - * - * @param scrollState The new scroll state of the challenge area. - */ - public void onScrollStateChanged(int scrollState); - - /** - * The precise position of the challenge area has changed. - * - * <p>NOTE: It is NOT safe to modify layout or call any View methods that may - * result in a requestLayout anywhere in your view hierarchy as a result of this call. - * It may be called during drawing.</p> - * - * @param scrollPosition New relative position of the challenge area. - * 1.f = fully visible/ready to be interacted with. - * 0.f = fully invisible/inaccessible to the user. - * @param challengeTop Position of the top edge of the challenge view in px in the - * SlidingChallengeLayout's coordinate system. - */ - public void onScrollPositionChanged(float scrollPosition, int challengeTop); - } - - public SlidingChallengeLayout(Context context) { - this(context, null); - } - - public SlidingChallengeLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mScroller = new Scroller(context, sMotionInterpolator); - - final ViewConfiguration vc = ViewConfiguration.get(context); - mMinVelocity = vc.getScaledMinimumFlingVelocity(); - mMaxVelocity = vc.getScaledMaximumFlingVelocity(); - - final Resources res = getResources(); - mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size); - - mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - mTouchSlopSquare = mTouchSlop * mTouchSlop; - - mDisplayMetrics = res.getDisplayMetrics(); - final float density = mDisplayMetrics.density; - - // top half of the lock icon, plus another 25% to be sure - mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f); - mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f); - mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f); - mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f); - - // how much space to account for in the handle when closed - mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding); - - setWillNotDraw(false); - setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); - } - - public void setEnableChallengeDragging(boolean enabled) { - mEnableChallengeDragging = enabled; - } - - public void setInsets(Rect insets) { - mInsets.set(insets); - } - - public void setHandleAlpha(float alpha) { - if (mExpandChallengeView != null) { - mExpandChallengeView.setAlpha(alpha); - } - } - - public void setChallengeInteractive(boolean interactive) { - mChallengeInteractiveExternal = interactive; - if (mExpandChallengeView != null) { - mExpandChallengeView.setEnabled(interactive); - } - } - - void animateHandle(boolean visible) { - if (mHandleAnimation != null) { - mHandleAnimation.cancel(); - mHandleAnimation = null; - } - final float targetAlpha = visible ? 1.f : 0.f; - if (targetAlpha == mHandleAlpha) { - return; - } - mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha); - mHandleAnimation.setInterpolator(sHandleFadeInterpolator); - mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION); - mHandleAnimation.start(); - } - - private void sendInitialListenerUpdates() { - if (mScrollListener != null) { - int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0; - mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop); - mScrollListener.onScrollStateChanged(mScrollState); - } - } - - public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) { - mScrollListener = listener; - if (mHasLayout) { - sendInitialListenerUpdates(); - } - } - - public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) { - mBouncerListener = listener; - } - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - - mHasLayout = false; - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - removeCallbacks(mEndScrollRunnable); - mHasLayout = false; - } - - @Override - public void requestChildFocus(View child, View focused) { - if (mIsBouncing && child != mChallengeView) { - // Clear out of the bouncer if the user tries to move focus outside of - // the security challenge view. - hideBouncer(); - } - super.requestChildFocus(child, focused); - } - - // We want the duration of the page snap animation to be influenced by the distance that - // the screen has to travel, however, we don't want this duration to be effected in a - // purely linear fashion. Instead, we use this method to moderate the effect that the distance - // of travel has on the overall snap duration. - float distanceInfluenceForSnapDuration(float f) { - f -= 0.5f; // center the values about 0. - f *= 0.3f * Math.PI / 2.0f; - return (float) Math.sin(f); - } - - void setScrollState(int state) { - if (mScrollState != state) { - mScrollState = state; - - animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing); - if (mScrollListener != null) { - mScrollListener.onScrollStateChanged(state); - } - } - } - - void completeChallengeScroll() { - setChallengeShowing(mChallengeShowingTargetState); - mChallengeOffset = mChallengeShowing ? 1.f : 0.f; - setScrollState(SCROLL_STATE_IDLE); - mChallengeInteractiveInternal = true; - mChallengeView.setLayerType(LAYER_TYPE_NONE, null); - } - - void setScrimView(View scrim) { - if (mScrimView != null) { - mScrimView.setOnClickListener(null); - } - mScrimView = scrim; - if (mScrimView != null) { - mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE); - mScrimView.setFocusable(true); - mScrimView.setOnClickListener(mScrimClickListener); - } - } - - /** - * Animate the bottom edge of the challenge view to the given position. - * - * @param y desired final position for the bottom edge of the challenge view in px - * @param velocity velocity in - */ - void animateChallengeTo(int y, int velocity) { - if (mChallengeView == null) { - // Nothing to do. - return; - } - - cancelTransitionsInProgress(); - - mChallengeInteractiveInternal = false; - enableHardwareLayerForChallengeView(); - final int sy = mChallengeView.getBottom(); - final int dy = y - sy; - if (dy == 0) { - completeChallengeScroll(); - return; - } - - setScrollState(SCROLL_STATE_SETTLING); - - final int childHeight = mChallengeView.getHeight(); - final int halfHeight = childHeight / 2; - final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight); - final float distance = halfHeight + halfHeight * - distanceInfluenceForSnapDuration(distanceRatio); - - int duration = 0; - velocity = Math.abs(velocity); - if (velocity > 0) { - duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); - } else { - final float childDelta = (float) Math.abs(dy) / childHeight; - duration = (int) ((childDelta + 1) * 100); - } - duration = Math.min(duration, MAX_SETTLE_DURATION); - - mScroller.startScroll(0, sy, 0, dy, duration); - postInvalidateOnAnimation(); - } - - private void setChallengeShowing(boolean showChallenge) { - if (mChallengeShowing == showChallenge) { - return; - } - mChallengeShowing = showChallenge; - - if (mExpandChallengeView == null || mChallengeView == null) { - // These might not be here yet if we haven't been through layout. - // If we haven't, the first layout pass will set everything up correctly - // based on mChallengeShowing as set above. - return; - } - - if (mChallengeShowing) { - mExpandChallengeView.setVisibility(View.INVISIBLE); - mChallengeView.setVisibility(View.VISIBLE); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - mChallengeView.requestAccessibilityFocus(); - mChallengeView.announceForAccessibility(mContext.getString( - R.string.keyguard_accessibility_unlock_area_expanded)); - } - } else { - mExpandChallengeView.setVisibility(View.VISIBLE); - mChallengeView.setVisibility(View.INVISIBLE); - if (AccessibilityManager.getInstance(mContext).isEnabled()) { - mExpandChallengeView.requestAccessibilityFocus(); - mChallengeView.announceForAccessibility(mContext.getString( - R.string.keyguard_accessibility_unlock_area_collapsed)); - } - } - } - - /** - * @return true if the challenge is at all visible. - */ - public boolean isChallengeShowing() { - return mChallengeShowing; - } - - @Override - public boolean isChallengeOverlapping() { - return mChallengeShowing; - } - - @Override - public boolean isBouncing() { - return mIsBouncing; - } - - @Override - public int getBouncerAnimationDuration() { - return HANDLE_ANIMATE_DURATION; - } - - @Override - public void showBouncer() { - if (mIsBouncing) return; - setSystemUiVisibility(getSystemUiVisibility() | STATUS_BAR_DISABLE_SEARCH); - mWasChallengeShowing = mChallengeShowing; - mIsBouncing = true; - showChallenge(true); - if (mScrimView != null) { - Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f); - anim.setDuration(HANDLE_ANIMATE_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mScrimView.setVisibility(VISIBLE); - } - }); - anim.start(); - } - if (mChallengeView != null) { - mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION); - } - - if (mBouncerListener != null) { - mBouncerListener.onBouncerStateChanged(true); - } - } - - @Override - public void hideBouncer() { - if (!mIsBouncing) return; - setSystemUiVisibility(getSystemUiVisibility() & ~STATUS_BAR_DISABLE_SEARCH); - if (!mWasChallengeShowing) showChallenge(false); - mIsBouncing = false; - - if (mScrimView != null) { - Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f); - anim.setDuration(HANDLE_ANIMATE_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mScrimView.setVisibility(GONE); - } - }); - anim.start(); - } - if (mChallengeView != null) { - mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION); - } - if (mBouncerListener != null) { - mBouncerListener.onBouncerStateChanged(false); - } - } - - private int getChallengeMargin(boolean expanded) { - return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop; - } - - private float getChallengeAlpha() { - float x = mChallengeOffset - 1; - return x * x * x + 1.f; - } - - @Override - public void requestDisallowInterceptTouchEvent(boolean allowIntercept) { - // We'll intercept whoever we feel like! ...as long as it isn't a challenge view. - // If there are one or more pointers in the challenge view before we take over - // touch events, onInterceptTouchEvent will set mBlockDrag. - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - final int action = ev.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: - mGestureStartX = ev.getX(); - mGestureStartY = ev.getY(); - mBlockDrag = false; - break; - - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - resetTouch(); - break; - - case MotionEvent.ACTION_MOVE: - final int count = ev.getPointerCount(); - for (int i = 0; i < count; i++) { - final float x = ev.getX(i); - final float y = ev.getY(i); - if (!mIsBouncing && mActivePointerId == INVALID_POINTER - && (crossedDragHandle(x, y, mGestureStartY) - && shouldEnableChallengeDragging() - || (isInChallengeView(x, y) && - mScrollState == SCROLL_STATE_SETTLING))) { - mActivePointerId = ev.getPointerId(i); - mGestureStartX = x; - mGestureStartY = y; - mGestureStartChallengeBottom = getChallengeBottom(); - mDragging = true; - enableHardwareLayerForChallengeView(); - } else if (mChallengeShowing && isInChallengeView(x, y) - && shouldEnableChallengeDragging()) { - mBlockDrag = true; - } - } - break; - } - - if (mBlockDrag || isChallengeInteractionBlocked()) { - mActivePointerId = INVALID_POINTER; - mDragging = false; - } - - return mDragging; - } - - private boolean shouldEnableChallengeDragging() { - return mEnableChallengeDragging || !mChallengeShowing; - } - - private boolean isChallengeInteractionBlocked() { - return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal; - } - - private void resetTouch() { - mVelocityTracker.recycle(); - mVelocityTracker = null; - mActivePointerId = INVALID_POINTER; - mDragging = mBlockDrag = false; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(ev); - - final int action = ev.getActionMasked(); - switch (action) { - case MotionEvent.ACTION_DOWN: - mBlockDrag = false; - mGestureStartX = ev.getX(); - mGestureStartY = ev.getY(); - break; - - case MotionEvent.ACTION_CANCEL: - if (mDragging && !isChallengeInteractionBlocked()) { - showChallenge(0); - } - resetTouch(); - break; - - case MotionEvent.ACTION_POINTER_UP: - if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) { - break; - } - case MotionEvent.ACTION_UP: - if (mDragging && !isChallengeInteractionBlocked()) { - mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); - showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId)); - } - resetTouch(); - break; - - case MotionEvent.ACTION_MOVE: - if (!mDragging && !mBlockDrag && !mIsBouncing) { - final int count = ev.getPointerCount(); - for (int i = 0; i < count; i++) { - final float x = ev.getX(i); - final float y = ev.getY(i); - - if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) || - (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) - && mActivePointerId == INVALID_POINTER - && !isChallengeInteractionBlocked()) { - mGestureStartX = x; - mGestureStartY = y; - mActivePointerId = ev.getPointerId(i); - mGestureStartChallengeBottom = getChallengeBottom(); - mDragging = true; - enableHardwareLayerForChallengeView(); - break; - } - } - } - // Not an else; this can be set above. - if (mDragging) { - // No-op if already in this state, but set it here in case we arrived - // at this point from either intercept or the above. - setScrollState(SCROLL_STATE_DRAGGING); - - final int index = ev.findPointerIndex(mActivePointerId); - if (index < 0) { - // Oops, bogus state. We lost some touch events somewhere. - // Just drop it with no velocity and let things settle. - resetTouch(); - showChallenge(0); - return true; - } - final float y = ev.getY(index); - final float pos = Math.min(y - mGestureStartY, - getLayoutBottom() - mChallengeBottomBound); - - moveChallengeTo(mGestureStartChallengeBottom + (int) pos); - } - break; - } - return true; - } - - /** - * The lifecycle of touch events is subtle and it's very easy to do something - * that will cause bugs that will be nasty to track when overriding this method. - * Normally one should always override onInterceptTouchEvent instead. - * - * To put it another way, don't try this at home. - */ - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - final int action = ev.getActionMasked(); - boolean handled = false; - if (action == MotionEvent.ACTION_DOWN) { - // Defensive programming: if we didn't get the UP or CANCEL, reset anyway. - mEdgeCaptured = false; - } - if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) { - // Normally we would need to do a lot of extra stuff here. - // We can only get away with this because we haven't padded in - // the widget pager or otherwise transformed it during layout. - // We also don't support things like splitting MotionEvents. - - // We set handled to captured even if dispatch is returning false here so that - // we don't send a different view a busted or incomplete event stream. - handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev); - } - - if (!handled && !mEdgeCaptured) { - handled = super.dispatchTouchEvent(ev); - } - - if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - mEdgeCaptured = false; - } - - return handled; - } - - private boolean isEdgeSwipeBeginEvent(MotionEvent ev) { - if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { - return false; - } - - final float x = ev.getX(); - return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop; - } - - /** - * We only want to add additional vertical space to the drag handle when the panel is fully - * closed. - */ - private int getDragHandleSizeAbove() { - return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove; - } - private int getDragHandleSizeBelow() { - return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow; - } - - private boolean isInChallengeView(float x, float y) { - return isPointInView(x, y, mChallengeView); - } - - private boolean isInDragHandle(float x, float y) { - return isPointInView(x, y, mExpandChallengeView); - } - - private boolean isPointInView(float x, float y, View view) { - if (view == null) { - return false; - } - return x >= view.getLeft() && y >= view.getTop() - && x < view.getRight() && y < view.getBottom(); - } - - private boolean crossedDragHandle(float x, float y, float initialY) { - - final int challengeTop = mChallengeView.getTop(); - final boolean horizOk = x >= 0 && x < getWidth(); - - final boolean vertOk; - if (mChallengeShowing) { - vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) && - y > challengeTop + getDragHandleSizeBelow(); - } else { - vertOk = initialY > challengeTop + getDragHandleSizeBelow() && - y < challengeTop - getDragHandleSizeAbove(); - } - return horizOk && vertOk; - } - - private int makeChildMeasureSpec(int maxSize, int childDimen) { - final int mode; - final int size; - switch (childDimen) { - case LayoutParams.WRAP_CONTENT: - mode = MeasureSpec.AT_MOST; - size = maxSize; - break; - case LayoutParams.MATCH_PARENT: - mode = MeasureSpec.EXACTLY; - size = maxSize; - break; - default: - mode = MeasureSpec.EXACTLY; - size = Math.min(maxSize, childDimen); - break; - } - return MeasureSpec.makeMeasureSpec(size, mode); - } - - @Override - protected void onMeasure(int widthSpec, int heightSpec) { - if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY || - MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) { - throw new IllegalArgumentException( - "SlidingChallengeLayout must be measured with an exact size"); - } - final int width = MeasureSpec.getSize(widthSpec); - final int height = MeasureSpec.getSize(heightSpec); - setMeasuredDimension(width, height); - - final int insetHeight = height - mInsets.top - mInsets.bottom; - final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY); - - // Find one and only one challenge view. - final View oldChallengeView = mChallengeView; - final View oldExpandChallengeView = mChallengeView; - mChallengeView = null; - mExpandChallengeView = null; - final int count = getChildCount(); - - // First iteration through the children finds special children and sets any associated - // state. - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) { - if (mChallengeView != null) { - throw new IllegalStateException( - "There may only be one child with layout_isChallenge=\"true\""); - } - if (!(child instanceof KeyguardSecurityContainer)) { - throw new IllegalArgumentException( - "Challenge must be a KeyguardSecurityContainer"); - } - mChallengeView = (KeyguardSecurityContainer) child; - if (mChallengeView != oldChallengeView) { - mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE); - } - // We're going to play silly games with the frame's background drawable later. - if (!mHasLayout) { - // Set up the margin correctly based on our content for the first run. - mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null; - lp.leftMargin = lp.rightMargin = getChallengeMargin(true); - } - } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) { - if (mExpandChallengeView != null) { - throw new IllegalStateException( - "There may only be one child with layout_childType" - + "=\"expandChallengeHandle\""); - } - mExpandChallengeView = child; - if (mExpandChallengeView != oldExpandChallengeView) { - mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE); - mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener); - } - } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { - setScrimView(child); - } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { - mWidgetsView = child; - } - } - - // We want to measure the challenge view first, since the KeyguardWidgetPager - // needs to do things its measure pass that are dependent on the challenge view - // having been measured. - if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) { - // This one's a little funny. If the IME is present - reported in the form - // of insets on the root view - we only give the challenge the space it would - // have had if the IME wasn't there in order to keep the rest of the layout stable. - // We base this on the layout_maxHeight on the challenge view. If it comes out - // negative or zero, either we didn't have a maxHeight or we're totally out of space, - // so give up and measure as if this rule weren't there. - int challengeHeightSpec = insetHeightSpec; - final View root = getRootView(); - if (root != null) { - final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams(); - final int windowHeight = mDisplayMetrics.heightPixels - - root.getPaddingTop() - mInsets.top; - final int diff = windowHeight - insetHeight; - final int maxChallengeHeight = lp.maxHeight - diff; - if (maxChallengeHeight > 0) { - challengeHeightSpec = makeChildMeasureSpec(maxChallengeHeight, lp.height); - } - } - measureChildWithMargins(mChallengeView, widthSpec, 0, challengeHeightSpec, 0); - } - - // Measure the rest of the children - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - // Don't measure the challenge view twice! - if (child == mChallengeView) continue; - - // Measure children. Widget frame measures special, so that we can ignore - // insets for the IME. - int parentWidthSpec = widthSpec, parentHeightSpec = insetHeightSpec; - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { - final View root = getRootView(); - if (root != null) { - // This calculation is super dodgy and relies on several assumptions. - // Specifically that the root of the window will be padded in for insets - // and that the window is LAYOUT_IN_SCREEN. - final int windowWidth = mDisplayMetrics.widthPixels; - final int windowHeight = mDisplayMetrics.heightPixels - - root.getPaddingTop() - mInsets.top; - parentWidthSpec = MeasureSpec.makeMeasureSpec( - windowWidth, MeasureSpec.EXACTLY); - parentHeightSpec = MeasureSpec.makeMeasureSpec( - windowHeight, MeasureSpec.EXACTLY); - } - } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { - // Allow scrim views to extend into the insets - parentWidthSpec = widthSpec; - parentHeightSpec = heightSpec; - } - measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0); - } - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int paddingLeft = getPaddingLeft(); - final int paddingTop = getPaddingTop(); - final int paddingRight = getPaddingRight(); - final int paddingBottom = getPaddingBottom(); - final int width = r - l; - final int height = b - t; - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - - if (child.getVisibility() == GONE) continue; - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) { - // Challenge views pin to the bottom, offset by a portion of their height, - // and center horizontally. - final int center = (paddingLeft + width - paddingRight) / 2; - final int childWidth = child.getMeasuredWidth(); - final int childHeight = child.getMeasuredHeight(); - final int left = center - childWidth / 2; - final int layoutBottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; - // We use the top of the challenge view to position the handle, so - // we never want less than the handle size showing at the bottom. - final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound) - * (1 - mChallengeOffset)); - child.setAlpha(getChallengeAlpha()); - child.layout(left, bottom - childHeight, left + childWidth, bottom); - } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) { - final int center = (paddingLeft + width - paddingRight) / 2; - final int left = center - child.getMeasuredWidth() / 2; - final int right = left + child.getMeasuredWidth(); - final int bottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; - final int top = bottom - child.getMeasuredHeight(); - child.layout(left, top, right, bottom); - } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { - // Scrim views use the entire area, including padding & insets - child.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); - } else { - // Non-challenge views lay out from the upper left, layered. - child.layout(paddingLeft + lp.leftMargin, - paddingTop + lp.topMargin + mInsets.top, - paddingLeft + child.getMeasuredWidth(), - paddingTop + child.getMeasuredHeight() + mInsets.top); - } - } - - if (!mHasLayout) { - mHasLayout = true; - } - } - - @Override - public void draw(Canvas c) { - super.draw(c); - if (DEBUG) { - final Paint debugPaint = new Paint(); - debugPaint.setColor(0x40FF00CC); - // show the isInDragHandle() rect - c.drawRect(mDragHandleEdgeSlop, - mChallengeView.getTop() - getDragHandleSizeAbove(), - getWidth() - mDragHandleEdgeSlop, - mChallengeView.getTop() + getDragHandleSizeBelow(), - debugPaint); - } - } - - @Override - protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { - // Focus security fileds before widgets. - if (mChallengeView != null && - mChallengeView.requestFocus(direction, previouslyFocusedRect)) { - return true; - } - return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); - } - - public void computeScroll() { - super.computeScroll(); - - if (!mScroller.isFinished()) { - if (mChallengeView == null) { - // Can't scroll if the view is missing. - Log.e(TAG, "Challenge view missing in computeScroll"); - mScroller.abortAnimation(); - return; - } - - mScroller.computeScrollOffset(); - moveChallengeTo(mScroller.getCurrY()); - - if (mScroller.isFinished()) { - post(mEndScrollRunnable); - } - } - } - - private void cancelTransitionsInProgress() { - if (!mScroller.isFinished()) { - mScroller.abortAnimation(); - completeChallengeScroll(); - } - if (mFader != null) { - mFader.cancel(); - } - } - - public void fadeInChallenge() { - fadeChallenge(true); - } - - public void fadeOutChallenge() { - fadeChallenge(false); - } - - public void fadeChallenge(final boolean show) { - if (mChallengeView != null) { - - cancelTransitionsInProgress(); - float alpha = show ? 1f : 0f; - int duration = show ? CHALLENGE_FADE_IN_DURATION : CHALLENGE_FADE_OUT_DURATION; - mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", alpha); - mFader.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - onFadeStart(show); - } - @Override - public void onAnimationEnd(Animator animation) { - onFadeEnd(show); - } - }); - mFader.setDuration(duration); - mFader.start(); - } - } - - private int getMaxChallengeBottom() { - if (mChallengeView == null) return 0; - final int layoutBottom = getLayoutBottom(); - final int challengeHeight = mChallengeView.getMeasuredHeight(); - - return (layoutBottom + challengeHeight - mChallengeBottomBound); - } - - private int getMinChallengeBottom() { - return getLayoutBottom(); - } - - - private void onFadeStart(boolean show) { - mChallengeInteractiveInternal = false; - enableHardwareLayerForChallengeView(); - - if (show) { - moveChallengeTo(getMinChallengeBottom()); - } - - setScrollState(SCROLL_STATE_FADING); - } - - private void enableHardwareLayerForChallengeView() { - if (mChallengeView.isHardwareAccelerated()) { - mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null); - } - } - - private void onFadeEnd(boolean show) { - mChallengeInteractiveInternal = true; - setChallengeShowing(show); - - if (!show) { - moveChallengeTo(getMaxChallengeBottom()); - } - - mChallengeView.setLayerType(LAYER_TYPE_NONE, null); - mFader = null; - setScrollState(SCROLL_STATE_IDLE); - } - - public int getMaxChallengeTop() { - if (mChallengeView == null) return 0; - - final int layoutBottom = getLayoutBottom(); - final int challengeHeight = mChallengeView.getMeasuredHeight(); - return layoutBottom - challengeHeight - mInsets.top; - } - - /** - * Move the bottom edge of mChallengeView to a new position and notify the listener - * if it represents a change in position. Changes made through this method will - * be stable across layout passes. If this method is called before first layout of - * this SlidingChallengeLayout it will have no effect. - * - * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system. - * @return true if the challenge view was moved - */ - private boolean moveChallengeTo(int bottom) { - if (mChallengeView == null || !mHasLayout) { - return false; - } - - final int layoutBottom = getLayoutBottom(); - final int challengeHeight = mChallengeView.getHeight(); - - bottom = Math.max(getMinChallengeBottom(), - Math.min(bottom, getMaxChallengeBottom())); - - float offset = 1.f - (float) (bottom - layoutBottom) / - (challengeHeight - mChallengeBottomBound); - mChallengeOffset = offset; - if (offset > 0 && !mChallengeShowing) { - setChallengeShowing(true); - } - - mChallengeView.layout(mChallengeView.getLeft(), - bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom); - - mChallengeView.setAlpha(getChallengeAlpha()); - if (mScrollListener != null) { - mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop()); - } - postInvalidateOnAnimation(); - return true; - } - - /** - * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with - * the bottom edge of mChallengeView when the challenge is fully opened. - */ - private int getLayoutBottom() { - final int bottomMargin = (mChallengeView == null) - ? 0 - : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin; - final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin - - mInsets.bottom; - return layoutBottom; - } - - /** - * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'. - */ - private int getChallengeBottom() { - if (mChallengeView == null) return 0; - - return mChallengeView.getBottom(); - } - - /** - * Show or hide the challenge view, animating it if necessary. - * @param show true to show, false to hide - */ - public void showChallenge(boolean show) { - showChallenge(show, 0); - if (!show) { - // Block any drags in progress so that callers can use this to disable dragging - // for other touch interactions. - mBlockDrag = true; - } - } - - private void showChallenge(int velocity) { - boolean show = false; - if (Math.abs(velocity) > mMinVelocity) { - show = velocity < 0; - } else { - show = mChallengeOffset >= 0.5f; - } - showChallenge(show, velocity); - } - - private void showChallenge(boolean show, int velocity) { - if (mChallengeView == null) { - setChallengeShowing(false); - return; - } - - if (mHasLayout) { - mChallengeShowingTargetState = show; - final int layoutBottom = getLayoutBottom(); - animateChallengeTo(show ? layoutBottom : - layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity); - } - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : - p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) : - new LayoutParams(p); - } - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams; - } - - public static class LayoutParams extends MarginLayoutParams { - public int childType = CHILD_TYPE_NONE; - public static final int CHILD_TYPE_NONE = 0; - public static final int CHILD_TYPE_CHALLENGE = 2; - public static final int CHILD_TYPE_SCRIM = 4; - public static final int CHILD_TYPE_WIDGETS = 5; - public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6; - - public int maxHeight; - - public LayoutParams() { - this(MATCH_PARENT, WRAP_CONTENT); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(android.view.ViewGroup.LayoutParams source) { - super(source); - } - - public LayoutParams(MarginLayoutParams source) { - super(source); - } - - public LayoutParams(LayoutParams source) { - super(source); - - childType = source.childType; - } - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - - final TypedArray a = c.obtainStyledAttributes(attrs, - R.styleable.SlidingChallengeLayout_Layout); - childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType, - CHILD_TYPE_NONE); - maxHeight = a.getDimensionPixelSize( - R.styleable.SlidingChallengeLayout_Layout_layout_maxHeight, 0); - a.recycle(); - } - } -} diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java index ab69a0c..ff463c6 100644 --- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java @@ -47,12 +47,6 @@ public interface ViewMediatorCallback { void setNeedsInput(boolean needsInput); /** - * Tell view mediator that the keyguard view's desired user activity timeout - * has changed and needs to be reapplied to the window. - */ - void onUserActivityTimeoutChanged(); - - /** * Report that the keyguard is dismissable, pending the next keyguardDone call. */ void keyguardDonePending(); @@ -68,6 +62,11 @@ public interface ViewMediatorCallback { void readyForKeyguardDone(); /** + * Reset the keyguard and bouncer. + */ + void resetKeyguard(); + + /** * Play the "device trusted" sound. */ void playTrustedSound(); @@ -77,4 +76,17 @@ public interface ViewMediatorCallback { * (legacy API) */ boolean isInputRestricted(); + + /** + * @return true if the screen is on + */ + boolean isScreenOn(); + + /** + * @return one of the reasons why the bouncer needs to be shown right now and the user can't use + * his normal unlock method like fingerprint or trust agents. See + * {@link KeyguardSecurityView#PROMPT_REASON_NONE} + * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}. + */ + int getBouncerPromptReason(); } |
