1 /* 2 * Copyright 2018 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 androidx.recommendation.app; 18 19 import android.app.Notification; 20 import android.app.PendingIntent; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.graphics.Bitmap; 24 import android.os.Bundle; 25 import android.text.TextUtils; 26 27 import androidx.annotation.ColorInt; 28 import androidx.annotation.DrawableRes; 29 import androidx.annotation.IntDef; 30 import androidx.annotation.Nullable; 31 import androidx.annotation.StringDef; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.Arrays; 36 37 /** 38 * The ContentRecommendation object encapsulates all application provided data for a single content 39 * recommendation item. 40 */ 41 public final class ContentRecommendation 42 { 43 @StringDef({ 44 CONTENT_TYPE_VIDEO, 45 CONTENT_TYPE_MOVIE, 46 CONTENT_TYPE_TRAILER, 47 CONTENT_TYPE_SERIAL, 48 CONTENT_TYPE_MUSIC, 49 CONTENT_TYPE_RADIO, 50 CONTENT_TYPE_PODCAST, 51 CONTENT_TYPE_NEWS, 52 CONTENT_TYPE_SPORTS, 53 CONTENT_TYPE_APP, 54 CONTENT_TYPE_GAME, 55 CONTENT_TYPE_BOOK, 56 CONTENT_TYPE_COMIC, 57 CONTENT_TYPE_MAGAZINE, 58 CONTENT_TYPE_WEBSITE, 59 }) 60 @Retention(RetentionPolicy.SOURCE) 61 public @interface ContentType {} 62 63 /** 64 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 65 * by the notification item is a video clip. 66 */ 67 public static final String CONTENT_TYPE_VIDEO = "android.contentType.video"; 68 69 /** 70 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 71 * by the notification item is a movie. 72 */ 73 public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie"; 74 75 /** 76 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 77 * by the notification item is a trailer. 78 */ 79 public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer"; 80 81 /** 82 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 83 * by the notification item is serial. It can refer to an entire show, a single season or 84 * series, or a single episode. 85 */ 86 public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial"; 87 88 /** 89 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 90 * by the notification item is a song or album. 91 */ 92 public static final String CONTENT_TYPE_MUSIC = "android.contentType.music"; 93 94 /** 95 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 96 * by the notification item is a radio station. 97 */ 98 public static final String CONTENT_TYPE_RADIO = "android.contentType.radio"; 99 100 /** 101 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 102 * by the notification item is a podcast. 103 */ 104 public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast"; 105 106 /** 107 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 108 * by the notification item is a news item. 109 */ 110 public static final String CONTENT_TYPE_NEWS = "android.contentType.news"; 111 112 /** 113 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 114 * by the notification item is sports. 115 */ 116 public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports"; 117 118 /** 119 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 120 * by the notification item is an application. 121 */ 122 public static final String CONTENT_TYPE_APP = "android.contentType.app"; 123 124 /** 125 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 126 * by the notification item is a game. 127 */ 128 public static final String CONTENT_TYPE_GAME = "android.contentType.game"; 129 130 /** 131 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 132 * by the notification item is a book. 133 */ 134 public static final String CONTENT_TYPE_BOOK = "android.contentType.book"; 135 136 /** 137 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 138 * by the notification item is a comic book. 139 */ 140 public static final String CONTENT_TYPE_COMIC = "android.contentType.comic"; 141 142 /** 143 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 144 * by the notification item is a magazine. 145 */ 146 public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine"; 147 148 /** 149 * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred 150 * by the notification item is a website. 151 */ 152 public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website"; 153 154 @StringDef({ 155 CONTENT_PRICING_FREE, 156 CONTENT_PRICING_RENTAL, 157 CONTENT_PRICING_PURCHASE, 158 CONTENT_PRICING_PREORDER, 159 CONTENT_PRICING_SUBSCRIPTION, 160 }) 161 @Retention(RetentionPolicy.SOURCE) 162 public @interface ContentPricing {} 163 164 /** 165 * Value to be used with {@link Builder#setPricingInformation} to indicate that the content 166 * referred by the notification item is free to consume. 167 */ 168 public static final String CONTENT_PRICING_FREE = "android.contentPrice.free"; 169 170 /** 171 * Value to be used with {@link Builder#setPricingInformation} to indicate that the content 172 * referred by the notification item is available as a rental, and the price value provided is 173 * the rental price for the item. 174 */ 175 public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental"; 176 177 /** 178 * Value to be used with {@link Builder#setPricingInformation} to indicate that the content 179 * referred by the notification item is available for purchase, and the price value provided is 180 * the purchase price for the item. 181 */ 182 public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase"; 183 184 /** 185 * Value to be used with {@link Builder#setPricingInformation} to indicate that the content 186 * referred by the notification item is available currently as a pre-order, and the price value 187 * provided is the purchase price for the item. 188 */ 189 public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder"; 190 191 /** 192 * Value to be used with {@link Builder#setPricingInformation} to indicate that the content 193 * referred by the notification item is available as part of a subscription based service, and 194 * the price value provided is the subscription price for the service. 195 */ 196 public static final String CONTENT_PRICING_SUBSCRIPTION = 197 "android.contentPrice.subscription"; 198 199 @IntDef({ 200 CONTENT_STATUS_READY, 201 CONTENT_STATUS_PENDING, 202 CONTENT_STATUS_AVAILABLE, 203 CONTENT_STATUS_UNAVAILABLE, 204 }) 205 @Retention(RetentionPolicy.SOURCE) 206 public @interface ContentStatus {} 207 208 /** 209 * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the 210 * notification is available and ready to be consumed immediately. 211 */ 212 public static final int CONTENT_STATUS_READY = 0; 213 214 /** 215 * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the 216 * notification is pending, waiting on either a download or purchase operation to complete 217 * before it can be consumed. 218 */ 219 public static final int CONTENT_STATUS_PENDING = 1; 220 221 /** 222 * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the 223 * notification is available, but needs to be first purchased, rented, subscribed or downloaded 224 * before it can be consumed. 225 */ 226 public static final int CONTENT_STATUS_AVAILABLE = 2; 227 228 /** 229 * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the 230 * notification is not available. This could be content not available in a certain region or 231 * incompatible with the device in use. 232 */ 233 public static final int CONTENT_STATUS_UNAVAILABLE = 3; 234 235 @StringDef({ 236 CONTENT_MATURITY_ALL, 237 CONTENT_MATURITY_LOW, 238 CONTENT_MATURITY_MEDIUM, 239 CONTENT_MATURITY_HIGH, 240 }) 241 @Retention(RetentionPolicy.SOURCE) 242 public @interface ContentMaturity {} 243 244 /** 245 * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred 246 * by the notification is suitable for all audiences. 247 */ 248 public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all"; 249 250 /** 251 * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred 252 * by the notification is suitable for audiences of low maturity and above. 253 */ 254 public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low"; 255 256 /** 257 * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred 258 * by the notification is suitable for audiences of medium maturity and above. 259 */ 260 public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium"; 261 262 /** 263 * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred 264 * by the notification is suitable for audiences of high maturity and above. 265 */ 266 public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high"; 267 268 @IntDef({ 269 INTENT_TYPE_ACTIVITY, 270 INTENT_TYPE_BROADCAST, 271 INTENT_TYPE_SERVICE, 272 }) 273 @Retention(RetentionPolicy.SOURCE) 274 public @interface IntentType { 275 } 276 277 /** 278 * Value to be used with {@link Builder#setContentIntentData} and 279 * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for an Activity 280 * should be created when posting the recommendation to the HomeScreen. 281 */ 282 public static final int INTENT_TYPE_ACTIVITY = 1; 283 284 /** 285 * Value to be used with {@link Builder#setContentIntentData} and 286 * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Broadcast 287 * should be created when posting the recommendation to the HomeScreen. 288 */ 289 public static final int INTENT_TYPE_BROADCAST = 2; 290 291 /** 292 * Value to be used with {@link Builder#setContentIntentData} and 293 * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Service 294 * should be created when posting the recommendation to the HomeScreen. 295 */ 296 public static final int INTENT_TYPE_SERVICE = 3; 297 298 /** 299 * Object used to encapsulate the data to be used to build the {@link PendingIntent} object 300 * associated with a given content recommendation, at the time this recommendation gets posted 301 * to the home Screen. 302 * <p> 303 * The members of this object correspond to the fields passed into the {@link PendingIntent} 304 * factory methods, when creating a new PendingIntent. 305 */ 306 public static class IntentData { 307 int mType; 308 Intent mIntent; 309 int mRequestCode; 310 Bundle mOptions; 311 } 312 313 private final String mIdTag; 314 private final String mTitle; 315 private final String mText; 316 private final String mSourceName; 317 private final Bitmap mContentImage; 318 private final int mBadgeIconId; 319 private final String mBackgroundImageUri; 320 private final int mColor; 321 private final IntentData mContentIntentData; 322 private final IntentData mDismissIntentData; 323 private final String[] mContentTypes; 324 private final String[] mContentGenres; 325 private final String mPriceType; 326 private final String mPriceValue; 327 private final String mMaturityRating; 328 private final long mRunningTime; 329 330 // Mutable fields 331 private String mGroup; 332 private String mSortKey; 333 private int mProgressAmount; 334 private int mProgressMax; 335 private boolean mAutoDismiss; 336 private int mStatus; 337 ContentRecommendation(Builder builder)338 private ContentRecommendation(Builder builder) { 339 mIdTag = builder.mBuilderIdTag; 340 mTitle = builder.mBuilderTitle; 341 mText = builder.mBuilderText; 342 mSourceName = builder.mBuilderSourceName; 343 mContentImage = builder.mBuilderContentImage; 344 mBadgeIconId = builder.mBuilderBadgeIconId; 345 mBackgroundImageUri = builder.mBuilderBackgroundImageUri; 346 mColor = builder.mBuilderColor; 347 mContentIntentData = builder.mBuilderContentIntentData; 348 mDismissIntentData = builder.mBuilderDismissIntentData; 349 mContentTypes = builder.mBuilderContentTypes; 350 mContentGenres = builder.mBuilderContentGenres; 351 mPriceType = builder.mBuilderPriceType; 352 mPriceValue = builder.mBuilderPriceValue; 353 mMaturityRating = builder.mBuilderMaturityRating; 354 mRunningTime = builder.mBuilderRunningTime; 355 356 mGroup = builder.mBuilderGroup; 357 mSortKey = builder.mBuilderSortKey; 358 mProgressAmount = builder.mBuilderProgressAmount; 359 mProgressMax = builder.mBuilderProgressMax; 360 mAutoDismiss = builder.mBuilderAutoDismiss; 361 mStatus = builder.mBuilderStatus; 362 } 363 364 /** 365 * Returns the String Id tag which uniquely identifies this recommendation. 366 * 367 * @return The String Id tag for this recommendation. 368 */ getIdTag()369 public String getIdTag() { 370 return mIdTag; 371 } 372 373 /** 374 * Returns the content title for this recommendation. 375 * 376 * @return A String containing the recommendation content title. 377 */ getTitle()378 public String getTitle() { 379 return mTitle; 380 } 381 382 /** 383 * Returns the description text for this recommendation. 384 * 385 * @return A String containing the recommendation description text. 386 */ getText()387 public String getText() { 388 return mText; 389 } 390 391 /** 392 * Returns the source application name for this recommendation. 393 * 394 * @return A String containing the recommendation source name. 395 */ getSourceName()396 public String getSourceName() { 397 return mSourceName; 398 } 399 400 /** 401 * Returns the Bitmap containing the recommendation image. 402 * 403 * @return A Bitmap containing the recommendation image. 404 */ getContentImage()405 public Bitmap getContentImage() { 406 return mContentImage; 407 } 408 409 /** 410 * Returns the resource id for the recommendation badging icon. 411 * <p> 412 * The resource id represents the icon resource in the source application package. 413 * 414 * @return An integer id for the badge icon resource. 415 */ getBadgeImageResourceId()416 public int getBadgeImageResourceId() { 417 return mBadgeIconId; 418 } 419 420 /** 421 * Returns a Content URI that can be used to retrieve the background image for this 422 * recommendation. 423 * 424 * @return A Content URI pointing to the recommendation background image. 425 */ getBackgroundImageUri()426 public String getBackgroundImageUri() { 427 return mBackgroundImageUri; 428 } 429 430 /** 431 * Returns the accent color value to be used in the UI when displaying this content 432 * recommendation to the user. 433 * 434 * @return An integer value representing the accent color for this recommendation. 435 */ getColor()436 public int getColor() { 437 return mColor; 438 } 439 440 /** 441 * Sets the String group ID tag for this recommendation. 442 * <p> 443 * Recommendations in the same group are ranked by the Home Screen together, and the sort order 444 * within a group is respected. This can be useful if the application has different sources for 445 * recommendations, like "trending", "subscriptions", and "new music" categories for YouTube, 446 * where the user can be more interested in recommendations from one group than another. 447 * 448 * @param groupTag A String containing the group ID tag for this recommendation. 449 */ setGroup(String groupTag)450 public void setGroup(String groupTag) { 451 mGroup = groupTag; 452 } 453 454 /** 455 * Returns the String group ID tag for this recommendation. 456 * 457 * @return A String containing the group ID tag for this recommendation. 458 */ getGroup()459 public String getGroup() { 460 return mGroup; 461 } 462 463 /** 464 * Sets the String sort key for this recommendation. 465 * <p> 466 * The sort key must be a String representation of a float number between 0.0 and 1.0, and is 467 * used to indicate the relative importance (and sort order) of a single recommendation within 468 * its specified group. The recommendations will be ordered in decreasing order of importance 469 * within a given group. 470 * 471 * @param sortKey A String containing the sort key for this recommendation. 472 */ setSortKey(String sortKey)473 public void setSortKey(String sortKey) { 474 mSortKey = sortKey; 475 } 476 477 /** 478 * Returns the String sort key for this recommendation. 479 * 480 * @return A String containing the sort key for this recommendation. 481 */ getSortKey()482 public String getSortKey() { 483 return mSortKey; 484 } 485 486 /** 487 * Sets the progress information for the content pointed to by this recommendation. 488 * 489 * @param max The maximum value for the progress of this content. 490 * @param progress The progress amount for this content. Must be in the range (0 - max). 491 */ setProgress(int max, int progress)492 public void setProgress(int max, int progress) { 493 if (max < 0 || progress < 0) { 494 throw new IllegalArgumentException(); 495 } 496 mProgressMax = max; 497 mProgressAmount = progress; 498 } 499 500 /** 501 * Indicates if this recommendation contains valid progress information. 502 * 503 * @return true if the recommendation contains valid progress data, false otherwise. 504 */ hasProgressInfo()505 public boolean hasProgressInfo() { 506 return mProgressMax != 0; 507 } 508 509 /** 510 * Returns the maximum value for the progress data of this recommendation. 511 * 512 * @return An integer representing the maximum progress value. 513 */ getProgressMax()514 public int getProgressMax() { 515 return mProgressMax; 516 } 517 518 /** 519 * Returns the progress amount for this recommendation. 520 * 521 * @return An integer representing the recommendation progress amount. 522 */ getProgressValue()523 public int getProgressValue() { 524 return mProgressAmount; 525 } 526 527 /** 528 * Sets the flag indicating if this recommendation should be dismissed automatically. 529 * <p> 530 * Auto-dismiss notifications are automatically removed from the Home Screen when the user 531 * clicks on them. 532 * 533 * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or 534 * not. 535 */ setAutoDismiss(boolean autoDismiss)536 public void setAutoDismiss(boolean autoDismiss) { 537 mAutoDismiss = autoDismiss; 538 } 539 540 /** 541 * Indicates whether this recommendation should be dismissed automatically. 542 * <p> 543 * Auto-dismiss notifications are automatically removed from the Home Screen when the user 544 * clicks on them. 545 * 546 * @return true if the recommendation is marked for auto dismissal, or false otherwise. 547 */ isAutoDismiss()548 public boolean isAutoDismiss() { 549 return mAutoDismiss; 550 } 551 552 /** 553 * Returns the data for the Intent that will be issued when the user clicks on the 554 * recommendation. 555 * 556 * @return An IntentData object, containing the data for the Intent that gets issued when the 557 * recommendation is clicked on. 558 */ getContentIntent()559 public IntentData getContentIntent() { 560 return mContentIntentData; 561 } 562 563 /** 564 * Returns the data for the Intent that will be issued when the recommendation gets dismissed 565 * from the Home Screen, due to an user action. 566 * 567 * @return An IntentData object, containing the data for the Intent that gets issued when the 568 * recommendation is dismissed from the Home Screen. 569 */ getDismissIntent()570 public IntentData getDismissIntent() { 571 return mDismissIntentData; 572 } 573 574 /** 575 * Returns an array containing the content types tags that describe the content. The first tag 576 * entry is considered the primary type for the content, and is used for content ranking 577 * purposes. 578 * 579 * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants) that 580 * describe the recommended content. 581 */ getContentTypes()582 public String[] getContentTypes() { 583 if (mContentTypes != null) { 584 return Arrays.copyOf(mContentTypes, mContentTypes.length); 585 } 586 return mContentTypes; 587 } 588 589 /** 590 * Returns the primary content type tag for the recommendation, or null if no content types have 591 * been specified. 592 * 593 * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating the 594 * primary content type for the recommendation. 595 */ getPrimaryContentType()596 public String getPrimaryContentType() { 597 if (mContentTypes != null && mContentTypes.length > 0) { 598 return mContentTypes[0]; 599 } 600 return null; 601 } 602 603 /** 604 * Returns an array containing the genres that describe the content. Genres are open ended 605 * String tags. 606 * 607 * @return An array of genre tags that describe the recommended content. 608 */ getGenres()609 public String[] getGenres() { 610 if (mContentGenres != null) { 611 return Arrays.copyOf(mContentGenres, mContentGenres.length); 612 } 613 return mContentGenres; 614 } 615 616 /** 617 * Gets the pricing type for the content. 618 * 619 * @return A predefined tag indicating the pricing type for the content (see the <code> 620 * CONTENT_PRICING_*</code> constants). 621 */ getPricingType()622 public String getPricingType() { 623 return mPriceType; 624 } 625 626 /** 627 * Gets the price value (when applicable) for the content. The value will be provided as a 628 * String containing the price in the appropriate currency for the current locale. 629 * 630 * @return A string containing a representation of the content price in the current locale and 631 * currency. 632 */ getPricingValue()633 public String getPricingValue() { 634 return mPriceValue; 635 } 636 637 /** 638 * Sets the availability status value for the content. This status indicates whether the content 639 * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe 640 * to, or download the content. 641 * 642 * @param status The status value for the content. (see the <code>CONTENT_STATUS_*</code> for 643 * the valid status values). 644 */ setStatus(@ontentStatus int status)645 public void setStatus(@ContentStatus int status) { 646 mStatus = status; 647 } 648 649 /** 650 * Returns availability status value for the content. This status indicates whether the content 651 * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe 652 * to, or download the content. 653 * 654 * @return The status value for the content, or -1 is a valid status has not been specified (see 655 * the <code>CONTENT_STATUS_*</code> constants for the valid status values). 656 */ getStatus()657 public int getStatus() { 658 return mStatus; 659 } 660 661 /** 662 * Returns the maturity level rating for the content. 663 * 664 * @return returns a predefined tag indicating the maturity level rating for the content (see 665 * the <code>CONTENT_MATURITY_*</code> constants). 666 */ getMaturityRating()667 public String getMaturityRating() { 668 return mMaturityRating; 669 } 670 671 /** 672 * Returns the running time for the content. 673 * 674 * @return The run length, in seconds, of the content associated with the notification. 675 */ getRunningTime()676 public long getRunningTime() { 677 return mRunningTime; 678 } 679 680 @Override equals(Object other)681 public boolean equals(Object other) { 682 if (other instanceof ContentRecommendation) { 683 return TextUtils.equals(mIdTag, ((ContentRecommendation) other).getIdTag()); 684 } 685 return false; 686 } 687 688 @Override hashCode()689 public int hashCode() { 690 if (mIdTag != null) { 691 return mIdTag.hashCode(); 692 } 693 return Integer.MAX_VALUE; 694 } 695 696 /** 697 * Builder class for {@link ContentRecommendation} objects. Provides a convenient way to set the 698 * various fields of a {@link ContentRecommendation}. 699 * <p> 700 * Example: 701 * 702 * <pre class="prettyprint"> 703 * ContentRecommendation rec = new ContentRecommendation.Builder() 704 * .setIdInfo(id, "MyTagId") 705 * .setTitle("My Content Recommendation") 706 * .setText("An example of content recommendation") 707 * .setContentImage(myBitmap) 708 * .setBadgeIcon(R.drawable.app_icon) 709 * .setGroup("Trending") 710 * .build(); 711 * </pre> 712 */ 713 public static final class Builder { 714 private String mBuilderIdTag; 715 private String mBuilderTitle; 716 private String mBuilderText; 717 private String mBuilderSourceName; 718 private Bitmap mBuilderContentImage; 719 private int mBuilderBadgeIconId; 720 private String mBuilderBackgroundImageUri; 721 private int mBuilderColor; 722 private String mBuilderGroup; 723 private String mBuilderSortKey; 724 private int mBuilderProgressAmount; 725 private int mBuilderProgressMax; 726 private boolean mBuilderAutoDismiss; 727 private IntentData mBuilderContentIntentData; 728 private IntentData mBuilderDismissIntentData; 729 private String[] mBuilderContentTypes; 730 private String[] mBuilderContentGenres; 731 private String mBuilderPriceType; 732 private String mBuilderPriceValue; 733 private int mBuilderStatus; 734 private String mBuilderMaturityRating; 735 private long mBuilderRunningTime; 736 737 /** 738 * Constructs a new Builder. 739 * 740 */ Builder()741 public Builder() { 742 } 743 744 /** 745 * Sets the Id tag that uniquely identifies this recommendation object. 746 * 747 * @param idTag A String tag identifier for this recommendation. 748 * @return The Builder object, for chaining. 749 */ setIdTag(String idTag)750 public Builder setIdTag(String idTag) { 751 mBuilderIdTag = checkNotNull(idTag); 752 return this; 753 } 754 755 /** 756 * Sets the content title for the recommendation. 757 * 758 * @param title A String containing the recommendation content title. 759 * @return The Builder object, for chaining. 760 */ setTitle(String title)761 public Builder setTitle(String title) { 762 mBuilderTitle = checkNotNull(title); 763 return this; 764 } 765 766 /** 767 * Sets the description text for the recommendation. 768 * 769 * @param description A String containing the recommendation description text. 770 * @return The Builder object, for chaining. 771 */ setText(@ullable String description)772 public Builder setText(@Nullable String description) { 773 mBuilderText = description; 774 return this; 775 } 776 777 /** 778 * Sets the source application name for the recommendation. 779 * <P> 780 * If the source name is never set, or set to null, the application name retrieved from its 781 * package will be used by default. 782 * 783 * @param source A String containing the recommendation source name. 784 * @return The Builder object, for chaining. 785 */ setSourceName(@ullable String source)786 public Builder setSourceName(@Nullable String source) { 787 mBuilderSourceName = source; 788 return this; 789 } 790 791 /** 792 * Sets the recommendation image. 793 * 794 * @param image A Bitmap containing the recommendation image. 795 * @return The Builder object, for chaining. 796 */ setContentImage(Bitmap image)797 public Builder setContentImage(Bitmap image) { 798 mBuilderContentImage = checkNotNull(image); 799 return this; 800 } 801 802 /** 803 * Sets the resource ID for the recommendation badging icon. 804 * <p> 805 * The resource id represents the icon resource in the source application package. If not 806 * set, or an invalid resource ID is specified, the application icon retrieved from its 807 * package will be used by default. 808 * 809 * @param iconResourceId An integer id for the badge icon resource. 810 * @return The Builder object, for chaining. 811 */ setBadgeIcon(@rawableRes int iconResourceId)812 public Builder setBadgeIcon(@DrawableRes int iconResourceId) { 813 mBuilderBadgeIconId = iconResourceId; 814 return this; 815 } 816 817 /** 818 * Sets the Content URI that will be used to retrieve the background image for the 819 * recommendation. 820 * 821 * @param imageUri A Content URI pointing to the recommendation background image. 822 * @return The Builder object, for chaining. 823 */ setBackgroundImageUri(@ullable String imageUri)824 public Builder setBackgroundImageUri(@Nullable String imageUri) { 825 mBuilderBackgroundImageUri = imageUri; 826 return this; 827 } 828 829 /** 830 * Sets the accent color value to be used in the UI when displaying this content 831 * recommendation to the user. 832 * 833 * @param color An integer value representing the accent color for this recommendation. 834 * @return The Builder object, for chaining. 835 */ setColor(@olorInt int color)836 public Builder setColor(@ColorInt int color) { 837 mBuilderColor = color; 838 return this; 839 } 840 841 /** 842 * Sets the String group ID tag for the recommendation. 843 * <p> 844 * Recommendations in the same group are ranked by the Home Screen together, and the sort 845 * order within a group is respected. This can be useful if the application has different 846 * sources for recommendations, like "trending", "subscriptions", and "new music" categories 847 * for YouTube, where the user can be more interested in recommendations from one group than 848 * another. 849 * 850 * @param groupTag A String containing the group ID tag for this recommendation. 851 * @return The Builder object, for chaining. 852 */ setGroup(@ullable String groupTag)853 public Builder setGroup(@Nullable String groupTag) { 854 mBuilderGroup = groupTag; 855 return this; 856 } 857 858 /** 859 * Sets the String sort key for the recommendation. 860 * <p> 861 * The sort key must be a String representation of a float number between 0.0 and 1.0, and 862 * is used to indicate the relative importance (and sort order) of a single recommendation 863 * within its specified group. The recommendations will be ordered in decreasing order of 864 * importance within a given group. 865 * 866 * @param sortKey A String containing the sort key for this recommendation. 867 * @return The Builder object, for chaining. 868 */ setSortKey(@ullable String sortKey)869 public Builder setSortKey(@Nullable String sortKey) { 870 mBuilderSortKey = sortKey; 871 return this; 872 } 873 874 /** 875 * Sets the progress information for the content pointed to by the recommendation. 876 * 877 * @param max The maximum value for the progress of this content. 878 * @param progress The progress amount for this content. Must be in the range (0 - max). 879 * @return The Builder object, for chaining. 880 */ setProgress(int max, int progress)881 public Builder setProgress(int max, int progress) { 882 if (max < 0 || progress < 0) { 883 throw new IllegalArgumentException(); 884 } 885 mBuilderProgressMax = max; 886 mBuilderProgressAmount = progress; 887 return this; 888 } 889 890 /** 891 * Sets the flag indicating if the recommendation should be dismissed automatically. 892 * <p> 893 * Auto-dismiss notifications are automatically removed from the Home Screen when the user 894 * clicks on them. 895 * 896 * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or 897 * not. 898 * @return The Builder object, for chaining. 899 */ setAutoDismiss(boolean autoDismiss)900 public Builder setAutoDismiss(boolean autoDismiss) { 901 mBuilderAutoDismiss = autoDismiss; 902 return this; 903 } 904 905 /** 906 * Sets the data for the Intent that will be issued when the user clicks on the 907 * recommendation. 908 * <p> 909 * The Intent data fields provided correspond to the fields passed into the 910 * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual 911 * PengindIntent object will only be created at the time a recommendation is posted to the 912 * Home Screen. 913 * 914 * @param intentType The type of {@link PendingIntent} to be created when posting this 915 * recommendation. 916 * @param intent The Intent which to be issued when the recommendation is clicked on. 917 * @param requestCode The private request code to be used when creating the 918 * {@link PendingIntent} 919 * @param options Only used for the Activity Intent type. Additional options for how the 920 * Activity should be started. May be null if there are no options. 921 * @return The Builder object, for chaining. 922 */ setContentIntentData(@ntentType int intentType, Intent intent, int requestCode, @Nullable Bundle options)923 public Builder setContentIntentData(@IntentType int intentType, Intent intent, 924 int requestCode, @Nullable Bundle options) { 925 if (intentType != INTENT_TYPE_ACTIVITY && 926 intentType != INTENT_TYPE_BROADCAST && 927 intentType != INTENT_TYPE_SERVICE) { 928 throw new IllegalArgumentException("Invalid Intent type specified."); 929 } 930 931 mBuilderContentIntentData = new IntentData(); 932 mBuilderContentIntentData.mType = intentType; 933 mBuilderContentIntentData.mIntent = checkNotNull(intent); 934 mBuilderContentIntentData.mRequestCode = requestCode; 935 mBuilderContentIntentData.mOptions = options; 936 937 return this; 938 } 939 940 /** 941 * Sets the data for the Intent that will be issued when the recommendation gets dismissed 942 * from the Home Screen, due to an user action. 943 * <p> 944 * The Intent data fields provided correspond to the fields passed into the 945 * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual 946 * PengindIntent object will only be created at the time a recommendation is posted to the 947 * Home Screen. 948 * 949 * @param intentType The type of {@link PendingIntent} to be created when posting this 950 * recommendation. 951 * @param intent The Intent which gets issued when the recommendation is dismissed from the 952 * Home Screen. 953 * @param requestCode The private request code to be used when creating the 954 * {@link PendingIntent} 955 * @param options Only used for the Activity Intent type. Additional options for how the 956 * Activity should be started. May be null if there are no options. 957 * @return The Builder object, for chaining. 958 */ setDismissIntentData(@ntentType int intentType, @Nullable Intent intent, int requestCode, @Nullable Bundle options)959 public Builder setDismissIntentData(@IntentType int intentType, @Nullable Intent intent, 960 int requestCode, @Nullable Bundle options) { 961 if (intent != null) { 962 if (intentType != INTENT_TYPE_ACTIVITY && 963 intentType != INTENT_TYPE_BROADCAST && 964 intentType != INTENT_TYPE_SERVICE) { 965 throw new IllegalArgumentException("Invalid Intent type specified."); 966 } 967 968 mBuilderDismissIntentData = new IntentData(); 969 mBuilderDismissIntentData.mType = intentType; 970 mBuilderDismissIntentData.mIntent = intent; 971 mBuilderDismissIntentData.mRequestCode = requestCode; 972 mBuilderDismissIntentData.mOptions = options; 973 } else { 974 mBuilderDismissIntentData = null; 975 } 976 return this; 977 } 978 979 /** 980 * Sets the content types associated with the content recommendation. The first tag entry 981 * will be considered the primary type for the content and will be used for ranking 982 * purposes. Other secondary type tags may be provided, if applicable, and may be used for 983 * filtering purposes. 984 * 985 * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code> 986 * constants) that describe the recommended content. 987 */ setContentTypes(String[] types)988 public Builder setContentTypes(String[] types) { 989 mBuilderContentTypes = checkNotNull(types); 990 return this; 991 } 992 993 /** 994 * Sets the content genres for the recommendation. These genres may be used for content 995 * ranking. Genres are open ended String tags. 996 * <p> 997 * Some examples: "comedy", "action", "dance", "electronica", "racing", etc. 998 * 999 * @param genres Array of genre string tags that describe the recommended content. 1000 */ setGenres(String[] genres)1001 public Builder setGenres(String[] genres) { 1002 mBuilderContentGenres = genres; 1003 return this; 1004 } 1005 1006 /** 1007 * Sets the pricing and availability information for the recommendation. The provided 1008 * information will indicate the access model for the content (free, rental, purchase or 1009 * subscription) and the price value (if not free). 1010 * 1011 * @param priceType Pricing type for this content. Must be one of the predefined pricing 1012 * type tags (see the <code>CONTENT_PRICING_*</code> constants). 1013 * @param priceValue A string containing a representation of the content price in the 1014 * current locale and currency. 1015 */ setPricingInformation(@ontentPricing String priceType, @Nullable String priceValue)1016 public Builder setPricingInformation(@ContentPricing String priceType, 1017 @Nullable String priceValue) { 1018 mBuilderPriceType = checkNotNull(priceType); 1019 mBuilderPriceValue = priceValue; 1020 return this; 1021 } 1022 1023 /** 1024 * Sets the availability status for the content. This status indicates whether the referred 1025 * content is ready to be consumed on the device, or if the user must first purchase, rent, 1026 * subscribe to, or download the content. 1027 * 1028 * @param contentStatus The status value for this content. Must be one of the predefined 1029 * content status values (see the <code>CONTENT_STATUS_*</code> constants). 1030 */ setStatus(@ontentStatus int contentStatus)1031 public Builder setStatus(@ContentStatus int contentStatus) { 1032 mBuilderStatus = contentStatus; 1033 return this; 1034 } 1035 1036 /** 1037 * Sets the maturity level rating for the content. 1038 * 1039 * @param maturityRating A tag indicating the maturity level rating for the content. This 1040 * tag must be one of the predefined maturity rating tags (see the <code> 1041 * CONTENT_MATURITY_*</code> constants). 1042 */ setMaturityRating(@ontentMaturity String maturityRating)1043 public Builder setMaturityRating(@ContentMaturity String maturityRating) { 1044 mBuilderMaturityRating = checkNotNull(maturityRating); 1045 return this; 1046 } 1047 1048 /** 1049 * Sets the running time (when applicable) for the content. 1050 * 1051 * @param length The running time, in seconds, of the content. 1052 */ setRunningTime(long length)1053 public Builder setRunningTime(long length) { 1054 if (length < 0) { 1055 throw new IllegalArgumentException(); 1056 } 1057 mBuilderRunningTime = length; 1058 return this; 1059 } 1060 1061 /** 1062 * Combine all of the options that have been set and return a new 1063 * {@link ContentRecommendation} object. 1064 */ build()1065 public ContentRecommendation build() { 1066 return new ContentRecommendation(this); 1067 } 1068 } 1069 1070 /** 1071 * Returns a {@link android.app.Notification Notification} object which contains the content 1072 * recommendation data encapsulated by this object, which can be used for posting the 1073 * recommendation via the {@link android.app.NotificationManager NotificationManager}. 1074 * 1075 * @param context A {@link Context} that will be used to construct the 1076 * {@link android.app.Notification Notification} object which will carry the 1077 * recommendation data. 1078 * @return A {@link android.app.Notification Notification} containing the stored recommendation 1079 * data. 1080 */ getNotificationObject(Context context)1081 public Notification getNotificationObject(Context context) { 1082 Notification.Builder builder = new Notification.Builder(context); 1083 RecommendationExtender recExtender = new RecommendationExtender(); 1084 1085 // Encode all the content recommendation data in a Notification object 1086 1087 builder.setCategory(Notification.CATEGORY_RECOMMENDATION); 1088 builder.setContentTitle(mTitle); 1089 builder.setContentText(mText); 1090 builder.setContentInfo(mSourceName); 1091 builder.setLargeIcon(mContentImage); 1092 builder.setSmallIcon(mBadgeIconId); 1093 if (mBackgroundImageUri != null) { 1094 builder.getExtras().putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, 1095 mBackgroundImageUri); 1096 } 1097 builder.setColor(mColor); 1098 builder.setGroup(mGroup); 1099 builder.setSortKey(mSortKey); 1100 builder.setProgress(mProgressMax, mProgressAmount, false); 1101 builder.setAutoCancel(mAutoDismiss); 1102 1103 if (mContentIntentData != null) { 1104 PendingIntent contentPending; 1105 if (mContentIntentData.mType == INTENT_TYPE_ACTIVITY) { 1106 contentPending = PendingIntent.getActivity(context, mContentIntentData.mRequestCode, 1107 mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT, 1108 mContentIntentData.mOptions); 1109 } else if (mContentIntentData.mType == INTENT_TYPE_SERVICE) { 1110 contentPending = PendingIntent.getService(context, mContentIntentData.mRequestCode, 1111 mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT); 1112 } else { // Default:INTENT_TYPE_BROADCAST{ 1113 contentPending = PendingIntent.getBroadcast(context, 1114 mContentIntentData.mRequestCode, 1115 mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT); 1116 } 1117 builder.setContentIntent(contentPending); 1118 } 1119 1120 if (mDismissIntentData != null) { 1121 PendingIntent dismissPending; 1122 if (mDismissIntentData.mType == INTENT_TYPE_ACTIVITY) { 1123 dismissPending = PendingIntent.getActivity(context, mDismissIntentData.mRequestCode, 1124 mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT, 1125 mDismissIntentData.mOptions); 1126 } else if (mDismissIntentData.mType == INTENT_TYPE_SERVICE) { 1127 dismissPending = PendingIntent.getService(context, mDismissIntentData.mRequestCode, 1128 mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT); 1129 } else { // Default:INTENT_TYPE_BROADCAST{ 1130 dismissPending = PendingIntent.getBroadcast(context, 1131 mDismissIntentData.mRequestCode, 1132 mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT); 1133 } 1134 builder.setDeleteIntent(dismissPending); 1135 } 1136 1137 recExtender.setContentTypes(mContentTypes); 1138 recExtender.setGenres(mContentGenres); 1139 recExtender.setPricingInformation(mPriceType, mPriceValue); 1140 recExtender.setStatus(mStatus); 1141 recExtender.setMaturityRating(mMaturityRating); 1142 recExtender.setRunningTime(mRunningTime); 1143 1144 builder.extend(recExtender); 1145 Notification notif = builder.build(); 1146 return notif; 1147 } 1148 1149 /** 1150 * Ensures that an object reference passed as a parameter to the calling method is not null. 1151 * 1152 * @param reference an object reference 1153 * @return the non-null reference that was validated 1154 * @throws NullPointerException if {@code reference} is null 1155 */ checkNotNull(final T reference)1156 private static <T> T checkNotNull(final T reference) { 1157 if (reference == null) { 1158 throw new NullPointerException(); 1159 } 1160 return reference; 1161 } 1162 1163 }