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