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;
2788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanhimport android.graphics.drawable.Drawable;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Path;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Rect;
31240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.media.AudioManager;
32240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.os.Bundle;
336a109b4874b79d9bf8f5d990bfe831a59e5a83dcKarl Rosaenimport android.os.Debug;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcel;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Parcelable;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock;
37240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.os.UserHandle;
38240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.provider.Settings;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.AttributeSet;
40240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.util.IntArray;
41240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.util.Log;
42f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaverimport android.util.SparseArray;
43613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggiimport android.view.DisplayListCanvas;
44aef555bcf26e770e37f2065913084588fb92c6fbJim Millerimport android.view.HapticFeedbackConstants;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent;
46613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggiimport android.view.RenderNodeAnimator;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.View;
48240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityEvent;
49530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganovimport android.view.accessibility.AccessibilityManager;
50240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityNodeInfo;
51240a295f12a04e888b09f1d815fbd72cffbef974Jim Millerimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
52c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.view.animation.AnimationUtils;
53c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggiimport android.view.animation.Interpolator;
54530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
55530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganovimport com.android.internal.R;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Displays and detects the user's unlock attempt, which is a drag of a finger
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * across 9 regions of the screen.
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Is also capable of displaying a static pattern in "in progress", "wrong" or
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * "correct" states.
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class LockPatternView extends View {
68bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    // Aspect to use when rendering this view
69bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_SQUARE = 0; // View will be the minimum of width/height
70bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_LOCK_WIDTH = 1; // Fixed width; height will be minimum of (w,h)
71bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h)
72bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final boolean PROFILE_DRAWING = false;
743018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    private final CellState[][] mCellStates;
75c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
76c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mDotSize;
77c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mDotSizeActivated;
78c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private final int mPathWidth;
79c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDrawingProfilingStarted = false;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
82a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Paint mPaint = new Paint();
83a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Paint mPathPaint = new Paint();
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * How many milliseconds we spend animating each circle of a lock pattern
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * if the animating mode is set.  The entire animation should take this
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * constant * the length of the pattern to complete.
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MILLIS_PER_CIRCLE_ANIMATING = 700;
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
920caa377f4688f175ae22229a10294468610a116eJim Miller    /**
930caa377f4688f175ae22229a10294468610a116eJim Miller     * This can be used to avoid updating the display for very small motions or noisy panels.
940caa377f4688f175ae22229a10294468610a116eJim Miller     * It didn't seem to have much impact on the devices tested, so currently set to 0.
950caa377f4688f175ae22229a10294468610a116eJim Miller     */
960caa377f4688f175ae22229a10294468610a116eJim Miller    private static final float DRAG_THRESHHOLD = 0.0f;
97240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    public static final int VIRTUAL_BASE_VIEW_ID = 1;
98d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    public static final boolean DEBUG_A11Y = false;
99240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private static final String TAG = "LockPatternView";
1000caa377f4688f175ae22229a10294468610a116eJim Miller
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private OnPatternListener mOnPatternListener;
102a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Lookup table for the circles of the pattern we are currently drawing.
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This will be the cells of the complete pattern unless we are animating,
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in which case we use this to hold the cells we are drawing for the in
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * progress animation.
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
110a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final boolean[][] mPatternDrawLookup = new boolean[3][3];
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the in progress point:
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * - during interaction: where the user's finger is
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * - during animation: the current tip of the animating line
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mInProgressX = -1;
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mInProgressY = -1;
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private long mAnimatingPeriodStart;
121dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan    private long[] mLineFadeStart = new long[9];
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mInputEnabled = true;
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mInStealthMode = false;
126aef555bcf26e770e37f2065913084588fb92c6fbJim Miller    private boolean mEnableHapticFeedback = true;
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mPatternInProgress = false;
1288c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan    private boolean mFadePattern = true;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mHitFactor = 0.6f;
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSquareWidth;
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float mSquareHeight;
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Path mCurrentPath = new Path();
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final Rect mInvalidate = new Rect();
1379ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller    private final Rect mTmpInvalidateRect = new Rect();
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
139bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller    private int mAspect;
140c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mRegularColor;
141c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mErrorColor;
142c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int mSuccessColor;
1432cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek
144a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Interpolator mFastOutSlowInInterpolator;
145a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private final Interpolator mLinearOutSlowInInterpolator;
146240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private PatternExploreByTouchHelper mExploreByTouchHelper;
147240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private AudioManager mAudioManager;
148bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
14988a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    private Drawable mSelectedDrawable;
15088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    private Drawable mNotSelectedDrawable;
15188a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    private boolean mUseLockPatternDrawable;
15288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
156a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    public static final class Cell {
157a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        final int row;
158a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        final int column;
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // keep # objects limited to 9
161a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        private static final Cell[][] sCells = createCells();
162a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley
163a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        private static Cell[][] createCells() {
164a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley            Cell[][] res = new Cell[3][3];
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < 3; i++) {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                for (int j = 0; j < 3; j++) {
167a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley                    res[i][j] = new Cell(i, j);
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
170a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley            return res;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param row The row of the cell.
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param column The column of the cell.
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private Cell(int row, int column) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checkRange(row, column);
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.row = row;
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.column = column;
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getRow() {
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return row;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getColumn() {
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return column;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
191a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        public static Cell of(int row, int column) {
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            checkRange(row, column);
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return sCells[row][column];
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static void checkRange(int row, int column) {
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (row < 0 || row > 2) {
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalArgumentException("row must be in range 0-2");
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (column < 0 || column > 2) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalArgumentException("column must be in range 0-2");
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
205a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        @Override
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "(row=" + row + ",clmn=" + column + ")";
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2113018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    public static class CellState {
212613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        int row;
213613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        int col;
214613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        boolean hwAnimating;
215613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwRadius;
216613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwCenterX;
217613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Float> hwCenterY;
218613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        CanvasProperty<Paint> hwPaint;
219613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float radius;
220613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float translationY;
221613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        float alpha = 1f;
222c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public float lineEndX = Float.MIN_VALUE;
223c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public float lineEndY = Float.MIN_VALUE;
224c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        public ValueAnimator lineAnimator;
2253018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek     }
2263018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * How to display the current pattern.
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum DisplayMode {
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern drawn is correct (i.e draw it in a friendly color)
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Correct,
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Animate the pattern (for demo, and help).
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Animate,
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern is wrong (i.e draw a foreboding color)
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Wrong
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The call back interface for detecting patterns entered by the user.
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface OnPatternListener {
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A new pattern has begun.
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternStart();
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The pattern was cleared.
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternCleared();
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
26441e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         * The user extended the pattern currently being drawn by one cell.
26541e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         * @param pattern The pattern with newly added cell.
26641e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller         */
26741e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller        void onPatternCellAdded(List<Cell> pattern);
26841e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller
26941e8dc02cdde381cc2d7d55bfb56d5c7f72eb86eJim Miller        /**
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A pattern was detected from the user.
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @param pattern The pattern.
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        void onPatternDetected(List<Cell> pattern);
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public LockPatternView(Context context) {
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        this(context, null);
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public LockPatternView(Context context, AttributeSet attrs) {
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super(context, attrs);
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
28358be7a675b7aa505255f0c91fee755f8290e8363Jason Monk        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LockPatternView,
28458be7a675b7aa505255f0c91fee755f8290e8363Jason Monk                R.attr.lockPatternStyle, R.style.Widget_LockPatternView);
285bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
286bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        final String aspect = a.getString(R.styleable.LockPatternView_aspect);
287bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
288bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        if ("square".equals(aspect)) {
289bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_SQUARE;
290bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else if ("lock_width".equals(aspect)) {
291bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_LOCK_WIDTH;
292bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else if ("lock_height".equals(aspect)) {
293bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_LOCK_HEIGHT;
294bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        } else {
295bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            mAspect = ASPECT_SQUARE;
296bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        }
297bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setClickable(true);
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3002cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setAntiAlias(true);
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setDither(true);
3031cf3594114b1846c97243ff536074c49bbe06061Fabrice Di Meglio
30458be7a675b7aa505255f0c91fee755f8290e8363Jason Monk        mRegularColor = a.getColor(R.styleable.LockPatternView_regularColor, 0);
30558be7a675b7aa505255f0c91fee755f8290e8363Jason Monk        mErrorColor = a.getColor(R.styleable.LockPatternView_errorColor, 0);
30658be7a675b7aa505255f0c91fee755f8290e8363Jason Monk        mSuccessColor = a.getColor(R.styleable.LockPatternView_successColor, 0);
307c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
308c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, mRegularColor);
3092cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPathPaint.setColor(pathColor);
3101cf3594114b1846c97243ff536074c49bbe06061Fabrice Di Meglio
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStyle(Paint.Style.STROKE);
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStrokeJoin(Paint.Join.ROUND);
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPathPaint.setStrokeCap(Paint.Cap.ROUND);
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
315c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPathWidth = getResources().getDimensionPixelSize(R.dimen.lock_pattern_dot_line_width);
316c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPathPaint.setStrokeWidth(mPathWidth);
317c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
318c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mDotSize = getResources().getDimensionPixelSize(R.dimen.lock_pattern_dot_size);
319c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mDotSizeActivated = getResources().getDimensionPixelSize(
320c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                R.dimen.lock_pattern_dot_size_activated);
32185d630020a29c24f3d51f00b5ce3f701c16f0a45Jim Miller
32288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        mUseLockPatternDrawable = getResources().getBoolean(R.bool.use_lock_pattern_drawable);
32388a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        if (mUseLockPatternDrawable) {
32488a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mSelectedDrawable = getResources().getDrawable(R.drawable.lockscreen_selected);
32588a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mNotSelectedDrawable = getResources().getDrawable(R.drawable.lockscreen_notselected);
32688a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        }
32788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh
3282cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPaint.setAntiAlias(true);
3292cb687e7b9d0cbb1af5ba753453a9a05350a100eSelim Cinek        mPaint.setDither(true);
3303018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
3313018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        mCellStates = new CellState[3][3];
3323018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        for (int i = 0; i < 3; i++) {
3333018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek            for (int j = 0; j < 3; j++) {
3343018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek                mCellStates[i][j] = new CellState();
335613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].radius = mDotSize/2;
336613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].row = i;
337613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                mCellStates[i][j].col = j;
3383018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek            }
3393018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        }
340c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
341c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mFastOutSlowInInterpolator =
342c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
343c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mLinearOutSlowInInterpolator =
344c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                AnimationUtils.loadInterpolator(context, android.R.interpolator.linear_out_slow_in);
345240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper = new PatternExploreByTouchHelper(this);
346240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        setAccessibilityDelegate(mExploreByTouchHelper);
347240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
348ac010478a6e64407ca0f18fcab4b2aff2ba0274dJim Miller        a.recycle();
3493018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    }
3503018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek
3513018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek    public CellState[][] getCellStates() {
3523018197cf0dff5a9061f6065a8ecc108a0866dabSelim Cinek        return mCellStates;
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Whether the view is in stealth mode.
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isInStealthMode() {
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mInStealthMode;
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return Whether the view has tactile feedback enabled.
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isTactileFeedbackEnabled() {
366aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        return mEnableHapticFeedback;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set whether the view is in stealth mode.  If true, there will be no
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * visible feedback as the user enters the pattern.
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param inStealthMode Whether in stealth mode.
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setInStealthMode(boolean inStealthMode) {
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInStealthMode = inStealthMode;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3808c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan     * Set whether the pattern should fade as it's being drawn. If
3818c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan     * true, each segment of the pattern fades over time.
3828c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan     */
3838c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan    public void setFadePattern(boolean fadePattern) {
3848c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan        mFadePattern = fadePattern;
3858c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan    }
3868c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan
3878c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan    /**
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set whether the view will use tactile feedback.  If true, there will be
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * tactile feedback as the user enters the pattern.
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tactileFeedbackEnabled Whether tactile feedback is enabled
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) {
394aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        mEnableHapticFeedback = tactileFeedbackEnabled;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the call back for pattern detection.
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param onPatternListener The call back.
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setOnPatternListener(
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OnPatternListener onPatternListener) {
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mOnPatternListener = onPatternListener;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the pattern explicitely (rather than waiting for the user to input
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a pattern).
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param displayMode How to display the pattern.
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param pattern The pattern.
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setPattern(DisplayMode displayMode, List<Cell> pattern) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.clear();
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.addAll(pattern);
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        clearPatternDrawLookup();
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (Cell cell : pattern) {
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPatternDrawLookup[cell.getRow()][cell.getColumn()] = true;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setDisplayMode(displayMode);
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Set the display mode of the current pattern.  This can be useful, for
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * instance, after detecting a pattern to tell this view whether change the
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in progress result to correct or wrong.
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param displayMode The display mode.
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setDisplayMode(DisplayMode displayMode) {
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = displayMode;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (displayMode == DisplayMode.Animate) {
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mPattern.size() == 0) {
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throw new IllegalStateException("you must have a pattern to "
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        + "animate if you want to set the display mode to animate");
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAnimatingPeriodStart = SystemClock.elapsedRealtime();
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Cell first = mPattern.get(0);
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInProgressX = getCenterXForColumn(first.getColumn());
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInProgressY = getCenterYForRow(first.getRow());
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            clearPatternDrawLookup();
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        invalidate();
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
445613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    public void startCellStateAnimation(CellState cellState, float startAlpha, float endAlpha,
446613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startTranslationY, float endTranslationY, float startScale, float endScale,
447613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration,
448613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Interpolator interpolator, Runnable finishRunnable) {
449613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        if (isHardwareAccelerated()) {
450613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            startCellStateAnimationHw(cellState, startAlpha, endAlpha, startTranslationY,
451613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    endTranslationY, startScale, endScale, delay, duration, interpolator,
452613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable);
453613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        } else {
454613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            startCellStateAnimationSw(cellState, startAlpha, endAlpha, startTranslationY,
455613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    endTranslationY, startScale, endScale, delay, duration, interpolator,
456613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable);
457613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        }
458613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
459613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
460613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startCellStateAnimationSw(final CellState cellState,
461613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startAlpha, final float endAlpha,
462613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startTranslationY, final float endTranslationY,
463613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            final float startScale, final float endScale,
464613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
465613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.alpha = startAlpha;
466613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.translationY = startTranslationY;
467613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.radius = mDotSize/2 * startScale;
468613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
469613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
470613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
471613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
472613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
473613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            @Override
474613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
475613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                float t = (float) animation.getAnimatedValue();
476613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.alpha = (1 - t) * startAlpha + t * endAlpha;
477613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.translationY = (1 - t) * startTranslationY + t * endTranslationY;
478613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                cellState.radius = mDotSize/2 * ((1 - t) * startScale + t * endScale);
479613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                invalidate();
480613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            }
481613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        });
482613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addListener(new AnimatorListenerAdapter() {
483613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            @Override
484613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            public void onAnimationEnd(Animator animation) {
485613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                if (finishRunnable != null) {
486613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    finishRunnable.run();
487613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                }
488613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            }
489613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        });
490613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
491613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
492613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
493613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startCellStateAnimationHw(final CellState cellState,
494613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startAlpha, float endAlpha,
495613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startTranslationY, float endTranslationY,
496613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            float startScale, float endScale,
497613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator, final Runnable finishRunnable) {
498613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.alpha = endAlpha;
499613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.translationY = endTranslationY;
500613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.radius = mDotSize/2 * endScale;
501613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwAnimating = true;
502613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwCenterY = CanvasProperty.createFloat(
503613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                getCenterYForRow(cellState.row) + startTranslationY);
504613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwCenterX = CanvasProperty.createFloat(getCenterXForColumn(cellState.col));
505613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwRadius = CanvasProperty.createFloat(mDotSize/2 * startScale);
506613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        mPaint.setColor(getCurrentColor(false));
507613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        mPaint.setAlpha((int) (startAlpha * 255));
508613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        cellState.hwPaint = CanvasProperty.createPaint(new Paint(mPaint));
509613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
510613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtFloatAnimation(cellState.hwCenterY,
511613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                getCenterYForRow(cellState.row) + endTranslationY, delay, duration, interpolator);
512613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtFloatAnimation(cellState.hwRadius, mDotSize/2 * endScale, delay, duration,
513613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                interpolator);
514613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRtAlphaAnimation(cellState, endAlpha, delay, duration, interpolator,
515613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                new AnimatorListenerAdapter() {
516613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    @Override
517613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    public void onAnimationEnd(Animator animation) {
518613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        cellState.hwAnimating = false;
519613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        if (finishRunnable != null) {
520613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                            finishRunnable.run();
521613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        }
522613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    }
523613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                });
524613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
525613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        invalidate();
526613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
527613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
528613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRtAlphaAnimation(CellState cellState, float endAlpha,
529613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator,
530613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Animator.AnimatorListener listener) {
531613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        RenderNodeAnimator animator = new RenderNodeAnimator(cellState.hwPaint,
532613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                RenderNodeAnimator.PAINT_ALPHA, (int) (endAlpha * 255));
533613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
534613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
535613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
536613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setTarget(this);
537613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.addListener(listener);
538613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
539613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
540613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
541613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRtFloatAnimation(CanvasProperty<Float> property, float endValue,
542613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            long delay, long duration, Interpolator interpolator) {
543613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        RenderNodeAnimator animator = new RenderNodeAnimator(property, endValue);
544613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setDuration(duration);
545613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setStartDelay(delay);
546613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setInterpolator(interpolator);
547613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.setTarget(this);
548613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        animator.start();
549613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    }
550613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
551530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyCellAdded() {
552240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        // sendAccessEvent(R.string.lockscreen_access_pattern_cell_added);
553530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
554530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternCellAdded(mPattern);
555530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
556240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        // Disable used cells for accessibility as they get added
557240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        if (DEBUG_A11Y) Log.v(TAG, "ivnalidating root because cell was added.");
558240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper.invalidateRoot();
559530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
560530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
561530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternStarted() {
562e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_start);
563530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
564530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternStart();
565530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
566530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
567530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
568530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternDetected() {
569e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_detected);
570530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
571530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternDetected(mPattern);
572530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
573530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
574530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
575530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void notifyPatternCleared() {
576e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        sendAccessEvent(R.string.lockscreen_access_pattern_cleared);
577530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (mOnPatternListener != null) {
578530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            mOnPatternListener.onPatternCleared();
579530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
580530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
581530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Clear the pattern.
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void clearPattern() {
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        resetPattern();
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    @Override
590240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    protected boolean dispatchHoverEvent(MotionEvent event) {
591d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        // Dispatch to onHoverEvent first so mPatternInProgress is up to date when the
592d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        // helper gets the event.
593d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        boolean handled = super.dispatchHoverEvent(event);
594d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        handled |= mExploreByTouchHelper.dispatchHoverEvent(event);
595d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        return handled;
596240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    }
597240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Reset all pattern state.
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void resetPattern() {
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.clear();
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        clearPatternDrawLookup();
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = DisplayMode.Correct;
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        invalidate();
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
609dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan     * Clear the pattern lookup table. Also reset the line fade start times for
610dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan     * the next attempt.
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void clearPatternDrawLookup() {
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = 0; j < 3; j++) {
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDrawLookup[i][j] = false;
616dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                mLineFadeStart[i+j] = 0;
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Disable input (for instance when displaying a message that will
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * timeout so user doesn't get view into messy state).
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void disableInput() {
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = false;
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enable input.
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void enableInput() {
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = true;
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int width = w - mPaddingLeft - mPaddingRight;
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSquareWidth = width / 3.0f;
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
641240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")");
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int height = h - mPaddingTop - mPaddingBottom;
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSquareHeight = height / 3.0f;
644240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        mExploreByTouchHelper.invalidateRoot();
64588a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh
64688a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        if (mUseLockPatternDrawable) {
64788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mNotSelectedDrawable.setBounds(mPaddingLeft, mPaddingTop, width, height);
64888a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mSelectedDrawable.setBounds(mPaddingLeft, mPaddingTop, width, height);
64988a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        }
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6520a0753808ea27955472c2283413fc230bc85595bJim Miller    private int resolveMeasured(int measureSpec, int desired)
6530a0753808ea27955472c2283413fc230bc85595bJim Miller    {
6540a0753808ea27955472c2283413fc230bc85595bJim Miller        int result = 0;
6550a0753808ea27955472c2283413fc230bc85595bJim Miller        int specSize = MeasureSpec.getSize(measureSpec);
6560a0753808ea27955472c2283413fc230bc85595bJim Miller        switch (MeasureSpec.getMode(measureSpec)) {
6570a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.UNSPECIFIED:
6580a0753808ea27955472c2283413fc230bc85595bJim Miller                result = desired;
6590a0753808ea27955472c2283413fc230bc85595bJim Miller                break;
6600a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.AT_MOST:
6617bc60abc97f77cc2478f5d19c391ebdb1433a893Peter Ng                result = Math.max(specSize, desired);
6620a0753808ea27955472c2283413fc230bc85595bJim Miller                break;
6630a0753808ea27955472c2283413fc230bc85595bJim Miller            case MeasureSpec.EXACTLY:
6640a0753808ea27955472c2283413fc230bc85595bJim Miller            default:
6651fd16378812792913a6aa6923acbec20037e09ffPhilip Milne                result = specSize;
6660a0753808ea27955472c2283413fc230bc85595bJim Miller        }
6670a0753808ea27955472c2283413fc230bc85595bJim Miller        return result;
6680a0753808ea27955472c2283413fc230bc85595bJim Miller    }
6690a0753808ea27955472c2283413fc230bc85595bJim Miller
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
6720d244193bed0c995e2beaff5e217200457b28b9dJim Miller        final int minimumWidth = getSuggestedMinimumWidth();
6730d244193bed0c995e2beaff5e217200457b28b9dJim Miller        final int minimumHeight = getSuggestedMinimumHeight();
6740a0753808ea27955472c2283413fc230bc85595bJim Miller        int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
6750a0753808ea27955472c2283413fc230bc85595bJim Miller        int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
6760a0753808ea27955472c2283413fc230bc85595bJim Miller
677bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        switch (mAspect) {
678bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_SQUARE:
6790d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewWidth = viewHeight = Math.min(viewWidth, viewHeight);
680bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
681bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_LOCK_WIDTH:
6820d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewHeight = Math.min(viewWidth, viewHeight);
683bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
684bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller            case ASPECT_LOCK_HEIGHT:
6850d244193bed0c995e2beaff5e217200457b28b9dJim Miller                viewWidth = Math.min(viewWidth, viewHeight);
686bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller                break;
687bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        }
6880a0753808ea27955472c2283413fc230bc85595bJim Miller        // Log.v(TAG, "LockPatternView dimensions: " + viewWidth + "x" + viewHeight);
689bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller        setMeasuredDimension(viewWidth, viewHeight);
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Determines whether the point x, y will add a new point to the current
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * pattern (in addition to finding the cell, also makes heuristic choices
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * such as filling in gaps based on current pattern).
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param x The x coordinate.
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param y The y coordinate.
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Cell detectAndAddHit(float x, float y) {
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Cell cell = checkForNewHit(x, y);
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cell != null) {
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // check for gaps in existing pattern
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cell fillInGapCell = null;
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final ArrayList<Cell> pattern = mPattern;
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!pattern.isEmpty()) {
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell lastCell = pattern.get(pattern.size() - 1);
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dRow = cell.row - lastCell.row;
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dColumn = cell.column - lastCell.column;
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fillInRow = lastCell.row;
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int fillInColumn = lastCell.column;
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) {
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1);
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) {
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1);
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                fillInGapCell = Cell.of(fillInRow, fillInColumn);
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (fillInGapCell != null &&
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                addCellToPattern(fillInGapCell);
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            addCellToPattern(cell);
730aef555bcf26e770e37f2065913084588fb92c6fbJim Miller            if (mEnableHapticFeedback) {
731aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
732aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
733aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                        | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return cell;
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void addCellToPattern(Cell newCell) {
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDrawLookup[newCell.getRow()][newCell.getColumn()] = true;
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPattern.add(newCell);
743c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (!mInStealthMode) {
744c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            startCellActivatedAnimation(newCell);
745c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
746530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        notifyCellAdded();
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void startCellActivatedAnimation(Cell cell) {
750c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        final CellState cellState = mCellStates[cell.row][cell.column];
751613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        startRadiusAnimation(mDotSize/2, mDotSizeActivated/2, 96, mLinearOutSlowInInterpolator,
752c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                cellState, new Runnable() {
753613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    @Override
754613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    public void run() {
755613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                        startRadiusAnimation(mDotSizeActivated/2, mDotSize/2, 192,
756613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                                mFastOutSlowInInterpolator,
757613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                                cellState, null);
758613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                    }
759613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                });
760c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        startLineEndAnimation(cellState, mInProgressX, mInProgressY,
761c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                getCenterXForColumn(cell.column), getCenterYForRow(cell.row));
762c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
763c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
764c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void startLineEndAnimation(final CellState state,
765c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            final float startX, final float startY, final float targetX, final float targetY) {
766c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
767c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
768c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
769c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
770c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                float t = (float) animation.getAnimatedValue();
771c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineEndX = (1 - t) * startX + t * targetX;
772c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineEndY = (1 - t) * startY + t * targetY;
773c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                invalidate();
774c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
775c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
776c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addListener(new AnimatorListenerAdapter() {
777c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
778c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationEnd(Animator animation) {
779c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                state.lineAnimator = null;
780c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
781c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
782c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setInterpolator(mFastOutSlowInInterpolator);
783c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setDuration(100);
784c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.start();
785c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        state.lineAnimator = valueAnimator;
786c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
787c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
788613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void startRadiusAnimation(float start, float end, long duration,
789613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi            Interpolator interpolator, final CellState state, final Runnable endRunnable) {
790c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
791c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
792c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            @Override
793c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            public void onAnimationUpdate(ValueAnimator animation) {
794613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                state.radius = (float) animation.getAnimatedValue();
795c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                invalidate();
796c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
797c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        });
798c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (endRunnable != null) {
799c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            valueAnimator.addListener(new AnimatorListenerAdapter() {
800c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                @Override
801c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                public void onAnimationEnd(Animator animation) {
802c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    endRunnable.run();
803c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                }
804c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            });
805c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
806c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setInterpolator(interpolator);
807c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.setDuration(duration);
808c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        valueAnimator.start();
809c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
810c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // helper method to find which cell a point maps to
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Cell checkForNewHit(float x, float y) {
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int rowHit = getRowHit(y);
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (rowHit < 0) {
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int columnHit = getColumnHit(x);
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (columnHit < 0) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPatternDrawLookup[rowHit][columnHit]) {
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return Cell.of(rowHit, columnHit);
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Helper method to find the row that y falls into.
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param y The y coordinate
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The row that y falls in, or -1 if it falls in no row.
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getRowHit(float y) {
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final float squareHeight = mSquareHeight;
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float hitSize = squareHeight * mHitFactor;
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float offset = mPaddingTop + (squareHeight - hitSize) / 2f;
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float hitTop = offset + squareHeight * i;
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (y >= hitTop && y <= hitTop + hitSize) {
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Helper method to find the column x fallis into.
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param x The x coordinate.
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The column that x falls in, or -1 if it falls in no column.
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int getColumnHit(float x) {
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final float squareWidth = mSquareWidth;
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float hitSize = squareWidth * mHitFactor;
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float offset = mPaddingLeft + (squareWidth - hitSize) / 2f;
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final float hitLeft = offset + squareWidth * i;
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (x >= hitLeft && x <= hitLeft + hitSize) {
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return i;
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return -1;
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
871530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    public boolean onHoverEvent(MotionEvent event) {
872530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
873530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            final int action = event.getAction();
874530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            switch (action) {
875530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER:
876530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_DOWN);
877530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
878530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE:
879530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_MOVE);
880530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
881530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT:
882530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    event.setAction(MotionEvent.ACTION_UP);
883530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                    break;
884530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            }
885530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            onTouchEvent(event);
886530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            event.setAction(action);
887530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        }
888530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        return super.onHoverEvent(event);
889530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
890530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
891530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    @Override
892aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    public boolean onTouchEvent(MotionEvent event) {
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mInputEnabled || !isEnabled()) {
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
897aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        switch(event.getAction()) {
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_DOWN:
899aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                handleActionDown(event);
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MotionEvent.ACTION_UP:
902a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley                handleActionUp();
903aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                return true;
904aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            case MotionEvent.ACTION_MOVE:
905aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                handleActionMove(event);
906aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                return true;
907aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            case MotionEvent.ACTION_CANCEL:
908c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                if (mPatternInProgress) {
909d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    setPatternInProgress(false);
910c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                    resetPattern();
911c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                    notifyPatternCleared();
912c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                }
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (PROFILE_DRAWING) {
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (mDrawingProfilingStarted) {
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        Debug.stopMethodTracing();
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        mDrawingProfilingStarted = false;
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
920aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
921aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        return false;
922aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
924d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    private void setPatternInProgress(boolean progress) {
925d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        mPatternInProgress = progress;
926d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        mExploreByTouchHelper.invalidateRoot();
927d2def946390505077766e34b6df4a529b25fdc23Adrian Roos    }
928d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
929aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    private void handleActionMove(MotionEvent event) {
930aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // Handle all recent motion events so we don't skip any cells even when the device
931aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // is busy...
932c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        final float radius = mPathWidth;
933aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final int historySize = event.getHistorySize();
9349ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller        mTmpInvalidateRect.setEmpty();
9350caa377f4688f175ae22229a10294468610a116eJim Miller        boolean invalidateNow = false;
936aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        for (int i = 0; i < historySize + 1; i++) {
937aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
938aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
939aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            Cell hitCell = detectAndAddHit(x, y);
940aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final int patternSize = mPattern.size();
941530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            if (hitCell != null && patternSize == 1) {
942d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                setPatternInProgress(true);
943530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov                notifyPatternStarted();
944aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
945aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            // note current x and y for rubber banding of in progress patterns
946aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float dx = Math.abs(x - mInProgressX);
947aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float dy = Math.abs(y - mInProgressY);
9480caa377f4688f175ae22229a10294468610a116eJim Miller            if (dx > DRAG_THRESHHOLD || dy > DRAG_THRESHHOLD) {
9490caa377f4688f175ae22229a10294468610a116eJim Miller                invalidateNow = true;
9500caa377f4688f175ae22229a10294468610a116eJim Miller            }
951aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
9520caa377f4688f175ae22229a10294468610a116eJim Miller            if (mPatternInProgress && patternSize > 0) {
9530caa377f4688f175ae22229a10294468610a116eJim Miller                final ArrayList<Cell> pattern = mPattern;
9540caa377f4688f175ae22229a10294468610a116eJim Miller                final Cell lastCell = pattern.get(patternSize - 1);
9559ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float lastCellCenterX = getCenterXForColumn(lastCell.column);
9569ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float lastCellCenterY = getCenterYForRow(lastCell.row);
9570caa377f4688f175ae22229a10294468610a116eJim Miller
9589ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                // Adjust for drawn segment from last cell to (x,y). Radius accounts for line width.
9599ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float left = Math.min(lastCellCenterX, x) - radius;
9609ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float right = Math.max(lastCellCenterX, x) + radius;
9619ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float top = Math.min(lastCellCenterY, y) - radius;
9629ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                float bottom = Math.max(lastCellCenterY, y) + radius;
9630caa377f4688f175ae22229a10294468610a116eJim Miller
9640caa377f4688f175ae22229a10294468610a116eJim Miller                // Invalidate between the pattern's new cell and the pattern's previous cell
9659ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                if (hitCell != null) {
9660caa377f4688f175ae22229a10294468610a116eJim Miller                    final float width = mSquareWidth * 0.5f;
9670caa377f4688f175ae22229a10294468610a116eJim Miller                    final float height = mSquareHeight * 0.5f;
9689ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    final float hitCellCenterX = getCenterXForColumn(hitCell.column);
9699ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    final float hitCellCenterY = getCenterYForRow(hitCell.row);
9709ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller
9719ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    left = Math.min(hitCellCenterX - width, left);
9729ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    right = Math.max(hitCellCenterX + width, right);
9739ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    top = Math.min(hitCellCenterY - height, top);
9749ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                    bottom = Math.max(hitCellCenterY + height, bottom);
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9760caa377f4688f175ae22229a10294468610a116eJim Miller
9770caa377f4688f175ae22229a10294468610a116eJim Miller                // Invalidate between the pattern's last cell and the previous location
9789ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller                mTmpInvalidateRect.union(Math.round(left), Math.round(top),
9790caa377f4688f175ae22229a10294468610a116eJim Miller                        Math.round(right), Math.round(bottom));
980aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
981aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
9820caa377f4688f175ae22229a10294468610a116eJim Miller        mInProgressX = event.getX();
9830caa377f4688f175ae22229a10294468610a116eJim Miller        mInProgressY = event.getY();
9840caa377f4688f175ae22229a10294468610a116eJim Miller
9850caa377f4688f175ae22229a10294468610a116eJim Miller        // To save updates, we only invalidate if the user moved beyond a certain amount.
9860caa377f4688f175ae22229a10294468610a116eJim Miller        if (invalidateNow) {
9879ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            mInvalidate.union(mTmpInvalidateRect);
9889ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            invalidate(mInvalidate);
9899ddfeb83aa071990110c0dac7f9280d35ebf2239Jim Miller            mInvalidate.set(mTmpInvalidateRect);
9900caa377f4688f175ae22229a10294468610a116eJim Miller        }
991aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
992aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
993530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    private void sendAccessEvent(int resId) {
994e303c5c3eb2f65ef3c6fc2693cc3cbcee92d63b7alanv        announceForAccessibility(mContext.getString(resId));
995530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov    }
996530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov
997a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley    private void handleActionUp() {
998aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        // report pattern detected
999530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (!mPattern.isEmpty()) {
1000d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(false);
1001c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            cancelLineAnimations();
1002530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternDetected();
100318dcd47796c4e6b5b3c31ea44abfb041a61857fcVishwath Mohan            // Also clear pattern if fading is enabled
100418dcd47796c4e6b5b3c31ea44abfb041a61857fcVishwath Mohan            if (mFadePattern) {
100518dcd47796c4e6b5b3c31ea44abfb041a61857fcVishwath Mohan                clearPatternDrawLookup();
100618dcd47796c4e6b5b3c31ea44abfb041a61857fcVishwath Mohan                mPatternDisplayMode = DisplayMode.Correct;
100718dcd47796c4e6b5b3c31ea44abfb041a61857fcVishwath Mohan            }
1008aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            invalidate();
1009aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1010aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (PROFILE_DRAWING) {
1011aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            if (mDrawingProfilingStarted) {
1012aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                Debug.stopMethodTracing();
1013aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                mDrawingProfilingStarted = false;
1014aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
1015aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1016aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    }
1017aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
1018c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private void cancelLineAnimations() {
1019c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        for (int i = 0; i < 3; i++) {
1020c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            for (int j = 0; j < 3; j++) {
1021c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                CellState state = mCellStates[i][j];
1022c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                if (state.lineAnimator != null) {
1023c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineAnimator.cancel();
1024c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineEndX = Float.MIN_VALUE;
1025c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    state.lineEndY = Float.MIN_VALUE;
1026c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                }
1027c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            }
1028c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        }
1029c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
1030aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller    private void handleActionDown(MotionEvent event) {
1031aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        resetPattern();
1032aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final float x = event.getX();
1033aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final float y = event.getY();
1034aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        final Cell hitCell = detectAndAddHit(x, y);
1035530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov        if (hitCell != null) {
1036d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(true);
1037aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            mPatternDisplayMode = DisplayMode.Correct;
1038530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternStarted();
1039c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        } else if (mPatternInProgress) {
1040d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            setPatternInProgress(false);
1041530d9f10a6af8ff83a184582ca81608b29510b42Svetoslav Ganov            notifyPatternCleared();
1042aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1043aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (hitCell != null) {
1044aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float startX = getCenterXForColumn(hitCell.column);
1045aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float startY = getCenterYForRow(hitCell.row);
1046aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
1047aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float widthOffset = mSquareWidth / 2f;
1048aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            final float heightOffset = mSquareHeight / 2f;
1049aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller
1050aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
1051aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                    (int) (startX + widthOffset), (int) (startY + heightOffset));
1052aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        }
1053aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        mInProgressX = x;
1054aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        mInProgressY = y;
1055aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller        if (PROFILE_DRAWING) {
1056aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            if (!mDrawingProfilingStarted) {
1057aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                Debug.startMethodTracing("LockPatternDrawing");
1058aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller                mDrawingProfilingStarted = true;
1059aced12fd2276ed7664af6bf70ff03ce2acaf6545Jim Miller            }
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float getCenterXForColumn(int column) {
10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f;
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private float getCenterYForRow(int row) {
10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mPaddingTop + row * mSquareHeight + mSquareHeight / 2f;
10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onDraw(Canvas canvas) {
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Cell> pattern = mPattern;
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = pattern.size();
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final boolean[][] drawLookup = mPatternDrawLookup;
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPatternDisplayMode == DisplayMode.Animate) {
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // figure out which circles to draw
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // + 1 so we pause on complete pattern
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int oneCycle = (count + 1) * MILLIS_PER_CIRCLE_ANIMATING;
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int spotInCycle = (int) (SystemClock.elapsedRealtime() -
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mAnimatingPeriodStart) % oneCycle;
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final int numCircles = spotInCycle / MILLIS_PER_CIRCLE_ANIMATING;
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            clearPatternDrawLookup();
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < numCircles; i++) {
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell cell = pattern.get(i);
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                drawLookup[cell.getRow()][cell.getColumn()] = true;
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // figure out in progress portion of ghosting line
10949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final boolean needToUpdateInProgressPoint = numCircles > 0
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    && numCircles < count;
10979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (needToUpdateInProgressPoint) {
10999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float percentageOfNextCircle =
11009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ((float) (spotInCycle % MILLIS_PER_CIRCLE_ANIMATING)) /
11019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                MILLIS_PER_CIRCLE_ANIMATING;
11029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell currentCell = pattern.get(numCircles - 1);
11049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float centerX = getCenterXForColumn(currentCell.column);
11059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float centerY = getCenterYForRow(currentCell.row);
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Cell nextCell = pattern.get(numCircles);
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float dx = percentageOfNextCircle *
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        (getCenterXForColumn(nextCell.column) - centerX);
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final float dy = percentageOfNextCircle *
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        (getCenterYForRow(nextCell.row) - centerY);
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mInProgressX = centerX + dx;
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mInProgressY = centerY + dy;
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // TODO: Infinite loop here...
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            invalidate();
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Path currentPath = mCurrentPath;
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        currentPath.rewind();
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // draw the circles
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < 3; i++) {
1124c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float centerY = getCenterYForRow(i);
11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = 0; j < 3; j++) {
1126c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                CellState cellState = mCellStates[i][j];
1127c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                float centerX = getCenterXForColumn(j);
1128613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                float translationY = cellState.translationY;
1129613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi
113088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                if (mUseLockPatternDrawable) {
113188a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                    drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
113288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                } else {
113388a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                    if (isHardwareAccelerated() && cellState.hwAnimating) {
113488a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                        DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
113588a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                        displayListCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
113688a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                                cellState.hwRadius, cellState.hwPaint);
113788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                    } else {
113888a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                        drawCircle(canvas, (int) centerX, (int) centerY + translationY,
113988a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                                cellState.radius, drawLookup[i][j], cellState.alpha);
114088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh                    }
1141613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi                }
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
114508a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        // TODO: the path should be created and cached every time we hit-detect a cell
114608a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        // only the last segment of the path should be computed here
1147e2d71e45209a4ac0787b360c4b0eb4d617863f3fAdrian Roos        // draw the path of the pattern (unless we are in stealth mode)
1148e2d71e45209a4ac0787b360c4b0eb4d617863f3fAdrian Roos        final boolean drawPath = !mInStealthMode;
114908a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (drawPath) {
1151c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            mPathPaint.setColor(getCurrentColor(true /* partOfPattern */));
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
115308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            boolean anyCircles = false;
1154c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float lastX = 0f;
1155c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            float lastY = 0f;
1156dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan            long elapsedRealtime = SystemClock.elapsedRealtime();
1157dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan           for (int i = 0; i < count; i++) {
115808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                Cell cell = pattern.get(i);
115908a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
116008a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // only draw the part of the pattern stored in
116108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // the lookup table (this is only different in the case
116208a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                // of animation).
116308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                if (!drawLookup[cell.row][cell.column]) {
116408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                    break;
116508a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                }
116608a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                anyCircles = true;
116708a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
1168dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                if (mLineFadeStart[i] == 0) {
1169dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                  mLineFadeStart[i] = SystemClock.elapsedRealtime();
1170dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                }
1171dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan
117208a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                float centerX = getCenterXForColumn(cell.column);
117308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                float centerY = getCenterYForRow(cell.row);
1174c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                if (i != 0) {
1175dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                   // Set this line segment to slowly fade over the next second.
1176dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                   int lineFadeVal = (int) Math.min((elapsedRealtime -
1177dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan                           mLineFadeStart[i])/2f, 255f);
1178dcc1f1941711be22960e0f58ec75215237e0a11eVishwath Mohan
1179c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    CellState state = mCellStates[cell.row][cell.column];
1180c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    currentPath.rewind();
1181c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    currentPath.moveTo(lastX, lastY);
1182c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
1183c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        currentPath.lineTo(state.lineEndX, state.lineEndY);
11848c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        if (mFadePattern) {
11858c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                            mPathPaint.setAlpha((int) 255 - lineFadeVal );
11868c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        } else {
11878c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                            mPathPaint.setAlpha(255);
11888c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        }
1189c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    } else {
1190c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        currentPath.lineTo(centerX, centerY);
11918c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        if (mFadePattern) {
11928c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                            mPathPaint.setAlpha((int) 255 - lineFadeVal );
11938c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        } else {
11948c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                            mPathPaint.setAlpha(255);
11958c8b9fb02a2dae7f6404a710550bad833bff3e0eVishwath Mohan                        }
1196c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    }
1197c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                    canvas.drawPath(currentPath, mPathPaint);
119808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                }
1199c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                lastX = centerX;
1200c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                lastY = centerY;
120108a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            }
120208a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller
1203c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            // draw last in progress section
120408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            if ((mPatternInProgress || mPatternDisplayMode == DisplayMode.Animate)
120508a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                    && anyCircles) {
1206c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                currentPath.rewind();
1207c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                currentPath.moveTo(lastX, lastY);
120808a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller                currentPath.lineTo(mInProgressX, mInProgressY);
1209c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi
1210c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                mPathPaint.setAlpha((int) (calculateLastSegmentAlpha(
1211c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                        mInProgressX, mInProgressY, lastX, lastY) * 255f));
1212c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi                canvas.drawPath(currentPath, mPathPaint);
121308a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller            }
121408a975ef86200a73db51bfbcca6bea8ad2a63cbdJim Miller        }
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1217c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) {
1218c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float diffX = x - lastX;
1219c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float diffY = y - lastY;
1220c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float dist = (float) Math.sqrt(diffX*diffX + diffY*diffY);
1221c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        float frac = dist/mSquareWidth;
1222c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f));
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1225c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    private int getCurrentColor(boolean partOfPattern) {
1226c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        if (!partOfPattern || mInStealthMode || mPatternInProgress) {
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // unselected circle
1228c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mRegularColor;
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mPatternDisplayMode == DisplayMode.Wrong) {
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the pattern is wrong
1231c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mErrorColor;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mPatternDisplayMode == DisplayMode.Correct ||
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDisplayMode == DisplayMode.Animate) {
1234c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            return mSuccessColor;
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("unknown display mode " + mPatternDisplayMode);
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1238c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    }
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1240c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi    /**
1241c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi     * @param partOfPattern Whether this circle is part of the pattern.
1242c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi     */
1243613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi    private void drawCircle(Canvas canvas, float centerX, float centerY, float radius,
1244c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi            boolean partOfPattern, float alpha) {
1245c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPaint.setColor(getCurrentColor(partOfPattern));
1246c15819748a78963f0dbfbb06300419daf3d2f5f6Jorim Jaggi        mPaint.setAlpha((int) (alpha * 255));
1247613f55fbbb23249d7c65e3f1fe8c943c4459b41aJorim Jaggi        canvas.drawCircle(centerX, centerY, radius, mPaint);
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
125088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    /**
125188a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh     * @param partOfPattern Whether this circle is part of the pattern.
125288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh     */
125388a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    private void drawCellDrawable(Canvas canvas, int i, int j, float radius,
125488a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            boolean partOfPattern) {
125588a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        Rect dst = new Rect(
125688a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            (int) (mPaddingLeft + j * mSquareWidth),
125788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            (int) (mPaddingTop + i * mSquareHeight),
125888a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            (int) (mPaddingLeft + (j + 1) * mSquareWidth),
125988a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            (int) (mPaddingTop + (i + 1) * mSquareHeight));
126088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        float scale = radius / (mDotSize / 2);
126188a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh
126288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        // Only draw on this square with the appropriate scale.
126388a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        canvas.save();
126488a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        canvas.clipRect(dst);
126588a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        canvas.scale(scale, scale, dst.centerX(), dst.centerY());
126688a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        if (!partOfPattern || scale > 1) {
126788a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mNotSelectedDrawable.draw(canvas);
126888a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        } else {
126988a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh            mSelectedDrawable.draw(canvas);
127088a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        }
127188a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh        canvas.restore();
127288a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh    }
127388a2a61f09ddf0beb60f7ee3d148f4dec4f466a8Alain Vongsouvanh
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected Parcelable onSaveInstanceState() {
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parcelable superState = super.onSaveInstanceState();
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return new SavedState(superState,
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LockPatternUtils.patternToString(mPattern),
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPatternDisplayMode.ordinal(),
1280aef555bcf26e770e37f2065913084588fb92c6fbJim Miller                mInputEnabled, mInStealthMode, mEnableHapticFeedback);
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected void onRestoreInstanceState(Parcelable state) {
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final SavedState ss = (SavedState) state;
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        super.onRestoreInstanceState(ss.getSuperState());
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setPattern(
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                DisplayMode.Correct,
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInputEnabled = ss.isInputEnabled();
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mInStealthMode = ss.isInStealthMode();
1293aef555bcf26e770e37f2065913084588fb92c6fbJim Miller        mEnableHapticFeedback = ss.isTactileFeedbackEnabled();
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The parecelable for saving and restoring a lock pattern view.
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class SavedState extends BaseSavedState {
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final String mSerializedPattern;
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final int mDisplayMode;
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mInputEnabled;
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mInStealthMode;
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private final boolean mTactileFeedbackEnabled;
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link LockPatternView#onSaveInstanceState()}
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private SavedState(Parcelable superState, String serializedPattern, int displayMode,
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) {
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(superState);
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSerializedPattern = serializedPattern;
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDisplayMode = displayMode;
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInputEnabled = inputEnabled;
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInStealthMode = inStealthMode;
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTactileFeedbackEnabled = tactileFeedbackEnabled;
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Constructor called from {@link #CREATOR}
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private SavedState(Parcel in) {
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super(in);
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mSerializedPattern = in.readString();
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDisplayMode = in.readInt();
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInputEnabled = (Boolean) in.readValue(null);
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mInStealthMode = (Boolean) in.readValue(null);
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTactileFeedbackEnabled = (Boolean) in.readValue(null);
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1331bf1259b8a6a44d7a4eab5131cd33dac0fbcb50b6Jim Miller
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getSerializedPattern() {
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mSerializedPattern;
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getDisplayMode() {
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mDisplayMode;
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isInputEnabled() {
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mInputEnabled;
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isInStealthMode() {
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mInStealthMode;
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public boolean isTactileFeedbackEnabled(){
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mTactileFeedbackEnabled;
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void writeToParcel(Parcel dest, int flags) {
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            super.writeToParcel(dest, flags);
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeString(mSerializedPattern);
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeInt(mDisplayMode);
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mInputEnabled);
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mInStealthMode);
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dest.writeValue(mTactileFeedbackEnabled);
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1362a97227add8ef08de811979ef9dfbb84cff3de014Paul Crowley        @SuppressWarnings({ "unused", "hiding" }) // Found using reflection
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public static final Parcelable.Creator<SavedState> CREATOR =
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new Creator<SavedState>() {
1365240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            @Override
1366240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public SavedState createFromParcel(Parcel in) {
1367240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return new SavedState(in);
1368240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1370240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            @Override
1371240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public SavedState[] newArray(int size) {
1372240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return new SavedState[size];
1373240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1374240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        };
1375240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    }
1376240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1377240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller    private final class PatternExploreByTouchHelper extends ExploreByTouchHelper {
1378240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private Rect mTempRect = new Rect();
1379f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver        private final SparseArray<VirtualViewContainer> mItems = new SparseArray<>();
1380240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1381240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        class VirtualViewContainer {
1382240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            public VirtualViewContainer(CharSequence description) {
1383240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                this.description = description;
1384240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1385240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            CharSequence description;
1386240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        };
1387240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1388240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        public PatternExploreByTouchHelper(View forView) {
1389240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            super(forView);
1390f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver            for (int i = VIRTUAL_BASE_VIEW_ID; i < VIRTUAL_BASE_VIEW_ID + 9; i++) {
1391f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver                mItems.put(i, new VirtualViewContainer(getTextForVirtualView(i)));
1392f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver            }
1393240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1394240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1395240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1396240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected int getVirtualViewAt(float x, float y) {
1397240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // This must use the same hit logic for the screen to ensure consistency whether
1398240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // accessibility is on or off.
1399240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int id = getVirtualViewIdForHit(x, y);
1400240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return id;
1401240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1402240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1403240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1404240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void getVisibleVirtualViews(IntArray virtualViewIds) {
1405240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "getVisibleVirtualViews(len=" + virtualViewIds.size() + ")");
1406d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (!mPatternInProgress) {
1407d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                return;
1408d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            }
1409240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            for (int i = VIRTUAL_BASE_VIEW_ID; i < VIRTUAL_BASE_VIEW_ID + 9; i++) {
1410240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                // Add all views. As views are added to the pattern, we remove them
1411240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                // from notification by making them non-clickable below.
1412240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                virtualViewIds.add(i);
1413240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1414240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1415240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1416240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1417240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
1418240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPopulateEventForVirtualView(" + virtualViewId + ")");
1419240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Announce this view
1420f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver            VirtualViewContainer container = mItems.get(virtualViewId);
1421f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver            if (container != null) {
1422f5d677d53a9030015ab4180b1acd2ae9d732fe88Phil Weaver                event.getText().add(container.description);
1423240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1424240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1425240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1426240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1427d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
1428d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            super.onPopulateAccessibilityEvent(host, event);
1429d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (!mPatternInProgress) {
1430d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                CharSequence contentDescription = getContext().getText(
1431d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                        com.android.internal.R.string.lockscreen_access_pattern_area);
1432d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                event.setContentDescription(contentDescription);
1433d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            }
1434d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        }
1435d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
1436d2def946390505077766e34b6df4a529b25fdc23Adrian Roos        @Override
1437240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
1438240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPopulateNodeForVirtualView(view=" + virtualViewId + ")");
1439240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1440240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Node and event text and content descriptions are usually
1441240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // identical, so we'll use the exact same string as before.
1442240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setText(getTextForVirtualView(virtualViewId));
1443240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setContentDescription(getTextForVirtualView(virtualViewId));
1444240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1445d2def946390505077766e34b6df4a529b25fdc23Adrian Roos            if (mPatternInProgress) {
1446d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                node.setFocusable(true);
1447d2def946390505077766e34b6df4a529b25fdc23Adrian Roos
1448d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                if (isClickable(virtualViewId)) {
1449d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    // Mark this node of interest by making it clickable.
1450d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    node.addAction(AccessibilityAction.ACTION_CLICK);
1451d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                    node.setClickable(isClickable(virtualViewId));
1452d2def946390505077766e34b6df4a529b25fdc23Adrian Roos                }
1453240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1454240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1455240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Compute bounds for this object
1456240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Rect bounds = getBoundsForVirtualView(virtualViewId);
1457240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "bounds:" + bounds.toString());
1458240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            node.setBoundsInParent(bounds);
1459240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1460240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1461240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private boolean isClickable(int virtualViewId) {
1462240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Dots are clickable if they're not part of the current pattern.
1463240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
1464240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
1465240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
1466240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return !mPatternDrawLookup[row][col];
1467240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1468240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return false;
1469240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1470240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1471240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        @Override
1472240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
1473240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                Bundle arguments) {
1474240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onPerformActionForVirtualView(id=" + virtualViewId
1475240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    + ", action=" + action);
1476240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            switch (action) {
1477240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                case AccessibilityNodeInfo.ACTION_CLICK:
1478240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // Click handling should be consistent with
1479240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // onTouchEvent(). This ensures that the view works the
1480240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    // same whether accessibility is turned on or off.
1481240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    return onItemClicked(virtualViewId);
1482240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                default:
1483240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    if (DEBUG_A11Y) Log.v(TAG, "*** action not handled in "
1484240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                            + "onPerformActionForVirtualView(viewId="
1485240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                            + virtualViewId + "action=" + action + ")");
1486240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1487240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return false;
1488240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1489240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1490240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        boolean onItemClicked(int index) {
1491240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "onItemClicked(" + index + ")");
1492240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1493240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // Since the item's checked state is exposed to accessibility
1494240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // services through its AccessibilityNodeInfo, we need to invalidate
1495240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // the item's virtual view. At some point in the future, the
1496240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // framework will obtain an updated version of the virtual view.
1497240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            invalidateVirtualView(index);
1498240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1499240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // We need to let the framework know what type of event
1500240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // happened. Accessibility services may use this event to provide
1501240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            // appropriate feedback to the user.
1502240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            sendEventForVirtualView(index, AccessibilityEvent.TYPE_VIEW_CLICKED);
1503240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1504240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return true;
1505240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1506240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1507240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private Rect getBoundsForVirtualView(int virtualViewId) {
1508240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int ordinal = virtualViewId - VIRTUAL_BASE_VIEW_ID;
1509240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Rect bounds = mTempRect;
1510240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int row = ordinal / 3;
1511240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int col = ordinal % 3;
1512240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final CellState cell = mCellStates[row][col];
1513240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float centerX = getCenterXForColumn(col);
1514240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float centerY = getCenterYForRow(row);
1515240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float cellheight = mSquareHeight * mHitFactor * 0.5f;
1516240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            float cellwidth = mSquareWidth * mHitFactor * 0.5f;
1517240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.left = (int) (centerX - cellwidth);
1518240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.right = (int) (centerX + cellwidth);
1519240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.top = (int) (centerY - cellheight);
1520240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            bounds.bottom = (int) (centerY + cellheight);
1521240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return bounds;
1522240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1523240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1524240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private CharSequence getTextForVirtualView(int virtualViewId) {
1525240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final Resources res = getResources();
1526385912ee2d78e0e557704cfd5f8c7dbe2b7fd280Phil Weaver            return res.getString(R.string.lockscreen_access_pattern_cell_added_verbose,
1527385912ee2d78e0e557704cfd5f8c7dbe2b7fd280Phil Weaver                    virtualViewId);
1528240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
1529240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller
1530240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        /**
1531240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * Helper method to find which cell a point maps to
1532240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         *
1533240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * if there's no hit.
1534240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @param x touch position x
1535240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @param y touch position y
1536240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         * @return VIRTUAL_BASE_VIEW_ID+id or 0 if no view was hit
1537240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller         */
1538240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        private int getVirtualViewIdForHit(float x, float y) {
1539240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int rowHit = getRowHit(y);
1540240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (rowHit < 0) {
1541240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return ExploreByTouchHelper.INVALID_ID;
1542240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1543240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            final int columnHit = getColumnHit(x);
1544240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (columnHit < 0) {
1545240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                return ExploreByTouchHelper.INVALID_ID;
1546240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            }
1547240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            boolean dotAvailable = mPatternDrawLookup[rowHit][columnHit];
1548240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int dotId = (rowHit * 3 + columnHit) + VIRTUAL_BASE_VIEW_ID;
1549240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            int view = dotAvailable ? dotId : ExploreByTouchHelper.INVALID_ID;
1550240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            if (DEBUG_A11Y) Log.v(TAG, "getVirtualViewIdForHit(" + x + "," + y + ") => "
1551240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller                    + view + "avail =" + dotAvailable);
1552240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller            return view;
1553240a295f12a04e888b09f1d815fbd72cffbef974Jim Miller        }
15549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1556