• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &quot;MyTagId&quot;)
705      *         .setTitle(&quot;My Content Recommendation&quot;)
706      *         .setText(&quot;An example of content recommendation&quot;)
707      *         .setContentImage(myBitmap)
708      *         .setBadgeIcon(R.drawable.app_icon)
709      *         .setGroup(&quot;Trending&quot;)
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 }