1 /* 2 * Copyright (C) 2023 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.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT; 20 21 import android.annotation.FlaggedApi; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.util.Log; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 34 /** 35 * Contains the information of the template of vertical list content view for Biometric Prompt. 36 * <p> 37 * Here's how you'd set a <code>PromptVerticalListContentView</code> on a Biometric Prompt: 38 * <pre class="prettyprint"> 39 * BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(...) 40 * .setTitle(...) 41 * .setSubTitle(...) 42 * .setContentView(new PromptVerticalListContentView.Builder() 43 * .setDescription("test description") 44 * .addListItem(new PromptContentItemPlainText("test item 1")) 45 * .addListItem(new PromptContentItemPlainText("test item 2")) 46 * .addListItem(new PromptContentItemBulletedText("test item 3")) 47 * .build()) 48 * .build(); 49 * </pre> 50 */ 51 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) 52 public final class PromptVerticalListContentView implements PromptContentViewParcelable { 53 private static final String TAG = "PromptVerticalListContentView"; 54 @VisibleForTesting 55 static final int MAX_ITEM_NUMBER = 20; 56 @VisibleForTesting 57 static final int MAX_EACH_ITEM_CHARACTER_NUMBER = 640; 58 @VisibleForTesting 59 static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225; 60 61 private final List<PromptContentItemParcelable> mContentList; 62 private final String mDescription; 63 PromptVerticalListContentView( @onNull List<PromptContentItemParcelable> contentList, @NonNull String description)64 private PromptVerticalListContentView( 65 @NonNull List<PromptContentItemParcelable> contentList, 66 @NonNull String description) { 67 mContentList = contentList; 68 mDescription = description; 69 } 70 PromptVerticalListContentView(Parcel in)71 private PromptVerticalListContentView(Parcel in) { 72 mContentList = in.readArrayList( 73 PromptContentItemParcelable.class.getClassLoader(), 74 PromptContentItemParcelable.class); 75 mDescription = in.readString(); 76 } 77 78 /** 79 * Returns the maximum count of the list items. 80 */ getMaxItemCount()81 public static int getMaxItemCount() { 82 return MAX_ITEM_NUMBER; 83 } 84 85 /** 86 * Returns the maximum number of characters allowed for each item's text. 87 */ getMaxEachItemCharacterNumber()88 public static int getMaxEachItemCharacterNumber() { 89 return MAX_EACH_ITEM_CHARACTER_NUMBER; 90 } 91 92 /** 93 * Gets the description for the content view, as set by 94 * {@link PromptVerticalListContentView.Builder#setDescription(String)}. 95 * 96 * @return The description for the content view, or null if the content view has no description. 97 */ 98 @Nullable getDescription()99 public String getDescription() { 100 return mDescription; 101 } 102 103 /** 104 * Gets the list of items on the content view, as set by 105 * {@link PromptVerticalListContentView.Builder#addListItem(PromptContentItem)}. 106 * 107 * @return The item list on the content view. 108 */ 109 @NonNull getListItems()110 public List<PromptContentItem> getListItems() { 111 return new ArrayList<>(mContentList); 112 } 113 114 /** 115 * {@inheritDoc} 116 */ 117 @Override describeContents()118 public int describeContents() { 119 return 0; 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override writeToParcel(@ndroidx.annotation.NonNull Parcel dest, int flags)126 public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { 127 dest.writeList(mContentList); 128 dest.writeString(mDescription); 129 } 130 131 /** 132 * @see Parcelable.Creator 133 */ 134 @NonNull 135 public static final Creator<PromptVerticalListContentView> CREATOR = new Creator<>() { 136 @Override 137 public PromptVerticalListContentView createFromParcel(Parcel in) { 138 return new PromptVerticalListContentView(in); 139 } 140 141 @Override 142 public PromptVerticalListContentView[] newArray(int size) { 143 return new PromptVerticalListContentView[size]; 144 } 145 }; 146 147 148 /** 149 * A builder that collects arguments to be shown on the vertical list view. 150 */ 151 public static final class Builder { 152 private final List<PromptContentItemParcelable> mContentList = new ArrayList<>(); 153 private String mDescription; 154 155 /** 156 * Optional: Sets a description that will be shown on the content view. 157 * 158 * @param description The description to display. 159 * @return This builder. 160 */ 161 @NonNull setDescription(@onNull String description)162 public Builder setDescription(@NonNull String description) { 163 if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) { 164 Log.w(TAG, "The character number of description exceeds " 165 + MAX_DESCRIPTION_CHARACTER_NUMBER); 166 } 167 mDescription = description; 168 return this; 169 } 170 171 /** 172 * Optional: Adds a list item in the current row. 173 * 174 * @param listItem The list item view to display 175 * @return This builder. 176 * @throws IllegalArgumentException If the number of list items exceeds certain limit. 177 */ 178 @NonNull addListItem(@onNull PromptContentItem listItem)179 public Builder addListItem(@NonNull PromptContentItem listItem) { 180 mContentList.add((PromptContentItemParcelable) listItem); 181 checkItemLimits(listItem); 182 return this; 183 } 184 185 /** 186 * Optional: Adds a list item in the current row. 187 * 188 * @param listItem The list item view to display 189 * @param index The position at which to add the item 190 * @return This builder. 191 * @throws IllegalArgumentException If the number of list items exceeds certain limit. 192 */ 193 @NonNull addListItem(@onNull PromptContentItem listItem, int index)194 public Builder addListItem(@NonNull PromptContentItem listItem, int index) { 195 mContentList.add(index, (PromptContentItemParcelable) listItem); 196 checkItemLimits(listItem); 197 return this; 198 } 199 checkItemLimits(@onNull PromptContentItem listItem)200 private void checkItemLimits(@NonNull PromptContentItem listItem) { 201 if (doesListItemExceedsCharLimit(listItem)) { 202 Log.w(TAG, "The character number of list item exceeds " 203 + MAX_EACH_ITEM_CHARACTER_NUMBER); 204 } 205 if (mContentList.size() > MAX_ITEM_NUMBER) { 206 throw new IllegalArgumentException( 207 "The number of list items exceeds " + MAX_ITEM_NUMBER); 208 } 209 } 210 doesListItemExceedsCharLimit(PromptContentItem listItem)211 private boolean doesListItemExceedsCharLimit(PromptContentItem listItem) { 212 if (listItem instanceof PromptContentItemPlainText) { 213 return ((PromptContentItemPlainText) listItem).getText().length() 214 > MAX_EACH_ITEM_CHARACTER_NUMBER; 215 } else if (listItem instanceof PromptContentItemBulletedText) { 216 return ((PromptContentItemBulletedText) listItem).getText().length() 217 > MAX_EACH_ITEM_CHARACTER_NUMBER; 218 } else { 219 return false; 220 } 221 } 222 223 224 /** 225 * Creates a {@link PromptVerticalListContentView}. 226 * 227 * @return An instance of {@link PromptVerticalListContentView}. 228 */ 229 @NonNull build()230 public PromptVerticalListContentView build() { 231 return new PromptVerticalListContentView(mContentList, mDescription); 232 } 233 } 234 } 235