Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d430d5fb authored by Michael Bestas's avatar Michael Bestas Committed by Bruno Martins
Browse files

Forward port CM Screen Security settings (1/2)



 * Variable size pattern lockscreen
 * Toggle dots/error pattern visibility

Change-Id: Ie109e82c1fb2fd96b07e977e1cd76ae3acb865ff

Fix pattern visibility settings (1/2)

Change-Id: Ic627953c5df854c442671a98b5da539b994da18b

LockPatternUtils: Use the actual user id to set pattern size

Ticket: CYNGNOS-2462
Change-Id: Ia68e26ec2dfc23317135d933bc25204c1380bb02

LockSettings: fix build

Change-Id: Ic65b1a2af398faffb83776ee4013d47f79ab6619
Signed-off-by: default avatarRoman Birg <roman@cyngn.com>
parent e2ba0f2e
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1085,9 +1085,11 @@ public class KeyguardManager {
                CharSequence pinStr = new String(password);
                return LockscreenCredential.createPin(pinStr);
            case PATTERN:
                byte patternSize = new LockPatternUtils(mContext).getLockPatternSize(
                        mContext.getUserId());
                List<LockPatternView.Cell> pattern =
                        LockPatternUtils.byteArrayToPattern(password);
                return LockscreenCredential.createPattern(pattern);
                        LockPatternUtils.byteArrayToPattern(password, patternSize);
                return LockscreenCredential.createPattern(pattern, patternSize);
            default:
                throw new IllegalArgumentException("Unknown lock type " + lockType);
        }
+22 −1
Original line number Diff line number Diff line
@@ -5997,10 +5997,13 @@ public final class Settings {
        @UnsupportedAppUsage
        private static final HashSet<String> MOVED_TO_GLOBAL;
        static {
            MOVED_TO_LOCK_SETTINGS = new HashSet<>(3);
            MOVED_TO_LOCK_SETTINGS = new HashSet<>(6);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_SIZE);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_DOTS_VISIBLE);
            MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_SHOW_ERROR_PATH);
            MOVED_TO_GLOBAL = new HashSet<>();
            MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED);
@@ -7309,6 +7312,24 @@ public final class Settings {
        public static final String
                LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
        /**
         * Determines the width and height of the LockPatternView widget
         * @hide
         */
        public static final String LOCK_PATTERN_SIZE = "lock_pattern_size";
        /**
         * Whether lock pattern will show dots (0 = false, 1 = true)
         * @hide
         */
        public static final String LOCK_DOTS_VISIBLE = "lock_pattern_dotsvisible";
        /**
         * Whether lockscreen error pattern is visible (0 = false, 1 = true)
         * @hide
         */
        public static final String LOCK_SHOW_ERROR_PATH = "lock_pattern_show_error_path";
        /**
         * This preference allows the device to be locked given time after screen goes off,
         * subject to current DeviceAdmin policy limits.
+1 −0
Original line number Diff line number Diff line
@@ -102,4 +102,5 @@ interface ILockSettings {
    boolean removeWeakEscrowToken(long handle, int userId);
    boolean isWeakEscrowTokenActive(long handle, int userId);
    boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
    byte getLockPatternSize(int userId);
}
+45 −4
Original line number Diff line number Diff line
@@ -103,6 +103,11 @@ public class LockPatternUtils {
     */
    public static final int MIN_LOCK_PASSWORD_SIZE = 4;

    /*
     * The default size of the pattern lockscreen. Ex: 3x3
     */
    public static final byte PATTERN_SIZE_DEFAULT = 3;

    /**
     * The minimum number of dots the user must include in a wrong pattern attempt for it to be
     * counted.
@@ -844,16 +849,18 @@ public class LockPatternUtils {
     * @param  bytes The pattern serialized with {@link #patternToByteArray}
     * @return The pattern.
     */
    public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes) {
    public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes, byte gridSize) {
        if (bytes == null) {
            return null;
        }

        List<LockPatternView.Cell> result = Lists.newArrayList();

        LockPatternView.Cell.updateSize(gridSize);

        for (int i = 0; i < bytes.length; i++) {
            byte b = (byte) (bytes[i] - '1');
            result.add(LockPatternView.Cell.of(b / 3, b % 3));
            result.add(LockPatternView.Cell.of(b / gridSize, b % gridSize, gridSize));
        }
        return result;
    }
@@ -863,7 +870,7 @@ public class LockPatternUtils {
     * @param pattern The pattern.
     * @return The pattern in byte array form.
     */
    public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
    public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern, byte gridSize) {
        if (pattern == null) {
            return new byte[0];
        }
@@ -872,7 +879,7 @@ public class LockPatternUtils {
        byte[] res = new byte[patternSize];
        for (int i = 0; i < patternSize; i++) {
            LockPatternView.Cell cell = pattern.get(i);
            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1');
            res[i] = (byte) (cell.getRow() * gridSize + cell.getColumn() + '1');
        }
        return res;
    }
@@ -942,6 +949,40 @@ public class LockPatternUtils {
        return mCredentialTypeCache.query(userHandle);
    }

    /**
     * @return the pattern lockscreen size
     */
    public byte getLockPatternSize(int userId) {
        long size = getLong(Settings.Secure.LOCK_PATTERN_SIZE, -1, userId);
        if (size > 0 && size < 128) {
            return (byte) size;
        }
        return LockPatternUtils.PATTERN_SIZE_DEFAULT;
    }

    /**
     * Set the pattern lockscreen size
     */
    public void setLockPatternSize(long size, int userId) {
        setLong(Settings.Secure.LOCK_PATTERN_SIZE, size, userId);
    }

    public void setVisibleDotsEnabled(boolean enabled, int userId) {
        setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled, userId);
    }

    public boolean isVisibleDotsEnabled(int userId) {
        return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true, userId);
    }

    public void setShowErrorPath(boolean enabled, int userId) {
        setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled, userId);
    }

    public boolean isShowErrorPath(int userId) {
        return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true, userId);
    }

    /**
     * @param userId the user for which to report the value
     * @return Whether the lock screen is secured.
+162 −78
Original line number Diff line number Diff line
@@ -58,13 +58,14 @@ import android.view.animation.Interpolator;

import com.android.internal.R;
import com.android.internal.graphics.ColorUtils;
import com.android.internal.widget.LockPatternUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * Displays and detects the user's unlock attempt, which is a drag of a finger
 * across 9 regions of the screen.
 * across regions of the screen.
 *
 * Is also capable of displaying a static pattern in "in progress", "wrong" or
 * "correct" states.
@@ -81,7 +82,7 @@ public class LockPatternView extends View {
    private static final int DOT_RADIUS_INCREASE_DURATION_MILLIS = 96;
    private static final int DOT_RADIUS_DECREASE_DURATION_MILLIS = 192;
    private static final float MIN_DOT_HIT_FACTOR = 0.2f;
    private final CellState[][] mCellStates;
    private CellState[][] mCellStates;

    private final int mDotSize;
    private final int mDotSizeActivated;
@@ -104,6 +105,8 @@ public class LockPatternView extends View {
     */
    private static final int MILLIS_PER_CIRCLE_ANIMATING = 700;

    private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT;

    /**
     * This can be used to avoid updating the display for very small motions or noisy panels.
     * It didn't seem to have much impact on the devices tested, so currently set to 0.
@@ -115,7 +118,7 @@ public class LockPatternView extends View {

    private OnPatternListener mOnPatternListener;
    @UnsupportedAppUsage
    private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9);
    private ArrayList<Cell> mPattern = new ArrayList<Cell>(mPatternSize * mPatternSize);

    /**
     * Lookup table for the circles of the pattern we are currently drawing.
@@ -123,7 +126,7 @@ public class LockPatternView extends View {
     * in which case we use this to hold the cells we are drawing for the in
     * progress animation.
     */
    private final boolean[][] mPatternDrawLookup = new boolean[3][3];
    private boolean[][] mPatternDrawLookup = new boolean[mPatternSize][mPatternSize];

    /**
     * the in progress point:
@@ -134,7 +137,7 @@ public class LockPatternView extends View {
    private float mInProgressY = -1;

    private long mAnimatingPeriodStart;
    private long[] mLineFadeStart = new long[9];
    private long[] mLineFadeStart = new long[mPatternSize * mPatternSize];

    @UnsupportedAppUsage
    private DisplayMode mPatternDisplayMode = DisplayMode.Correct;
@@ -144,6 +147,8 @@ public class LockPatternView extends View {
    @UnsupportedAppUsage
    private boolean mPatternInProgress = false;
    private boolean mFadePattern = true;
    private boolean mVisibleDots = true;
    private boolean mShowErrorPath = true;

    @UnsupportedAppUsage
    private float mSquareWidth;
@@ -171,8 +176,10 @@ public class LockPatternView extends View {
    private Drawable mNotSelectedDrawable;
    private boolean mUseLockPatternDrawable;

    private LockPatternUtils mLockPatternUtils;

    /**
     * Represents a cell in the 3 X 3 matrix of the unlock pattern view.
     * Represents a cell in the matrix of the unlock pattern view.
     */
    public static final class Cell {
        @UnsupportedAppUsage
@@ -180,25 +187,18 @@ public class LockPatternView extends View {
        @UnsupportedAppUsage
        final int column;

        // keep # objects limited to 9
        private static final Cell[][] sCells = createCells();

        private static Cell[][] createCells() {
            Cell[][] res = new Cell[3][3];
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    res[i][j] = new Cell(i, j);
                }
            }
            return res;
        static Cell[][] sCells;
        static {
            updateSize(LockPatternUtils.PATTERN_SIZE_DEFAULT);
        }

        /**
         * @param row The row of the cell.
         * @param column The column of the cell.
         * @param size The size of the cell.
         */
        private Cell(int row, int column) {
            checkRange(row, column);
        private Cell(int row, int column, byte size) {
            checkRange(row, column, size);
            this.row = row;
            this.column = column;
        }
@@ -211,20 +211,28 @@ public class LockPatternView extends View {
            return column;
        }

        public static Cell of(int row, int column) {
            checkRange(row, column);
        public static Cell of(int row, int column, byte size) {
            checkRange(row, column, size);
            return sCells[row][column];
        }

        private static void checkRange(int row, int column) {
            if (row < 0 || row > 2) {
                throw new IllegalArgumentException("row must be in range 0-2");
        public static void updateSize(byte size) {
            sCells = new Cell[size][size];
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size; j++) {
                    sCells[i][j] = new Cell(i, j, size);
                }
            if (column < 0 || column > 2) {
                throw new IllegalArgumentException("column must be in range 0-2");
            }
        }

        private static void checkRange(int row, int column, byte size) {
            if (row < 0 || row > size - 1) {
                throw new IllegalArgumentException("row must be in range 0-" + (size - 1));
            }
            if (column < 0 || column > size - 1) {
                throw new IllegalArgumentException("column must be in range 0-" + (size - 1));
            }
        }
        @Override
        public String toString() {
            return "(row=" + row + ",clmn=" + column + ")";
@@ -367,9 +375,9 @@ public class LockPatternView extends View {
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);

        mCellStates = new CellState[3][3];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
        mCellStates = new CellState[mPatternSize][mPatternSize];
        for (int i = 0; i < mPatternSize; i++) {
            for (int j = 0; j < mPatternSize; j++) {
                mCellStates[i][j] = new CellState();
                mCellStates[i][j].radius = mDotSize/2;
                mCellStates[i][j].row = i;
@@ -406,6 +414,13 @@ public class LockPatternView extends View {
        return mInStealthMode;
    }

    /**
     * @return the current pattern lockscreen size.
     */
    public byte getLockPatternSize() {
        return mPatternSize;
    }

    /**
     * Set whether the view is in stealth mode.  If true, there will be no
     * visible feedback as the user enters the pattern.
@@ -417,6 +432,22 @@ public class LockPatternView extends View {
        mInStealthMode = inStealthMode;
    }

    public void setVisibleDots(boolean visibleDots) {
        mVisibleDots = visibleDots;
    }

    public boolean isVisibleDots() {
        return mVisibleDots;
    }

    public void setShowErrorPath(boolean showErrorPath) {
        mShowErrorPath = showErrorPath;
    }

    public boolean isShowErrorPath() {
        return mShowErrorPath;
    }

    /**
     * Set whether the pattern should fade as it's being drawn. If
     * true, each segment of the pattern fades over time.
@@ -425,6 +456,36 @@ public class LockPatternView extends View {
        mFadePattern = fadePattern;
    }

    /**
     * Set the pattern size of the lockscreen
     *
     * @param size The pattern size.
     */
    public void setLockPatternSize(byte size) {
        mPatternSize = size;
        Cell.updateSize(size);
        mCellStates = new CellState[mPatternSize][mPatternSize];
        for (int i = 0; i < mPatternSize; i++) {
            for (int j = 0; j < mPatternSize; j++) {
                mCellStates[i][j] = new CellState();
                mCellStates[i][j].radius = mDotSize / 2;
                mCellStates[i][j].row = i;
                mCellStates[i][j].col = j;
            }
        }
        mPattern = new ArrayList<Cell>(size * size);
        mLineFadeStart = new long[size * size];
        mPatternDrawLookup = new boolean[size][size];
    }

    /**
     * Set the LockPatternUtil instance used to encode a pattern to a string
     * @param utils The instance.
     */
    public void setLockPatternUtils(LockPatternUtils utils) {
        mLockPatternUtils = utils;
    }

    /**
     * Set the call back for pattern detection.
     * @param onPatternListener The call back.
@@ -652,10 +713,10 @@ public class LockPatternView extends View {
     * the next attempt.
     */
    private void clearPatternDrawLookup() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
        for (int i = 0; i < mPatternSize; i++) {
            for (int j = 0; j < mPatternSize; j++) {
                mPatternDrawLookup[i][j] = false;
                mLineFadeStart[i+j*3] = 0;
                mLineFadeStart[i * mPatternSize + j] = 0;
            }
        }
    }
@@ -680,11 +741,11 @@ public class LockPatternView extends View {
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        final int width = w - mPaddingLeft - mPaddingRight;
        mSquareWidth = width / 3.0f;
        mSquareWidth = width / (float) mPatternSize;

        if (DEBUG_A11Y) Log.v(TAG, "onSizeChanged(" + w + "," + h + ")");
        final int height = h - mPaddingTop - mPaddingBottom;
        mSquareHeight = height / 3.0f;
        mSquareHeight = height / (float) mPatternSize;
        mExploreByTouchHelper.invalidateRoot();
        mDotHitRadius = Math.min(mSquareHeight / 2, mSquareWidth / 2) * mDotHitFactor;

@@ -746,7 +807,6 @@ public class LockPatternView extends View {
        if (cell != null) {

            // check for gaps in existing pattern
            Cell fillInGapCell = null;
            final ArrayList<Cell> pattern = mPattern;
            if (!pattern.isEmpty()) {
                final Cell lastCell = pattern.get(pattern.size() - 1);
@@ -756,21 +816,19 @@ public class LockPatternView extends View {
                int fillInRow = lastCell.row;
                int fillInColumn = lastCell.column;

                if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) {
                    fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1);
                if (dRow == 0 || dColumn == 0 || Math.abs(dRow) == Math.abs(dColumn)) {
                    while (true) {
                        fillInRow += Integer.signum(dRow);
                        fillInColumn += Integer.signum(dColumn);
                        if (fillInRow == cell.row && fillInColumn == cell.column) break;
                        Cell fillInGapCell = Cell.of(fillInRow, fillInColumn, mPatternSize);
                        if (!mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
                            addCellToPattern(fillInGapCell);
                        }

                if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) {
                    fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1);
                    }

                fillInGapCell = Cell.of(fillInRow, fillInColumn);
                }

            if (fillInGapCell != null &&
                    !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
                addCellToPattern(fillInGapCell);
            }

            addCellToPattern(cell);
            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
@@ -910,13 +968,13 @@ public class LockPatternView extends View {
    @Nullable
    private Cell detectCellHit(float x, float y) {
        final float hitRadiusSquared = mDotHitRadius * mDotHitRadius;
        for (int row = 0; row < 3; row++) {
            for (int column = 0; column < 3; column++) {
        for (int row = 0; row < mPatternSize; row++) {
            for (int column = 0; column < mPatternSize; column++) {
                float centerY = getCenterYForRow(row);
                float centerX = getCenterXForColumn(column);
                if ((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)
                        < hitRadiusSquared) {
                    return Cell.of(row, column);
                    return Cell.of(row, column, mPatternSize);
                }
            }
        }
@@ -1056,11 +1114,6 @@ public class LockPatternView extends View {
            setPatternInProgress(false);
            cancelLineAnimations();
            notifyPatternDetected();
            // Also clear pattern if fading is enabled
            if (mFadePattern) {
                clearPatternDrawLookup();
                mPatternDisplayMode = DisplayMode.Correct;
            }
            invalidate();
        }
        if (PROFILE_DRAWING) {
@@ -1072,8 +1125,8 @@ public class LockPatternView extends View {
    }

    private void cancelLineAnimations() {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
        for (int i = 0; i < mPatternSize; i++) {
            for (int j = 0; j < mPatternSize; j++) {
                CellState state = mCellStates[i][j];
                if (state.activationAnimator != null) {
                    state.activationAnimator.cancel();
@@ -1195,7 +1248,9 @@ public class LockPatternView extends View {
        // TODO: the path should be created and cached every time we hit-detect a cell
        // only the last segment of the path should be computed here
        // draw the path of the pattern (unless we are in stealth mode)
        final boolean drawPath = !mInStealthMode;
        final boolean drawWrongPath = mPatternDisplayMode == DisplayMode.Wrong && mShowErrorPath;
        final boolean drawPath = (!mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong)
                || drawWrongPath;

        if (drawPath) {
            mPathPaint.setColor(getCurrentColor(true /* partOfPattern */));
@@ -1254,20 +1309,16 @@ public class LockPatternView extends View {
        }

        // draw the circles
        for (int i = 0; i < 3; i++) {
        if (mVisibleDots) {
            for (int i = 0; i < mPatternSize; i++) {
                float centerY = getCenterYForRow(i);
            for (int j = 0; j < 3; j++) {
                for (int j = 0; j < mPatternSize; j++) {
                    CellState cellState = mCellStates[i][j];
                    float centerX = getCenterXForColumn(j);
                    float translationY = cellState.translationY;

                    if (mUseLockPatternDrawable) {
                        drawCellDrawable(canvas, i, j, cellState.radius, drawLookup[i][j]);
                } else {
                    if (isHardwareAccelerated() && cellState.hwAnimating) {
                        RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
                        recordingCanvas.drawCircle(cellState.hwCenterX, cellState.hwCenterY,
                                cellState.hwRadius, cellState.hwPaint);
                    } else {
                        drawCircle(canvas, (int) centerX, (int) centerY + translationY,
                                cellState.radius, drawLookup[i][j], cellState.alpha,
@@ -1353,7 +1404,10 @@ public class LockPatternView extends View {
    }

    private int getCurrentColor(boolean partOfPattern) {
        if (!partOfPattern || mInStealthMode || mPatternInProgress) {
        if (!partOfPattern
                || (mInStealthMode && mPatternDisplayMode != DisplayMode.Wrong)
                || (mPatternDisplayMode == DisplayMode.Wrong && !mShowErrorPath)
                || mPatternInProgress) {
            // unselected circle
            return mRegularColor;
        } else if (mPatternDisplayMode == DisplayMode.Wrong) {
@@ -1410,12 +1464,13 @@ public class LockPatternView extends View {
    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern);
        byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern, mPatternSize);
        String patternString = patternBytes != null ? new String(patternBytes) : null;
        return new SavedState(superState,
                patternString,
                mPatternDisplayMode.ordinal(),
                mInputEnabled, mInStealthMode);
                mPatternSize, mInputEnabled, mInStealthMode,
                mVisibleDots, mShowErrorPath);
    }

    @Override
@@ -1424,10 +1479,14 @@ public class LockPatternView extends View {
        super.onRestoreInstanceState(ss.getSuperState());
        setPattern(
                DisplayMode.Correct,
                LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
                LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes(),
                        ss.getPatternSize()));
        mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
        mPatternSize = ss.getPatternSize();
        mInputEnabled = ss.isInputEnabled();
        mInStealthMode = ss.isInStealthMode();
        mVisibleDots = ss.isVisibleDots();
        mShowErrorPath = ss.isShowErrorPath();
    }

    /**
@@ -1437,20 +1496,27 @@ public class LockPatternView extends View {

        private final String mSerializedPattern;
        private final int mDisplayMode;
        private final byte mPatternSize;
        private final boolean mInputEnabled;
        private final boolean mInStealthMode;
        private final boolean mVisibleDots;
        private final boolean mShowErrorPath;

        /**
         * Constructor called from {@link LockPatternView#onSaveInstanceState()}
         */
        @UnsupportedAppUsage
        private SavedState(Parcelable superState, String serializedPattern, int displayMode,
                boolean inputEnabled, boolean inStealthMode) {
                byte patternSize, boolean inputEnabled, boolean inStealthMode,
                boolean visibleDots, boolean showErrorPath) {
            super(superState);
            mSerializedPattern = serializedPattern;
            mDisplayMode = displayMode;
            mPatternSize = patternSize;
            mInputEnabled = inputEnabled;
            mInStealthMode = inStealthMode;
            mVisibleDots = visibleDots;
            mShowErrorPath = showErrorPath;
        }

        /**
@@ -1461,8 +1527,11 @@ public class LockPatternView extends View {
            super(in);
            mSerializedPattern = in.readString();
            mDisplayMode = in.readInt();
            mPatternSize = (byte) in.readByte();
            mInputEnabled = (Boolean) in.readValue(null);
            mInStealthMode = (Boolean) in.readValue(null);
            mVisibleDots = (Boolean) in.readValue(null);
            mShowErrorPath = (Boolean) in.readValue(null);
        }

        public String getSerializedPattern() {
@@ -1473,6 +1542,10 @@ public class LockPatternView extends View {
            return mDisplayMode;
        }

        public byte getPatternSize() {
            return mPatternSize;
        }

        public boolean isInputEnabled() {
            return mInputEnabled;
        }
@@ -1481,13 +1554,24 @@ public class LockPatternView extends View {
            return mInStealthMode;
        }

        public boolean isVisibleDots() {
            return mVisibleDots;
        }

        public boolean isShowErrorPath() {
            return mShowErrorPath;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeString(mSerializedPattern);
            dest.writeInt(mDisplayMode);
            dest.writeByte(mPatternSize);
            dest.writeValue(mInputEnabled);
            dest.writeValue(mInStealthMode);
            dest.writeValue(mVisibleDots);
            dest.writeValue(mShowErrorPath);
        }

        @SuppressWarnings({ "unused", "hiding" }) // Found using reflection
Loading