Move new Recommendation code from Recline to frameworks/support

Change-Id: Idd569011aa35f27bc29ad88928cb5449ea114638
diff --git a/recommendation/Android.mk b/recommendation/Android.mk
new file mode 100644
index 0000000..3d8edf8
--- /dev/null
+++ b/recommendation/Android.mk
@@ -0,0 +1,60 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := 21
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+
+LOCAL_JAVA_LIBRARIES := \
+    android-support-v4
+
+LOCAL_MODULE := android-support-recommendation
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# ===========================================================
+# Common Droiddoc vars
+recommendation.docs.src_files := \
+    $(call all-java-files-under, src) \
+    $(call all-html-files-under, src)
+recommendation.docs.java_libraries := \
+    framework \
+    android-support-v4 \
+    android-support-recommendation
+
+# Documentation
+# ===========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android-support-recommendation
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(recommendation.docs.src_files)
+
+LOCAL_SDK_VERSION := 21
+LOCAL_IS_HOST_MODULE := false
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
+
+LOCAL_JAVA_LIBRARIES := $(recommendation.docs.java_libraries)
+
+LOCAL_DROIDDOC_OPTIONS := \
+    -offlinemode \
+    -hdf android.whichdoc offline \
+    -federate Android http://developer.android.com \
+    -federationapi Android prebuilts/sdk/api/21.txt \
+    -hide 113
+
+include $(BUILD_DROIDDOC)
+
+
+# API Check
+# ---------------------------------------------
+support_module := $(LOCAL_MODULE)
+support_module_api_dir := $(LOCAL_PATH)/api
+support_module_src_files := $(recommendation.docs.src_files)
+support_module_java_libraries := $(recommendation.docs.java_libraries)
+support_module_java_packages := android.support.app.recommendation*
+include $(SUPPORT_API_CHECK)
diff --git a/recommendation/AndroidManifest.xml b/recommendation/AndroidManifest.xml
new file mode 100644
index 0000000..ef1223e
--- /dev/null
+++ b/recommendation/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.support.recommendation">
+    <uses-sdk android:minSdkVersion="21"/>
+    <application />
+</manifest>
diff --git a/recommendation/api/current.txt b/recommendation/api/current.txt
new file mode 100644
index 0000000..ce69b31
--- /dev/null
+++ b/recommendation/api/current.txt
@@ -0,0 +1,132 @@
+package android.support.app.recommendation {
+
+  public final class ContentRecommendation {
+    method public java.lang.String getBackgroundImageUri();
+    method public int getBadgeImageResourceId();
+    method public int getColor();
+    method public android.graphics.Bitmap getContentImage();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getContentIntent();
+    method public java.lang.String[] getContentTypes();
+    method public android.support.app.recommendation.ContentRecommendation.IntentData getDismissIntent();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getGroup();
+    method public java.lang.String getIdTag();
+    method public java.lang.String getMaturityRating();
+    method public android.app.Notification getNotificationObject(android.content.Context);
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public int getProgressMax();
+    method public int getProgressValue();
+    method public long getRunningTime();
+    method public java.lang.String getSortKey();
+    method public java.lang.String getSourceName();
+    method public int getStatus();
+    method public java.lang.String getText();
+    method public java.lang.String getTitle();
+    method public boolean hasProgressInfo();
+    method public boolean isAutoDismiss();
+    method public void setAutoDismiss(boolean);
+    method public void setGroup(java.lang.String);
+    method public void setProgress(int, int);
+    method public void setSortKey(java.lang.String);
+    method public void setStatus(int);
+    field public static final java.lang.String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+    field public static final java.lang.String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+    field public static final java.lang.String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+    field public static final java.lang.String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+    field public static final java.lang.String CONTENT_PRICING_FREE = "android.contentPrice.free";
+    field public static final java.lang.String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+    field public static final java.lang.String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+    field public static final java.lang.String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+    field public static final java.lang.String CONTENT_PRICING_SUBSCRIPTION = "android.contentPrice.subscription";
+    field public static final int CONTENT_STATUS_AVAILABLE = 2; // 0x2
+    field public static final int CONTENT_STATUS_PENDING = 1; // 0x1
+    field public static final int CONTENT_STATUS_READY = 0; // 0x0
+    field public static final int CONTENT_STATUS_UNAVAILABLE = 3; // 0x3
+    field public static final java.lang.String CONTENT_TYPE_APP = "android.contentType.app";
+    field public static final java.lang.String CONTENT_TYPE_BOOK = "android.contentType.book";
+    field public static final java.lang.String CONTENT_TYPE_COMIC = "android.contentType.comic";
+    field public static final java.lang.String CONTENT_TYPE_GAME = "android.contentType.game";
+    field public static final java.lang.String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+    field public static final java.lang.String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+    field public static final java.lang.String CONTENT_TYPE_MUSIC = "android.contentType.music";
+    field public static final java.lang.String CONTENT_TYPE_NEWS = "android.contentType.news";
+    field public static final java.lang.String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+    field public static final java.lang.String CONTENT_TYPE_RADIO = "android.contentType.radio";
+    field public static final java.lang.String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+    field public static final java.lang.String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+    field public static final java.lang.String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+    field public static final java.lang.String CONTENT_TYPE_VIDEO = "android.contentType.video";
+    field public static final java.lang.String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+    field public static final int INTENT_TYPE_ACTIVITY = 1; // 0x1
+    field public static final int INTENT_TYPE_BROADCAST = 2; // 0x2
+    field public static final int INTENT_TYPE_SERVICE = 3; // 0x3
+  }
+
+  public static final class ContentRecommendation.Builder {
+    ctor public ContentRecommendation.Builder();
+    method public android.support.app.recommendation.ContentRecommendation build();
+    method public android.support.app.recommendation.ContentRecommendation.Builder setAutoDismiss(boolean);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBackgroundImageUri(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setBadgeIcon(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setColor(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentImage(android.graphics.Bitmap);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setDismissIntentData(int, android.content.Intent, int, android.os.Bundle);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setGroup(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setIdTag(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setProgress(int, int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setRunningTime(long);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSortKey(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setSourceName(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setStatus(int);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setText(java.lang.String);
+    method public android.support.app.recommendation.ContentRecommendation.Builder setTitle(java.lang.String);
+  }
+
+  public static abstract class ContentRecommendation.ContentMaturity implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentPricing implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentStatus implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ContentRecommendation.ContentType implements java.lang.annotation.Annotation {
+  }
+
+  public static class ContentRecommendation.IntentData {
+    ctor public ContentRecommendation.IntentData();
+  }
+
+  public static abstract class ContentRecommendation.IntentType implements java.lang.annotation.Annotation {
+  }
+
+  public final class RecommendationExtender implements android.app.Notification.Extender {
+    ctor public RecommendationExtender();
+    ctor public RecommendationExtender(android.app.Notification);
+    method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+    method public java.lang.String[] getContentTypes();
+    method public java.lang.String[] getGenres();
+    method public java.lang.String getMaturityRating();
+    method public java.lang.String getPricingType();
+    method public java.lang.String getPricingValue();
+    method public java.lang.String getPrimaryContentType();
+    method public long getRunningTime();
+    method public int getStatus();
+    method public android.support.app.recommendation.RecommendationExtender setContentTypes(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setGenres(java.lang.String[]);
+    method public android.support.app.recommendation.RecommendationExtender setMaturityRating(java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setPricingInformation(java.lang.String, java.lang.String);
+    method public android.support.app.recommendation.RecommendationExtender setRunningTime(long);
+    method public android.support.app.recommendation.RecommendationExtender setStatus(int);
+  }
+
+}
+
diff --git a/recommendation/api/removed.txt b/recommendation/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/recommendation/api/removed.txt
diff --git a/recommendation/src/android/support/app/recommendation/ContentRecommendation.java b/recommendation/src/android/support/app/recommendation/ContentRecommendation.java
new file mode 100644
index 0000000..fdc0907
--- /dev/null
+++ b/recommendation/src/android/support/app/recommendation/ContentRecommendation.java
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.app.recommendation;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringDef;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+
+/**
+ * The ContentRecommendation object encapsulates all application provided data for a single content
+ * recommendation item.
+ */
+public final class ContentRecommendation
+{
+    @StringDef({
+        CONTENT_TYPE_VIDEO,
+        CONTENT_TYPE_MOVIE,
+        CONTENT_TYPE_TRAILER,
+        CONTENT_TYPE_SERIAL,
+        CONTENT_TYPE_MUSIC,
+        CONTENT_TYPE_RADIO,
+        CONTENT_TYPE_PODCAST,
+        CONTENT_TYPE_NEWS,
+        CONTENT_TYPE_SPORTS,
+        CONTENT_TYPE_APP,
+        CONTENT_TYPE_GAME,
+        CONTENT_TYPE_BOOK,
+        CONTENT_TYPE_COMIC,
+        CONTENT_TYPE_MAGAZINE,
+        CONTENT_TYPE_WEBSITE,
+    })
+    public @interface ContentType {}
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a video clip.
+     */
+    public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a movie.
+     */
+    public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a trailer.
+     */
+    public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is serial. It can refer to an entire show, a single season or
+     * series, or a single episode.
+     */
+    public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a song or album.
+     */
+    public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a radio station.
+     */
+    public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a podcast.
+     */
+    public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a news item.
+     */
+    public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is sports.
+     */
+    public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is an application.
+     */
+    public static final String CONTENT_TYPE_APP = "android.contentType.app";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a game.
+     */
+    public static final String CONTENT_TYPE_GAME = "android.contentType.game";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a book.
+     */
+    public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a comic book.
+     */
+    public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a magazine.
+     */
+    public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
+
+    /**
+     * Value to be used with {@link Builder#setContentTypes} to indicate that the content referred
+     * by the notification item is a website.
+     */
+    public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
+
+    @StringDef({
+        CONTENT_PRICING_FREE,
+        CONTENT_PRICING_RENTAL,
+        CONTENT_PRICING_PURCHASE,
+        CONTENT_PRICING_PREORDER,
+        CONTENT_PRICING_SUBSCRIPTION,
+    })
+    public @interface ContentPricing {}
+
+    /**
+     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
+     * referred by the notification item is free to consume.
+     */
+    public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
+
+    /**
+     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
+     * referred by the notification item is available as a rental, and the price value provided is
+     * the rental price for the item.
+     */
+    public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
+
+    /**
+     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
+     * referred by the notification item is available for purchase, and the price value provided is
+     * the purchase price for the item.
+     */
+    public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
+
+    /**
+     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
+     * referred by the notification item is available currently as a pre-order, and the price value
+     * provided is the purchase price for the item.
+     */
+    public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
+
+    /**
+     * Value to be used with {@link Builder#setPricingInformation} to indicate that the content
+     * referred by the notification item is available as part of a subscription based service, and
+     * the price value provided is the subscription price for the service.
+     */
+    public static final String CONTENT_PRICING_SUBSCRIPTION =
+            "android.contentPrice.subscription";
+
+    @IntDef({
+        CONTENT_STATUS_READY,
+        CONTENT_STATUS_PENDING,
+        CONTENT_STATUS_AVAILABLE,
+        CONTENT_STATUS_UNAVAILABLE,
+    })
+    public @interface ContentStatus {}
+
+    /**
+     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
+     * notification is available and ready to be consumed immediately.
+     */
+    public static final int CONTENT_STATUS_READY = 0;
+
+    /**
+     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
+     * notification is pending, waiting on either a download or purchase operation to complete
+     * before it can be consumed.
+     */
+    public static final int CONTENT_STATUS_PENDING = 1;
+
+    /**
+     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
+     * notification is available, but needs to be first purchased, rented, subscribed or downloaded
+     * before it can be consumed.
+     */
+    public static final int CONTENT_STATUS_AVAILABLE = 2;
+
+    /**
+     * Value to be used with {@link Builder#setStatus} to indicate that the content referred by the
+     * notification is not available. This could be content not available in a certain region or
+     * incompatible with the device in use.
+     */
+    public static final int CONTENT_STATUS_UNAVAILABLE = 3;
+
+    @StringDef({
+        CONTENT_MATURITY_ALL,
+        CONTENT_MATURITY_LOW,
+        CONTENT_MATURITY_MEDIUM,
+        CONTENT_MATURITY_HIGH,
+    })
+    public @interface ContentMaturity {}
+
+    /**
+     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
+     * by the notification is suitable for all audiences.
+     */
+    public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
+
+    /**
+     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
+     * by the notification is suitable for audiences of low maturity and above.
+     */
+    public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
+
+    /**
+     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
+     * by the notification is suitable for audiences of medium maturity and above.
+     */
+    public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
+
+    /**
+     * Value to be used with {@link Builder#setMaturityRating} to indicate that the content referred
+     * by the notification is suitable for audiences of high maturity and above.
+     */
+    public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
+
+    @IntDef({
+            INTENT_TYPE_ACTIVITY,
+            INTENT_TYPE_BROADCAST,
+            INTENT_TYPE_SERVICE,
+    })
+    public @interface IntentType {
+    }
+
+    /**
+     * Value to be used with {@link Builder#setContentIntentData} and
+     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for an Activity
+     * should be created when posting the recommendation to the HomeScreen.
+     */
+    public static final int INTENT_TYPE_ACTIVITY = 1;
+
+    /**
+     * Value to be used with {@link Builder#setContentIntentData} and
+     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Broadcast
+     * should be created when posting the recommendation to the HomeScreen.
+     */
+    public static final int INTENT_TYPE_BROADCAST = 2;
+
+    /**
+     * Value to be used with {@link Builder#setContentIntentData} and
+     * {@link Builder#setDismissIntentData} to indicate that a {@link PendingIntent} for a Service
+     * should be created when posting the recommendation to the HomeScreen.
+     */
+    public static final int INTENT_TYPE_SERVICE = 3;
+
+    /**
+     * Object used to encapsulate the data to be used to build the {@link PendingIntent} object
+     * associated with a given content recommendation, at the time this recommendation gets posted
+     * to the home Screen.
+     * <p>
+     * The members of this object correspond to the fields passed into the {@link PendingIntent}
+     * factory methods, when creating a new PendingIntent.
+     */
+    public static class IntentData {
+        int mType;
+        Intent mIntent;
+        int mRequestCode;
+        Bundle mOptions;
+    }
+
+    private final String mIdTag;
+    private final String mTitle;
+    private final String mText;
+    private final String mSourceName;
+    private final Bitmap mContentImage;
+    private final int mBadgeIconId;
+    private final String mBackgroundImageUri;
+    private final int mColor;
+    private final IntentData mContentIntentData;
+    private final IntentData mDismissIntentData;
+    private final String[] mContentTypes;
+    private final String[] mContentGenres;
+    private final String mPriceType;
+    private final String mPriceValue;
+    private final String mMaturityRating;
+    private final long mRunningTime;
+
+    // Mutable fields
+    private String mGroup;
+    private String mSortKey;
+    private int mProgressAmount;
+    private int mProgressMax;
+    private boolean mAutoDismiss;
+    private int mStatus;
+
+    private ContentRecommendation(Builder builder) {
+        mIdTag = builder.mBuilderIdTag;
+        mTitle = builder.mBuilderTitle;
+        mText = builder.mBuilderText;
+        mSourceName = builder.mBuilderSourceName;
+        mContentImage = builder.mBuilderContentImage;
+        mBadgeIconId = builder.mBuilderBadgeIconId;
+        mBackgroundImageUri = builder.mBuilderBackgroundImageUri;
+        mColor = builder.mBuilderColor;
+        mContentIntentData = builder.mBuilderContentIntentData;
+        mDismissIntentData = builder.mBuilderDismissIntentData;
+        mContentTypes = builder.mBuilderContentTypes;
+        mContentGenres = builder.mBuilderContentGenres;
+        mPriceType = builder.mBuilderPriceType;
+        mPriceValue = builder.mBuilderPriceValue;
+        mMaturityRating = builder.mBuilderMaturityRating;
+        mRunningTime = builder.mBuilderRunningTime;
+
+        mGroup = builder.mBuilderGroup;
+        mSortKey = builder.mBuilderSortKey;
+        mProgressAmount = builder.mBuilderProgressAmount;
+        mProgressMax = builder.mBuilderProgressMax;
+        mAutoDismiss = builder.mBuilderAutoDismiss;
+        mStatus = builder.mBuilderStatus;
+    }
+
+    /**
+     * Returns the String Id tag which uniquely identifies this recommendation.
+     *
+     * @return The String Id tag for this recommendation.
+     */
+    public String getIdTag() {
+        return mIdTag;
+    }
+
+    /**
+     * Returns the content title for this recommendation.
+     *
+     * @return A String containing the recommendation content title.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns the description text for this recommendation.
+     *
+     * @return A String containing the recommendation description text.
+     */
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the source application name for this recommendation.
+     *
+     * @return A String containing the recommendation source name.
+     */
+    public String getSourceName() {
+        return mSourceName;
+    }
+
+    /**
+     * Returns the Bitmap containing the recommendation image.
+     *
+     * @return A Bitmap containing the recommendation image.
+     */
+    public Bitmap getContentImage() {
+        return mContentImage;
+    }
+
+    /**
+     * Returns the resource id for the recommendation badging icon.
+     * <p>
+     * The resource id represents the icon resource in the source application package.
+     *
+     * @return An integer id for the badge icon resource.
+     */
+    public int getBadgeImageResourceId() {
+        return mBadgeIconId;
+    }
+
+    /**
+     * Returns a Content URI that can be used to retrieve the background image for this
+     * recommendation.
+     *
+     * @return A Content URI pointing to the recommendation background image.
+     */
+    public String getBackgroundImageUri() {
+        return mBackgroundImageUri;
+    }
+
+    /**
+     * Returns the accent color value to be used in the UI when displaying this content
+     * recommendation to the user.
+     *
+     * @return An integer value representing the accent color for this recommendation.
+     */
+    public int getColor() {
+        return mColor;
+    }
+
+    /**
+     * Sets the String group ID tag for this recommendation.
+     * <p>
+     * Recommendations in the same group are ranked by the Home Screen together, and the sort order
+     * within a group is respected. This can be useful if the application has different sources for
+     * recommendations, like "trending", "subscriptions", and "new music" categories for YouTube,
+     * where the user can be more interested in recommendations from one group than another.
+     *
+     * @param groupTag A String containing the group ID tag for this recommendation.
+     */
+    public void setGroup(String groupTag) {
+        mGroup = groupTag;
+    }
+
+    /**
+     * Returns the String group ID tag for this recommendation.
+     *
+     * @return A String containing the group ID tag for this recommendation.
+     */
+    public String getGroup() {
+        return mGroup;
+    }
+
+    /**
+     * Sets the String sort key for this recommendation.
+     * <p>
+     * The sort key must be a String representation of a float number between 0.0 and 1.0, and is
+     * used to indicate the relative importance (and sort order) of a single recommendation within
+     * its specified group. The recommendations will be ordered in decreasing order of importance
+     * within a given group.
+     *
+     * @param sortKey A String containing the sort key for this recommendation.
+     */
+    public void setSortKey(String sortKey) {
+        mSortKey = sortKey;
+    }
+
+    /**
+     * Returns the String sort key for this recommendation.
+     *
+     * @return A String containing the sort key for this recommendation.
+     */
+    public String getSortKey() {
+        return mSortKey;
+    }
+
+    /**
+     * Sets the progress information for the content pointed to by this recommendation.
+     *
+     * @param max The maximum value for the progress of this content.
+     * @param progress The progress amount for this content. Must be in the range (0 - max).
+     */
+    public void setProgress(int max, int progress) {
+        if (max < 0 || progress < 0) {
+            throw new IllegalArgumentException();
+        }
+        mProgressMax = max;
+        mProgressAmount = progress;
+    }
+
+    /**
+     * Indicates if this recommendation contains valid progress information.
+     *
+     * @return true if the recommendation contains valid progress data, false otherwise.
+     */
+    public boolean hasProgressInfo() {
+        return mProgressMax != 0;
+    }
+
+    /**
+     * Returns the maximum value for the progress data of this recommendation.
+     *
+     * @return An integer representing the maximum progress value.
+     */
+    public int getProgressMax() {
+        return mProgressMax;
+    }
+
+    /**
+     * Returns the progress amount for this recommendation.
+     *
+     * @return An integer representing the recommendation progress amount.
+     */
+    public int getProgressValue() {
+        return mProgressAmount;
+    }
+
+    /**
+     * Sets the flag indicating if this recommendation should be dismissed automatically.
+     * <p>
+     * Auto-dismiss notifications are automatically removed from the Home Screen when the user
+     * clicks on them.
+     *
+     * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or
+     *            not.
+     */
+    public void setAutoDismiss(boolean autoDismiss) {
+        mAutoDismiss = autoDismiss;
+    }
+
+    /**
+     * Indicates whether this recommendation should be dismissed automatically.
+     * <p>
+     * Auto-dismiss notifications are automatically removed from the Home Screen when the user
+     * clicks on them.
+     *
+     * @return true if the recommendation is marked for auto dismissal, or false otherwise.
+     */
+    public boolean isAutoDismiss() {
+        return mAutoDismiss;
+    }
+
+    /**
+     * Returns the data for the Intent that will be issued when the user clicks on the
+     * recommendation.
+     *
+     * @return An IntentData object, containing the data for the Intent that gets issued when the
+     *         recommendation is clicked on.
+     */
+    public IntentData getContentIntent() {
+        return mContentIntentData;
+    }
+
+    /**
+     * Returns the data for the Intent that will be issued when the recommendation gets dismissed
+     * from the Home Screen, due to an user action.
+     *
+     * @return An IntentData object, containing the data for the Intent that gets issued when the
+     *         recommendation is dismissed from the Home Screen.
+     */
+    public IntentData getDismissIntent() {
+        return mDismissIntentData;
+    }
+
+    /**
+     * Returns an array containing the content types tags that describe the content. The first tag
+     * entry is considered the primary type for the content, and is used for content ranking
+     * purposes.
+     *
+     * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants) that
+     *         describe the recommended content.
+     */
+    public String[] getContentTypes() {
+        if (mContentTypes != null) {
+            return Arrays.copyOf(mContentTypes, mContentTypes.length);
+        }
+        return mContentTypes;
+    }
+
+    /**
+     * Returns the primary content type tag for the recommendation, or null if no content types have
+     * been specified.
+     *
+     * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating the
+     *         primary content type for the recommendation.
+     */
+    public String getPrimaryContentType() {
+        if (mContentTypes != null && mContentTypes.length > 0) {
+            return mContentTypes[0];
+        }
+        return null;
+    }
+
+    /**
+     * Returns an array containing the genres that describe the content. Genres are open ended
+     * String tags.
+     *
+     * @return An array of genre tags that describe the recommended content.
+     */
+    public String[] getGenres() {
+        if (mContentGenres != null) {
+            return Arrays.copyOf(mContentGenres, mContentGenres.length);
+        }
+        return mContentGenres;
+    }
+
+    /**
+     * Gets the pricing type for the content.
+     *
+     * @return A predefined tag indicating the pricing type for the content (see the <code>
+     *         CONTENT_PRICING_*</code> constants).
+     */
+    public String getPricingType() {
+        return mPriceType;
+    }
+
+    /**
+     * Gets the price value (when applicable) for the content. The value will be provided as a
+     * String containing the price in the appropriate currency for the current locale.
+     *
+     * @return A string containing a representation of the content price in the current locale and
+     *         currency.
+     */
+    public String getPricingValue() {
+        return mPriceValue;
+    }
+
+    /**
+     * Sets the availability status value for the content. This status indicates whether the content
+     * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe
+     * to, or download the content.
+     *
+     * @param status The status value for the content. (see the <code>CONTENT_STATUS_*</code> for
+     *            the valid status values).
+     */
+    public void setStatus(@ContentStatus int status) {
+        mStatus = status;
+    }
+
+    /**
+     * Returns availability status value for the content. This status indicates whether the content
+     * is ready to be consumed on the device, or if the user must first purchase, rent, subscribe
+     * to, or download the content.
+     *
+     * @return The status value for the content, or -1 is a valid status has not been specified (see
+     *         the <code>CONTENT_STATUS_*</code> constants for the valid status values).
+     */
+    public int getStatus() {
+        return mStatus;
+    }
+
+    /**
+     * Returns the maturity level rating for the content.
+     *
+     * @return returns a predefined tag indicating the maturity level rating for the content (see
+     *         the <code>CONTENT_MATURITY_*</code> constants).
+     */
+    public String getMaturityRating() {
+        return mMaturityRating;
+    }
+
+    /**
+     * Returns the running time for the content.
+     *
+     * @return The run length, in seconds, of the content associated with the notification.
+     */
+    public long getRunningTime() {
+        return mRunningTime;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof ContentRecommendation) {
+            return TextUtils.equals(mIdTag, ((ContentRecommendation) other).getIdTag());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mIdTag != null) {
+            return mIdTag.hashCode();
+        }
+        return Integer.MAX_VALUE;
+    }
+
+    /**
+     * Builder class for {@link ContentRecommendation} objects. Provides a convenient way to set the
+     * various fields of a {@link ContentRecommendation}.
+     * <p>
+     * Example:
+     *
+     * <pre class="prettyprint">
+     * ContentRecommendation rec = new ContentRecommendation.Builder()
+     *         .setIdInfo(id, &quot;MyTagId&quot;)
+     *         .setTitle(&quot;My Content Recommendation&quot;)
+     *         .setText(&quot;An example of content recommendation&quot;)
+     *         .setContentImage(myBitmap)
+     *         .setBadgeIcon(R.drawable.app_icon)
+     *         .setGroup(&quot;Trending&quot;)
+     *         .build();
+     * </pre>
+     */
+    public static final class Builder {
+        private String mBuilderIdTag;
+        private String mBuilderTitle;
+        private String mBuilderText;
+        private String mBuilderSourceName;
+        private Bitmap mBuilderContentImage;
+        private int mBuilderBadgeIconId;
+        private String mBuilderBackgroundImageUri;
+        private int mBuilderColor;
+        private String mBuilderGroup;
+        private String mBuilderSortKey;
+        private int mBuilderProgressAmount;
+        private int mBuilderProgressMax;
+        private boolean mBuilderAutoDismiss;
+        private IntentData mBuilderContentIntentData;
+        private IntentData mBuilderDismissIntentData;
+        private String[] mBuilderContentTypes;
+        private String[] mBuilderContentGenres;
+        private String mBuilderPriceType;
+        private String mBuilderPriceValue;
+        private int mBuilderStatus;
+        private String mBuilderMaturityRating;
+        private long mBuilderRunningTime;
+
+        /**
+         * Constructs a new Builder.
+         *
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the Id tag that uniquely identifies this recommendation object.
+         *
+         * @param idTag A String tag identifier for this recommendation.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setIdTag(String idTag) {
+            mBuilderIdTag = checkNotNull(idTag);
+            return this;
+        }
+
+        /**
+         * Sets the content title for the recommendation.
+         *
+         * @param title A String containing the recommendation content title.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setTitle(String title) {
+            mBuilderTitle = checkNotNull(title);
+            return this;
+        }
+
+        /**
+         * Sets the description text for the recommendation.
+         *
+         * @param description A String containing the recommendation description text.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setText(@Nullable String description) {
+            mBuilderText = description;
+            return this;
+        }
+
+        /**
+         * Sets the source application name for the recommendation.
+         * <P>
+         * If the source name is never set, or set to null, the application name retrieved from its
+         * package will be used by default.
+         *
+         * @param source A String containing the recommendation source name.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setSourceName(@Nullable String source) {
+            mBuilderSourceName = source;
+            return this;
+        }
+
+        /**
+         * Sets the recommendation image.
+         *
+         * @param image A Bitmap containing the recommendation image.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setContentImage(Bitmap image) {
+            mBuilderContentImage = checkNotNull(image);
+            return this;
+        }
+
+        /**
+         * Sets the resource ID for the recommendation badging icon.
+         * <p>
+         * The resource id represents the icon resource in the source application package. If not
+         * set, or an invalid resource ID is specified, the application icon retrieved from its
+         * package will be used by default.
+         *
+         * @param iconResourceId An integer id for the badge icon resource.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setBadgeIcon(int iconResourceId) {
+            mBuilderBadgeIconId = iconResourceId;
+            return this;
+        }
+
+        /**
+         * Sets the Content URI that will be used to retrieve the background image for the
+         * recommendation.
+         *
+         * @param imageUri A Content URI pointing to the recommendation background image.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setBackgroundImageUri(@Nullable String imageUri) {
+            mBuilderBackgroundImageUri = imageUri;
+            return this;
+        }
+
+        /**
+         * Sets the accent color value to be used in the UI when displaying this content
+         * recommendation to the user.
+         *
+         * @param color An integer value representing the accent color for this recommendation.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setColor(int color) {
+            mBuilderColor = color;
+            return this;
+        }
+
+        /**
+         * Sets the String group ID tag for the recommendation.
+         * <p>
+         * Recommendations in the same group are ranked by the Home Screen together, and the sort
+         * order within a group is respected. This can be useful if the application has different
+         * sources for recommendations, like "trending", "subscriptions", and "new music" categories
+         * for YouTube, where the user can be more interested in recommendations from one group than
+         * another.
+         *
+         * @param groupTag A String containing the group ID tag for this recommendation.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setGroup(@Nullable String groupTag) {
+            mBuilderGroup = groupTag;
+            return this;
+        }
+
+        /**
+         * Sets the String sort key for the recommendation.
+         * <p>
+         * The sort key must be a String representation of a float number between 0.0 and 1.0, and
+         * is used to indicate the relative importance (and sort order) of a single recommendation
+         * within its specified group. The recommendations will be ordered in decreasing order of
+         * importance within a given group.
+         *
+         * @param sortKey A String containing the sort key for this recommendation.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setSortKey(@Nullable String sortKey) {
+            mBuilderSortKey = sortKey;
+            return this;
+        }
+
+        /**
+         * Sets the progress information for the content pointed to by the recommendation.
+         *
+         * @param max The maximum value for the progress of this content.
+         * @param progress The progress amount for this content. Must be in the range (0 - max).
+         * @return The Builder object, for chaining.
+         */
+        public Builder setProgress(int max, int progress) {
+            if (max < 0 || progress < 0) {
+                throw new IllegalArgumentException();
+            }
+            mBuilderProgressMax = max;
+            mBuilderProgressAmount = progress;
+            return this;
+        }
+
+        /**
+         * Sets the flag indicating if the recommendation should be dismissed automatically.
+         * <p>
+         * Auto-dismiss notifications are automatically removed from the Home Screen when the user
+         * clicks on them.
+         *
+         * @param autoDismiss A boolean indicating if the recommendation should be auto dismissed or
+         *            not.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setAutoDismiss(boolean autoDismiss) {
+            mBuilderAutoDismiss = autoDismiss;
+            return this;
+        }
+
+        /**
+         * Sets the data for the Intent that will be issued when the user clicks on the
+         * recommendation.
+         * <p>
+         * The Intent data fields provided correspond to the fields passed into the
+         * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual
+         * PengindIntent object will only be created at the time a recommendation is posted to the
+         * Home Screen.
+         *
+         * @param intentType The type of {@link PendingIntent} to be created when posting this
+         *            recommendation.
+         * @param intent The Intent which to be issued when the recommendation is clicked on.
+         * @param requestCode The private request code to be used when creating the
+         *            {@link PendingIntent}
+         * @param options Only used for the Activity Intent type. Additional options for how the
+         *            Activity should be started. May be null if there are no options.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setContentIntentData(@IntentType int intentType, Intent intent,
+                int requestCode, @Nullable Bundle options) {
+            if (intentType != INTENT_TYPE_ACTIVITY &&
+                    intentType != INTENT_TYPE_BROADCAST &&
+                    intentType != INTENT_TYPE_SERVICE) {
+                throw new IllegalArgumentException("Invalid Intent type specified.");
+            }
+
+            mBuilderContentIntentData = new IntentData();
+            mBuilderContentIntentData.mType = intentType;
+            mBuilderContentIntentData.mIntent = checkNotNull(intent);
+            mBuilderContentIntentData.mRequestCode = requestCode;
+            mBuilderContentIntentData.mOptions = options;
+
+            return this;
+        }
+
+        /**
+         * Sets the data for the Intent that will be issued when the recommendation gets dismissed
+         * from the Home Screen, due to an user action.
+         * <p>
+         * The Intent data fields provided correspond to the fields passed into the
+         * {@link PendingIntent} factory methods, when creating a new PendingIntent. The actual
+         * PengindIntent object will only be created at the time a recommendation is posted to the
+         * Home Screen.
+         *
+         * @param intentType The type of {@link PendingIntent} to be created when posting this
+         *            recommendation.
+         * @param intent The Intent which gets issued when the recommendation is dismissed from the
+         *            Home Screen.
+         * @param requestCode The private request code to be used when creating the
+         *            {@link PendingIntent}
+         * @param options Only used for the Activity Intent type. Additional options for how the
+         *            Activity should be started. May be null if there are no options.
+         * @return The Builder object, for chaining.
+         */
+        public Builder setDismissIntentData(@IntentType int intentType, @Nullable Intent intent,
+                int requestCode, @Nullable Bundle options) {
+            if (intent != null) {
+                if (intentType != INTENT_TYPE_ACTIVITY &&
+                        intentType != INTENT_TYPE_BROADCAST &&
+                        intentType != INTENT_TYPE_SERVICE) {
+                    throw new IllegalArgumentException("Invalid Intent type specified.");
+                }
+
+                mBuilderDismissIntentData = new IntentData();
+                mBuilderDismissIntentData.mType = intentType;
+                mBuilderDismissIntentData.mIntent = intent;
+                mBuilderDismissIntentData.mRequestCode = requestCode;
+                mBuilderDismissIntentData.mOptions = options;
+            } else {
+                mBuilderDismissIntentData = null;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the content types associated with the content recommendation. The first tag entry
+         * will be considered the primary type for the content and will be used for ranking
+         * purposes. Other secondary type tags may be provided, if applicable, and may be used for
+         * filtering purposes.
+         *
+         * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
+         *            constants) that describe the recommended content.
+         */
+        public Builder setContentTypes(String[] types) {
+            mBuilderContentTypes = checkNotNull(types);
+            return this;
+        }
+
+        /**
+         * Sets the content genres for the recommendation. These genres may be used for content
+         * ranking. Genres are open ended String tags.
+         * <p>
+         * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
+         *
+         * @param genres Array of genre string tags that describe the recommended content.
+         */
+        public Builder setGenres(String[] genres) {
+            mBuilderContentGenres = genres;
+            return this;
+        }
+
+        /**
+         * Sets the pricing and availability information for the recommendation. The provided
+         * information will indicate the access model for the content (free, rental, purchase or
+         * subscription) and the price value (if not free).
+         *
+         * @param priceType Pricing type for this content. Must be one of the predefined pricing
+         *            type tags (see the <code>CONTENT_PRICING_*</code> constants).
+         * @param priceValue A string containing a representation of the content price in the
+         *            current locale and currency.
+         */
+        public Builder setPricingInformation(@ContentPricing String priceType,
+                @Nullable String priceValue) {
+            mBuilderPriceType = checkNotNull(priceType);
+            mBuilderPriceValue = priceValue;
+            return this;
+        }
+
+        /**
+         * Sets the availability status for the content. This status indicates whether the referred
+         * content is ready to be consumed on the device, or if the user must first purchase, rent,
+         * subscribe to, or download the content.
+         *
+         * @param contentStatus The status value for this content. Must be one of the predefined
+         *            content status values (see the <code>CONTENT_STATUS_*</code> constants).
+         */
+        public Builder setStatus(@ContentStatus int contentStatus) {
+            mBuilderStatus = contentStatus;
+            return this;
+        }
+
+        /**
+         * Sets the maturity level rating for the content.
+         *
+         * @param maturityRating A tag indicating the maturity level rating for the content. This
+         *            tag must be one of the predefined maturity rating tags (see the <code>
+         *            CONTENT_MATURITY_*</code> constants).
+         */
+        public Builder setMaturityRating(@ContentMaturity String maturityRating) {
+            mBuilderMaturityRating = checkNotNull(maturityRating);
+            return this;
+        }
+
+        /**
+         * Sets the running time (when applicable) for the content.
+         *
+         * @param length The running time, in seconds, of the content.
+         */
+        public Builder setRunningTime(long length) {
+            if (length < 0) {
+                throw new IllegalArgumentException();
+            }
+            mBuilderRunningTime = length;
+            return this;
+        }
+
+        /**
+         * Combine all of the options that have been set and return a new
+         * {@link ContentRecommendation} object.
+         */
+        public ContentRecommendation build() {
+            return new ContentRecommendation(this);
+        }
+    }
+
+    /**
+     * Returns a {@link android.app.Notification Notification} object which contains the content
+     * recommendation data encapsulated by this object, which can be used for posting the
+     * recommendation via the {@link android.app.NotificationManager NotificationManager}.
+     *
+     * @param context A {@link Context} that will be used to construct the
+     *            {@link android.app.Notification Notification} object which will carry the
+     *            recommendation data.
+     * @return A {@link android.app.Notification Notification} containing the stored recommendation
+     *         data.
+     */
+    public Notification getNotificationObject(Context context) {
+        Notification.Builder builder = new Notification.Builder(context);
+        RecommendationExtender recExtender = new RecommendationExtender();
+
+        // Encode all the content recommendation data in a Notification object
+
+        builder.setCategory(Notification.CATEGORY_RECOMMENDATION);
+        builder.setContentTitle(mTitle);
+        builder.setContentText(mText);
+        builder.setContentInfo(mSourceName);
+        builder.setLargeIcon(mContentImage);
+        builder.setSmallIcon(mBadgeIconId);
+        if (mBackgroundImageUri != null) {
+            builder.getExtras().putString(Notification.EXTRA_BACKGROUND_IMAGE_URI,
+                    mBackgroundImageUri);
+        }
+        builder.setColor(mColor);
+        builder.setGroup(mGroup);
+        builder.setSortKey(mSortKey);
+        builder.setProgress(mProgressMax, mProgressAmount, false);
+        builder.setAutoCancel(mAutoDismiss);
+
+        if (mContentIntentData != null) {
+            PendingIntent contentPending;
+            if (mContentIntentData.mType == INTENT_TYPE_ACTIVITY) {
+                contentPending = PendingIntent.getActivity(context, mContentIntentData.mRequestCode,
+                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+                        mContentIntentData.mOptions);
+            } else if (mContentIntentData.mType == INTENT_TYPE_SERVICE) {
+                contentPending = PendingIntent.getService(context, mContentIntentData.mRequestCode,
+                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            } else { // Default:INTENT_TYPE_BROADCAST{
+                contentPending = PendingIntent.getBroadcast(context,
+                        mContentIntentData.mRequestCode,
+                        mContentIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            }
+            builder.setContentIntent(contentPending);
+        }
+
+        if (mDismissIntentData != null) {
+            PendingIntent dismissPending;
+            if (mDismissIntentData.mType == INTENT_TYPE_ACTIVITY) {
+                dismissPending = PendingIntent.getActivity(context, mDismissIntentData.mRequestCode,
+                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+                        mDismissIntentData.mOptions);
+            } else if (mDismissIntentData.mType == INTENT_TYPE_SERVICE) {
+                dismissPending = PendingIntent.getService(context, mDismissIntentData.mRequestCode,
+                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            } else { // Default:INTENT_TYPE_BROADCAST{
+                dismissPending = PendingIntent.getBroadcast(context,
+                        mDismissIntentData.mRequestCode,
+                        mDismissIntentData.mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+            }
+            builder.setDeleteIntent(dismissPending);
+        }
+
+        recExtender.setContentTypes(mContentTypes);
+        recExtender.setGenres(mContentGenres);
+        recExtender.setPricingInformation(mPriceType, mPriceValue);
+        recExtender.setStatus(mStatus);
+        recExtender.setMaturityRating(mMaturityRating);
+        recExtender.setRunningTime(mRunningTime);
+
+        builder.extend(recExtender);
+        Notification notif = builder.build();
+        return notif;
+    }
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling method is not null.
+     *
+     * @param reference an object reference
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    private static <T> T checkNotNull(final T reference) {
+        if (reference == null) {
+            throw new NullPointerException();
+        }
+        return reference;
+    }
+
+}
\ No newline at end of file
diff --git a/recommendation/src/android/support/app/recommendation/RecommendationExtender.java b/recommendation/src/android/support/app/recommendation/RecommendationExtender.java
new file mode 100644
index 0000000..5762a28
--- /dev/null
+++ b/recommendation/src/android/support/app/recommendation/RecommendationExtender.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.app.recommendation;
+
+import android.app.Notification;
+import android.os.Bundle;
+
+/**
+ * <p>
+ * Helper class to add content info extensions to notifications. To create a notification with
+ * content info extensions:
+ * <ol>
+ * <li>Create an {@link Notification.Builder}, setting any desired properties.
+ * <li>Create a {@link RecommendationExtender}.
+ * <li>Set content info specific properties using the {@code add} and {@code set} methods of
+ * {@link RecommendationExtender}.
+ * <li>Call {@link android.app.Notification.Builder#extend(Notification.Extender) 
+ * Notification.Builder.extend(Notification.Extender)} to apply the extensions to a notification.
+ * </ol>
+ *
+ * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new RecommendationExtender() * .set*(...)) * .build(); * </pre>
+ * <p>
+ * Content info extensions can be accessed on an existing notification by using the
+ * {@code RecommendationExtender(Notification)} constructor, and then using the {@code get} methods
+ * to access values.
+ */
+public final class RecommendationExtender implements Notification.Extender {
+    private static final String TAG = "RecommendationExtender";
+
+    // Key for the Content info extensions bundle in the main Notification extras bundle
+    private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
+
+    // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
+
+    private static final String KEY_CONTENT_TYPE = "android.contentType";
+
+    private static final String KEY_CONTENT_GENRES = "android.contentGenre";
+
+    private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
+
+    private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
+
+    private static final String KEY_CONTENT_STATUS = "android.contentStatus";
+
+    private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
+
+    private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
+
+    private String[] mTypes;
+    private String[] mGenres;
+    private String mPricingType;
+    private String mPricingValue;
+    private int mContentStatus = -1;
+    private String mMaturityRating;
+    private long mRunLength = -1;
+
+    /**
+     * Create a {@link RecommendationExtender} with default options.
+     */
+    public RecommendationExtender() {
+    }
+
+    /**
+     * Create a {@link RecommendationExtender} from the RecommendationExtender options of an
+     * existing Notification.
+     *
+     * @param notif The notification from which to copy options.
+     */
+    public RecommendationExtender(Notification notif) {
+        Bundle contentBundle = notif.extras == null ?
+                null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
+        if (contentBundle != null) {
+            mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
+            mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
+            mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
+            mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
+            mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
+            mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
+            mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
+        }
+    }
+
+    /**
+     * Apply content extensions to a notification that is being built. This is typically called by
+     * the {@link android.app.Notification.Builder#extend(Notification.Extender) 
+     * Notification.Builder.extend(Notification.Extender)} method of {@link Notification.Builder}.
+     */
+    @Override
+    public Notification.Builder extend(Notification.Builder builder) {
+        Bundle contentBundle = new Bundle();
+
+        if (mTypes != null) {
+            contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
+        }
+        if (mGenres != null) {
+            contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
+        }
+        if (mPricingType != null) {
+            contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
+        }
+        if (mPricingValue != null) {
+            contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
+        }
+        if (mContentStatus != -1) {
+            contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
+        }
+        if (mMaturityRating != null) {
+            contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
+        }
+        if (mRunLength > 0) {
+            contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
+        }
+
+        builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
+        return builder;
+    }
+
+    /**
+     * Sets the content types associated with the notification content. The first tag entry will be
+     * considered the primary type for the content and will be used for ranking purposes. Other
+     * secondary type tags may be provided, if applicable, and may be used for filtering purposes.
+     *
+     * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
+     *            that describe the content referred to by a notification.
+     */
+    public RecommendationExtender setContentTypes(String[] types) {
+        mTypes = types;
+        return this;
+    }
+
+    /**
+     * Returns an array containing the content types that describe the content associated with the
+     * notification. The first tag entry is considered the primary type for the content, and is used
+     * for content ranking purposes.
+     *
+     * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants) that
+     *         describe the content associated with the notification.
+     * @see RecommendationExtender#setContentTypes
+     */
+    public String[] getContentTypes() {
+        return mTypes;
+    }
+
+    /**
+     * Returns the primary content type tag for the content associated with the notification.
+     *
+     * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating the
+     *         primary type for the content associated with the notification.
+     * @see RecommendationExtender#setContentTypes
+     */
+    public String getPrimaryContentType() {
+        if (mTypes == null || mTypes.length == 0) {
+            return null;
+        }
+        return mTypes[0];
+    }
+
+    /**
+     * Sets the content genres associated with the notification content. These genres may be used
+     * for content ranking. Genres are open ended String tags.
+     * <p>
+     * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
+     *
+     * @param genres Array of genre string tags that describe the content referred to by a
+     *            notification.
+     */
+    public RecommendationExtender setGenres(String[] genres) {
+        mGenres = genres;
+        return this;
+    }
+
+    /**
+     * Returns an array containing the content genres that describe the content associated with the
+     * notification.
+     *
+     * @return An array of genre tags that describe the content associated with the notification.
+     * @see RecommendationExtender#setGenres
+     */
+    public String[] getGenres() {
+        return mGenres;
+    }
+
+    /**
+     * Sets the pricing and availability information for the content associated with the
+     * notification. The provided information will indicate the access model for the content (free,
+     * rental, purchase or subscription) and the price value (if not free).
+     *
+     * @param priceType Pricing type for this content. Must be one of the predefined pricing type
+     *            tags (see the <code>CONTENT_PRICING_*</code> constants).
+     * @param priceValue A string containing a representation of the content price in the current
+     *            locale and currency.
+     * @return This object for method chaining.
+     */
+    public RecommendationExtender setPricingInformation(
+            @ContentRecommendation.ContentPricing String priceType, String priceValue) {
+        mPricingType = priceType;
+        mPricingValue = priceValue;
+        return this;
+    }
+
+    /**
+     * Gets the pricing type for the content associated with the notification.
+     *
+     * @return A predefined tag indicating the pricing type for the content (see the <code> CONTENT_PRICING_*</code>
+     *         constants).
+     * @see RecommendationExtender#setPricingInformation
+     */
+    public String getPricingType() {
+        return mPricingType;
+    }
+
+    /**
+     * Gets the price value (when applicable) for the content associated with a notification. The
+     * value will be provided as a String containing the price in the appropriate currency for the
+     * current locale.
+     *
+     * @return A string containing a representation of the content price in the current locale and
+     *         currency.
+     * @see RecommendationExtender#setPricingInformation
+     */
+    public String getPricingValue() {
+        if (mPricingType == null) {
+            return null;
+        }
+        return mPricingValue;
+    }
+
+    /**
+     * Sets the availability status for the content associated with the notification. This status
+     * indicates whether the referred content is ready to be consumed on the device, or if the user
+     * must first purchase, rent, subscribe to, or download the content.
+     *
+     * @param contentStatus The status value for this content. Must be one of the predefined content
+     *            status values (see the <code>CONTENT_STATUS_*</code> constants).
+     */
+    public RecommendationExtender setStatus(
+            @ContentRecommendation.ContentStatus int contentStatus) {
+        mContentStatus = contentStatus;
+        return this;
+    }
+
+    /**
+     * Returns status value for the content associated with the notification. This status indicates
+     * whether the referred content is ready to be consumed on the device, or if the user must first
+     * purchase, rent, subscribe to, or download the content.
+     *
+     * @return The status value for this content, or -1 is a valid status has not been specified
+     *         (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
+     * @see RecommendationExtender#setStatus
+     */
+    public int getStatus() {
+        return mContentStatus;
+    }
+
+    /**
+     * Sets the maturity level rating for the content associated with the notification.
+     *
+     * @param maturityRating A tag indicating the maturity level rating for the content. This tag
+     *            must be one of the predefined maturity rating tags (see the <code> CONTENT_MATURITY_*</code>
+     *            constants).
+     */
+    public RecommendationExtender setMaturityRating(
+            @ContentRecommendation.ContentMaturity String maturityRating) {
+        mMaturityRating = maturityRating;
+        return this;
+    }
+
+    /**
+     * Returns the maturity level rating for the content associated with the notification.
+     *
+     * @return returns a predefined tag indicating the maturity level rating for the content (see
+     *         the <code>CONTENT_MATURITY_*</code> constants).
+     * @see RecommendationExtender#setMaturityRating
+     */
+    public String getMaturityRating() {
+        return mMaturityRating;
+    }
+
+    /**
+     * Sets the running time (when applicable) for the content associated with the notification.
+     *
+     * @param length The runing time, in seconds, of the content associated with the notification.
+     */
+    public RecommendationExtender setRunningTime(long length) {
+        if (length < 0) {
+            throw new IllegalArgumentException("Invalid value for Running Time");
+        }
+        mRunLength = length;
+        return this;
+    }
+
+    /**
+     * Returns the running time for the content associated with the notification.
+     *
+     * @return The running time, in seconds, of the content associated with the notification.
+     * @see RecommendationExtender#setRunningTime
+     */
+    public long getRunningTime() {
+        return mRunLength;
+    }
+}