1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings.security;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.os.UserHandle;
22 import android.view.View;
23 import android.widget.TextView;
24 
25 import androidx.annotation.LayoutRes;
26 import androidx.annotation.StringRes;
27 
28 import com.android.car.settings.R;
29 import com.android.car.settings.common.BaseFragment;
30 import com.android.internal.widget.LockPatternView;
31 import com.android.internal.widget.LockscreenCredential;
32 
33 import java.util.List;
34 
35 /**
36  * Fragment for confirming existing lock pattern
37  */
38 public class ConfirmLockPatternFragment extends BaseFragment {
39 
40     private static final String FRAGMENT_TAG_CHECK_LOCK_WORKER = "check_lock_worker";
41     // Time we wait before clearing a wrong pattern and the error message.
42     private static final long CLEAR_WRONG_ATTEMPT_TIMEOUT_MS = 2500L;
43 
44     private LockPatternView mLockPatternView;
45     private TextView mMsgView;
46 
47     private CheckLockWorker mCheckLockWorker;
48     private CheckLockListener mCheckLockListener;
49 
50     private int mUserId;
51     private List<LockPatternView.Cell> mPattern;
52 
53     private ConfirmLockLockoutHelper mConfirmLockLockoutHelper;
54 
55     @Override
56     @LayoutRes
getLayoutId()57     protected int getLayoutId() {
58         return R.layout.confirm_lock_pattern;
59     }
60 
61     @Override
62     @StringRes
getTitleId()63     protected int getTitleId() {
64         return R.string.security_settings_title;
65     }
66 
67     @Override
onAttach(Context context)68     public void onAttach(Context context) {
69         super.onAttach(context);
70         if ((getActivity() instanceof CheckLockListener)) {
71             mCheckLockListener = (CheckLockListener) getActivity();
72         } else {
73             throw new RuntimeException("The activity must implement CheckLockListener");
74         }
75 
76         mUserId = UserHandle.myUserId();
77         mConfirmLockLockoutHelper = ConfirmLockLockoutHelper.getInstance(requireContext(), mUserId);
78     }
79 
80     @Override
onViewCreated(View view, Bundle savedInstanceState)81     public void onViewCreated(View view, Bundle savedInstanceState) {
82         super.onViewCreated(view, savedInstanceState);
83 
84         mMsgView = (TextView) view.findViewById(R.id.message);
85         mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
86         mLockPatternView.setFadePattern(false);
87         mLockPatternView.setInStealthMode(
88                 !mConfirmLockLockoutHelper.getLockPatternUtils().isVisiblePatternEnabled(mUserId));
89         mLockPatternView.setOnPatternListener(mLockPatternListener);
90         mConfirmLockLockoutHelper.setConfirmLockUIController(
91                 new ConfirmLockLockoutHelper.ConfirmLockUIController() {
92                     @Override
93                     public void setErrorText(String text) {
94                         mMsgView.setText(text);
95                     }
96 
97                     @Override
98                     public void refreshUI(boolean isLockedOut) {
99                         mLockPatternView.setEnabled(!isLockedOut);
100                         mLockPatternView.clearPattern();
101                     }
102                 });
103 
104         if (savedInstanceState != null) {
105             mCheckLockWorker = (CheckLockWorker) getFragmentManager().findFragmentByTag(
106                     FRAGMENT_TAG_CHECK_LOCK_WORKER);
107         }
108     }
109 
110     @Override
onStart()111     public void onStart() {
112         super.onStart();
113         if (mCheckLockWorker != null) {
114             mCheckLockWorker.setListener(this::onCheckCompleted);
115         }
116     }
117 
118     @Override
onResume()119     public void onResume() {
120         super.onResume();
121 
122         mConfirmLockLockoutHelper.onResumeUI();
123     }
124 
125     @Override
onPause()126     public void onPause() {
127         super.onPause();
128 
129         mConfirmLockLockoutHelper.onPauseUI();
130     }
131 
132     @Override
onStop()133     public void onStop() {
134         super.onStop();
135         if (mCheckLockWorker != null) {
136             mCheckLockWorker.setListener(null);
137         }
138     }
139 
140     private Runnable mClearErrorRunnable = () -> {
141         mLockPatternView.setEnabled(true);
142         mLockPatternView.clearPattern();
143         mMsgView.setText("");
144     };
145 
146     private LockPatternView.OnPatternListener mLockPatternListener =
147             new LockPatternView.OnPatternListener() {
148 
149                 public void onPatternStart() {
150                     mLockPatternView.removeCallbacks(mClearErrorRunnable);
151                     mMsgView.setText("");
152                 }
153 
154                 public void onPatternCleared() {
155                     mLockPatternView.removeCallbacks(mClearErrorRunnable);
156                 }
157 
158                 public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
159                 }
160 
161                 public void onPatternDetected(List<LockPatternView.Cell> pattern) {
162                     mLockPatternView.setEnabled(false);
163 
164                     if (mCheckLockWorker == null) {
165                         mCheckLockWorker = new CheckLockWorker();
166                         mCheckLockWorker.setListener(
167                                 ConfirmLockPatternFragment.this::onCheckCompleted);
168 
169                         getFragmentManager()
170                                 .beginTransaction()
171                                 .add(mCheckLockWorker, FRAGMENT_TAG_CHECK_LOCK_WORKER)
172                                 .commitNow();
173                     }
174 
175                     mPattern = pattern;
176                     mCheckLockWorker.checkPattern(mUserId, pattern);
177                 }
178             };
179 
onCheckCompleted(boolean lockMatched, int timeoutMs)180     private void onCheckCompleted(boolean lockMatched, int timeoutMs) {
181         if (lockMatched) {
182             mCheckLockListener.onLockVerified(LockscreenCredential.createPattern(mPattern));
183         } else {
184             if (timeoutMs > 0) {
185                 mConfirmLockLockoutHelper.onCheckCompletedWithTimeout(timeoutMs);
186             } else {
187                 mLockPatternView.setEnabled(true);
188                 mMsgView.setText(R.string.lockpattern_pattern_wrong);
189 
190                 // Set timer to clear wrong pattern
191                 mLockPatternView.removeCallbacks(mClearErrorRunnable);
192                 mLockPatternView.postDelayed(mClearErrorRunnable, CLEAR_WRONG_ATTEMPT_TIMEOUT_MS);
193             }
194         }
195     }
196 }
197