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