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 72 public StreamCard() { 73 } 74 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 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 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 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 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 167 public void setPriority(int priority) { 168 mPriority = priority; 169 } 170 171 public int getPriority() { 172 return mPriority; 173 } 174 175 public String getDescription() { 176 return mDescription; 177 } 178 179 public long getId() { 180 return mId; 181 } 182 183 public int getType() { 184 return mType; 185 } 186 187 public long getTimestamp() { 188 return mTimestamp; 189 } 190 191 public CharSequence getPrimaryText() { 192 return mPrimaryText; 193 } 194 195 public CharSequence getSecondaryText() { 196 return mSecondaryText; 197 } 198 199 public Bitmap getPrimaryIcon() { 200 return mPrimaryIcon; 201 } 202 203 public Bitmap getSecondaryIcon() { 204 return mSecondaryIcon; 205 } 206 207 public PendingIntent getContentPendingIntent() { 208 return mContentPendingIntent; 209 } 210 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 231 public Builder(int type, long id, long timestamp) { 232 this.type = type; 233 this.id = id; 234 this.timestamp = timestamp; 235 } 236 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 */ 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 */ 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 */ 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 */ 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 */ 277 public Builder setSecondaryIcon(Bitmap bitmap){ 278 this.secondaryIcon = bitmap; 279 return this; 280 } 281 282 /** 283 * Set the description of the card. 284 */ 285 public Builder setDescription(String description) { 286 this.description = description; 287 return this; 288 } 289 290 /** 291 * Set the extension of the card. 292 */ 293 public Builder setCardExtension(StreamCardExtension extension) { 294 this.cardExtension = extension; 295 return this; 296 } 297 } 298 } 299