1 /* 2 * Copyright (C) 2024 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 android.hardware.biometrics; 18 19 import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED; 20 import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT; 21 22 import android.annotation.CallbackExecutor; 23 import android.annotation.FlaggedApi; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.content.DialogInterface; 28 import android.hardware.biometrics.BiometricPrompt.ButtonInfo; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.util.Log; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.concurrent.Executor; 36 37 /** 38 * Contains the information of the template of content view with a more options button for 39 * Biometric Prompt. 40 * <p> 41 * This button should be used to provide more options for sign in or other purposes, such as when a 42 * user needs to select between multiple app-specific accounts or profiles that are available for 43 * sign in. 44 * <p> 45 * Apps should avoid using this when possible because it will create additional steps that the user 46 * must navigate through - clicking the more options button will dismiss the prompt, provide the app 47 * an opportunity to ask the user for the correct option, and finally allow the app to decide how to 48 * proceed once selected. 49 * 50 * <p> 51 * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric 52 * Prompt: 53 * <pre class="prettyprint"> 54 * BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(...) 55 * .setTitle(...) 56 * .setSubTitle(...) 57 * .setContentView(new PromptContentViewWithMoreOptionsButton.Builder() 58 * .setDescription("test description") 59 * .setMoreOptionsButtonListener(executor, listener) 60 * .build()) 61 * .build(); 62 * </pre> 63 */ 64 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) 65 public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable { 66 private static final String TAG = "PromptContentViewWithMoreOptionsButton"; 67 @VisibleForTesting 68 static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225; 69 70 private final String mDescription; 71 private DialogInterface.OnClickListener mListener; 72 private ButtonInfo mButtonInfo; 73 PromptContentViewWithMoreOptionsButton( @onNull String description, @NonNull @CallbackExecutor Executor executor, @NonNull DialogInterface.OnClickListener listener)74 private PromptContentViewWithMoreOptionsButton( 75 @NonNull String description, @NonNull @CallbackExecutor Executor executor, 76 @NonNull DialogInterface.OnClickListener listener) { 77 mDescription = description; 78 mListener = listener; 79 mButtonInfo = new ButtonInfo(executor, listener); 80 } 81 PromptContentViewWithMoreOptionsButton(Parcel in)82 private PromptContentViewWithMoreOptionsButton(Parcel in) { 83 mDescription = in.readString(); 84 } 85 86 /** 87 * Gets the description for the content view, as set by 88 * {@link PromptContentViewWithMoreOptionsButton.Builder#setDescription(String)}. 89 * 90 * @return The description for the content view, or null if the content view has no description. 91 */ 92 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 93 @Nullable getDescription()94 public String getDescription() { 95 return mDescription; 96 } 97 98 /** 99 * Gets the click listener for the more options button on the content view, as set by 100 * {@link PromptContentViewWithMoreOptionsButton.Builder#setMoreOptionsButtonListener(Executor, 101 * DialogInterface.OnClickListener)}. 102 * 103 * @return The click listener for the more options button on the content view. 104 */ 105 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) 106 @NonNull getMoreOptionsButtonListener()107 public DialogInterface.OnClickListener getMoreOptionsButtonListener() { 108 return mListener; 109 } 110 getButtonInfo()111 ButtonInfo getButtonInfo() { 112 return mButtonInfo; 113 } 114 115 @Override describeContents()116 public int describeContents() { 117 return 0; 118 } 119 120 @Override writeToParcel(@onNull Parcel dest, int flags)121 public void writeToParcel(@NonNull Parcel dest, int flags) { 122 dest.writeString(mDescription); 123 } 124 125 /** 126 * @see Parcelable.Creator 127 */ 128 @NonNull 129 public static final Creator<PromptContentViewWithMoreOptionsButton> CREATOR = new Creator<>() { 130 @Override 131 public PromptContentViewWithMoreOptionsButton createFromParcel(Parcel in) { 132 return new PromptContentViewWithMoreOptionsButton(in); 133 } 134 135 @Override 136 public PromptContentViewWithMoreOptionsButton[] newArray(int size) { 137 return new PromptContentViewWithMoreOptionsButton[size]; 138 } 139 }; 140 141 /** 142 * A builder that collects arguments to be shown on the content view with more options button. 143 */ 144 public static final class Builder { 145 private String mDescription; 146 private Executor mExecutor; 147 private DialogInterface.OnClickListener mListener; 148 149 /** 150 * Optional: Sets a description that will be shown on the content view. 151 * 152 * @param description The description to display. 153 * @return This builder. 154 */ 155 @NonNull 156 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) setDescription(@onNull String description)157 public Builder setDescription(@NonNull String description) { 158 if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) { 159 Log.w(TAG, "The character number of description exceeds " 160 + MAX_DESCRIPTION_CHARACTER_NUMBER); 161 } 162 mDescription = description; 163 return this; 164 } 165 166 /** 167 * Required: Sets the executor and click listener for the more options button on the 168 * prompt content. 169 * 170 * @param executor Executor that will be used to run the on click callback. 171 * @param listener Listener containing a callback to be run when the button is pressed. 172 * @return This builder. 173 */ 174 @NonNull 175 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) setMoreOptionsButtonListener(@onNull @allbackExecutor Executor executor, @NonNull DialogInterface.OnClickListener listener)176 public Builder setMoreOptionsButtonListener(@NonNull @CallbackExecutor Executor executor, 177 @NonNull DialogInterface.OnClickListener listener) { 178 mExecutor = executor; 179 mListener = listener; 180 return this; 181 } 182 183 184 /** 185 * Creates a {@link PromptContentViewWithMoreOptionsButton}. 186 * 187 * @return An instance of {@link PromptContentViewWithMoreOptionsButton}. 188 * @throws IllegalArgumentException If the executor of more options button is null, or the 189 * listener of more options button is null. 190 */ 191 @NonNull 192 @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) build()193 public PromptContentViewWithMoreOptionsButton build() { 194 if (mExecutor == null) { 195 throw new IllegalArgumentException( 196 "The executor for the listener of more options button on prompt content " 197 + "must be set and non-null if " 198 + "PromptContentViewWithMoreOptionsButton is used."); 199 } 200 if (mListener == null) { 201 throw new IllegalArgumentException( 202 "The listener of more options button on prompt content must be set and " 203 + "non-null if PromptContentViewWithMoreOptionsButton is used."); 204 } 205 return new PromptContentViewWithMoreOptionsButton(mDescription, mExecutor, mListener); 206 } 207 } 208 } 209