Home | History | Annotate | Download | only in security
      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.support.annotation.VisibleForTesting;
     23 import android.text.Editable;
     24 import android.text.TextUtils;
     25 import android.text.TextWatcher;
     26 import android.view.View;
     27 import android.view.inputmethod.EditorInfo;
     28 import android.view.inputmethod.InputMethodManager;
     29 import android.widget.EditText;
     30 import android.widget.TextView;
     31 
     32 import com.android.car.settings.R;
     33 import com.android.car.settings.common.BaseFragment;
     34 
     35 /**
     36  * Fragment for confirming existing lock PIN or password.  The containing activity must implement
     37  * CheckLockListener.
     38  */
     39 public class ConfirmLockPinPasswordFragment extends BaseFragment {
     40 
     41     private static final String FRAGMENT_TAG_CHECK_LOCK_WORKER = "check_lock_worker";
     42     private static final String EXTRA_IS_PIN = "extra_is_pin";
     43 
     44     private PinPadView mPinPad;
     45     private EditText mPasswordField;
     46     private TextView mMsgView;
     47 
     48     private CheckLockWorker mCheckLockWorker;
     49     private CheckLockListener mCheckLockListener;
     50 
     51     private int mUserId;
     52     private boolean mIsPin;
     53     private String mEnteredPassword;
     54 
     55     /**
     56      * Factory method for creating fragment in PIN mode.
     57      */
     58     public static ConfirmLockPinPasswordFragment newPinInstance() {
     59         ConfirmLockPinPasswordFragment patternFragment = new ConfirmLockPinPasswordFragment();
     60         Bundle bundle = BaseFragment.getBundle();
     61         bundle.putInt(EXTRA_TITLE_ID, R.string.security_settings_title);
     62         bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar_with_button);
     63         bundle.putInt(EXTRA_LAYOUT, R.layout.confirm_lock_pin_fragment);
     64         bundle.putBoolean(EXTRA_IS_PIN, true);
     65         patternFragment.setArguments(bundle);
     66         return patternFragment;
     67     }
     68 
     69     /**
     70      * Factory method for creating fragment in password mode.
     71      */
     72     public static ConfirmLockPinPasswordFragment newPasswordInstance() {
     73         ConfirmLockPinPasswordFragment patternFragment = new ConfirmLockPinPasswordFragment();
     74         Bundle bundle = BaseFragment.getBundle();
     75         bundle.putInt(EXTRA_TITLE_ID, R.string.security_settings_title);
     76         bundle.putInt(EXTRA_ACTION_BAR_LAYOUT, R.layout.action_bar_with_button);
     77         bundle.putInt(EXTRA_LAYOUT, R.layout.confirm_lock_password_fragment);
     78         bundle.putBoolean(EXTRA_IS_PIN, false);
     79         patternFragment.setArguments(bundle);
     80         return patternFragment;
     81     }
     82 
     83     @Override
     84     public void onAttach(Context context) {
     85         super.onAttach(context);
     86         if ((getActivity() instanceof CheckLockListener)) {
     87             mCheckLockListener = (CheckLockListener) getActivity();
     88         } else {
     89             throw new RuntimeException("The activity must implement CheckLockListener");
     90         }
     91     }
     92 
     93     @Override
     94     public void onCreate(Bundle savedInstanceState) {
     95         super.onCreate(savedInstanceState);
     96         mUserId = UserHandle.myUserId();
     97         Bundle args = getArguments();
     98         if (args != null) {
     99             mIsPin = args.getBoolean(EXTRA_IS_PIN);
    100         }
    101     }
    102 
    103     @Override
    104     public void onViewCreated(View view, Bundle savedInstanceState) {
    105         super.onViewCreated(view, savedInstanceState);
    106 
    107         mPasswordField = (EditText) view.findViewById(R.id.password_entry);
    108         mMsgView = (TextView) view.findViewById(R.id.message);
    109 
    110         if (mIsPin) {
    111             initPinView(view);
    112         } else {
    113             initPasswordView();
    114         }
    115 
    116         if (savedInstanceState != null) {
    117             mCheckLockWorker = (CheckLockWorker) getFragmentManager().findFragmentByTag(
    118                     FRAGMENT_TAG_CHECK_LOCK_WORKER);
    119         }
    120     }
    121 
    122     @Override
    123     public void onStart() {
    124         super.onStart();
    125         if (mCheckLockWorker != null) {
    126             mCheckLockWorker.setListener(this::onCheckCompleted);
    127         }
    128     }
    129 
    130     @Override
    131     public void onStop() {
    132         super.onStop();
    133         if (mCheckLockWorker != null) {
    134             mCheckLockWorker.setListener(null);
    135         }
    136     }
    137 
    138     private void initCheckLockWorker() {
    139         if (mCheckLockWorker == null) {
    140             mCheckLockWorker = new CheckLockWorker();
    141             mCheckLockWorker.setListener(this::onCheckCompleted);
    142 
    143             getFragmentManager()
    144                     .beginTransaction()
    145                     .add(mCheckLockWorker, FRAGMENT_TAG_CHECK_LOCK_WORKER)
    146                     .commitNow();
    147         }
    148     }
    149 
    150     private void initPinView(View view) {
    151         mPinPad = (PinPadView) view.findViewById(R.id.pin_pad);
    152         mPinPad.setEnterKeyIcon(R.drawable.ic_done);
    153 
    154         PinPadView.PinPadClickListener pinPadClickListener = new PinPadView.PinPadClickListener() {
    155             @Override
    156             public void onDigitKeyClick(String digit) {
    157                 clearError();
    158                 mPasswordField.append(digit);
    159             }
    160 
    161             @Override
    162             public void onBackspaceClick() {
    163                 clearError();
    164                 String pin = mPasswordField.getText().toString();
    165                 if (pin.length() > 0) {
    166                     mPasswordField.setText(pin.substring(0, pin.length() - 1));
    167                 }
    168             }
    169 
    170             @Override
    171             public void onEnterKeyClick() {
    172                 mEnteredPassword = mPasswordField.getText().toString();
    173                 if (!TextUtils.isEmpty(mEnteredPassword)) {
    174                     initCheckLockWorker();
    175                     mPinPad.setEnabled(false);
    176                     mCheckLockWorker.checkPinPassword(mUserId, mEnteredPassword);
    177                 }
    178             }
    179         };
    180 
    181         mPinPad.setPinPadClickListener(pinPadClickListener);
    182     }
    183 
    184     private void initPasswordView() {
    185         mPasswordField.setOnEditorActionListener((textView, actionId, keyEvent) -> {
    186             // Check if this was the result of hitting the enter or "done" key.
    187             if (actionId == EditorInfo.IME_NULL
    188                     || actionId == EditorInfo.IME_ACTION_DONE
    189                     || actionId == EditorInfo.IME_ACTION_NEXT) {
    190 
    191                 initCheckLockWorker();
    192                 if (!mCheckLockWorker.isCheckInProgress()) {
    193                     mEnteredPassword = mPasswordField.getText().toString();
    194                     mCheckLockWorker.checkPinPassword(mUserId, mEnteredPassword);
    195                 }
    196                 return true;
    197             }
    198             return false;
    199         });
    200 
    201         mPasswordField.addTextChangedListener(new TextWatcher() {
    202             @Override
    203             public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    204 
    205             @Override
    206             public void onTextChanged(CharSequence s, int start, int before, int count) {}
    207 
    208             @Override
    209             public void afterTextChanged(Editable s) {
    210                 clearError();
    211             }
    212         });
    213     }
    214 
    215     private void clearError() {
    216         if (!TextUtils.isEmpty(mMsgView.getText())) {
    217             mMsgView.setText("");
    218         }
    219     }
    220 
    221     private void hideKeyboard() {
    222         View currentFocus = getActivity().getCurrentFocus();
    223         if (currentFocus == null) {
    224             currentFocus = getActivity().getWindow().getDecorView();
    225         }
    226 
    227         if (currentFocus != null) {
    228             InputMethodManager inputMethodManager =
    229                     (InputMethodManager) currentFocus.getContext()
    230                             .getSystemService(Context.INPUT_METHOD_SERVICE);
    231             inputMethodManager
    232                     .hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
    233         }
    234     }
    235 
    236     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    237     void onCheckCompleted(boolean lockMatched) {
    238         if (lockMatched) {
    239             mCheckLockListener.onLockVerified(mEnteredPassword);
    240         } else {
    241             mMsgView.setText(
    242                     mIsPin ? R.string.lockscreen_wrong_pin : R.string.lockscreen_wrong_password);
    243             mPinPad.setEnabled(true);
    244         }
    245 
    246         if (!mIsPin) {
    247             hideKeyboard();
    248         }
    249     }
    250 }
    251