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