1 /* 2 * Copyright (C) 2019 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.controls; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SuppressLint; 23 import android.app.PendingIntent; 24 import android.content.Intent; 25 import android.content.res.ColorStateList; 26 import android.graphics.drawable.Icon; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.service.controls.actions.ControlAction; 30 import android.service.controls.templates.ControlTemplate; 31 import android.service.controls.templates.ControlTemplateWrapper; 32 import android.util.Log; 33 34 import com.android.internal.util.Preconditions; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 39 /** 40 * Represents a physical object that can be represented by a {@link ControlTemplate} and whose 41 * properties may be modified through a {@link ControlAction}. 42 * 43 * The information is provided by a {@link ControlsProviderService} and represents static 44 * information (not current status) about the device. 45 * <p> 46 * Each control needs a unique (per provider) identifier that is persistent across reboots of the 47 * system. 48 * <p> 49 * Each {@link Control} will have a name, a subtitle and will optionally belong to a structure 50 * and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService} 51 * and will be used to display the control as well as group them for management. 52 * <p> 53 * Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors 54 * used to display it. 55 * <p> 56 * An {@link Intent} linking to the provider Activity that expands on this {@link Control} and 57 * allows for further actions should be provided. 58 */ 59 public final class Control implements Parcelable { 60 private static final String TAG = "Control"; 61 62 private static final int NUM_STATUS = 5; 63 /** 64 * @hide 65 */ 66 @Retention(RetentionPolicy.SOURCE) 67 @IntDef({ 68 STATUS_UNKNOWN, 69 STATUS_OK, 70 STATUS_NOT_FOUND, 71 STATUS_ERROR, 72 STATUS_DISABLED, 73 }) 74 public @interface Status {}; 75 76 /** 77 * Reserved for use with the {@link StatelessBuilder}, and while loading. When state is 78 * requested via {@link ControlsProviderService#createPublisherFor}, use other status codes 79 * to indicate the proper device state. 80 */ 81 public static final int STATUS_UNKNOWN = 0; 82 83 /** 84 * Used to indicate that the state of the device was successfully retrieved. This includes 85 * all scenarios where the device may have a warning for the user, such as "Lock jammed", 86 * or "Vacuum stuck". Any information for the user should be set through 87 * {@link StatefulBuilder#setStatusText}. 88 */ 89 public static final int STATUS_OK = 1; 90 91 /** 92 * The device corresponding to the {@link Control} cannot be found or was removed. The user 93 * will be alerted and directed to the application to resolve. 94 */ 95 public static final int STATUS_NOT_FOUND = 2; 96 97 /** 98 * Used to indicate that there was a temporary error while loading the device state. A default 99 * error message will be displayed in place of any custom text that was set through 100 * {@link StatefulBuilder#setStatusText}. 101 */ 102 public static final int STATUS_ERROR = 3; 103 104 /** 105 * The {@link Control} is currently disabled. A default error message will be displayed in 106 * place of any custom text that was set through {@link StatefulBuilder#setStatusText}. 107 */ 108 public static final int STATUS_DISABLED = 4; 109 110 private final @NonNull String mControlId; 111 private final @DeviceTypes.DeviceType int mDeviceType; 112 private final @NonNull CharSequence mTitle; 113 private final @NonNull CharSequence mSubtitle; 114 private final @Nullable CharSequence mStructure; 115 private final @Nullable CharSequence mZone; 116 private final @NonNull PendingIntent mAppIntent; 117 118 private final @Nullable Icon mCustomIcon; 119 private final @Nullable ColorStateList mCustomColor; 120 121 private final @Status int mStatus; 122 private final @NonNull ControlTemplate mControlTemplate; 123 private final @NonNull CharSequence mStatusText; 124 125 /** 126 * @param controlId the unique persistent identifier for this object. 127 * @param deviceType the type of device for this control. This will determine icons and colors. 128 * @param title the user facing name of this control (e.g. "Bedroom thermostat"). 129 * @param subtitle a user facing subtitle with extra information about this control 130 * @param structure a user facing name for the structure containing the device associated with 131 * this control. 132 * @param zone 133 * @param appIntent a {@link PendingIntent} linking to a page to interact with the 134 * corresponding device. 135 * @param customIcon 136 * @param customColor 137 * @param status 138 * @param controlTemplate 139 * @param statusText 140 */ Control(@onNull String controlId, @DeviceTypes.DeviceType int deviceType, @NonNull CharSequence title, @NonNull CharSequence subtitle, @Nullable CharSequence structure, @Nullable CharSequence zone, @NonNull PendingIntent appIntent, @Nullable Icon customIcon, @Nullable ColorStateList customColor, @Status int status, @NonNull ControlTemplate controlTemplate, @NonNull CharSequence statusText)141 Control(@NonNull String controlId, 142 @DeviceTypes.DeviceType int deviceType, 143 @NonNull CharSequence title, 144 @NonNull CharSequence subtitle, 145 @Nullable CharSequence structure, 146 @Nullable CharSequence zone, 147 @NonNull PendingIntent appIntent, 148 @Nullable Icon customIcon, 149 @Nullable ColorStateList customColor, 150 @Status int status, 151 @NonNull ControlTemplate controlTemplate, 152 @NonNull CharSequence statusText) { 153 Preconditions.checkNotNull(controlId); 154 Preconditions.checkNotNull(title); 155 Preconditions.checkNotNull(subtitle); 156 Preconditions.checkNotNull(appIntent); 157 Preconditions.checkNotNull(controlTemplate); 158 Preconditions.checkNotNull(statusText); 159 mControlId = controlId; 160 if (!DeviceTypes.validDeviceType(deviceType)) { 161 Log.e(TAG, "Invalid device type:" + deviceType); 162 mDeviceType = DeviceTypes.TYPE_UNKNOWN; 163 } else { 164 mDeviceType = deviceType; 165 } 166 mTitle = title; 167 mSubtitle = subtitle; 168 mStructure = structure; 169 mZone = zone; 170 mAppIntent = appIntent; 171 172 mCustomColor = customColor; 173 mCustomIcon = customIcon; 174 175 if (status < 0 || status >= NUM_STATUS) { 176 mStatus = STATUS_UNKNOWN; 177 Log.e(TAG, "Status unknown:" + status); 178 } else { 179 mStatus = status; 180 } 181 mControlTemplate = controlTemplate; 182 mStatusText = statusText; 183 } 184 185 /** 186 * @param in 187 * @hide 188 */ Control(Parcel in)189 Control(Parcel in) { 190 mControlId = in.readString(); 191 mDeviceType = in.readInt(); 192 mTitle = in.readCharSequence(); 193 mSubtitle = in.readCharSequence(); 194 if (in.readByte() == (byte) 1) { 195 mStructure = in.readCharSequence(); 196 } else { 197 mStructure = null; 198 } 199 if (in.readByte() == (byte) 1) { 200 mZone = in.readCharSequence(); 201 } else { 202 mZone = null; 203 } 204 mAppIntent = PendingIntent.CREATOR.createFromParcel(in); 205 206 if (in.readByte() == (byte) 1) { 207 mCustomIcon = Icon.CREATOR.createFromParcel(in); 208 } else { 209 mCustomIcon = null; 210 } 211 212 if (in.readByte() == (byte) 1) { 213 mCustomColor = ColorStateList.CREATOR.createFromParcel(in); 214 } else { 215 mCustomColor = null; 216 } 217 218 mStatus = in.readInt(); 219 ControlTemplateWrapper wrapper = ControlTemplateWrapper.CREATOR.createFromParcel(in); 220 mControlTemplate = wrapper.getWrappedTemplate(); 221 mStatusText = in.readCharSequence(); 222 } 223 224 /** 225 * @return the identifier for the {@link Control} 226 */ 227 @NonNull getControlId()228 public String getControlId() { 229 return mControlId; 230 } 231 232 233 /** 234 * @return type of device represented by this {@link Control}, used to determine the default 235 * icon and color 236 */ 237 @DeviceTypes.DeviceType getDeviceType()238 public int getDeviceType() { 239 return mDeviceType; 240 } 241 242 /** 243 * @return the user facing name of the {@link Control} 244 */ 245 @NonNull getTitle()246 public CharSequence getTitle() { 247 return mTitle; 248 } 249 250 /** 251 * @return additional information about the {@link Control}, to appear underneath the title 252 */ 253 @NonNull getSubtitle()254 public CharSequence getSubtitle() { 255 return mSubtitle; 256 } 257 258 /** 259 * Optional top-level group to help define the {@link Control}'s location, visible to the user. 260 * If not present, the application name will be used as the top-level group. A structure 261 * contains zones which contains controls. 262 * 263 * @return name of the structure containing the control 264 */ 265 @Nullable getStructure()266 public CharSequence getStructure() { 267 return mStructure; 268 } 269 270 /** 271 * Optional group name to help define the {@link Control}'s location within a structure, 272 * visible to the user. A structure contains zones which contains controls. 273 * 274 * @return name of the zone containing the control 275 */ 276 @Nullable getZone()277 public CharSequence getZone() { 278 return mZone; 279 } 280 281 /** 282 * @return a {@link PendingIntent} linking to an Activity for the {@link Control} 283 */ 284 @NonNull getAppIntent()285 public PendingIntent getAppIntent() { 286 return mAppIntent; 287 } 288 289 /** 290 * Optional icon to be shown with the {@link Control}. It is highly recommended 291 * to let the system default the icon unless the default icon is not suitable. 292 * 293 * @return icon to show 294 */ 295 @Nullable getCustomIcon()296 public Icon getCustomIcon() { 297 return mCustomIcon; 298 } 299 300 /** 301 * Optional color to be shown with the {@link Control}. It is highly recommended 302 * to let the system default the color unless the default is not suitable for the 303 * application. 304 * 305 * @return background color to use 306 */ 307 @Nullable getCustomColor()308 public ColorStateList getCustomColor() { 309 return mCustomColor; 310 } 311 312 /** 313 * @return status of the {@link Control}, used to convey information about the attempt to 314 * fetch the current state 315 */ 316 @Status getStatus()317 public int getStatus() { 318 return mStatus; 319 } 320 321 /** 322 * @return instance of {@link ControlTemplate}, that defines how the {@link Control} will 323 * behave and what interactions are available to the user 324 */ 325 @NonNull getControlTemplate()326 public ControlTemplate getControlTemplate() { 327 return mControlTemplate; 328 } 329 330 /** 331 * @return user-facing text description of the {@link Control}'s status, describing its current 332 * state 333 */ 334 @NonNull getStatusText()335 public CharSequence getStatusText() { 336 return mStatusText; 337 } 338 339 @Override describeContents()340 public int describeContents() { 341 return 0; 342 } 343 344 @Override writeToParcel(@onNull Parcel dest, int flags)345 public void writeToParcel(@NonNull Parcel dest, int flags) { 346 dest.writeString(mControlId); 347 dest.writeInt(mDeviceType); 348 dest.writeCharSequence(mTitle); 349 dest.writeCharSequence(mSubtitle); 350 if (mStructure != null) { 351 dest.writeByte((byte) 1); 352 dest.writeCharSequence(mStructure); 353 } else { 354 dest.writeByte((byte) 0); 355 } 356 if (mZone != null) { 357 dest.writeByte((byte) 1); 358 dest.writeCharSequence(mZone); 359 } else { 360 dest.writeByte((byte) 0); 361 } 362 mAppIntent.writeToParcel(dest, flags); 363 if (mCustomIcon != null) { 364 dest.writeByte((byte) 1); 365 mCustomIcon.writeToParcel(dest, flags); 366 } else { 367 dest.writeByte((byte) 0); 368 } 369 if (mCustomColor != null) { 370 dest.writeByte((byte) 1); 371 mCustomColor.writeToParcel(dest, flags); 372 } else { 373 dest.writeByte((byte) 0); 374 } 375 376 dest.writeInt(mStatus); 377 new ControlTemplateWrapper(mControlTemplate).writeToParcel(dest, flags); 378 dest.writeCharSequence(mStatusText); 379 } 380 381 public static final @NonNull Creator<Control> CREATOR = new Creator<Control>() { 382 @Override 383 public Control createFromParcel(@NonNull Parcel source) { 384 return new Control(source); 385 } 386 387 @Override 388 public Control[] newArray(int size) { 389 return new Control[size]; 390 } 391 }; 392 393 /** 394 * Builder class for {@link Control}. 395 * 396 * This class facilitates the creation of {@link Control} with no state. Must be used to 397 * provide controls for {@link ControlsProviderService#createPublisherForAllAvailable} and 398 * {@link ControlsProviderService#createPublisherForSuggested}. 399 * 400 * It provides the following defaults for non-optional parameters: 401 * <ul> 402 * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN} 403 * <li> Title: {@code ""} 404 * <li> Subtitle: {@code ""} 405 * </ul> 406 * This fixes the values relating to state of the {@link Control} as required by 407 * {@link ControlsProviderService#createPublisherForAllAvailable}: 408 * <ul> 409 * <li> Status: {@link Status#STATUS_UNKNOWN} 410 * <li> Control template: {@link ControlTemplate#getNoTemplateObject} 411 * <li> Status text: {@code ""} 412 * </ul> 413 */ 414 @SuppressLint("MutableBareField") 415 public static final class StatelessBuilder { 416 private static final String TAG = "StatelessBuilder"; 417 private @NonNull String mControlId; 418 private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN; 419 private @NonNull CharSequence mTitle = ""; 420 private @NonNull CharSequence mSubtitle = ""; 421 private @Nullable CharSequence mStructure; 422 private @Nullable CharSequence mZone; 423 private @NonNull PendingIntent mAppIntent; 424 private @Nullable Icon mCustomIcon; 425 private @Nullable ColorStateList mCustomColor; 426 427 /** 428 * @param controlId the identifier for the {@link Control} 429 * @param appIntent the pending intent linking to the device Activity 430 */ StatelessBuilder(@onNull String controlId, @NonNull PendingIntent appIntent)431 public StatelessBuilder(@NonNull String controlId, 432 @NonNull PendingIntent appIntent) { 433 Preconditions.checkNotNull(controlId); 434 Preconditions.checkNotNull(appIntent); 435 mControlId = controlId; 436 mAppIntent = appIntent; 437 } 438 439 /** 440 * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base. 441 * 442 * @param control base for the builder. 443 */ StatelessBuilder(@onNull Control control)444 public StatelessBuilder(@NonNull Control control) { 445 Preconditions.checkNotNull(control); 446 mControlId = control.mControlId; 447 mDeviceType = control.mDeviceType; 448 mTitle = control.mTitle; 449 mSubtitle = control.mSubtitle; 450 mStructure = control.mStructure; 451 mZone = control.mZone; 452 mAppIntent = control.mAppIntent; 453 mCustomIcon = control.mCustomIcon; 454 mCustomColor = control.mCustomColor; 455 } 456 457 /** 458 * @param controlId the identifier for the {@link Control} 459 * @return {@code this} 460 */ 461 @NonNull setControlId(@onNull String controlId)462 public StatelessBuilder setControlId(@NonNull String controlId) { 463 Preconditions.checkNotNull(controlId); 464 mControlId = controlId; 465 return this; 466 } 467 468 /** 469 * @param deviceType type of device represented by this {@link Control}, used to 470 * determine the default icon and color 471 * @return {@code this} 472 */ 473 @NonNull setDeviceType(@eviceTypes.DeviceType int deviceType)474 public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) { 475 if (!DeviceTypes.validDeviceType(deviceType)) { 476 Log.e(TAG, "Invalid device type:" + deviceType); 477 mDeviceType = DeviceTypes.TYPE_UNKNOWN; 478 } else { 479 mDeviceType = deviceType; 480 } 481 return this; 482 } 483 484 /** 485 * @param title the user facing name of the {@link Control} 486 * @return {@code this} 487 */ 488 @NonNull setTitle(@onNull CharSequence title)489 public StatelessBuilder setTitle(@NonNull CharSequence title) { 490 Preconditions.checkNotNull(title); 491 mTitle = title; 492 return this; 493 } 494 495 /** 496 * @param subtitle additional information about the {@link Control}, to appear underneath 497 * the title 498 * @return {@code this} 499 */ 500 @NonNull setSubtitle(@onNull CharSequence subtitle)501 public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) { 502 Preconditions.checkNotNull(subtitle); 503 mSubtitle = subtitle; 504 return this; 505 } 506 507 /** 508 * Optional top-level group to help define the {@link Control}'s location, visible to the 509 * user. If not present, the application name will be used as the top-level group. A 510 * structure contains zones which contains controls. 511 * 512 * @param structure name of the structure containing the control 513 * @return {@code this} 514 */ 515 @NonNull setStructure(@ullable CharSequence structure)516 public StatelessBuilder setStructure(@Nullable CharSequence structure) { 517 mStructure = structure; 518 return this; 519 } 520 521 /** 522 * Optional group name to help define the {@link Control}'s location within a structure, 523 * visible to the user. A structure contains zones which contains controls. 524 * 525 * @param zone name of the zone containing the control 526 * @return {@code this} 527 */ 528 @NonNull setZone(@ullable CharSequence zone)529 public StatelessBuilder setZone(@Nullable CharSequence zone) { 530 mZone = zone; 531 return this; 532 } 533 534 /** 535 * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control} 536 * @return {@code this} 537 */ 538 @NonNull setAppIntent(@onNull PendingIntent appIntent)539 public StatelessBuilder setAppIntent(@NonNull PendingIntent appIntent) { 540 Preconditions.checkNotNull(appIntent); 541 mAppIntent = appIntent; 542 return this; 543 } 544 545 /** 546 * Optional icon to be shown with the {@link Control}. It is highly recommended 547 * to let the system default the icon unless the default icon is not suitable. 548 * 549 * @param customIcon icon to show 550 * @return {@code this} 551 */ 552 @NonNull setCustomIcon(@ullable Icon customIcon)553 public StatelessBuilder setCustomIcon(@Nullable Icon customIcon) { 554 mCustomIcon = customIcon; 555 return this; 556 } 557 558 /** 559 * Optional color to be shown with the {@link Control}. It is highly recommended 560 * to let the system default the color unless the default is not suitable for the 561 * application. 562 * 563 * @param customColor background color to use 564 * @return {@code this} 565 */ 566 @NonNull setCustomColor(@ullable ColorStateList customColor)567 public StatelessBuilder setCustomColor(@Nullable ColorStateList customColor) { 568 mCustomColor = customColor; 569 return this; 570 } 571 572 /** 573 * @return a valid {@link Control} 574 */ 575 @NonNull build()576 public Control build() { 577 return new Control(mControlId, 578 mDeviceType, 579 mTitle, 580 mSubtitle, 581 mStructure, 582 mZone, 583 mAppIntent, 584 mCustomIcon, 585 mCustomColor, 586 STATUS_UNKNOWN, 587 ControlTemplate.NO_TEMPLATE, 588 ""); 589 } 590 } 591 592 /** 593 * Builder class for {@link Control} that contains state information. 594 * 595 * State information is passed through an instance of a {@link ControlTemplate} and will 596 * determine how the user can interact with the {@link Control}. User interactions will 597 * be sent through the method call {@link ControlsProviderService#performControlAction} 598 * with an instance of {@link ControlAction} to convey any potential new value. 599 * 600 * Must be used to provide controls for {@link ControlsProviderService#createPublisherFor}. 601 * 602 * It provides the following defaults for non-optional parameters: 603 * <ul> 604 * <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN} 605 * <li> Title: {@code ""} 606 * <li> Subtitle: {@code ""} 607 * <li> Status: {@link Status#STATUS_UNKNOWN} 608 * <li> Control template: {@link ControlTemplate#getNoTemplateObject} 609 * <li> Status text: {@code ""} 610 * </ul> 611 */ 612 public static final class StatefulBuilder { 613 private static final String TAG = "StatefulBuilder"; 614 private @NonNull String mControlId; 615 private @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN; 616 private @NonNull CharSequence mTitle = ""; 617 private @NonNull CharSequence mSubtitle = ""; 618 private @Nullable CharSequence mStructure; 619 private @Nullable CharSequence mZone; 620 private @NonNull PendingIntent mAppIntent; 621 private @Nullable Icon mCustomIcon; 622 private @Nullable ColorStateList mCustomColor; 623 private @Status int mStatus = STATUS_UNKNOWN; 624 private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE; 625 private @NonNull CharSequence mStatusText = ""; 626 627 /** 628 * @param controlId the identifier for the {@link Control}. 629 * @param appIntent the pending intent linking to the device Activity. 630 */ StatefulBuilder(@onNull String controlId, @NonNull PendingIntent appIntent)631 public StatefulBuilder(@NonNull String controlId, 632 @NonNull PendingIntent appIntent) { 633 Preconditions.checkNotNull(controlId); 634 Preconditions.checkNotNull(appIntent); 635 mControlId = controlId; 636 mAppIntent = appIntent; 637 } 638 639 /** 640 * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base. 641 * 642 * @param control base for the builder. 643 */ StatefulBuilder(@onNull Control control)644 public StatefulBuilder(@NonNull Control control) { 645 Preconditions.checkNotNull(control); 646 mControlId = control.mControlId; 647 mDeviceType = control.mDeviceType; 648 mTitle = control.mTitle; 649 mSubtitle = control.mSubtitle; 650 mStructure = control.mStructure; 651 mZone = control.mZone; 652 mAppIntent = control.mAppIntent; 653 mCustomIcon = control.mCustomIcon; 654 mCustomColor = control.mCustomColor; 655 mStatus = control.mStatus; 656 mControlTemplate = control.mControlTemplate; 657 mStatusText = control.mStatusText; 658 } 659 660 /** 661 * @param controlId the identifier for the {@link Control}. 662 * @return {@code this} 663 */ 664 @NonNull setControlId(@onNull String controlId)665 public StatefulBuilder setControlId(@NonNull String controlId) { 666 Preconditions.checkNotNull(controlId); 667 mControlId = controlId; 668 return this; 669 } 670 671 /** 672 * @param deviceType type of device represented by this {@link Control}, used to 673 * determine the default icon and color 674 * @return {@code this} 675 */ 676 @NonNull setDeviceType(@eviceTypes.DeviceType int deviceType)677 public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) { 678 if (!DeviceTypes.validDeviceType(deviceType)) { 679 Log.e(TAG, "Invalid device type:" + deviceType); 680 mDeviceType = DeviceTypes.TYPE_UNKNOWN; 681 } else { 682 mDeviceType = deviceType; 683 } 684 return this; 685 } 686 687 /** 688 * @param title the user facing name of the {@link Control} 689 * @return {@code this} 690 */ 691 @NonNull setTitle(@onNull CharSequence title)692 public StatefulBuilder setTitle(@NonNull CharSequence title) { 693 Preconditions.checkNotNull(title); 694 mTitle = title; 695 return this; 696 } 697 698 /** 699 * @param subtitle additional information about the {@link Control}, to appear underneath 700 * the title 701 * @return {@code this} 702 */ 703 @NonNull setSubtitle(@onNull CharSequence subtitle)704 public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) { 705 Preconditions.checkNotNull(subtitle); 706 mSubtitle = subtitle; 707 return this; 708 } 709 710 /** 711 * Optional top-level group to help define the {@link Control}'s location, visible to the 712 * user. If not present, the application name will be used as the top-level group. A 713 * structure contains zones which contains controls. 714 * 715 * @param structure name of the structure containing the control 716 * @return {@code this} 717 */ 718 @NonNull setStructure(@ullable CharSequence structure)719 public StatefulBuilder setStructure(@Nullable CharSequence structure) { 720 mStructure = structure; 721 return this; 722 } 723 724 /** 725 * Optional group name to help define the {@link Control}'s location within a structure, 726 * visible to the user. A structure contains zones which contains controls. 727 * 728 * @param zone name of the zone containing the control 729 * @return {@code this} 730 */ 731 @NonNull setZone(@ullable CharSequence zone)732 public StatefulBuilder setZone(@Nullable CharSequence zone) { 733 mZone = zone; 734 return this; 735 } 736 737 /** 738 * @param appIntent a {@link PendingIntent} linking to an Activity for the {@link Control} 739 * @return {@code this} 740 */ 741 @NonNull setAppIntent(@onNull PendingIntent appIntent)742 public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) { 743 Preconditions.checkNotNull(appIntent); 744 mAppIntent = appIntent; 745 return this; 746 } 747 748 /** 749 * Optional icon to be shown with the {@link Control}. It is highly recommended 750 * to let the system default the icon unless the default icon is not suitable. 751 * 752 * @param customIcon icon to show 753 * @return {@code this} 754 */ 755 @NonNull setCustomIcon(@ullable Icon customIcon)756 public StatefulBuilder setCustomIcon(@Nullable Icon customIcon) { 757 mCustomIcon = customIcon; 758 return this; 759 } 760 761 /** 762 * Optional color to be shown with the {@link Control}. It is highly recommended 763 * to let the system default the color unless the default is not suitable for the 764 * application. 765 * 766 * @param customColor background color to use 767 * @return {@code this} 768 */ 769 @NonNull setCustomColor(@ullable ColorStateList customColor)770 public StatefulBuilder setCustomColor(@Nullable ColorStateList customColor) { 771 mCustomColor = customColor; 772 return this; 773 } 774 775 /** 776 * @param status status of the {@link Control}, used to convey information about the 777 * attempt to fetch the current state 778 * @return {@code this} 779 */ 780 @NonNull setStatus(@tatus int status)781 public StatefulBuilder setStatus(@Status int status) { 782 if (status < 0 || status >= NUM_STATUS) { 783 mStatus = STATUS_UNKNOWN; 784 Log.e(TAG, "Status unknown:" + status); 785 } else { 786 mStatus = status; 787 } 788 return this; 789 } 790 791 /** 792 * Set the {@link ControlTemplate} to define the primary user interaction 793 * 794 * Devices may support a variety of user interactions, and all interactions cannot be 795 * represented with a single {@link ControlTemplate}. Therefore, the selected template 796 * should be most closely aligned with what the expected primary device action will be. 797 * Any secondary interactions can be done via the {@link #setAppIntent(PendingIntent)}. 798 * 799 * @param controlTemplate instance of {@link ControlTemplate}, that defines how the 800 * {@link Control} will behave and what interactions are 801 * available to the user 802 * @return {@code this} 803 */ 804 @NonNull setControlTemplate(@onNull ControlTemplate controlTemplate)805 public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) { 806 Preconditions.checkNotNull(controlTemplate); 807 mControlTemplate = controlTemplate; 808 return this; 809 } 810 811 /** 812 * @param statusText user-facing text description of the {@link Control}'s status, 813 * describing its current state 814 * @return {@code this} 815 */ 816 @NonNull setStatusText(@onNull CharSequence statusText)817 public StatefulBuilder setStatusText(@NonNull CharSequence statusText) { 818 Preconditions.checkNotNull(statusText); 819 mStatusText = statusText; 820 return this; 821 } 822 823 /** 824 * @return a valid {@link Control} 825 */ 826 @NonNull build()827 public Control build() { 828 return new Control(mControlId, 829 mDeviceType, 830 mTitle, 831 mSubtitle, 832 mStructure, 833 mZone, 834 mAppIntent, 835 mCustomIcon, 836 mCustomColor, 837 mStatus, 838 mControlTemplate, 839 mStatusText); 840 } 841 } 842 } 843