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 17 package com.example.android.tvleanback.recommendation; 18 19 import android.app.Notification; 20 import android.app.PendingIntent; 21 import android.content.ContentProvider; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.database.Cursor; 25 import android.graphics.Bitmap; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.ParcelFileDescriptor; 29 import android.support.v4.app.NotificationCompat; 30 import android.util.Log; 31 32 import com.example.android.tvleanback.R; 33 34 import java.io.File; 35 import java.io.FileNotFoundException; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 39 /* 40 * This class builds recommendations as notifications with videos as inputs. 41 */ 42 public class RecommendationBuilder { 43 private static final String TAG = "RecommendationBuilder"; 44 private static final String 45 BACKGROUND_URI_PREFIX = "content://com.example.android.tvleanback.recommendation/"; 46 47 private Context mContext; 48 49 private int mId; 50 private int mPriority; 51 private int mSmallIcon; 52 private String mTitle; 53 private String mDescription; 54 private Bitmap mBitmap; 55 private String mBackgroundUri; 56 private String mGroupKey; 57 private String mSort; 58 private PendingIntent mIntent; 59 60 public RecommendationBuilder() { 61 } 62 63 public RecommendationBuilder setContext(Context context) { 64 mContext = context; 65 return this; 66 } 67 68 public RecommendationBuilder setId(int id) { 69 mId = id; 70 return this; 71 } 72 73 public RecommendationBuilder setPriority(int priority) { 74 mPriority = priority; 75 return this; 76 } 77 78 public RecommendationBuilder setTitle(String title) { 79 mTitle = title; 80 return this; 81 } 82 83 public RecommendationBuilder setDescription(String description) { 84 mDescription = description; 85 return this; 86 } 87 88 public RecommendationBuilder setBitmap(Bitmap bitmap) { 89 mBitmap = bitmap; 90 return this; 91 } 92 93 public RecommendationBuilder setBackground(String uri) { 94 mBackgroundUri = uri; 95 return this; 96 } 97 98 public RecommendationBuilder setIntent(PendingIntent intent) { 99 mIntent = intent; 100 return this; 101 } 102 103 public RecommendationBuilder setSmallIcon(int resourceId) { 104 mSmallIcon = resourceId; 105 return this; 106 } 107 108 public Notification build() { 109 110 Bundle extras = new Bundle(); 111 File bitmapFile = getNotificationBackground(mContext, mId); 112 113 if (mBackgroundUri != null) { 114 extras.putString(Notification.EXTRA_BACKGROUND_IMAGE_URI, 115 Uri.parse(BACKGROUND_URI_PREFIX + Integer.toString(mId)).toString()); 116 } 117 118 // the following simulates group assignment into "Top", "Middle", "Bottom" 119 // by checking mId and similarly sort order 120 mGroupKey = (mId < 3) ? "Top" : (mId < 5) ? "Middle" : "Bottom"; 121 mSort = (mId < 3) ? "1.0" : (mId < 5) ? "0.7" : "0.3"; 122 123 // save bitmap into files for content provider to serve later 124 try { 125 bitmapFile.createNewFile(); 126 FileOutputStream fOut = new FileOutputStream(bitmapFile); 127 mBitmap.compress(Bitmap.CompressFormat.PNG, 85, fOut); 128 fOut.flush(); 129 fOut.close(); 130 } catch (IOException ioe) { 131 Log.d(TAG, "Exception caught writing bitmap to file!", ioe); 132 } 133 134 Notification notification = new NotificationCompat.BigPictureStyle( 135 new NotificationCompat.Builder(mContext) 136 .setAutoCancel(true) 137 .setContentTitle(mTitle) 138 .setContentText(mDescription) 139 .setPriority(mPriority) 140 .setLocalOnly(true) 141 .setOngoing(true) 142 /* 143 groupKey (optional): Can be used to group together recommendations, so 144 they are ranked by the launcher as a separate group. Can be useful if the 145 application has different sources for recommendations, like "trending", 146 "subscriptions", and "new music" categories for YouTube, where the user can 147 be more interested in recommendations from one group than another. 148 */ 149 .setGroup(mGroupKey) 150 /* 151 sortKey (optional): A float number between 0.0 and 1.0, used to indicate 152 the relative importance (and sort order) of a single recommendation within 153 its specified group. The recommendations will be ordered in decreasing 154 order of importance within a given group. 155 */ 156 .setSortKey(mSort) 157 .setColor(mContext.getResources().getColor(R.color.fastlane_background)) 158 .setCategory(Notification.CATEGORY_RECOMMENDATION) 159 .setLargeIcon(mBitmap) 160 .setSmallIcon(mSmallIcon) 161 .setContentIntent(mIntent) 162 .setExtras(extras)) 163 .build(); 164 165 Log.d(TAG, "Building notification - " + this.toString()); 166 167 return notification; 168 } 169 170 @Override 171 public String toString() { 172 return "RecommendationBuilder{" + 173 ", mId=" + mId + 174 ", mPriority=" + mPriority + 175 ", mSmallIcon=" + mSmallIcon + 176 ", mTitle='" + mTitle + '\'' + 177 ", mDescription='" + mDescription + '\'' + 178 ", mBitmap='" + mBitmap + '\'' + 179 ", mBackgroundUri='" + mBackgroundUri + '\'' + 180 ", mIntent=" + mIntent + 181 '}'; 182 } 183 184 public static class RecommendationBackgroundContentProvider extends ContentProvider { 185 186 @Override 187 public boolean onCreate() { 188 return true; 189 } 190 191 @Override 192 public int delete(Uri uri, String selection, String[] selectionArgs) { 193 return 0; 194 } 195 196 @Override 197 public String getType(Uri uri) { 198 return null; 199 } 200 201 @Override 202 public Uri insert(Uri uri, ContentValues values) { 203 return null; 204 } 205 206 @Override 207 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 208 String sortOrder) { 209 return null; 210 } 211 212 @Override 213 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 214 return 0; 215 } 216 217 @Override 218 /* 219 * content provider serving files that are saved locally when recommendations are built 220 */ 221 public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { 222 int backgroundId = Integer.parseInt(uri.getLastPathSegment()); 223 File bitmapFile = getNotificationBackground(getContext(), backgroundId); 224 return ParcelFileDescriptor.open(bitmapFile, ParcelFileDescriptor.MODE_READ_ONLY); 225 } 226 } 227 228 private static File getNotificationBackground(Context context, int notificationId) { 229 return new File(context.getCacheDir(), "tmp" + Integer.toString(notificationId) + ".png"); 230 } 231 232 } 233