Loading core/java/com/android/internal/widget/LockPatternView.java +122 −25 Original line number Diff line number Diff line Loading @@ -19,16 +19,20 @@ package com.android.internal.widget; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.Bundle; Loading Loading @@ -70,7 +74,9 @@ public class LockPatternView extends View { private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h) private static final boolean PROFILE_DRAWING = false; private static final float LINE_FADE_ALPHA_MULTIPLIER = 1.5f; private static final int LINE_END_ANIMATION_DURATION_MILLIS = 50; private static final int LINE_FADE_OUT_DURATION_MILLIS = 500; private static final int LINE_FADE_OUT_DELAY_MILLIS = 150; private final CellState[][] mCellStates; private final int mDotSize; Loading Loading @@ -139,6 +145,7 @@ public class LockPatternView extends View { private float mSquareWidth; @UnsupportedAppUsage private float mSquareHeight; private final LinearGradient mFadeOutGradientShader; private final Path mCurrentPath = new Path(); private final Rect mInvalidate = new Rect(); Loading Loading @@ -232,7 +239,10 @@ public class LockPatternView extends View { float alpha = 1f; public float lineEndX = Float.MIN_VALUE; public float lineEndY = Float.MIN_VALUE; public ValueAnimator lineAnimator; @Nullable ValueAnimator lineEndAnimator; @Nullable ValueAnimator lineDisappearingAnimator; } /** Loading Loading @@ -361,6 +371,14 @@ public class LockPatternView extends View { mExploreByTouchHelper = new PatternExploreByTouchHelper(this); setAccessibilityDelegate(mExploreByTouchHelper); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int fadeAwayGradientWidth = getResources().getDimensionPixelSize( R.dimen.lock_pattern_fade_away_gradient_width); // Set up gradient shader with the middle in point (0, 0). mFadeOutGradientShader = new LinearGradient(/* x0= */ -fadeAwayGradientWidth / 2f, /* y0= */ 0,/* x1= */ fadeAwayGradientWidth / 2f, /* y1= */ 0, Color.TRANSPARENT, pathColor, Shader.TileMode.CLAMP); a.recycle(); } Loading Loading @@ -791,8 +809,13 @@ public class LockPatternView extends View { }); startLineEndAnimation(cellState, mInProgressX, mInProgressY, getCenterXForColumn(cell.column), getCenterYForRow(cell.row)); startLineDisappearingAnimation(cellState); } /** * On the last frame before cell activates the end point of in progress line is not aligned * with dot center so we execute a short animation moving the end point to exact dot center. */ private void startLineEndAnimation(final CellState state, final float startX, final float startY, final float targetX, final float targetY) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); Loading @@ -808,13 +831,37 @@ public class LockPatternView extends View { valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { state.lineAnimator = null; state.lineEndAnimator = null; } }); valueAnimator.setInterpolator(mFastOutSlowInInterpolator); valueAnimator.setDuration(100); valueAnimator.setDuration(LINE_END_ANIMATION_DURATION_MILLIS); valueAnimator.start(); state.lineAnimator = valueAnimator; state.lineEndAnimator = valueAnimator; } /** * Starts animator to fade out a line segment. It does only invalidate because all the * transitions are applied in {@code onDraw} method. */ private void startLineDisappearingAnimation(final CellState state) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } }); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { state.lineDisappearingAnimator = null; } }); valueAnimator.setStartDelay(LINE_FADE_OUT_DELAY_MILLIS); valueAnimator.setDuration(LINE_FADE_OUT_DURATION_MILLIS); valueAnimator.start(); state.lineDisappearingAnimator = valueAnimator; } private void startRadiusAnimation(float start, float end, long duration, Loading Loading @@ -1051,11 +1098,16 @@ public class LockPatternView extends View { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { CellState state = mCellStates[i][j]; if (state.lineAnimator != null) { state.lineAnimator.cancel(); if (state.lineEndAnimator != null) { state.lineEndAnimator.cancel(); state.lineEndAnimator = null; state.lineEndX = Float.MIN_VALUE; state.lineEndY = Float.MIN_VALUE; } if (state.lineDisappearingAnimator != null) { state.lineDisappearingAnimator.cancel(); state.lineDisappearingAnimator = null; } } } } Loading Loading @@ -1195,29 +1247,19 @@ public class LockPatternView extends View { float centerX = getCenterXForColumn(cell.column); float centerY = getCenterYForRow(cell.row); if (i != 0) { // Set this line segment to fade away animated. int lineFadeVal = (int) Math.min((elapsedRealtime - mLineFadeStart[i]) * LINE_FADE_ALPHA_MULTIPLIER, 255f); CellState state = mCellStates[cell.row][cell.column]; currentPath.rewind(); currentPath.moveTo(lastX, lastY); float endX; float endY; if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) { currentPath.lineTo(state.lineEndX, state.lineEndY); if (mFadePattern) { mPathPaint.setAlpha((int) 255 - lineFadeVal ); } else { mPathPaint.setAlpha(255); } } else { currentPath.lineTo(centerX, centerY); if (mFadePattern) { mPathPaint.setAlpha((int) 255 - lineFadeVal ); endX = state.lineEndX; endY = state.lineEndY; } else { mPathPaint.setAlpha(255); } endX = centerX; endY = centerY; } canvas.drawPath(currentPath, mPathPaint); drawLineSegment(canvas, /* startX = */ lastX, /* startY = */ lastY, endX, endY, mLineFadeStart[i], elapsedRealtime); } lastX = centerX; lastY = centerY; Loading Loading @@ -1260,6 +1302,61 @@ public class LockPatternView extends View { } } private void drawLineSegment(Canvas canvas, float startX, float startY, float endX, float endY, long lineFadeStart, long elapsedRealtime) { float fadeAwayProgress; if (mFadePattern) { if (elapsedRealtime - lineFadeStart >= LINE_FADE_OUT_DELAY_MILLIS + LINE_FADE_OUT_DURATION_MILLIS) { // Time for this segment animation is out so we don't need to draw it. return; } // Set this line segment to fade away animated. fadeAwayProgress = Math.max( ((float) (elapsedRealtime - lineFadeStart - LINE_FADE_OUT_DELAY_MILLIS)) / LINE_FADE_OUT_DURATION_MILLIS, 0f); drawFadingAwayLineSegment(canvas, startX, startY, endX, endY, fadeAwayProgress); } else { mPathPaint.setAlpha(255); canvas.drawLine(startX, startY, endX, endY, mPathPaint); } } private void drawFadingAwayLineSegment(Canvas canvas, float startX, float startY, float endX, float endY, float fadeAwayProgress) { mPathPaint.setAlpha((int) (255 * (1 - fadeAwayProgress))); // To draw gradient segment we use mFadeOutGradientShader which has immutable coordinates // thus we will need to translate and rotate the canvas. mPathPaint.setShader(mFadeOutGradientShader); canvas.save(); // First translate canvas to gradient middle point. float gradientMidX = endX * fadeAwayProgress + startX * (1 - fadeAwayProgress); float gradientMidY = endY * fadeAwayProgress + startY * (1 - fadeAwayProgress); canvas.translate(gradientMidX, gradientMidY); // Then rotate it to the direction of the segment. double segmentAngleRad = Math.atan((endY - startY) / (endX - startX)); float segmentAngleDegrees = (float) Math.toDegrees(segmentAngleRad); if (endX - startX < 0) { // Arc tangent gives us angle degrees [-90; 90] thus to cover [90; 270] degrees we // need this hack. segmentAngleDegrees += 180f; } canvas.rotate(segmentAngleDegrees); // Pythagoras theorem. float segmentLength = (float) Math.hypot(endX - startX, endY - startY); // Draw the segment in coordinates aligned with shader coordinates. canvas.drawLine(/* startX= */ -segmentLength * fadeAwayProgress, /* startY= */ 0,/* stopX= */ segmentLength * (1 - fadeAwayProgress), /* stopY= */ 0, mPathPaint); canvas.restore(); mPathPaint.setShader(null); } private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) { float diffX = x - lastX; float diffY = y - lastY; Loading core/res/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -641,6 +641,8 @@ <dimen name="lock_pattern_dot_line_width">22dp</dimen> <dimen name="lock_pattern_dot_size">14dp</dimen> <dimen name="lock_pattern_dot_size_activated">30dp</dimen> <!-- Width of a gradient applied to a lock pattern line while its disappearing animation. --> <dimen name="lock_pattern_fade_away_gradient_width">8dp</dimen> <dimen name="text_handle_min_size">40dp</dimen> Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1318,6 +1318,7 @@ <java-symbol type="dimen" name="lock_pattern_dot_line_width" /> <java-symbol type="dimen" name="lock_pattern_dot_size" /> <java-symbol type="dimen" name="lock_pattern_dot_size_activated" /> <java-symbol type="dimen" name="lock_pattern_fade_away_gradient_width" /> <java-symbol type="drawable" name="clock_dial" /> <java-symbol type="drawable" name="clock_hand_hour" /> <java-symbol type="drawable" name="clock_hand_minute" /> Loading Loading
core/java/com/android/internal/widget/LockPatternView.java +122 −25 Original line number Diff line number Diff line Loading @@ -19,16 +19,20 @@ package com.android.internal.widget; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.Bundle; Loading Loading @@ -70,7 +74,9 @@ public class LockPatternView extends View { private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h) private static final boolean PROFILE_DRAWING = false; private static final float LINE_FADE_ALPHA_MULTIPLIER = 1.5f; private static final int LINE_END_ANIMATION_DURATION_MILLIS = 50; private static final int LINE_FADE_OUT_DURATION_MILLIS = 500; private static final int LINE_FADE_OUT_DELAY_MILLIS = 150; private final CellState[][] mCellStates; private final int mDotSize; Loading Loading @@ -139,6 +145,7 @@ public class LockPatternView extends View { private float mSquareWidth; @UnsupportedAppUsage private float mSquareHeight; private final LinearGradient mFadeOutGradientShader; private final Path mCurrentPath = new Path(); private final Rect mInvalidate = new Rect(); Loading Loading @@ -232,7 +239,10 @@ public class LockPatternView extends View { float alpha = 1f; public float lineEndX = Float.MIN_VALUE; public float lineEndY = Float.MIN_VALUE; public ValueAnimator lineAnimator; @Nullable ValueAnimator lineEndAnimator; @Nullable ValueAnimator lineDisappearingAnimator; } /** Loading Loading @@ -361,6 +371,14 @@ public class LockPatternView extends View { mExploreByTouchHelper = new PatternExploreByTouchHelper(this); setAccessibilityDelegate(mExploreByTouchHelper); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int fadeAwayGradientWidth = getResources().getDimensionPixelSize( R.dimen.lock_pattern_fade_away_gradient_width); // Set up gradient shader with the middle in point (0, 0). mFadeOutGradientShader = new LinearGradient(/* x0= */ -fadeAwayGradientWidth / 2f, /* y0= */ 0,/* x1= */ fadeAwayGradientWidth / 2f, /* y1= */ 0, Color.TRANSPARENT, pathColor, Shader.TileMode.CLAMP); a.recycle(); } Loading Loading @@ -791,8 +809,13 @@ public class LockPatternView extends View { }); startLineEndAnimation(cellState, mInProgressX, mInProgressY, getCenterXForColumn(cell.column), getCenterYForRow(cell.row)); startLineDisappearingAnimation(cellState); } /** * On the last frame before cell activates the end point of in progress line is not aligned * with dot center so we execute a short animation moving the end point to exact dot center. */ private void startLineEndAnimation(final CellState state, final float startX, final float startY, final float targetX, final float targetY) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); Loading @@ -808,13 +831,37 @@ public class LockPatternView extends View { valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { state.lineAnimator = null; state.lineEndAnimator = null; } }); valueAnimator.setInterpolator(mFastOutSlowInInterpolator); valueAnimator.setDuration(100); valueAnimator.setDuration(LINE_END_ANIMATION_DURATION_MILLIS); valueAnimator.start(); state.lineAnimator = valueAnimator; state.lineEndAnimator = valueAnimator; } /** * Starts animator to fade out a line segment. It does only invalidate because all the * transitions are applied in {@code onDraw} method. */ private void startLineDisappearingAnimation(final CellState state) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } }); valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { state.lineDisappearingAnimator = null; } }); valueAnimator.setStartDelay(LINE_FADE_OUT_DELAY_MILLIS); valueAnimator.setDuration(LINE_FADE_OUT_DURATION_MILLIS); valueAnimator.start(); state.lineDisappearingAnimator = valueAnimator; } private void startRadiusAnimation(float start, float end, long duration, Loading Loading @@ -1051,11 +1098,16 @@ public class LockPatternView extends View { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { CellState state = mCellStates[i][j]; if (state.lineAnimator != null) { state.lineAnimator.cancel(); if (state.lineEndAnimator != null) { state.lineEndAnimator.cancel(); state.lineEndAnimator = null; state.lineEndX = Float.MIN_VALUE; state.lineEndY = Float.MIN_VALUE; } if (state.lineDisappearingAnimator != null) { state.lineDisappearingAnimator.cancel(); state.lineDisappearingAnimator = null; } } } } Loading Loading @@ -1195,29 +1247,19 @@ public class LockPatternView extends View { float centerX = getCenterXForColumn(cell.column); float centerY = getCenterYForRow(cell.row); if (i != 0) { // Set this line segment to fade away animated. int lineFadeVal = (int) Math.min((elapsedRealtime - mLineFadeStart[i]) * LINE_FADE_ALPHA_MULTIPLIER, 255f); CellState state = mCellStates[cell.row][cell.column]; currentPath.rewind(); currentPath.moveTo(lastX, lastY); float endX; float endY; if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) { currentPath.lineTo(state.lineEndX, state.lineEndY); if (mFadePattern) { mPathPaint.setAlpha((int) 255 - lineFadeVal ); } else { mPathPaint.setAlpha(255); } } else { currentPath.lineTo(centerX, centerY); if (mFadePattern) { mPathPaint.setAlpha((int) 255 - lineFadeVal ); endX = state.lineEndX; endY = state.lineEndY; } else { mPathPaint.setAlpha(255); } endX = centerX; endY = centerY; } canvas.drawPath(currentPath, mPathPaint); drawLineSegment(canvas, /* startX = */ lastX, /* startY = */ lastY, endX, endY, mLineFadeStart[i], elapsedRealtime); } lastX = centerX; lastY = centerY; Loading Loading @@ -1260,6 +1302,61 @@ public class LockPatternView extends View { } } private void drawLineSegment(Canvas canvas, float startX, float startY, float endX, float endY, long lineFadeStart, long elapsedRealtime) { float fadeAwayProgress; if (mFadePattern) { if (elapsedRealtime - lineFadeStart >= LINE_FADE_OUT_DELAY_MILLIS + LINE_FADE_OUT_DURATION_MILLIS) { // Time for this segment animation is out so we don't need to draw it. return; } // Set this line segment to fade away animated. fadeAwayProgress = Math.max( ((float) (elapsedRealtime - lineFadeStart - LINE_FADE_OUT_DELAY_MILLIS)) / LINE_FADE_OUT_DURATION_MILLIS, 0f); drawFadingAwayLineSegment(canvas, startX, startY, endX, endY, fadeAwayProgress); } else { mPathPaint.setAlpha(255); canvas.drawLine(startX, startY, endX, endY, mPathPaint); } } private void drawFadingAwayLineSegment(Canvas canvas, float startX, float startY, float endX, float endY, float fadeAwayProgress) { mPathPaint.setAlpha((int) (255 * (1 - fadeAwayProgress))); // To draw gradient segment we use mFadeOutGradientShader which has immutable coordinates // thus we will need to translate and rotate the canvas. mPathPaint.setShader(mFadeOutGradientShader); canvas.save(); // First translate canvas to gradient middle point. float gradientMidX = endX * fadeAwayProgress + startX * (1 - fadeAwayProgress); float gradientMidY = endY * fadeAwayProgress + startY * (1 - fadeAwayProgress); canvas.translate(gradientMidX, gradientMidY); // Then rotate it to the direction of the segment. double segmentAngleRad = Math.atan((endY - startY) / (endX - startX)); float segmentAngleDegrees = (float) Math.toDegrees(segmentAngleRad); if (endX - startX < 0) { // Arc tangent gives us angle degrees [-90; 90] thus to cover [90; 270] degrees we // need this hack. segmentAngleDegrees += 180f; } canvas.rotate(segmentAngleDegrees); // Pythagoras theorem. float segmentLength = (float) Math.hypot(endX - startX, endY - startY); // Draw the segment in coordinates aligned with shader coordinates. canvas.drawLine(/* startX= */ -segmentLength * fadeAwayProgress, /* startY= */ 0,/* stopX= */ segmentLength * (1 - fadeAwayProgress), /* stopY= */ 0, mPathPaint); canvas.restore(); mPathPaint.setShader(null); } private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) { float diffX = x - lastX; float diffY = y - lastY; Loading
core/res/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -641,6 +641,8 @@ <dimen name="lock_pattern_dot_line_width">22dp</dimen> <dimen name="lock_pattern_dot_size">14dp</dimen> <dimen name="lock_pattern_dot_size_activated">30dp</dimen> <!-- Width of a gradient applied to a lock pattern line while its disappearing animation. --> <dimen name="lock_pattern_fade_away_gradient_width">8dp</dimen> <dimen name="text_handle_min_size">40dp</dimen> Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -1318,6 +1318,7 @@ <java-symbol type="dimen" name="lock_pattern_dot_line_width" /> <java-symbol type="dimen" name="lock_pattern_dot_size" /> <java-symbol type="dimen" name="lock_pattern_dot_size_activated" /> <java-symbol type="dimen" name="lock_pattern_fade_away_gradient_width" /> <java-symbol type="drawable" name="clock_dial" /> <java-symbol type="drawable" name="clock_hand_hour" /> <java-symbol type="drawable" name="clock_hand_minute" /> Loading