19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.internal.widget;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.animation.Animator;
20c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.animation.AnimatorListenerAdapter;
21c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.animation.ValueAnimator;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
23240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.content.res.Resources;
24bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Millerimport android.content.res.TypedArray;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas;
26613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggiimport android.graphics.CanvasProperty;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Path;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
30240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.media.AudioManager;
31240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.os.Bundle;
326a109b4874b79d9bf8f5d990bfe831a59e5a83dcKarl Rosaenimport android.os.Debug;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock;
36240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.os.UserHandle;
37240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.provider.Settings;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
39240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.util.IntArray;
40240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.util.Log;
41613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggiimport android.view.DisplayListCanvas;
42aef555bcf26e770e37f2065913084588fb92c6fbJim Millerimport android.view.HapticFeedbackConstants;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
44613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggiimport android.view.RenderNodeAnimator;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
46240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityEvent;
47530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
48240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityNodeInfo;
49240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
50c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.view.animation.AnimationUtils;
51c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.view.animation.Interpolator;
52530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
53530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganovimport com.android.internal.R;
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
56240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport java.util.HashMap;
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Displays and detects the user's unlock attempt, which is a drag of a finger
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * across 9 regions of the screen.
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Is also capable of displaying a static pattern in "in progress", "wrong" or
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * "correct" states.
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class LockPatternView extends View {
67bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    // Aspect to use when rendering this view
68bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_SQUARE = 0; // View will be the minimum of width/height
69bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_LOCK_WIDTH = 1; // Fixed width; height will be minimum of (w,h)
70bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
71bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final boolean PROFILE_DRAWING = false;
733018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    private final CellState[][] mCellStates;
74c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
75c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mDotSize;
76c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mDotSizeActivated;
77c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mPathWidth;
78c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDrawingProfilingStarted = false;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
81a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Paint mPaint = new Paint();
82a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Paint mPathPaint = new Paint();
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * How many milliseconds we spend animating each circle of a lock pattern
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * if the animating mode is set.  The entire animation should take this
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * constant * the length of the pattern to complete.
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MILLIS_PER_CIRCLE_ANIMATING = 700;
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
910caa377f4688f175ae22229a10294468610a116eJim Miller    /**
920caa377f4688f175ae22229a10294468610a116eJim Miller     * This can be used to avoid updating the display for very small motions or noisy panels.
930caa377f4688f175ae22229a10294468610a116eJim Miller     * It didn't seem to have much impact on the devices tested, so currently set to 0.
940caa377f4688f175ae22229a10294468610a116eJim Miller     */
950caa377f4688f175ae22229a10294468610a116eJim Miller    private static final float DRAG_THRESHHOLD = 0.0f;
96240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    public static final int VIRTUAL_BASE_VIEW_ID = 1;
97d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    public static final boolean DEBUG_A11Y = false;
98240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private static final String TAG = "LockPatternView";
990caa377f4688f175ae22229a10294468610a116eJim Miller
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnPatternListener mOnPatternListener;
101a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Lookup table for the circles of the pattern we are currently drawing.
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This will be the cells of the complete pattern unless we are animating,
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in which case we use this to hold the cells we are drawing for the in
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * progress animation.
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
109a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final boolean[][] mPatternDrawLookup = new boolean[3][3];
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the in progress point:
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * - during interaction: where the user's finger is
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * - during animation: the current tip of the animating line
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mInProgressX = -1;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mInProgressY = -1;
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private long mAnimatingPeriodStart;
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mInputEnabled = true;
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mInStealthMode = false;
124aef555bcf26e770e37f2065913084588fb92c6fbJim Miller    private boolean mEnableHapticFeedback = true;
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mPatternInProgress = false;
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mHitFactor = 0.6f;
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSquareWidth;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSquareHeight;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Path mCurrentPath = new Path();
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Rect mInvalidate = new Rect();
1349ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller    private final Rect mTmpInvalidateRect = new Rect();
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
136bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private int mAspect;
137c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mRegularColor;
138c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mErrorColor;
139c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mSuccessColor;
1402cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek
141a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Interpolator mFastOutSlowInInterpolator;
142a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Interpolator mLinearOutSlowInInterpolator;
143240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private PatternExploreByTouchHelper mExploreByTouchHelper;
144240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private AudioManager mAudioManager;
145bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
149a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    public static final class Cell {
150a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        final int row;
151a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        final int column;
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // keep # objects limited to 9
154a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        private static final Cell[][] sCells = createCells();
155a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley
156a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        private static Cell[][] createCells() {
157a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley            Cell[][] res = new Cell[3][3];
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < 3; i++) {
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int j = 0; j < 3; j++) {
160a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley                    res[i][j] = new Cell(i, j);
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
163a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley            return res;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param row The row of the cell.
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param column The column of the cell.
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Cell(int row, int column) {
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checkRange(row, column);
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.row = row;
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.column = column;
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getRow() {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return row;
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getColumn() {
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return column;
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
184a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        public static Cell of(int row, int column) {
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checkRange(row, column);
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sCells[row][column];
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static void checkRange(int row, int column) {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (row < 0 || row > 2) {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalArgumentException("row must be in range 0-2");
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (column < 0 || column > 2) {
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalArgumentException("column must be in range 0-2");
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
198a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        @Override
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "(row=" + row + ",clmn=" + column + ")";
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2043018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    public static class CellState {
205613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        int row;
206613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        int col;
207613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        boolean hwAnimating;
208613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwRadius;
209613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwCenterX;
210613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwCenterY;
211613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Paint> hwPaint;
212613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float radius;
213613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float translationY;
214613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float alpha = 1f;
215c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public float lineEndX = Float.MIN_VALUE;
216c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public float lineEndY = Float.MIN_VALUE;
217c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public ValueAnimator lineAnimator;
2183018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek     }
2193018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * How to display the current pattern.
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum DisplayMode {
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern drawn is correct (i.e draw it in a friendly color)
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Correct,
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Animate the pattern (for demo, and help).
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Animate,
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern is wrong (i.e draw a foreboding color)
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Wrong
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The call back interface for detecting patterns entered by the user.
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface OnPatternListener {
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A new pattern has begun.
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternStart();
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern was cleared.
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternCleared();
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
25741e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         * The user extended the pattern currently being drawn by one cell.
25841e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         * @param pattern The pattern with newly added cell.
25941e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         */
26041e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller        void onPatternCellAdded(List<Cell> pattern);
26141e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller
26241e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller        /**
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A pattern was detected from the user.
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param pattern The pattern.
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternDetected(List<Cell> pattern);
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public LockPatternView(Context context) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public LockPatternView(Context context, AttributeSet attrs) {
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super(context, attrs);
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
276bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LockPatternView);
277bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
278bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        final String aspect = a.getString(R.styleable.LockPatternView_aspect);
279bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
280bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        if ("square".equals(aspect)) {
281bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_SQUARE;
282bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else if ("lock_width".equals(aspect)) {
283bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_LOCK_WIDTH;
284bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else if ("lock_height".equals(aspect)) {
285bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_LOCK_HEIGHT;
286bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else {
287bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_SQUARE;
288bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        }
289bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setClickable(true);
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2922cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setAntiAlias(true);
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setDither(true);
2951cf3594114b1846c97243ff536074c49bbe06061Fabrice Di Meglio
2964a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette        mRegularColor = context.getColor(R.color.lock_pattern_view_regular_color);
2974a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette        mErrorColor = context.getColor(R.color.lock_pattern_view_error_color);
2984a357cd2e55293402d7172766f7f9419815fc1e8Alan Viverette        mSuccessColor = context.getColor(R.color.lock_pattern_view_success_color);
299c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, mRegularColor);
300c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, mErrorColor);
301c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, mSuccessColor);
302c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
303c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
3042cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPathPaint.setColor(pathColor);
3051cf3594114b1846c97243ff536074c49bbe06061Fabrice Di Meglio
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStyle(Paint.Style.STROKE);
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStrokeJoin(Paint.Join.ROUND);
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStrokeCap(Paint.Cap.ROUND);
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
310c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPathWidth = getResources().getDimensionPixelSize(R.dimen.lock_pattern_dot_line_width);
311c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPathPaint.setStrokeWidth(mPathWidth);
312c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
313c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mDotSize = getResources().getDimensionPixelSize(R.dimen.lock_pattern_dot_size);
314c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mDotSizeActivated = getResources().getDimensionPixelSize(
315c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                R.dimen.lock_pattern_dot_size_activated);
31685d630020a29c24f3d51f00b5ce3f701c16f0a45Jim Miller
3172cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPaint.setAntiAlias(true);
3182cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPaint.setDither(true);
3193018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
3203018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        mCellStates = new CellState[3][3];
3213018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        for (int i = 0; i < 3; i++) {
3223018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek            for (int j = 0; j < 3; j++) {
3233018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek                mCellStates[i][j] = new CellState();
324613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].radius = mDotSize/2;
325613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].row = i;
326613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].col = j;
3273018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek            }
3283018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        }
329c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
330c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mFastOutSlowInInterpolator =
331c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
332c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mLinearOutSlowInInterpolator =
333c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
334240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper = new PatternExploreByTouchHelper(this);
335240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        setAccessibilityDelegate(mExploreByTouchHelper);
336240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
337ac010478a6e64407ca0f18fcab4b2aff2ba0274dJim Miller        a.recycle();
3383018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    }
3393018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
3403018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    public CellState[][] getCellStates() {
3413018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        return mCellStates;
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Whether the view is in stealth mode.
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isInStealthMode() {
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mInStealthMode;
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Whether the view has tactile feedback enabled.
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isTactileFeedbackEnabled() {
355aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        return mEnableHapticFeedback;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set whether the view is in stealth mode.  If true, there will be no
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * visible feedback as the user enters the pattern.
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param inStealthMode Whether in stealth mode.
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setInStealthMode(boolean inStealthMode) {
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInStealthMode = inStealthMode;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set whether the view will use tactile feedback.  If true, there will be
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * tactile feedback as the user enters the pattern.
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tactileFeedbackEnabled Whether tactile feedback is enabled
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
375aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        mEnableHapticFeedback = tactileFeedbackEnabled;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the call back for pattern detection.
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param onPatternListener The call back.
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setOnPatternListener(
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OnPatternListener onPatternListener) {
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOnPatternListener = onPatternListener;
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the pattern explicitely (rather than waiting for the user to input
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a pattern).
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param displayMode How to display the pattern.
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param pattern The pattern.
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setPattern(DisplayMode displayMode, List<Cell> pattern) {
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.clear();
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.addAll(pattern);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        clearPatternDrawLookup();
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Cell cell : pattern) {
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPatternDrawLookup[cell.getRow()][cell.getColumn()] = true;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setDisplayMode(displayMode);
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the display mode of the current pattern.  This can be useful, for
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * instance, after detecting a pattern to tell this view whether change the
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in progress result to correct or wrong.
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param displayMode The display mode.
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setDisplayMode(DisplayMode displayMode) {
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = displayMode;
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (displayMode == DisplayMode.Animate) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mPattern.size() == 0) {
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalStateException("you must have a pattern to "
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + "animate if you want to set the display mode to animate");
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAnimatingPeriodStart = SystemClock.elapsedRealtime();
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Cell first = mPattern.get(0);
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInProgressX = getCenterXForColumn(first.getColumn());
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInProgressY = getCenterYForRow(first.getRow());
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            clearPatternDrawLookup();
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        invalidate();
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
426613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    public void startCellStateAnimation(CellState cellState, float startAlpha, float endAlpha,
427613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startTranslationY, float endTranslationY, float startScale, float endScale,
428613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration,
429613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Interpolator interpolator, Runnable finishRunnable) {
430613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        if (isHardwareAccelerated()) {
431613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            startCellStateAnimationHw(cellState, startAlpha, endAlpha, startTranslationY,
432613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    endTranslationY, startScale, endScale, delay, duration, interpolator,
433613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable);
434613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        } else {
435613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            startCellStateAnimationSw(cellState, startAlpha, endAlpha, startTranslationY,
436613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    endTranslationY, startScale, endScale, delay, duration, interpolator,
437613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable);
438613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        }
439613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
440613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
441613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startCellStateAnimationSw(final CellState cellState,
442613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startAlpha, final float endAlpha,
443613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startTranslationY, final float endTranslationY,
444613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startScale, final float endScale,
445613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
446613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.alpha = startAlpha;
447613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.translationY = startTranslationY;
448613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.radius = mDotSize/2 * startScale;
449613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
450613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
451613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
452613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
453613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
454613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            @Override
455613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
456613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                float t = (float) animation.getAnimatedValue();
457613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.alpha = (1 - t) * startAlpha + t * endAlpha;
458613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.translationY = (1 - t) * startTranslationY + t * endTranslationY;
459613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.radius = mDotSize/2 * ((1 - t) * startScale + t * endScale);
460613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                invalidate();
461613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            }
462613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        });
463613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addListener(new AnimatorListenerAdapter() {
464613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            @Override
465613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            public void onAnimationEnd(Animator animation) {
466613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                if (finishRunnable != null) {
467613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable.run();
468613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                }
469613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            }
470613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        });
471613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
472613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
473613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
474613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startCellStateAnimationHw(final CellState cellState,
475613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startAlpha, float endAlpha,
476613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startTranslationY, float endTranslationY,
477613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startScale, float endScale,
478613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
479613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.alpha = endAlpha;
480613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.translationY = endTranslationY;
481613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.radius = mDotSize/2 * endScale;
482613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwAnimating = true;
483613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwCenterY = CanvasProperty.createFloat(
484613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                getCenterYForRow(cellState.row) + startTranslationY);
485613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
486613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
487613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        mPaint.setColor(getCurrentColor(false));
488613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        mPaint.setAlpha((int) (startAlpha * 255));
489613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
490613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
491613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtFloatAnimation(cellState.hwCenterY,
492613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                getCenterYForRow(cellState.row) + endTranslationY, delay, duration, interpolator);
493613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtFloatAnimation(cellState.hwRadius, mDotSize/2 * endScale, delay, duration,
494613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                interpolator);
495613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtAlphaAnimation(cellState, endAlpha, delay, duration, interpolator,
496613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                new AnimatorListenerAdapter() {
497613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    @Override
498613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    public void onAnimationEnd(Animator animation) {
499613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        cellState.hwAnimating = false;
500613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        if (finishRunnable != null) {
501613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                            finishRunnable.run();
502613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        }
503613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    }
504613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                });
505613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
506613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        invalidate();
507613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
508613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
509613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRtAlphaAnimation(CellState cellState, float endAlpha,
510613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator,
511613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Animator.AnimatorListener listener) {
512613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        RenderNodeAnimator animator = new RenderNodeAnimator(cellState.hwPaint,
513613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                RenderNodeAnimator.PAINT_ALPHA, (int) (endAlpha * 255));
514613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
515613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
516613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
517613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setTarget(this);
518613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addListener(listener);
519613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
520613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
521613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
522613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRtFloatAnimation(CanvasProperty<Float> property, float endValue,
523613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator) {
524613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        RenderNodeAnimator animator = new RenderNodeAnimator(property, endValue);
525613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
526613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
527613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
528613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setTarget(this);
529613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
530613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
531613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
532530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyCellAdded() {
533240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        // sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
534530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
535530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternCellAdded(mPattern);
536530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
537240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        // Disable used cells for accessibility as they get added
538240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        if (DEBUG_A11Y) Log.v(TAG, "ivnalidating root because cell was added.");
539240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper.invalidateRoot();
540530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
541530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
542530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternStarted() {
543e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_start);
544530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
545530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternStart();
546530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
547530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
548530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
549530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternDetected() {
550e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_detected);
551530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
552530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternDetected(mPattern);
553530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
554530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
555530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
556530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternCleared() {
557e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_cleared);
558530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
559530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternCleared();
560530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
561530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
562530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Clear the pattern.
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void clearPattern() {
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        resetPattern();
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
570240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    @Override
571240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    protected boolean dispatchHoverEvent(MotionEvent event) {
572d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        // Dispatch to onHoverEvent first so mPatternInProgress is up to date when the
573d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        // helper gets the event.
574d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        boolean handled = super.dispatchHoverEvent(event);
575d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        handled |= mExploreByTouchHelper.dispatchHoverEvent(event);
576d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        return handled;
577240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    }
578240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Reset all pattern state.
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void resetPattern() {
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.clear();
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        clearPatternDrawLookup();
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = DisplayMode.Correct;
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        invalidate();
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Clear the pattern lookup table.
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void clearPatternDrawLookup() {
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = 0; j < 3; j++) {
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDrawLookup[i][j] = false;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Disable input (for instance when displaying a message that will
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * timeout so user doesn't get view into messy state).
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void disableInput() {
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = false;
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enable input.
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void enableInput() {
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = true;
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int width = w - mPaddingLeft - mPaddingRight;
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSquareWidth = width / 3.0f;
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
620240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")");
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int height = h - mPaddingTop - mPaddingBottom;
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSquareHeight = height / 3.0f;
623240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper.invalidateRoot();
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6260a0753808ea27955472c2283413fc230bc85595bJim Miller    private int resolveMeasured(int measureSpec, int desired)
6270a0753808ea27955472c2283413fc230bc85595bJim Miller    {
6280a0753808ea27955472c2283413fc230bc85595bJim Miller        int result = 0;
6290a0753808ea27955472c2283413fc230bc85595bJim Miller        int specSize = MeasureSpec.getSize(measureSpec);
6300a0753808ea27955472c2283413fc230bc85595bJim Miller        switch (MeasureSpec.getMode(measureSpec)) {
6310a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.UNSPECIFIED:
6320a0753808ea27955472c2283413fc230bc85595bJim Miller                result = desired;
6330a0753808ea27955472c2283413fc230bc85595bJim Miller                break;
6340a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.AT_MOST:
6357bc60abc97f77cc2478f5d19c391ebdb1433a893Peter Ng                result = Math.max(specSize, desired);
6360a0753808ea27955472c2283413fc230bc85595bJim Miller                break;
6370a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.EXACTLY:
6380a0753808ea27955472c2283413fc230bc85595bJim Miller            default:
6391fd16378812792913a6aa6923acbec20037e09ffPhilip Milne                result = specSize;
6400a0753808ea27955472c2283413fc230bc85595bJim Miller        }
6410a0753808ea27955472c2283413fc230bc85595bJim Miller        return result;
6420a0753808ea27955472c2283413fc230bc85595bJim Miller    }
6430a0753808ea27955472c2283413fc230bc85595bJim Miller
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
6460d244193bed0c995e2beaff5e217200457b28b9dJim Miller        final int minimumWidth = getSuggestedMinimumWidth();
6470d244193bed0c995e2beaff5e217200457b28b9dJim Miller        final int minimumHeight = getSuggestedMinimumHeight();
6480a0753808ea27955472c2283413fc230bc85595bJim Miller        int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
6490a0753808ea27955472c2283413fc230bc85595bJim Miller        int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
6500a0753808ea27955472c2283413fc230bc85595bJim Miller
651bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        switch (mAspect) {
652bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_SQUARE:
6530d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewWidth = viewHeight = Math.min(viewWidth, viewHeight);
654bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
655bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_LOCK_WIDTH:
6560d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewHeight = Math.min(viewWidth, viewHeight);
657bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
658bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_LOCK_HEIGHT:
6590d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewWidth = Math.min(viewWidth, viewHeight);
660bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
661bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        }
6620a0753808ea27955472c2283413fc230bc85595bJim Miller        // Log.v(TAG, "LockPatternView dimensions: " + viewWidth + "x" + viewHeight);
663bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        setMeasuredDimension(viewWidth, viewHeight);
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Determines whether the point x, y will add a new point to the current
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * pattern (in addition to finding the cell, also makes heuristic choices
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * such as filling in gaps based on current pattern).
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param x The x coordinate.
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param y The y coordinate.
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Cell detectAndAddHit(float x, float y) {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Cell cell = checkForNewHit(x, y);
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cell != null) {
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // check for gaps in existing pattern
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cell fillInGapCell = null;
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final ArrayList<Cell> pattern = mPattern;
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!pattern.isEmpty()) {
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell lastCell = pattern.get(pattern.size() - 1);
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dRow = cell.row - lastCell.row;
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dColumn = cell.column - lastCell.column;
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fillInRow = lastCell.row;
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fillInColumn = lastCell.column;
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) {
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1);
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) {
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1);
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                fillInGapCell = Cell.of(fillInRow, fillInColumn);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fillInGapCell != null &&
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                addCellToPattern(fillInGapCell);
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addCellToPattern(cell);
704aef555bcf26e770e37f2065913084588fb92c6fbJim Miller            if (mEnableHapticFeedback) {
705aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
706aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
707aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                        | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cell;
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void addCellToPattern(Cell newCell) {
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.add(newCell);
717c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (!mInStealthMode) {
718c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            startCellActivatedAnimation(newCell);
719c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
720530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        notifyCellAdded();
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
723c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void startCellActivatedAnimation(Cell cell) {
724c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        final CellState cellState = mCellStates[cell.row][cell.column];
725613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRadiusAnimation(mDotSize/2, mDotSizeActivated/2, 96, mLinearOutSlowInInterpolator,
726c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                cellState, new Runnable() {
727613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    @Override
728613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    public void run() {
729613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        startRadiusAnimation(mDotSizeActivated/2, mDotSize/2, 192,
730613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                                mFastOutSlowInInterpolator,
731613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                                cellState, null);
732613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    }
733613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                });
734c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        startLineEndAnimation(cellState, mInProgressX, mInProgressY,
735c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
736c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
737c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
738c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void startLineEndAnimation(final CellState state,
739c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            final float startX, final float startY, final float targetX, final float targetY) {
740c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
741c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
742c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
743c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
744c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                float t = (float) animation.getAnimatedValue();
745c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineEndX = (1 - t) * startX + t * targetX;
746c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineEndY = (1 - t) * startY + t * targetY;
747c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                invalidate();
748c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
749c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
750c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addListener(new AnimatorListenerAdapter() {
751c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
752c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationEnd(Animator animation) {
753c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineAnimator = null;
754c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
755c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
756c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setInterpolator(mFastOutSlowInInterpolator);
757c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setDuration(100);
758c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.start();
759c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        state.lineAnimator = valueAnimator;
760c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
761c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
762613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRadiusAnimation(float start, float end, long duration,
763613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Interpolator interpolator, final CellState state, final Runnable endRunnable) {
764c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
765c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
766c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
767c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
768613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                state.radius = (float) animation.getAnimatedValue();
769c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                invalidate();
770c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
771c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
772c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (endRunnable != null) {
773c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            valueAnimator.addListener(new AnimatorListenerAdapter() {
774c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                @Override
775c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                public void onAnimationEnd(Animator animation) {
776c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    endRunnable.run();
777c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                }
778c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            });
779c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
780c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setInterpolator(interpolator);
781c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setDuration(duration);
782c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.start();
783c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
784c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // helper method to find which cell a point maps to
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Cell checkForNewHit(float x, float y) {
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int rowHit = getRowHit(y);
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (rowHit < 0) {
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int columnHit = getColumnHit(x);
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (columnHit < 0) {
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPatternDrawLookup[rowHit][columnHit]) {
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Cell.of(rowHit, columnHit);
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Helper method to find the row that y falls into.
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param y The y coordinate
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The row that y falls in, or -1 if it falls in no row.
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getRowHit(float y) {
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final float squareHeight = mSquareHeight;
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float hitSize = squareHeight * mHitFactor;
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float offset = mPaddingTop + (squareHeight - hitSize) / 2f;
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float hitTop = offset + squareHeight * i;
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (y >= hitTop && y <= hitTop + hitSize) {
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Helper method to find the column x fallis into.
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param x The x coordinate.
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The column that x falls in, or -1 if it falls in no column.
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getColumnHit(float x) {
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final float squareWidth = mSquareWidth;
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float hitSize = squareWidth * mHitFactor;
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float offset = mPaddingLeft + (squareWidth - hitSize) / 2f;
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float hitLeft = offset + squareWidth * i;
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (x >= hitLeft && x <= hitLeft + hitSize) {
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
845530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    public boolean onHoverEvent(MotionEvent event) {
846530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
847530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            final int action = event.getAction();
848530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            switch (action) {
849530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER:
850530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_DOWN);
851530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
852530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE:
853530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_MOVE);
854530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
855530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT:
856530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_UP);
857530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
858530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            }
859530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            onTouchEvent(event);
860530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            event.setAction(action);
861530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
862530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        return super.onHoverEvent(event);
863530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
864530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
865530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    @Override
866aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    public boolean onTouchEvent(MotionEvent event) {
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mInputEnabled || !isEnabled()) {
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
871aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        switch(event.getAction()) {
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_DOWN:
873aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                handleActionDown(event);
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_UP:
876a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley                handleActionUp();
877aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                return true;
878aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            case MotionEvent.ACTION_MOVE:
879aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                handleActionMove(event);
880aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                return true;
881aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            case MotionEvent.ACTION_CANCEL:
882c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                if (mPatternInProgress) {
883d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    setPatternInProgress(false);
884c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                    resetPattern();
885c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                    notifyPatternCleared();
886c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                }
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (PROFILE_DRAWING) {
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mDrawingProfilingStarted) {
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Debug.stopMethodTracing();
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mDrawingProfilingStarted = false;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
894aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
895aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        return false;
896aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
898d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    private void setPatternInProgress(boolean progress) {
899d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        mPatternInProgress = progress;
900d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        mExploreByTouchHelper.invalidateRoot();
901d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    }
902d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
903aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    private void handleActionMove(MotionEvent event) {
904aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // Handle all recent motion events so we don't skip any cells even when the device
905aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // is busy...
906c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        final float radius = mPathWidth;
907aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final int historySize = event.getHistorySize();
9089ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller        mTmpInvalidateRect.setEmpty();
9090caa377f4688f175ae22229a10294468610a116eJim Miller        boolean invalidateNow = false;
910aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        for (int i = 0; i < historySize + 1; i++) {
911aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
912aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
913aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            Cell hitCell = detectAndAddHit(x, y);
914aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final int patternSize = mPattern.size();
915530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            if (hitCell != null && patternSize == 1) {
916d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                setPatternInProgress(true);
917530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                notifyPatternStarted();
918aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
919aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            // note current x and y for rubber banding of in progress patterns
920aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float dx = Math.abs(x - mInProgressX);
921aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float dy = Math.abs(y - mInProgressY);
9220caa377f4688f175ae22229a10294468610a116eJim Miller            if (dx > DRAG_THRESHHOLD || dy > DRAG_THRESHHOLD) {
9230caa377f4688f175ae22229a10294468610a116eJim Miller                invalidateNow = true;
9240caa377f4688f175ae22229a10294468610a116eJim Miller            }
925aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
9260caa377f4688f175ae22229a10294468610a116eJim Miller            if (mPatternInProgress && patternSize > 0) {
9270caa377f4688f175ae22229a10294468610a116eJim Miller                final ArrayList<Cell> pattern = mPattern;
9280caa377f4688f175ae22229a10294468610a116eJim Miller                final Cell lastCell = pattern.get(patternSize - 1);
9299ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float lastCellCenterX = getCenterXForColumn(lastCell.column);
9309ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float lastCellCenterY = getCenterYForRow(lastCell.row);
9310caa377f4688f175ae22229a10294468610a116eJim Miller
9329ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                // Adjust for drawn segment from last cell to (x,y). Radius accounts for line width.
9339ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float left = Math.min(lastCellCenterX, x) - radius;
9349ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float right = Math.max(lastCellCenterX, x) + radius;
9359ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float top = Math.min(lastCellCenterY, y) - radius;
9369ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float bottom = Math.max(lastCellCenterY, y) + radius;
9370caa377f4688f175ae22229a10294468610a116eJim Miller
9380caa377f4688f175ae22229a10294468610a116eJim Miller                // Invalidate between the pattern's new cell and the pattern's previous cell
9399ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                if (hitCell != null) {
9400caa377f4688f175ae22229a10294468610a116eJim Miller                    final float width = mSquareWidth * 0.5f;
9410caa377f4688f175ae22229a10294468610a116eJim Miller                    final float height = mSquareHeight * 0.5f;
9429ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    final float hitCellCenterX = getCenterXForColumn(hitCell.column);
9439ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    final float hitCellCenterY = getCenterYForRow(hitCell.row);
9449ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller
9459ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    left = Math.min(hitCellCenterX - width, left);
9469ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    right = Math.max(hitCellCenterX + width, right);
9479ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    top = Math.min(hitCellCenterY - height, top);
9489ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    bottom = Math.max(hitCellCenterY + height, bottom);
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9500caa377f4688f175ae22229a10294468610a116eJim Miller
9510caa377f4688f175ae22229a10294468610a116eJim Miller                // Invalidate between the pattern's last cell and the previous location
9529ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                mTmpInvalidateRect.union(Math.round(left), Math.round(top),
9530caa377f4688f175ae22229a10294468610a116eJim Miller                        Math.round(right), Math.round(bottom));
954aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
955aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
9560caa377f4688f175ae22229a10294468610a116eJim Miller        mInProgressX = event.getX();
9570caa377f4688f175ae22229a10294468610a116eJim Miller        mInProgressY = event.getY();
9580caa377f4688f175ae22229a10294468610a116eJim Miller
9590caa377f4688f175ae22229a10294468610a116eJim Miller        // To save updates, we only invalidate if the user moved beyond a certain amount.
9600caa377f4688f175ae22229a10294468610a116eJim Miller        if (invalidateNow) {
9619ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            mInvalidate.union(mTmpInvalidateRect);
9629ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            invalidate(mInvalidate);
9639ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            mInvalidate.set(mTmpInvalidateRect);
9640caa377f4688f175ae22229a10294468610a116eJim Miller        }
965aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
966aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
967530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void sendAccessEvent(int resId) {
968e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        announceForAccessibility(mContext.getString(resId));
969530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
970530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
971a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private void handleActionUp() {
972aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // report pattern detected
973530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (!mPattern.isEmpty()) {
974d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(false);
975c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            cancelLineAnimations();
976530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternDetected();
977aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            invalidate();
978aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
979aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (PROFILE_DRAWING) {
980aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            if (mDrawingProfilingStarted) {
981aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                Debug.stopMethodTracing();
982aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                mDrawingProfilingStarted = false;
983aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
984aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
985aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
986aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
987c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void cancelLineAnimations() {
988c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        for (int i = 0; i < 3; i++) {
989c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            for (int j = 0; j < 3; j++) {
990c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                CellState state = mCellStates[i][j];
991c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                if (state.lineAnimator != null) {
992c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineAnimator.cancel();
993c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineEndX = Float.MIN_VALUE;
994c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineEndY = Float.MIN_VALUE;
995c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                }
996c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
997c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
998c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
999aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    private void handleActionDown(MotionEvent event) {
1000aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        resetPattern();
1001aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final float x = event.getX();
1002aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final float y = event.getY();
1003aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final Cell hitCell = detectAndAddHit(x, y);
1004530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (hitCell != null) {
1005d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(true);
1006aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            mPatternDisplayMode = DisplayMode.Correct;
1007530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternStarted();
1008c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        } else if (mPatternInProgress) {
1009d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(false);
1010530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternCleared();
1011aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1012aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (hitCell != null) {
1013aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float startX = getCenterXForColumn(hitCell.column);
1014aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float startY = getCenterYForRow(hitCell.row);
1015aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
1016aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float widthOffset = mSquareWidth / 2f;
1017aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float heightOffset = mSquareHeight / 2f;
1018aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
1019aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
1020aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                    (int) (startX + widthOffset), (int) (startY + heightOffset));
1021aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1022aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        mInProgressX = x;
1023aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        mInProgressY = y;
1024aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (PROFILE_DRAWING) {
1025aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            if (!mDrawingProfilingStarted) {
1026aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                Debug.startMethodTracing("LockPatternDrawing");
1027aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                mDrawingProfilingStarted = true;
1028aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float getCenterXForColumn(int column) {
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f;
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float getCenterYForRow(int row) {
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaddingTop + row * mSquareHeight + mSquareHeight / 2f;
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onDraw(Canvas canvas) {
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Cell> pattern = mPattern;
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = pattern.size();
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final boolean[][] drawLookup = mPatternDrawLookup;
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPatternDisplayMode == DisplayMode.Animate) {
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // figure out which circles to draw
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // + 1 so we pause on complete pattern
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int oneCycle = (count + 1) * MILLIS_PER_CIRCLE_ANIMATING;
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int spotInCycle = (int) (SystemClock.elapsedRealtime() -
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mAnimatingPeriodStart) % oneCycle;
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int numCircles = spotInCycle / MILLIS_PER_CIRCLE_ANIMATING;
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            clearPatternDrawLookup();
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < numCircles; i++) {
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell cell = pattern.get(i);
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                drawLookup[cell.getRow()][cell.getColumn()] = true;
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // figure out in progress portion of ghosting line
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final boolean needToUpdateInProgressPoint = numCircles > 0
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && numCircles < count;
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (needToUpdateInProgressPoint) {
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float percentageOfNextCircle =
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ((float) (spotInCycle % MILLIS_PER_CIRCLE_ANIMATING)) /
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                MILLIS_PER_CIRCLE_ANIMATING;
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell currentCell = pattern.get(numCircles - 1);
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float centerX = getCenterXForColumn(currentCell.column);
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float centerY = getCenterYForRow(currentCell.row);
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell nextCell = pattern.get(numCircles);
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float dx = percentageOfNextCircle *
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        (getCenterXForColumn(nextCell.column) - centerX);
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float dy = percentageOfNextCircle *
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        (getCenterYForRow(nextCell.row) - centerY);
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mInProgressX = centerX + dx;
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mInProgressY = centerY + dy;
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // TODO: Infinite loop here...
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            invalidate();
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Path currentPath = mCurrentPath;
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        currentPath.rewind();
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // draw the circles
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
1093c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float centerY = getCenterYForRow(i);
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = 0; j < 3; j++) {
1095c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                CellState cellState = mCellStates[i][j];
1096c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                float centerX = getCenterXForColumn(j);
1097613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                float translationY = cellState.translationY;
1098613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                if (isHardwareAccelerated() && cellState.hwAnimating) {
1099613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
1100613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
1101613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                            cellState.hwRadius, cellState.hwPaint);
1102613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                } else {
1103613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    drawCircle(canvas, (int) centerX, (int) centerY + translationY,
1104613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                            cellState.radius, drawLookup[i][j], cellState.alpha);
1105613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
1106613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                }
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
111008a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        // TODO: the path should be created and cached every time we hit-detect a cell
111108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        // only the last segment of the path should be computed here
1112e2d71e45209a4ac0787b360c4b0eb4d617863f3fAdrian Roos        // draw the path of the pattern (unless we are in stealth mode)
1113e2d71e45209a4ac0787b360c4b0eb4d617863f3fAdrian Roos        final boolean drawPath = !mInStealthMode;
111408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (drawPath) {
1116c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            mPathPaint.setColor(getCurrentColor(true /* partOfPattern */));
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
111808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            boolean anyCircles = false;
1119c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float lastX = 0f;
1120c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float lastY = 0f;
112108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            for (int i = 0; i < count; i++) {
112208a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                Cell cell = pattern.get(i);
112308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
112408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // only draw the part of the pattern stored in
112508a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // the lookup table (this is only different in the case
112608a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // of animation).
112708a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                if (!drawLookup[cell.row][cell.column]) {
112808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                    break;
112908a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                }
113008a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                anyCircles = true;
113108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
113208a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                float centerX = getCenterXForColumn(cell.column);
113308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                float centerY = getCenterYForRow(cell.row);
1134c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                if (i != 0) {
1135c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    CellState state = mCellStates[cell.row][cell.column];
1136c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    currentPath.rewind();
1137c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    currentPath.moveTo(lastX, lastY);
1138c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
1139c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        currentPath.lineTo(state.lineEndX, state.lineEndY);
1140c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    } else {
1141c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        currentPath.lineTo(centerX, centerY);
1142c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    }
1143c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    canvas.drawPath(currentPath, mPathPaint);
114408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                }
1145c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                lastX = centerX;
1146c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                lastY = centerY;
114708a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            }
114808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
1149c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            // draw last in progress section
115008a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            if ((mPatternInProgress || mPatternDisplayMode == DisplayMode.Animate)
115108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                    && anyCircles) {
1152c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                currentPath.rewind();
1153c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                currentPath.moveTo(lastX, lastY);
115408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                currentPath.lineTo(mInProgressX, mInProgressY);
1155c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
1156c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                mPathPaint.setAlpha((int) (calculateLastSegmentAlpha(
1157c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        mInProgressX, mInProgressY, lastX, lastY) * 255f));
1158c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                canvas.drawPath(currentPath, mPathPaint);
115908a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            }
116008a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        }
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1163c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) {
1164c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float diffX = x - lastX;
1165c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float diffY = y - lastY;
1166c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float dist = (float) Math.sqrt(diffX*diffX + diffY*diffY);
1167c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float frac = dist/mSquareWidth;
1168c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f));
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1171c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int getCurrentColor(boolean partOfPattern) {
1172c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (!partOfPattern || mInStealthMode || mPatternInProgress) {
11739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // unselected circle
1174c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mRegularColor;
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mPatternDisplayMode == DisplayMode.Wrong) {
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the pattern is wrong
1177c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mErrorColor;
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mPatternDisplayMode == DisplayMode.Correct ||
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDisplayMode == DisplayMode.Animate) {
1180c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mSuccessColor;
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("unknown display mode " + mPatternDisplayMode);
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1184c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1186c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    /**
1187c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi     * @param partOfPattern Whether this circle is part of the pattern.
1188c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi     */
1189613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
1190c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            boolean partOfPattern, float alpha) {
1191c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPaint.setColor(getCurrentColor(partOfPattern));
1192c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPaint.setAlpha((int) (alpha * 255));
1193613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        canvas.drawCircle(centerX, centerY, radius, mPaint);
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Parcelable onSaveInstanceState() {
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parcelable superState = super.onSaveInstanceState();
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new SavedState(superState,
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LockPatternUtils.patternToString(mPattern),
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDisplayMode.ordinal(),
1202aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                mInputEnabled, mInStealthMode, mEnableHapticFeedback);
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onRestoreInstanceState(Parcelable state) {
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final SavedState ss = (SavedState) state;
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onRestoreInstanceState(ss.getSuperState());
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setPattern(
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                DisplayMode.Correct,
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = ss.isInputEnabled();
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInStealthMode = ss.isInStealthMode();
1215aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The parecelable for saving and restoring a lock pattern view.
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class SavedState extends BaseSavedState {
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String mSerializedPattern;
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final int mDisplayMode;
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mInputEnabled;
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mInStealthMode;
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mTactileFeedbackEnabled;
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link LockPatternView#onSaveInstanceState()}
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private SavedState(Parcelable superState, String serializedPattern, int displayMode,
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) {
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(superState);
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSerializedPattern = serializedPattern;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDisplayMode = displayMode;
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInputEnabled = inputEnabled;
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInStealthMode = inStealthMode;
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTactileFeedbackEnabled = tactileFeedbackEnabled;
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link #CREATOR}
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private SavedState(Parcel in) {
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(in);
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSerializedPattern = in.readString();
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDisplayMode = in.readInt();
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInputEnabled = (Boolean) in.readValue(null);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInStealthMode = (Boolean) in.readValue(null);
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTactileFeedbackEnabled = (Boolean) in.readValue(null);
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1253bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getSerializedPattern() {
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSerializedPattern;
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getDisplayMode() {
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mDisplayMode;
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isInputEnabled() {
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mInputEnabled;
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isInStealthMode() {
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mInStealthMode;
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isTactileFeedbackEnabled(){
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mTactileFeedbackEnabled;
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel dest, int flags) {
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super.writeToParcel(dest, flags);
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeString(mSerializedPattern);
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeInt(mDisplayMode);
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mInputEnabled);
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mInStealthMode);
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mTactileFeedbackEnabled);
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1284a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        @SuppressWarnings({ "unused", "hiding" }) // Found using reflection
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final Parcelable.Creator<SavedState> CREATOR =
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new Creator<SavedState>() {
1287240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            @Override
1288240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public SavedState createFromParcel(Parcel in) {
1289240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return new SavedState(in);
1290240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1292240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            @Override
1293240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public SavedState[] newArray(int size) {
1294240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return new SavedState[size];
1295240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1296240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        };
1297240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    }
1298240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1299240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private final class PatternExploreByTouchHelper extends ExploreByTouchHelper {
1300240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private Rect mTempRect = new Rect();
1301240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private HashMap<Integer, VirtualViewContainer> mItems = new HashMap<Integer,
1302240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                VirtualViewContainer>();
1303240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1304240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        class VirtualViewContainer {
1305240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public VirtualViewContainer(CharSequence description) {
1306240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                this.description = description;
1307240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1308240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            CharSequence description;
1309240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        };
1310240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1311240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        public PatternExploreByTouchHelper(View forView) {
1312240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            super(forView);
1313240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1314240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1315240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1316240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected int getVirtualViewAt(float x, float y) {
1317240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // This must use the same hit logic for the screen to ensure consistency whether
1318240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // accessibility is on or off.
1319240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int id = getVirtualViewIdForHit(x, y);
1320240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return id;
1321240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1322240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1323240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1324240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
1325240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "getVisibleVirtualViews(len=" + virtualViewIds.size() + ")");
1326d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (!mPatternInProgress) {
1327d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                return;
1328d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            }
1329240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            for (int i = VIRTUAL_BASE_VIEW_ID; i < VIRTUAL_BASE_VIEW_ID + 9; i++) {
1330240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                if (!mItems.containsKey(i)) {
1331240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    VirtualViewContainer item = new VirtualViewContainer(getTextForVirtualView(i));
1332240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    mItems.put(i, item);
1333240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                }
1334240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                // Add all views. As views are added to the pattern, we remove them
1335240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                // from notification by making them non-clickable below.
1336240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                virtualViewIds.add(i);
1337240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1338240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1339240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1340240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1341240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
1342240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPopulateEventForVirtualView(" + virtualViewId + ")");
1343240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Announce this view
1344240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (mItems.containsKey(virtualViewId)) {
1345240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                CharSequence contentDescription = mItems.get(virtualViewId).description;
1346240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                event.getText().add(contentDescription);
1347240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1348240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1349240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1350240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1351d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
1352d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            super.onPopulateAccessibilityEvent(host, event);
1353d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (!mPatternInProgress) {
1354d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                CharSequence contentDescription = getContext().getText(
1355d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                        com.android.internal.R.string.lockscreen_access_pattern_area);
1356d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                event.setContentDescription(contentDescription);
1357d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            }
1358d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        }
1359d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
1360d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        @Override
1361240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
1362240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPopulateNodeForVirtualView(view=" + virtualViewId + ")");
1363240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1364240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Node and event text and content descriptions are usually
1365240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // identical, so we'll use the exact same string as before.
1366240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setText(getTextForVirtualView(virtualViewId));
1367240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setContentDescription(getTextForVirtualView(virtualViewId));
1368240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1369d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (mPatternInProgress) {
1370d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                node.setFocusable(true);
1371d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
1372d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                if (isClickable(virtualViewId)) {
1373d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    // Mark this node of interest by making it clickable.
1374d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    node.addAction(AccessibilityAction.ACTION_CLICK);
1375d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    node.setClickable(isClickable(virtualViewId));
1376d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                }
1377240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1378240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1379240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Compute bounds for this object
1380240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Rect bounds = getBoundsForVirtualView(virtualViewId);
1381240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "bounds:" + bounds.toString());
1382240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setBoundsInParent(bounds);
1383240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1384240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1385240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private boolean isClickable(int virtualViewId) {
1386240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Dots are clickable if they're not part of the current pattern.
1387240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
1388240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
1389240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
1390240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return !mPatternDrawLookup[row][col];
1391240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1392240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return false;
1393240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1394240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1395240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1396240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
1397240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                Bundle arguments) {
1398240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPerformActionForVirtualView(id=" + virtualViewId
1399240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    + ", action=" + action);
1400240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            switch (action) {
1401240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                case AccessibilityNodeInfo.ACTION_CLICK:
1402240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // Click handling should be consistent with
1403240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // onTouchEvent(). This ensures that the view works the
1404240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // same whether accessibility is turned on or off.
1405240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    return onItemClicked(virtualViewId);
1406240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                default:
1407240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    if (DEBUG_A11Y) Log.v(TAG, "*** action not handled in "
1408240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                            + "onPerformActionForVirtualView(viewId="
1409240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                            + virtualViewId + "action=" + action + ")");
1410240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1411240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return false;
1412240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1413240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1414240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        boolean onItemClicked(int index) {
1415240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onItemClicked(" + index + ")");
1416240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1417240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Since the item's checked state is exposed to accessibility
1418240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // services through its AccessibilityNodeInfo, we need to invalidate
1419240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // the item's virtual view. At some point in the future, the
1420240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // framework will obtain an updated version of the virtual view.
1421240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            invalidateVirtualView(index);
1422240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1423240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // We need to let the framework know what type of event
1424240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // happened. Accessibility services may use this event to provide
1425240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // appropriate feedback to the user.
1426240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            sendEventForVirtualView(index, AccessibilityEvent.TYPE_VIEW_CLICKED);
1427240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1428240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return true;
1429240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1430240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1431240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private Rect getBoundsForVirtualView(int virtualViewId) {
1432240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int ordinal = virtualViewId - VIRTUAL_BASE_VIEW_ID;
1433240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Rect bounds = mTempRect;
1434240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int row = ordinal / 3;
1435240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int col = ordinal % 3;
1436240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final CellState cell = mCellStates[row][col];
1437240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float centerX = getCenterXForColumn(col);
1438240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float centerY = getCenterYForRow(row);
1439240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float cellheight = mSquareHeight * mHitFactor * 0.5f;
1440240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float cellwidth = mSquareWidth * mHitFactor * 0.5f;
1441240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.left = (int) (centerX - cellwidth);
1442240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.right = (int) (centerX + cellwidth);
1443240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.top = (int) (centerY - cellheight);
1444240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.bottom = (int) (centerY + cellheight);
1445240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return bounds;
1446240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1447240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1448240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private boolean shouldSpeakPassword() {
1449240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final boolean speakPassword = Settings.Secure.getIntForUser(
1450240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0,
1451240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    UserHandle.USER_CURRENT_OR_SELF) != 0;
1452240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final boolean hasHeadphones = mAudioManager != null ?
1453240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn())
1454240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    : false;
1455240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return speakPassword || hasHeadphones;
1456240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1457240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1458240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private CharSequence getTextForVirtualView(int virtualViewId) {
1459240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Resources res = getResources();
1460240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return shouldSpeakPassword() ? res.getString(
1461240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                R.string.lockscreen_access_pattern_cell_added_verbose, virtualViewId)
1462240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                : res.getString(R.string.lockscreen_access_pattern_cell_added);
1463240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1464240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1465240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        /**
1466240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * Helper method to find which cell a point maps to
1467240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         *
1468240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * if there's no hit.
1469240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @param x touch position x
1470240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @param y touch position y
1471240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @return VIRTUAL_BASE_VIEW_ID+id or 0 if no view was hit
1472240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         */
1473240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private int getVirtualViewIdForHit(float x, float y) {
1474240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int rowHit = getRowHit(y);
1475240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (rowHit < 0) {
1476240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return ExploreByTouchHelper.INVALID_ID;
1477240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1478240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int columnHit = getColumnHit(x);
1479240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (columnHit < 0) {
1480240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return ExploreByTouchHelper.INVALID_ID;
1481240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1482240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            boolean dotAvailable = mPatternDrawLookup[rowHit][columnHit];
1483240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int dotId = (rowHit * 3 + columnHit) + VIRTUAL_BASE_VIEW_ID;
1484240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int view = dotAvailable ? dotId : ExploreByTouchHelper.INVALID_ID;
1485240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "getVirtualViewIdForHit(" + x + "," + y + ") => "
1486240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    + view + "avail =" + dotAvailable);
1487240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return view;
1488240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1491