1 /* 2 * Copyright (c) 2016, 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 package com.android.car.stream; 17 18 import android.app.PendingIntent; 19 import android.graphics.Bitmap; 20 import android.os.Bundle; 21 import android.util.Log; 22 23 import java.text.SimpleDateFormat; 24 import java.util.Date; 25 26 /** 27 * A parcelable that is used for communication between various stream components. 28 * The card is uniquely identified by the type and id. 29 */ 30 public class StreamCard extends AbstractBundleable { 31 private final static String TAG = "StreamCard"; 32 33 private final static String TYPE_KEY = "type"; 34 private final static String ID_KEY = "id"; 35 private final static String PRIORITY_KEY = "priority"; 36 private final static String TIMESTAMP_KEY = "timestamp"; 37 private final static String CONTENT_PENDING_INTENT_KEY = "content_pending_intent"; 38 private final static String PRIMARY_ICON_KEY = "primary_icon"; 39 private final static String SECONDARY_ICON_KEY = "secondary_icon"; 40 private final static String PRIMARY_TEXT_KEY = "primary_text"; 41 private final static String SECONDARY_TEXT_KEY = "secondary_text"; 42 private final static String DESCRIPTION_KEY = "description"; 43 private final static String CARD_EXTENSION_BUNDLE_KEY = "extension_bundle"; 44 private final static String CARD_EXTENSION_CLASS_KEY = "extension_key"; 45 46 /** The type of the card */ 47 private int mType; 48 /** The unique id of this card */ 49 private long mId; 50 /** Priority or rank compared to other cards */ 51 private int mPriority; 52 /** Time at which the StreamCard was generated */ 53 private long mTimestamp; 54 /** Primary action to be taken when the entire card is clicked */ 55 private PendingIntent mContentPendingIntent; 56 /** Primary icon of the stream card */ 57 private Bitmap mPrimaryIcon; 58 /** Secondary icon of the stream card */ 59 private Bitmap mSecondaryIcon; 60 /** Primary text of the stream card */ 61 private CharSequence mPrimaryText; 62 /** Secondary text of the stream card */ 63 private CharSequence mSecondaryText; 64 /** Additional meta information for the card */ 65 private StreamCardExtension mCardExtension; 66 67 /** String description of the {@link StreamCard} */ 68 private String mDescription; 69 70 public static final Creator<StreamCard> CREATOR = new BundleableCreator<>(StreamCard.class); 71 StreamCard()72 public StreamCard() { 73 } 74 StreamCard(int type, long id, int priority, long timestamp, PendingIntent contentPendingIntent, Bitmap primaryIcon, Bitmap secondaryIcon, CharSequence primaryText, CharSequence secondaryText, String description, StreamCardExtension cardExtension)75 private StreamCard(int type, long id, int priority, long timestamp, 76 PendingIntent contentPendingIntent, Bitmap primaryIcon, Bitmap secondaryIcon, 77 CharSequence primaryText, CharSequence secondaryText, String description, 78 StreamCardExtension cardExtension) { 79 mType = type; 80 mId = id; 81 mPriority = priority; 82 mTimestamp = timestamp; 83 mContentPendingIntent = contentPendingIntent; 84 mPrimaryIcon = primaryIcon; 85 mSecondaryIcon = secondaryIcon; 86 mPrimaryText = primaryText; 87 mSecondaryText = secondaryText; 88 mDescription = description; 89 mCardExtension = cardExtension; 90 } 91 92 @Override writeToBundle(Bundle bundle)93 protected void writeToBundle(Bundle bundle) { 94 bundle.putInt(TYPE_KEY, mType); 95 bundle.putLong(ID_KEY, mId); 96 bundle.putInt(PRIORITY_KEY, mPriority); 97 bundle.putLong(TIMESTAMP_KEY, mTimestamp); 98 bundle.putParcelable(CONTENT_PENDING_INTENT_KEY, mContentPendingIntent); 99 // Note there is a 1MB limit on the transaction size of the binder, if the bitmaps 100 // are too large, pass through a resource or URI 101 bundle.putParcelable(PRIMARY_ICON_KEY, mPrimaryIcon); 102 bundle.putParcelable(SECONDARY_ICON_KEY, mSecondaryIcon); 103 bundle.putString(DESCRIPTION_KEY, mDescription); 104 bundle.putCharSequence(PRIMARY_TEXT_KEY, mPrimaryText); 105 bundle.putCharSequence(SECONDARY_TEXT_KEY, mSecondaryText); 106 107 if (mCardExtension != null) { 108 Bundle extension = new Bundle(); 109 mCardExtension.writeToBundle(extension); 110 bundle.putString(CARD_EXTENSION_CLASS_KEY, mCardExtension.getClass().getName()); 111 bundle.putBundle(CARD_EXTENSION_BUNDLE_KEY, extension); 112 } 113 } 114 115 @Override readFromBundle(Bundle bundle)116 protected void readFromBundle(Bundle bundle) { 117 mType = bundle.getInt(TYPE_KEY); 118 mId = bundle.getLong(ID_KEY); 119 mPriority = bundle.getInt(PRIORITY_KEY); 120 mTimestamp = bundle.getLong(TIMESTAMP_KEY, System.currentTimeMillis()); 121 mContentPendingIntent = bundle.getParcelable(CONTENT_PENDING_INTENT_KEY); 122 mPrimaryIcon = bundle.getParcelable(PRIMARY_ICON_KEY); 123 mSecondaryIcon = bundle.getParcelable(SECONDARY_ICON_KEY); 124 mDescription = bundle.getString(DESCRIPTION_KEY); 125 mPrimaryText = bundle.getCharSequence(PRIMARY_TEXT_KEY); 126 mSecondaryText = bundle.getCharSequence(SECONDARY_TEXT_KEY); 127 mCardExtension = extractExtension(bundle); 128 } 129 extractExtension(Bundle bundle)130 private StreamCardExtension extractExtension(Bundle bundle) { 131 String className = bundle.getString(CARD_EXTENSION_CLASS_KEY); 132 Bundle extensionBundle = bundle.getBundle(CARD_EXTENSION_BUNDLE_KEY); 133 if (className == null || extensionBundle == null) { 134 return null; 135 } 136 137 StreamCardExtension extension = null; 138 try { 139 Class clazz = getClass().getClassLoader().loadClass(className); 140 extension = (StreamCardExtension) clazz.newInstance(); 141 extension.readFromBundle(extensionBundle); 142 } catch (Exception e) { 143 Log.e(TAG, "Failed to instantiate " + className, e); 144 } 145 return extension; 146 } 147 148 @Override toString()149 public String toString() { 150 String extensionName = null; 151 if (mCardExtension != null) { 152 extensionName = mCardExtension.getClass().getName(); 153 } 154 155 StringBuilder sb = new StringBuilder(); 156 sb.append("\nType: " + mType); 157 sb.append("\nId: " + mId); 158 sb.append("\nContent Pending Intent: " + mContentPendingIntent); 159 sb.append("\nDescription: " + mDescription); 160 sb.append("\nCard Extension: " + extensionName); 161 sb.append("\nTimestamp: " 162 + SimpleDateFormat.getDateInstance().format(new Date(mTimestamp))); 163 164 return sb.toString(); 165 } 166 setPriority(int priority)167 public void setPriority(int priority) { 168 mPriority = priority; 169 } 170 getPriority()171 public int getPriority() { 172 return mPriority; 173 } 174 getDescription()175 public String getDescription() { 176 return mDescription; 177 } 178 getId()179 public long getId() { 180 return mId; 181 } 182 getType()183 public int getType() { 184 return mType; 185 } 186 getTimestamp()187 public long getTimestamp() { 188 return mTimestamp; 189 } 190 getPrimaryText()191 public CharSequence getPrimaryText() { 192 return mPrimaryText; 193 } 194 getSecondaryText()195 public CharSequence getSecondaryText() { 196 return mSecondaryText; 197 } 198 getPrimaryIcon()199 public Bitmap getPrimaryIcon() { 200 return mPrimaryIcon; 201 } 202 getSecondaryIcon()203 public Bitmap getSecondaryIcon() { 204 return mSecondaryIcon; 205 } 206 getContentPendingIntent()207 public PendingIntent getContentPendingIntent() { 208 return mContentPendingIntent; 209 } 210 getCardExtension()211 public AbstractBundleable getCardExtension() { 212 return mCardExtension; 213 } 214 215 /** 216 * Builder to generate a {@link StreamCard} 217 */ 218 public static class Builder { 219 private int type; 220 private long id; 221 private int priority; 222 private long timestamp; 223 private Bitmap primaryIcon; 224 private Bitmap secondaryIcon; 225 private CharSequence primaryText; 226 private CharSequence secondaryText; 227 private String description; 228 private PendingIntent contentPendingIntent; 229 private StreamCardExtension cardExtension; 230 Builder(int type, long id, long timestamp)231 public Builder(int type, long id, long timestamp) { 232 this.type = type; 233 this.id = id; 234 this.timestamp = timestamp; 235 } 236 build()237 public StreamCard build() { 238 return new StreamCard(type, id, priority, timestamp, contentPendingIntent, primaryIcon, 239 secondaryIcon, primaryText, secondaryText, description, cardExtension); 240 } 241 242 /** 243 * Set the primary text of this Stream Card. 244 */ setPrimaryText(CharSequence text)245 public Builder setPrimaryText(CharSequence text) { 246 this.primaryText = text; 247 return this; 248 } 249 250 /** 251 * Set the secondary text of this Stream Card. 252 */ setSecondaryText(CharSequence text)253 public Builder setSecondaryText(CharSequence text) { 254 this.secondaryText = text; 255 return this; 256 } 257 258 /** 259 * Set the pending intent to be executed when the card is clicked. 260 */ setClickAction(PendingIntent action)261 public Builder setClickAction(PendingIntent action) { 262 this.contentPendingIntent = action; 263 return this; 264 } 265 266 /** 267 * Set the bitmap for the primary icon. 268 */ setPrimaryIcon(Bitmap bitmap)269 public Builder setPrimaryIcon(Bitmap bitmap){ 270 this.primaryIcon = bitmap; 271 return this; 272 } 273 274 /** 275 * Set the bitmap for the secondary icon. 276 */ setSecondaryIcon(Bitmap bitmap)277 public Builder setSecondaryIcon(Bitmap bitmap){ 278 this.secondaryIcon = bitmap; 279 return this; 280 } 281 282 /** 283 * Set the description of the card. 284 */ setDescription(String description)285 public Builder setDescription(String description) { 286 this.description = description; 287 return this; 288 } 289 290 /** 291 * Set the extension of the card. 292 */ setCardExtension(StreamCardExtension extension)293 public Builder setCardExtension(StreamCardExtension extension) { 294 this.cardExtension = extension; 295 return this; 296 } 297 } 298 } 299