ChooserTarget.java revision c6d5e3a406c0e80638304980bac13abaa703a9a0
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 18package android.service.chooser; 19 20import android.app.Activity; 21import android.app.PendingIntent; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.IntentSender; 27import android.graphics.Bitmap; 28import android.os.Bundle; 29import android.os.Parcel; 30import android.os.Parcelable; 31import android.os.UserHandle; 32import android.util.Log; 33 34/** 35 * A ChooserTarget represents a deep-link into an application as returned by a 36 * {@link android.service.chooser.ChooserTargetService}. 37 */ 38public final class ChooserTarget implements Parcelable { 39 private static final String TAG = "ChooserTarget"; 40 41 /** 42 * The title of this target that will be shown to the user. The title may be truncated 43 * if it is too long to display in the space provided. 44 */ 45 private CharSequence mTitle; 46 47 /** 48 * The icon that will be shown to the user to represent this target. 49 * The system may resize this icon as appropriate. 50 */ 51 private Bitmap mIcon; 52 53 /** 54 * The IntentSender that will be used to deliver the intent to the target. 55 * It will be {@link android.content.Intent#fillIn(android.content.Intent, int)} filled in} 56 * by the real intent sent by the application. 57 */ 58 private IntentSender mIntentSender; 59 60 /** 61 * A raw intent provided in lieu of an IntentSender. Will be filled in and sent 62 * by {@link #sendIntent(Context, Intent)}. 63 */ 64 private Intent mIntent; 65 66 /** 67 * The score given to this item. It can be normalized. 68 */ 69 private float mScore; 70 71 /** 72 * Construct a deep link target for presentation by a chooser UI. 73 * 74 * <p>A target is composed of a title and an icon for presentation to the user. 75 * The UI presenting this target may truncate the title if it is too long to be presented 76 * in the available space, as well as crop, resize or overlay the supplied icon.</p> 77 * 78 * <p>The creator of a target may supply a ranking score. This score is assumed to be relative 79 * to the other targets supplied by the same 80 * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. 81 * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). 82 * Scores for a set of targets do not need to sum to 1.</p> 83 * 84 * <p>Before being sent, the PendingIntent supplied will be 85 * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied 86 * to the chooser. When constructing a PendingIntent for use in a ChooserTarget, make sure 87 * that you permit the relevant fields to be filled in using the appropriate flags such as 88 * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, 89 * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that 90 * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants 91 * for {@link Intent#ACTION_SEND} intents.</p> 92 * 93 * <p>Take care not to place custom {@link android.os.Parcelable} types into 94 * the PendingIntent as extras, as the system will not be able to unparcel it to merge 95 * additional extras.</p> 96 * 97 * @param title title of this target that will be shown to a user 98 * @param icon icon to represent this target 99 * @param score ranking score for this target between 0.0f and 1.0f, inclusive 100 * @param pendingIntent PendingIntent to fill in and send if the user chooses this target 101 */ 102 public ChooserTarget(CharSequence title, Bitmap icon, float score, 103 PendingIntent pendingIntent) { 104 this(title, icon, score, pendingIntent.getIntentSender()); 105 } 106 107 /** 108 * Construct a deep link target for presentation by a chooser UI. 109 * 110 * <p>A target is composed of a title and an icon for presentation to the user. 111 * The UI presenting this target may truncate the title if it is too long to be presented 112 * in the available space, as well as crop, resize or overlay the supplied icon.</p> 113 * 114 * <p>The creator of a target may supply a ranking score. This score is assumed to be relative 115 * to the other targets supplied by the same 116 * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. 117 * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). 118 * Scores for a set of targets do not need to sum to 1.</p> 119 * 120 * <p>Before being sent, the IntentSender supplied will be 121 * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied 122 * to the chooser. When constructing an IntentSender for use in a ChooserTarget, make sure 123 * that you permit the relevant fields to be filled in using the appropriate flags such as 124 * {@link Intent#FILL_IN_ACTION}, {@link Intent#FILL_IN_CATEGORIES}, 125 * {@link Intent#FILL_IN_DATA} and {@link Intent#FILL_IN_CLIP_DATA}. Note that 126 * {@link Intent#FILL_IN_CLIP_DATA} is required to appropriately receive URI permission grants 127 * for {@link Intent#ACTION_SEND} intents.</p> 128 * 129 * <p>Take care not to place custom {@link android.os.Parcelable} types into 130 * the IntentSender as extras, as the system will not be able to unparcel it to merge 131 * additional extras.</p> 132 * 133 * @param title title of this target that will be shown to a user 134 * @param icon icon to represent this target 135 * @param score ranking score for this target between 0.0f and 1.0f, inclusive 136 * @param intentSender IntentSender to fill in and send if the user chooses this target 137 */ 138 public ChooserTarget(CharSequence title, Bitmap icon, float score, IntentSender intentSender) { 139 mTitle = title; 140 mIcon = icon; 141 if (score > 1.f || score < 0.f) { 142 throw new IllegalArgumentException("Score " + score + " out of range; " 143 + "must be between 0.0f and 1.0f"); 144 } 145 mScore = score; 146 mIntentSender = intentSender; 147 } 148 149 /** 150 * Construct a deep link target for presentation by a chooser UI. 151 * 152 * <p>A target is composed of a title and an icon for presentation to the user. 153 * The UI presenting this target may truncate the title if it is too long to be presented 154 * in the available space, as well as crop, resize or overlay the supplied icon.</p> 155 * 156 * <p>The creator of a target may supply a ranking score. This score is assumed to be relative 157 * to the other targets supplied by the same 158 * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}. 159 * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match). 160 * Scores for a set of targets do not need to sum to 1.</p> 161 * 162 * <p>Before being sent, the Intent supplied will be 163 * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied 164 * to the chooser.</p> 165 * 166 * <p>Take care not to place custom {@link android.os.Parcelable} types into 167 * the Intent as extras, as the system will not be able to unparcel it to merge 168 * additional extras.</p> 169 * 170 * @param title title of this target that will be shown to a user 171 * @param icon icon to represent this target 172 * @param score ranking score for this target between 0.0f and 1.0f, inclusive 173 * @param intent Intent to fill in and send if the user chooses this target 174 */ 175 public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) { 176 mTitle = title; 177 mIcon = icon; 178 if (score > 1.f || score < 0.f) { 179 throw new IllegalArgumentException("Score " + score + " out of range; " 180 + "must be between 0.0f and 1.0f"); 181 } 182 mScore = score; 183 mIntent = intent; 184 } 185 186 ChooserTarget(Parcel in) { 187 mTitle = in.readCharSequence(); 188 if (in.readInt() != 0) { 189 mIcon = Bitmap.CREATOR.createFromParcel(in); 190 } else { 191 mIcon = null; 192 } 193 mScore = in.readFloat(); 194 mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in); 195 if (in.readInt() != 0) { 196 mIntent = Intent.CREATOR.createFromParcel(in); 197 } 198 } 199 200 /** 201 * Returns the title of this target for display to a user. The UI displaying the title 202 * may truncate this title if it is too long to be displayed in full. 203 * 204 * @return the title of this target, intended to be shown to a user 205 */ 206 public CharSequence getTitle() { 207 return mTitle; 208 } 209 210 /** 211 * Returns the icon representing this target for display to a user. The UI displaying the icon 212 * may crop, resize or overlay this icon. 213 * 214 * @return the icon representing this target, intended to be shown to a user 215 */ 216 public Bitmap getIcon() { 217 return mIcon; 218 } 219 220 /** 221 * Returns the ranking score supplied by the creator of this ChooserTarget. 222 * Values are between 0.0f and 1.0f. The UI displaying the target may 223 * take this score into account when sorting and merging targets from multiple sources. 224 * 225 * @return the ranking score for this target between 0.0f and 1.0f, inclusive 226 */ 227 public float getScore() { 228 return mScore; 229 } 230 231 /** 232 * Returns the raw IntentSender supplied by the ChooserTarget's creator. 233 * This may be null if the creator specified a regular Intent instead. 234 * 235 * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> 236 * 237 * @return the IntentSender supplied by the ChooserTarget's creator 238 */ 239 public IntentSender getIntentSender() { 240 return mIntentSender; 241 } 242 243 /** 244 * Returns the Intent supplied by the ChooserTarget's creator. 245 * This may be null if the creator specified an IntentSender or PendingIntent instead. 246 * 247 * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p> 248 * 249 * @return the Intent supplied by the ChooserTarget's creator 250 */ 251 public Intent getIntent() { 252 return mIntent; 253 } 254 255 /** 256 * Fill in the IntentSender supplied by the ChooserTarget's creator and send it. 257 * 258 * @param context the sending Context; generally the Activity presenting the chooser UI 259 * @param fillInIntent the Intent provided to the Chooser to be sent to a selected target 260 * @return true if sending the Intent was successful 261 */ 262 public boolean sendIntent(Context context, Intent fillInIntent) { 263 if (fillInIntent != null) { 264 fillInIntent.migrateExtraStreamToClipData(); 265 fillInIntent.prepareToLeaveProcess(); 266 } 267 if (mIntentSender != null) { 268 try { 269 mIntentSender.sendIntent(context, 0, fillInIntent, null, null); 270 return true; 271 } catch (IntentSender.SendIntentException e) { 272 Log.e(TAG, "sendIntent " + this + " failed", e); 273 return false; 274 } 275 } else if (mIntent != null) { 276 try { 277 final Intent toSend = new Intent(mIntent); 278 toSend.fillIn(fillInIntent, 0); 279 context.startActivity(toSend); 280 return true; 281 } catch (Exception e) { 282 Log.e(TAG, "sendIntent " + this + " failed", e); 283 return false; 284 } 285 } else { 286 Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); 287 return false; 288 } 289 } 290 291 /** 292 * Same as {@link #sendIntent(Context, Intent)}, but offers a userId field to use 293 * for launching the {@link #getIntent() intent} using 294 * {@link Activity#startActivityAsCaller(Intent, Bundle, int)} if the 295 * {@link #getIntentSender() IntentSender} is not present. If the IntentSender is present, 296 * it will be invoked as usual with its own calling identity. 297 * 298 * @hide internal use only. 299 */ 300 public boolean sendIntentAsCaller(Activity context, Intent fillInIntent, int userId) { 301 if (fillInIntent != null) { 302 fillInIntent.migrateExtraStreamToClipData(); 303 fillInIntent.prepareToLeaveProcess(); 304 } 305 if (mIntentSender != null) { 306 try { 307 mIntentSender.sendIntent(context, 0, fillInIntent, null, null); 308 return true; 309 } catch (IntentSender.SendIntentException e) { 310 Log.e(TAG, "sendIntent " + this + " failed", e); 311 return false; 312 } 313 } else if (mIntent != null) { 314 try { 315 final Intent toSend = new Intent(mIntent); 316 toSend.fillIn(fillInIntent, 0); 317 context.startActivityAsCaller(toSend, null, userId); 318 return true; 319 } catch (Exception e) { 320 Log.e(TAG, "sendIntent " + this + " failed", e); 321 return false; 322 } 323 } else { 324 Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); 325 return false; 326 } 327 } 328 329 /** 330 * The UserHandle is only used if we're launching a raw intent. The IntentSender will be 331 * launched with its associated identity. 332 * 333 * @hide Internal use only 334 */ 335 public boolean sendIntentAsUser(Activity context, Intent fillInIntent, UserHandle user) { 336 if (fillInIntent != null) { 337 fillInIntent.migrateExtraStreamToClipData(); 338 fillInIntent.prepareToLeaveProcess(); 339 } 340 if (mIntentSender != null) { 341 try { 342 mIntentSender.sendIntent(context, 0, fillInIntent, null, null); 343 return true; 344 } catch (IntentSender.SendIntentException e) { 345 Log.e(TAG, "sendIntent " + this + " failed", e); 346 return false; 347 } 348 } else if (mIntent != null) { 349 try { 350 final Intent toSend = new Intent(mIntent); 351 toSend.fillIn(fillInIntent, 0); 352 context.startActivityAsUser(toSend, user); 353 return true; 354 } catch (Exception e) { 355 Log.e(TAG, "sendIntent " + this + " failed", e); 356 return false; 357 } 358 } else { 359 Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send"); 360 return false; 361 } 362 } 363 364 @Override 365 public String toString() { 366 return "ChooserTarget{" 367 + (mIntentSender != null ? mIntentSender.getCreatorPackage() : mIntent) 368 + ", " 369 + "'" + mTitle 370 + "', " + mScore + "}"; 371 } 372 373 @Override 374 public int describeContents() { 375 return 0; 376 } 377 378 @Override 379 public void writeToParcel(Parcel dest, int flags) { 380 dest.writeCharSequence(mTitle); 381 if (mIcon != null) { 382 dest.writeInt(1); 383 mIcon.writeToParcel(dest, 0); 384 } else { 385 dest.writeInt(0); 386 } 387 dest.writeFloat(mScore); 388 IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest); 389 dest.writeInt(mIntent != null ? 1 : 0); 390 if (mIntent != null) { 391 mIntent.writeToParcel(dest, 0); 392 } 393 } 394 395 public static final Creator<ChooserTarget> CREATOR 396 = new Creator<ChooserTarget>() { 397 @Override 398 public ChooserTarget createFromParcel(Parcel source) { 399 return new ChooserTarget(source); 400 } 401 402 @Override 403 public ChooserTarget[] newArray(int size) { 404 return new ChooserTarget[size]; 405 } 406 }; 407} 408