[go: nahoru, domu]

blob: b99677690c5e0a9b19891b0b5c370a9cc2ae5e4f [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.biometric;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricManager.Authenticators;
import androidx.biometric.BiometricPrompt;
/**
* Controller for the biometric prompt demo app. Coordinates the logic of initializing buttons,
* generating the info, and launching the prompt itself.
*/
abstract class BiometricPromptDemoController {
private static final String TAG = "bio_prompt_demo_control";
private static final String KEY_COUNTER = "saved_counter";
private static final String KEY_LOG = "saved_log";
private static final String BIOMETRIC_SUCCESS_MESSAGE = "BIOMETRIC_SUCCESS_MESSAGE";
private static final String BIOMETRIC_ERROR_HW_UNAVAILABLE_MESSAGE =
"BIOMETRIC_ERROR_HW_UNAVAILABLE";
private static final String BIOMETRIC_ERROR_NONE_ENROLLED_MESSAGE =
"BIOMETRIC_ERROR_NONE_ENROLLED";
private static final String BIOMETRIC_ERROR_NO_HARDWARE =
"BIOMETRIC_ERROR_NO_HARDWARE";
private static final String BIOMETRIC_ERROR_UNKNOWN = "Error unknown return result";
private static final int MODE_NONE = 0;
private static final int MODE_PERSIST_ACROSS_CONFIGURATION_CHANGES = 1;
private static final int MODE_CANCEL_ON_CONFIGURATION_CHANGE = 2;
private static final int MODE_CANCEL_AFTER_THREE_FAILURES = 3;
BiometricPrompt mBiometricPrompt;
private int mCounter;
private final Button mCanAuthenticateButton;
private final Button mAuthenticateButton;
private final CheckBox mRequireConfirmationCheckbox;
private final CheckBox mAllowDeviceCredentialCheckbox;
private final RadioGroup mBiometricPromptConfigurationRadioGroup;
private final TextView mLogView;
final BiometricPrompt.AuthenticationCallback mAuthenticationCallback =
new BiometricPrompt.AuthenticationCallback() {
private int mNumberFailedAttempts = 0;
@Override
public void onAuthenticationError(int err, @NonNull CharSequence message) {
log("onAuthenticationError " + err + ": " + message);
mNumberFailedAttempts = 0;
}
@Override
public void onAuthenticationSucceeded(
@NonNull BiometricPrompt.AuthenticationResult result) {
log("onAuthenticationSucceeded");
mNumberFailedAttempts = 0;
}
@Override
public void onAuthenticationFailed() {
log("onAuthenticationFailed");
mNumberFailedAttempts++;
// Cancel authentication after 3 failed attempts to test the cancel() method.
if (getMode() == MODE_CANCEL_AFTER_THREE_FAILURES
&& mNumberFailedAttempts == 3) {
mBiometricPrompt.cancelAuthentication();
}
}
};
abstract Context getApplicationContext();
/**
* (Re)establish a connection between the host fragment/activity and the BiometricPrompt
* library.
*
* Developers should (re)create the BiometricPrompt every time the activity/fragment is
* created. This allows authentication to work across configuration changes. The internal
* implementation of the library uses fragments, which can be saved/restored. Instantiating
* the library with a new callback and executor early in the fragment/activity lifecycle (e.g.
* onCreate or onCreateView) allows the new instance to receive callbacks properly.
*/
abstract void reconnect();
BiometricPromptDemoController(@NonNull View inflatedRootView) {
mCanAuthenticateButton = inflatedRootView.findViewById(R.id.can_authenticate);
mAuthenticateButton = inflatedRootView.findViewById(R.id.button_authenticate);
mRequireConfirmationCheckbox = inflatedRootView.findViewById(
R.id.checkbox_require_confirmation);
mAllowDeviceCredentialCheckbox = inflatedRootView.findViewById(
R.id.checkbox_allow_device_credential);
mBiometricPromptConfigurationRadioGroup = inflatedRootView.findViewById(
R.id.radio_group_biometric_prompt_configuration);
mLogView = inflatedRootView.findViewById(R.id.log_text);
Button clearLogButton = inflatedRootView.findViewById(R.id.button_clear_log);
clearLogButton.setOnClickListener((view1) -> mLogView.setText(""));
}
/** Sets up button callbacks and other state for the biometric prompt demo controller. */
void init(@Nullable Bundle savedInstanceState) {
if (savedInstanceState != null) {
mCounter = savedInstanceState.getInt(KEY_COUNTER);
mLogView.setText(savedInstanceState.getCharSequence(KEY_LOG));
}
mAuthenticateButton.setOnClickListener(v -> startAuthentication());
mCanAuthenticateButton.setOnClickListener(v -> {
BiometricManager biometricManager = BiometricManager.from(getApplicationContext());
String message;
switch (biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK)) {
case BiometricManager.BIOMETRIC_SUCCESS:
message = BIOMETRIC_SUCCESS_MESSAGE;
break;
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
message = BIOMETRIC_ERROR_HW_UNAVAILABLE_MESSAGE;
break;
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
message = BIOMETRIC_ERROR_NONE_ENROLLED_MESSAGE;
break;
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
message = BIOMETRIC_ERROR_NO_HARDWARE;
break;
default:
message = BIOMETRIC_ERROR_UNKNOWN;
}
log("canAuthenticate: " + message);
});
}
void onPause() {
if (getMode() == MODE_CANCEL_ON_CONFIGURATION_CHANGE) {
mBiometricPrompt.cancelAuthentication();
}
}
void onSaveInstanceState(@NonNull Bundle outState) {
outState.putInt(KEY_COUNTER, mCounter);
outState.putCharSequence(KEY_LOG, mLogView.getText());
}
private void startAuthentication() {
if (getMode() == MODE_NONE) {
log("Select a test first");
return;
}
// Build the biometric prompt info
BiometricPrompt.PromptInfo.Builder builder = new BiometricPrompt.PromptInfo.Builder()
.setTitle("Title " + mCounter)
.setSubtitle("Subtitle " + mCounter)
.setDescription(
"Lorem ipsum dolor sit amet, consecte etur adipisicing elit. "
+ mCounter)
.setConfirmationRequired(mRequireConfirmationCheckbox.isChecked());
if (mAllowDeviceCredentialCheckbox.isChecked()) {
builder.setDeviceCredentialAllowed(true);
} else {
builder.setNegativeButtonText("Negative Button " + mCounter);
}
BiometricPrompt.PromptInfo info = builder.build();
mCounter++;
// Show the biometric prompt.
mBiometricPrompt.authenticate(info);
}
/**
* @return The currently selected configuration.
*/
private int getMode() {
int id = mBiometricPromptConfigurationRadioGroup.getCheckedRadioButtonId();
switch (id) {
case R.id.radio_persist_across_configuration_changes:
return MODE_PERSIST_ACROSS_CONFIGURATION_CHANGES;
case R.id.radio_cancel_on_configuration_change:
return MODE_CANCEL_ON_CONFIGURATION_CHANGE;
case R.id.radio_cancel_after_three_failures:
return MODE_CANCEL_AFTER_THREE_FAILURES;
default:
return MODE_NONE;
}
}
private void log(String s) {
Log.d(TAG, s);
mLogView.append(s + '\n');
}
}