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.service.settings.preferences; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.SuppressLint; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.text.TextUtils; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.Nullable; 30 31 import com.android.settingslib.flags.Flags; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Data object representation of a Settings Preference definition and state. 42 */ 43 @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) 44 public final class SettingsPreferenceMetadata implements Parcelable { 45 46 @NonNull 47 private final String mKey; 48 @NonNull 49 private final String mScreenKey; 50 @Nullable 51 private final String mTitle; 52 @Nullable 53 private final String mSummary; 54 @NonNull 55 private final List<String> mBreadcrumbs; 56 @NonNull 57 private final List<String> mReadPermissions; 58 @NonNull 59 private final List<String> mWritePermissions; 60 private final boolean mEnabled; 61 private final boolean mAvailable; 62 private final boolean mWritable; 63 private final boolean mRestricted; 64 private final int mSensitivity; 65 @Nullable 66 private final Intent mLaunchIntent; 67 @NonNull 68 private final Bundle mExtras; 69 70 /** 71 * Returns the key of Preference. 72 */ 73 @NonNull getKey()74 public String getKey() { 75 return mKey; 76 } 77 78 /** 79 * Returns the screen key of Preference. 80 */ 81 @NonNull getScreenKey()82 public String getScreenKey() { 83 return mScreenKey; 84 } 85 86 /** 87 * Returns the title of Preference. 88 */ 89 @Nullable getTitle()90 public String getTitle() { 91 return mTitle; 92 } 93 94 /** 95 * Returns the summary of Preference. 96 */ 97 @Nullable getSummary()98 public String getSummary() { 99 return mSummary; 100 } 101 102 /** 103 * Returns the breadcrumbs (navigation context) of Preference. 104 * <p>May be empty. 105 * @hide restrict to platform; may be opened wider in the future 106 */ 107 @NonNull getBreadcrumbs()108 public List<String> getBreadcrumbs() { 109 return mBreadcrumbs; 110 } 111 112 /** 113 * Returns the permissions required to read this Preference's value. 114 * <p>May be empty. 115 */ 116 @NonNull getReadPermissions()117 public List<String> getReadPermissions() { 118 return mReadPermissions; 119 } 120 121 /** 122 * Returns the permissions required to write this Preference's value. 123 * <p>May be empty. 124 */ 125 @NonNull getWritePermissions()126 public List<String> getWritePermissions() { 127 return mWritePermissions; 128 } 129 130 /** 131 * Returns whether Preference is enabled. 132 */ isEnabled()133 public boolean isEnabled() { 134 return mEnabled; 135 } 136 137 /** 138 * Returns whether Preference is available. 139 */ isAvailable()140 public boolean isAvailable() { 141 return mAvailable; 142 } 143 144 /** 145 * Returns whether Preference is writable. 146 */ isWritable()147 public boolean isWritable() { 148 return mWritable; 149 } 150 151 /** 152 * Returns whether Preference is restricted. 153 * <p>If true, this means the Preference is treated as a Restricted Preference which indicates 154 * that it could be conditionally disabled/unavailable due to admin settings. 155 */ isRestricted()156 public boolean isRestricted() { 157 return mRestricted; 158 } 159 160 /** 161 * Returns the write-level sensitivity of Preference. 162 */ 163 @WriteSensitivity getWriteSensitivity()164 public int getWriteSensitivity() { 165 return mSensitivity; 166 } 167 168 /** 169 * Returns the intent to launch the host app page for this Preference. 170 */ 171 @SuppressLint("IntentBuilderName") 172 @Nullable getLaunchIntent()173 public Intent getLaunchIntent() { 174 return mLaunchIntent; 175 } 176 177 /** 178 * Returns any additional fields specific to this preference. 179 * <p>Treat all data as optional. This may contain unstructured data for a given preference, 180 * where the type and format of this data may only known by inspecting the source code of that 181 * preference. As such, any access of this data must handle failures gracefully to account for 182 * changing or missing data. 183 */ 184 @NonNull getExtras()185 public Bundle getExtras() { 186 return mExtras; 187 } 188 189 /** @hide */ 190 @IntDef(value = { 191 NO_SENSITIVITY, 192 EXPECT_POST_CONFIRMATION, 193 DEEPLINK_ONLY, 194 NO_DIRECT_ACCESS, 195 }) 196 @Retention(RetentionPolicy.SOURCE) 197 public @interface WriteSensitivity {} 198 199 /** 200 * Indicates preference is not write-sensitive. 201 * <p>Its value is writable without explicit consent, assuming all necessary permissions are 202 * granted. 203 */ 204 public static final int NO_SENSITIVITY = 0; 205 /** 206 * Indicates preference is mildly write-sensitive. 207 * <p>In addition to necessary permissions, after writing its value the user should be 208 * given the option to revert back. 209 */ 210 public static final int EXPECT_POST_CONFIRMATION = 1; 211 /** 212 * Indicates preference is write-sensitive. 213 * <p>This preference cannot be changed through this API; instead a deeplink to the Preference's 214 * page should be used instead, accessible via {@link #getLaunchIntent}. 215 */ 216 public static final int DEEPLINK_ONLY = 2; 217 /** 218 * Indicates preference is highly write-sensitivity and carries significant user-risk. 219 * <p>This Preference cannot be changed through this API and no direct deeplink is available. 220 * Other Metadata is still available. 221 */ 222 public static final int NO_DIRECT_ACCESS = 3; 223 SettingsPreferenceMetadata(@onNull Builder builder)224 private SettingsPreferenceMetadata(@NonNull Builder builder) { 225 mKey = builder.mKey; 226 mScreenKey = builder.mScreenKey; 227 mTitle = builder.mTitle; 228 mSummary = builder.mSummary; 229 mBreadcrumbs = builder.mBreadcrumbs; 230 mReadPermissions = builder.mReadPermissions; 231 mWritePermissions = builder.mWritePermissions; 232 mEnabled = builder.mEnabled; 233 mAvailable = builder.mAvailable; 234 mWritable = builder.mWritable; 235 mRestricted = builder.mRestricted; 236 mSensitivity = builder.mSensitivity; 237 mLaunchIntent = builder.mLaunchIntent; 238 mExtras = Objects.requireNonNullElseGet(builder.mExtras, Bundle::new); 239 } 240 @SuppressLint("ParcelClassLoader") SettingsPreferenceMetadata(@onNull Parcel in)241 private SettingsPreferenceMetadata(@NonNull Parcel in) { 242 mKey = Objects.requireNonNull(in.readString8()); 243 mScreenKey = Objects.requireNonNull(in.readString8()); 244 mTitle = in.readString8(); 245 mSummary = in.readString8(); 246 mBreadcrumbs = new ArrayList<>(); 247 in.readStringList(mBreadcrumbs); 248 mReadPermissions = new ArrayList<>(); 249 in.readStringList(mReadPermissions); 250 mWritePermissions = new ArrayList<>(); 251 in.readStringList(mWritePermissions); 252 mEnabled = in.readBoolean(); 253 mAvailable = in.readBoolean(); 254 mWritable = in.readBoolean(); 255 mRestricted = in.readBoolean(); 256 mSensitivity = in.readInt(); 257 mLaunchIntent = in.readParcelable(Intent.class.getClassLoader(), 258 Intent.class); 259 mExtras = Objects.requireNonNullElseGet(in.readBundle(), Bundle::new); 260 } 261 262 /** @hide */ 263 @Override describeContents()264 public int describeContents() { 265 return 0; 266 } 267 268 /** @hide */ 269 @Override writeToParcel(@onNull Parcel dest, int flags)270 public void writeToParcel(@NonNull Parcel dest, int flags) { 271 dest.writeString8(mKey); 272 dest.writeString8(mScreenKey); 273 dest.writeString8(mTitle); 274 dest.writeString8(mSummary); 275 dest.writeStringList(mBreadcrumbs); 276 dest.writeStringList(mReadPermissions); 277 dest.writeStringList(mWritePermissions); 278 dest.writeBoolean(mEnabled); 279 dest.writeBoolean(mAvailable); 280 dest.writeBoolean(mWritable); 281 dest.writeBoolean(mRestricted); 282 dest.writeInt(mSensitivity); 283 dest.writeParcelable(mLaunchIntent, flags); 284 dest.writeBundle(mExtras); 285 } 286 287 /** 288 * Parcelable Creator for {@link SettingsPreferenceMetadata}. 289 */ 290 @NonNull 291 public static final Creator<SettingsPreferenceMetadata> CREATOR = new Creator<>() { 292 @Override 293 public SettingsPreferenceMetadata createFromParcel(@NonNull Parcel in) { 294 return new SettingsPreferenceMetadata(in); 295 } 296 297 @Override 298 public SettingsPreferenceMetadata[] newArray(int size) { 299 return new SettingsPreferenceMetadata[size]; 300 } 301 }; 302 303 /** 304 * Builder to construct {@link SettingsPreferenceMetadata}. 305 */ 306 @FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) 307 public static final class Builder { 308 private final String mScreenKey; 309 private final String mKey; 310 private String mTitle; 311 private String mSummary; 312 private List<String> mBreadcrumbs = Collections.emptyList(); 313 private List<String> mReadPermissions = Collections.emptyList(); 314 private List<String> mWritePermissions = Collections.emptyList(); 315 private boolean mEnabled = false; 316 private boolean mAvailable = false; 317 private boolean mWritable = false; 318 private boolean mRestricted = false; 319 @WriteSensitivity private int mSensitivity = NO_DIRECT_ACCESS; 320 private Intent mLaunchIntent; 321 private Bundle mExtras; 322 323 /** 324 * Create Builder instance. 325 * @param screenKey required to be not empty 326 * @param key required to be not empty 327 */ Builder(@onNull String screenKey, @NonNull String key)328 public Builder(@NonNull String screenKey, @NonNull String key) { 329 if (TextUtils.isEmpty(screenKey)) { 330 throw new IllegalArgumentException("screenKey cannot be empty"); 331 } 332 if (TextUtils.isEmpty(key)) { 333 throw new IllegalArgumentException("key cannot be empty"); 334 } 335 mScreenKey = screenKey; 336 mKey = key; 337 } 338 339 /** 340 * Sets the preference title. 341 */ 342 @NonNull setTitle(@ullable String title)343 public Builder setTitle(@Nullable String title) { 344 mTitle = title; 345 return this; 346 } 347 348 /** 349 * Sets the preference summary. 350 */ 351 @NonNull setSummary(@ullable String summary)352 public Builder setSummary(@Nullable String summary) { 353 mSummary = summary; 354 return this; 355 } 356 357 /** 358 * Sets the preference breadcrumbs (navigation context). 359 * @hide 360 */ 361 @NonNull setBreadcrumbs(@onNull List<String> breadcrumbs)362 public Builder setBreadcrumbs(@NonNull List<String> breadcrumbs) { 363 mBreadcrumbs = breadcrumbs; 364 return this; 365 } 366 367 /** 368 * Sets the permissions required for reading this preference. 369 */ 370 @NonNull setReadPermissions(@onNull List<String> readPermissions)371 public Builder setReadPermissions(@NonNull List<String> readPermissions) { 372 mReadPermissions = readPermissions; 373 return this; 374 } 375 376 /** 377 * Sets the permissions required for writing this preference. 378 */ 379 @NonNull setWritePermissions(@onNull List<String> writePermissions)380 public Builder setWritePermissions(@NonNull List<String> writePermissions) { 381 mWritePermissions = writePermissions; 382 return this; 383 } 384 385 /** 386 * Set whether the preference is enabled. 387 */ 388 @NonNull setEnabled(boolean enabled)389 public Builder setEnabled(boolean enabled) { 390 mEnabled = enabled; 391 return this; 392 } 393 394 /** 395 * Sets whether the preference is available. 396 */ 397 @NonNull setAvailable(boolean available)398 public Builder setAvailable(boolean available) { 399 mAvailable = available; 400 return this; 401 } 402 403 /** 404 * Sets whether the preference is writable. 405 */ 406 @NonNull setWritable(boolean writable)407 public Builder setWritable(boolean writable) { 408 mWritable = writable; 409 return this; 410 } 411 412 /** 413 * Sets whether the preference is restricted. 414 */ 415 @NonNull setRestricted(boolean restricted)416 public Builder setRestricted(boolean restricted) { 417 mRestricted = restricted; 418 return this; 419 } 420 421 /** 422 * Sets the preference write-level sensitivity. 423 */ 424 @NonNull setWriteSensitivity(@riteSensitivity int sensitivity)425 public Builder setWriteSensitivity(@WriteSensitivity int sensitivity) { 426 mSensitivity = sensitivity; 427 return this; 428 } 429 430 /** 431 * Sets the intent to launch the host app page for this preference. 432 */ 433 @NonNull setLaunchIntent(@ullable Intent launchIntent)434 public Builder setLaunchIntent(@Nullable Intent launchIntent) { 435 mLaunchIntent = launchIntent; 436 return this; 437 } 438 439 /** 440 * Sets additional fields specific to this preference. Treat all data as optional. 441 */ 442 @NonNull setExtras(@onNull Bundle extras)443 public Builder setExtras(@NonNull Bundle extras) { 444 mExtras = extras; 445 return this; 446 } 447 448 /** 449 * Constructs an immutable {@link SettingsPreferenceMetadata} object. 450 */ 451 @NonNull build()452 public SettingsPreferenceMetadata build() { 453 if (mSensitivity == NO_DIRECT_ACCESS) { 454 mLaunchIntent = null; 455 } 456 return new SettingsPreferenceMetadata(this); 457 } 458 } 459 } 460