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