OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.share; | 5 package org.chromium.chrome.browser.share; |
6 | 6 |
7 import android.annotation.TargetApi; | 7 import android.annotation.TargetApi; |
8 import android.app.Activity; | 8 import android.app.Activity; |
9 import android.app.PendingIntent; | 9 import android.app.PendingIntent; |
10 import android.content.BroadcastReceiver; | 10 import android.content.BroadcastReceiver; |
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 | 278 |
279 /** | 279 /** |
280 * Creates and shows a share intent picker dialog or starts a share intent d
irectly with the | 280 * Creates and shows a share intent picker dialog or starts a share intent d
irectly with the |
281 * activity that was most recently used to share based on shareDirectly valu
e. | 281 * activity that was most recently used to share based on shareDirectly valu
e. |
282 * | 282 * |
283 * This function will save |screenshot| under {app's root}/files/images/scre
enshot (or | 283 * This function will save |screenshot| under {app's root}/files/images/scre
enshot (or |
284 * /sdcard/DCIM/browser-images/screenshot if ADK is lower than JB MR2). | 284 * /sdcard/DCIM/browser-images/screenshot if ADK is lower than JB MR2). |
285 * Cleaning up doesn't happen automatically, and so an app should call clear
SharedScreenshots() | 285 * Cleaning up doesn't happen automatically, and so an app should call clear
SharedScreenshots() |
286 * explicitly when needed. | 286 * explicitly when needed. |
287 * | 287 * |
288 * @param shareDirectly Whether it should share directly with the activity t
hat was most | 288 * @param params The container holding the share parameters. |
289 * recently used to share. | |
290 * @param saveLastUsed Whether to save the chosen activity for future direct
sharing. | |
291 * @param activity Activity that is used to access package manager. | |
292 * @param title Title of the page to be shared. | |
293 * @param text Text to be shared. If both |text| and |url| are supplied, the
y are concatenated | |
294 * with a space. | |
295 * @param url URL of the page to be shared. | |
296 * @param offlineUri URI to the offline MHTML file to be shared. | |
297 * @param screenshotUri Uri of the screenshot of the page to be shared. | |
298 * @param callback Optional callback to be called when user makes a choice.
Will not be called | |
299 * if receiving a response when the user makes a choice is n
ot supported (on | |
300 * older Android versions). | |
301 */ | 289 */ |
302 public static void share(boolean shareDirectly, boolean saveLastUsed, Activi
ty activity, | 290 public static void share(ShareParams params) { |
303 String title, String text, String url, @Nullable Uri offlineUri, Uri
screenshotUri, | 291 if (params.shareDirectly()) { |
304 @Nullable TargetChosenCallback callback) { | 292 ComponentName component = getLastShareComponentName(); |
305 if (shareDirectly) { | 293 if (component == null) return; |
306 shareWithLastUsed(activity, title, text, url, offlineUri, screenshot
Uri); | 294 assert params.getCallback() == null; |
| 295 makeIntentAndShare(params, component); |
307 } else if (TargetChosenReceiver.isSupported()) { | 296 } else if (TargetChosenReceiver.isSupported()) { |
308 makeIntentAndShare(saveLastUsed, activity, title, text, url, offline
Uri, screenshotUri, | 297 makeIntentAndShare(params, null); |
309 null, callback); | |
310 } else { | 298 } else { |
311 showShareDialog( | 299 showShareDialog(params); |
312 saveLastUsed, activity, title, text, url, offlineUri, screen
shotUri, callback); | |
313 } | 300 } |
314 } | 301 } |
315 | 302 |
316 /** | 303 /** |
317 * Trigger the share action for the given image data. | 304 * Trigger the share action for the given image data. |
318 * @param activity The activity used to trigger the share action. | 305 * @param activity The activity used to trigger the share action. |
319 * @param jpegImageData The image data to be shared in jpeg format. | 306 * @param jpegImageData The image data to be shared in jpeg format. |
320 * @param name When this is not null, it will share the image directly with
the | 307 * @param name When this is not null, it will share the image directly with
the |
321 * {@link ComponentName} | 308 * {@link ComponentName} |
322 */ | 309 */ |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 fileUri = ApiCompatibilityUtils.getUriForImageCaptureFile(sa
vedFile); | 420 fileUri = ApiCompatibilityUtils.getUriForImageCaptureFile(sa
vedFile); |
434 } | 421 } |
435 callback.onResult(fileUri); | 422 callback.onResult(fileUri); |
436 } | 423 } |
437 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | 424 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
438 } | 425 } |
439 | 426 |
440 /** | 427 /** |
441 * Creates and shows a share intent picker dialog. | 428 * Creates and shows a share intent picker dialog. |
442 * | 429 * |
443 * @param saveLastUsed Whether to save the chosen activity for future direct
sharing. | 430 * @param params The container holding the share parameters. |
444 * @param activity Activity that is used to access package manager. | |
445 * @param title Title of the page to be shared. | |
446 * @param text Text to be shared. If both |text| and |url| are supplied, the
y are concatenated | |
447 * with a space. | |
448 * @param url URL of the page to be shared. | |
449 * @oaram offlineUri URI of the offline page to be shared. | |
450 * @param screenshotUri Uri of the screenshot of the page to be shared. | |
451 * @param callback Optional callback to be called when user makes a choice.
Will not be called | |
452 * if receiving a response when the user makes a choice is n
ot supported (on | |
453 * older Android versions). | |
454 */ | 431 */ |
455 private static void showShareDialog(final boolean saveLastUsed, final Activi
ty activity, | 432 private static void showShareDialog(final ShareParams params) { |
456 final String title, final String text, final String url, final Uri o
fflineUri, | 433 Activity activity = params.getActivity(); |
457 final Uri screenshotUri, @Nullable final TargetChosenCallback callba
ck) { | 434 final TargetChosenCallback callback = params.getCallback(); |
458 Intent intent = getShareIntent(activity, title, text, url, null, null); | 435 Intent intent = getShareLinkAppCompatibilityIntent(); |
459 PackageManager manager = activity.getPackageManager(); | 436 PackageManager manager = activity.getPackageManager(); |
460 List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent
, 0); | 437 List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent
, 0); |
461 assert resolveInfoList.size() > 0; | 438 assert resolveInfoList.size() > 0; |
462 if (resolveInfoList.size() == 0) return; | 439 if (resolveInfoList.size() == 0) return; |
463 Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator(
manager)); | 440 Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator(
manager)); |
464 | 441 |
465 final ShareDialogAdapter adapter = | 442 final ShareDialogAdapter adapter = |
466 new ShareDialogAdapter(activity, manager, resolveInfoList); | 443 new ShareDialogAdapter(activity, manager, resolveInfoList); |
467 AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.
AlertDialogTheme); | 444 AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.
AlertDialogTheme); |
468 builder.setTitle(activity.getString(R.string.share_link_chooser_title)); | 445 builder.setTitle(activity.getString(R.string.share_link_chooser_title)); |
469 builder.setAdapter(adapter, null); | 446 builder.setAdapter(adapter, null); |
470 | 447 |
471 // Need a mutable object to record whether the callback has been fired. | 448 // Need a mutable object to record whether the callback has been fired. |
472 final boolean[] callbackCalled = new boolean[1]; | 449 final boolean[] callbackCalled = new boolean[1]; |
473 | 450 |
474 final AlertDialog dialog = builder.create(); | 451 final AlertDialog dialog = builder.create(); |
475 dialog.show(); | 452 dialog.show(); |
476 dialog.getListView().setOnItemClickListener(new OnItemClickListener() { | 453 dialog.getListView().setOnItemClickListener(new OnItemClickListener() { |
477 @Override | 454 @Override |
478 public void onItemClick(AdapterView<?> parent, View view, int positi
on, long id) { | 455 public void onItemClick(AdapterView<?> parent, View view, int positi
on, long id) { |
479 ResolveInfo info = adapter.getItem(position); | 456 ResolveInfo info = adapter.getItem(position); |
480 ActivityInfo ai = info.activityInfo; | 457 ActivityInfo ai = info.activityInfo; |
481 ComponentName component = | 458 ComponentName component = |
482 new ComponentName(ai.applicationInfo.packageName, ai.nam
e); | 459 new ComponentName(ai.applicationInfo.packageName, ai.nam
e); |
| 460 |
483 if (callback != null && !callbackCalled[0]) { | 461 if (callback != null && !callbackCalled[0]) { |
484 callback.onTargetChosen(component); | 462 callback.onTargetChosen(component); |
485 callbackCalled[0] = true; | 463 callbackCalled[0] = true; |
486 } | 464 } |
487 if (saveLastUsed) setLastShareComponentName(component); | 465 if (params.saveLastUsed()) setLastShareComponentName(component); |
488 makeIntentAndShare(false, activity, title, text, url, offlineUri
, screenshotUri, | 466 makeIntentAndShare(params, component); |
489 component, null); | |
490 dialog.dismiss(); | 467 dialog.dismiss(); |
491 } | 468 } |
492 }); | 469 }); |
493 | 470 |
494 if (callback != null) { | 471 if (callback != null) { |
495 dialog.setOnDismissListener(new OnDismissListener() { | 472 dialog.setOnDismissListener(new OnDismissListener() { |
496 @Override | 473 @Override |
497 public void onDismiss(DialogInterface dialog) { | 474 public void onDismiss(DialogInterface dialog) { |
498 if (!callbackCalled[0]) { | 475 if (!callbackCalled[0]) { |
499 callback.onCancel(); | 476 callback.onCancel(); |
500 callbackCalled[0] = true; | 477 callbackCalled[0] = true; |
501 } | 478 } |
502 } | 479 } |
503 }); | 480 }); |
504 } | 481 } |
505 | 482 |
506 if (sFakeIntentReceiverForTesting != null) { | 483 if (sFakeIntentReceiverForTesting != null) { |
507 sFakeIntentReceiverForTesting.onCustomChooserShown(dialog); | 484 sFakeIntentReceiverForTesting.onCustomChooserShown(dialog); |
508 } | 485 } |
509 } | 486 } |
510 | 487 |
511 /** | 488 private static void makeIntentAndShare(ShareParams params, @Nullable Compone
ntName component) { |
512 * Starts a share intent with the activity that was most recently used to sh
are. | 489 Intent intent = getShareLinkIntent(params); |
513 * If there is no most recently used activity, it does nothing. | 490 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVI
TY_PREVIOUS_IS_TOP); |
514 * @param activity Activity that is used to start the share intent. | 491 intent.setComponent(component); |
515 * @param title Title of the page to be shared. | 492 if (intent.getComponent() != null) { |
516 * @param text Text to be shared. If both |text| and |url| are supplied, the
y are concatenated | 493 fireIntent(params.getActivity(), intent); |
517 * with a space. | |
518 * @param url URL of the page to be shared. | |
519 * @oaram offlineUri URI of the offline page to be shared. | |
520 * @param screenshotUri Uri of the screenshot of the page to be shared. | |
521 */ | |
522 private static void shareWithLastUsed(Activity activity, String title, Strin
g text, String url, | |
523 Uri offlineUri, Uri screenshotUri) { | |
524 ComponentName component = getLastShareComponentName(); | |
525 if (component == null) return; | |
526 makeIntentAndShare( | |
527 false, activity, title, text, url, offlineUri, screenshotUri, co
mponent, null); | |
528 } | |
529 | |
530 private static void shareIntent(boolean saveLastUsed, Activity activity, Int
ent sharingIntent, | |
531 @Nullable TargetChosenCallback callback) { | |
532 if (sharingIntent.getComponent() != null) { | |
533 // If a component was specified, there should not also be a callback
. | |
534 assert callback == null; | |
535 fireIntent(activity, sharingIntent); | |
536 } else { | 494 } else { |
537 assert TargetChosenReceiver.isSupported(); | 495 assert TargetChosenReceiver.isSupported(); |
538 TargetChosenReceiver.sendChooserIntent(saveLastUsed, activity, shari
ngIntent, callback); | 496 TargetChosenReceiver.sendChooserIntent( |
| 497 params.saveLastUsed(), params.getActivity(), intent, params.
getCallback()); |
539 } | 498 } |
540 } | 499 } |
541 | 500 |
542 private static void makeIntentAndShare(final boolean saveLastUsed, final Act
ivity activity, | |
543 final String title, final String text, final String url, final Uri o
fflineUri, | |
544 final Uri screenshotUri, final ComponentName component, | |
545 @Nullable final TargetChosenCallback callback) { | |
546 Intent intent = getDirectShareIntentForComponent( | |
547 activity, title, text, url, offlineUri, screenshotUri, component
); | |
548 shareIntent(saveLastUsed, activity, intent, callback); | |
549 } | |
550 | |
551 /** | 501 /** |
552 * Set the icon and the title for the menu item used for direct share. | 502 * Set the icon and the title for the menu item used for direct share. |
553 * | 503 * |
554 * @param activity Activity that is used to access the package manager. | 504 * @param activity Activity that is used to access the package manager. |
555 * @param item The menu item that is used for direct share | 505 * @param item The menu item that is used for direct share |
556 */ | 506 */ |
557 public static void configureDirectShareMenuItem(Activity activity, MenuItem
item) { | 507 public static void configureDirectShareMenuItem(Activity activity, MenuItem
item) { |
558 Intent shareIntent = getShareIntent(activity, "", "", "", null, null); | 508 Intent shareIntent = getShareLinkAppCompatibilityIntent(); |
559 Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activ
ity, shareIntent); | 509 Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activ
ity, shareIntent); |
560 Drawable directShareIcon = directShare.first; | 510 Drawable directShareIcon = directShare.first; |
561 CharSequence directShareTitle = directShare.second; | 511 CharSequence directShareTitle = directShare.second; |
562 | 512 |
563 item.setIcon(directShareIcon); | 513 item.setIcon(directShareIcon); |
564 if (directShareTitle != null) { | 514 if (directShareTitle != null) { |
565 item.setTitle( | 515 item.setTitle( |
566 activity.getString(R.string.accessibility_menu_share_via, di
rectShareTitle)); | 516 activity.getString(R.string.accessibility_menu_share_via, di
rectShareTitle)); |
567 } | 517 } |
568 } | 518 } |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 @VisibleForTesting | 592 @VisibleForTesting |
643 public static void setLastShareComponentName(ComponentName component) { | 593 public static void setLastShareComponentName(ComponentName component) { |
644 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | 594 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); |
645 SharedPreferences.Editor editor = preferences.edit(); | 595 SharedPreferences.Editor editor = preferences.edit(); |
646 editor.putString(PACKAGE_NAME_KEY, component.getPackageName()); | 596 editor.putString(PACKAGE_NAME_KEY, component.getPackageName()); |
647 editor.putString(CLASS_NAME_KEY, component.getClassName()); | 597 editor.putString(CLASS_NAME_KEY, component.getClassName()); |
648 editor.apply(); | 598 editor.apply(); |
649 } | 599 } |
650 | 600 |
651 @VisibleForTesting | 601 @VisibleForTesting |
652 public static Intent getShareIntent(Activity activity, String title, String
text, String url, | 602 public static Intent getShareLinkIntent(ShareParams params) { |
653 Uri offlineUri, Uri screenshotUri) { | 603 String url = params.getUrl(); |
| 604 String text = params.getText(); |
654 if (!TextUtils.isEmpty(url)) { | 605 if (!TextUtils.isEmpty(url)) { |
655 url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); | 606 url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); |
656 if (!TextUtils.isEmpty(text)) { | 607 if (!TextUtils.isEmpty(text)) { |
657 // Concatenate text and URL with a space. | 608 // Concatenate text and URL with a space. |
658 text = text + " " + url; | 609 text = text + " " + url; |
659 } else { | 610 } else { |
660 text = url; | 611 text = url; |
661 } | 612 } |
662 } | 613 } |
663 | 614 |
664 Intent intent = new Intent(Intent.ACTION_SEND); | 615 Intent intent = new Intent(Intent.ACTION_SEND); |
665 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); | 616 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
666 intent.putExtra(Intent.EXTRA_SUBJECT, title); | 617 intent.putExtra(Intent.EXTRA_SUBJECT, params.getTitle()); |
667 intent.putExtra(Intent.EXTRA_TEXT, text); | 618 intent.putExtra(Intent.EXTRA_TEXT, text); |
668 intent.putExtra(EXTRA_TASK_ID, activity.getTaskId()); | 619 intent.putExtra(EXTRA_TASK_ID, params.getActivity().getTaskId()); |
669 | 620 |
| 621 Uri screenshotUri = params.getScreenshotUri(); |
670 if (screenshotUri != null) { | 622 if (screenshotUri != null) { |
671 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | 623 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
672 } | |
673 if (screenshotUri != null) { | |
674 // To give read access to an Intent target, we need to put |screensh
otUri| in clipData | 624 // To give read access to an Intent target, we need to put |screensh
otUri| in clipData |
675 // because adding Intent.FLAG_GRANT_READ_URI_PERMISSION doesn't work
for | 625 // because adding Intent.FLAG_GRANT_READ_URI_PERMISSION doesn't work
for |
676 // EXTRA_SHARE_SCREENSHOT_AS_STREAM. | 626 // EXTRA_SHARE_SCREENSHOT_AS_STREAM. |
677 intent.setClipData(ClipData.newRawUri("", screenshotUri)); | 627 intent.setClipData(ClipData.newRawUri("", screenshotUri)); |
678 intent.putExtra(EXTRA_SHARE_SCREENSHOT_AS_STREAM, screenshotUri); | 628 intent.putExtra(EXTRA_SHARE_SCREENSHOT_AS_STREAM, screenshotUri); |
679 } | 629 } |
680 if (offlineUri == null) { | 630 if (params.getOfflineUri() == null) { |
681 intent.setType("text/plain"); | 631 intent.setType("text/plain"); |
682 } else { | 632 } else { |
683 intent.setType("multipart/related"); | 633 intent.setType("multipart/related"); |
684 intent.putExtra(Intent.EXTRA_STREAM, offlineUri); | 634 intent.putExtra(Intent.EXTRA_STREAM, params.getOfflineUri()); |
685 } | 635 } |
686 return intent; | 636 return intent; |
687 } | 637 } |
688 | 638 |
689 /** | 639 /** |
690 * Creates an Intent to share an image. | 640 * Creates an Intent to share an image. |
691 * @param imageUri The Uri of the image. | 641 * @param imageUri The Uri of the image. |
692 * @return The Intent used to share the image. | 642 * @return The Intent used to share the image. |
693 */ | 643 */ |
694 public static Intent getShareImageIntent(Uri imageUri) { | 644 public static Intent getShareImageIntent(Uri imageUri) { |
695 Intent intent = new Intent(Intent.ACTION_SEND); | 645 Intent intent = new Intent(Intent.ACTION_SEND); |
696 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); | 646 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
697 intent.setType("image/jpeg"); | 647 intent.setType("image/jpeg"); |
698 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | 648 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
699 intent.putExtra(Intent.EXTRA_STREAM, imageUri); | 649 intent.putExtra(Intent.EXTRA_STREAM, imageUri); |
700 return intent; | 650 return intent; |
701 } | 651 } |
702 | 652 |
703 private static Intent getDirectShareIntentForComponent(Activity activity, St
ring title, | 653 /** |
704 String text, String url, Uri offlineUri, Uri screenshotUri, Componen
tName component) { | 654 * Convenience method to create an Intent to retrieve all the apps support s
haring text. |
705 Intent intent = getShareIntent(activity, title, text, url, offlineUri, s
creenshotUri); | 655 */ |
706 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | 656 public static Intent getShareLinkAppCompatibilityIntent() { |
707 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); | 657 Intent intent = new Intent(Intent.ACTION_SEND); |
708 intent.setComponent(component); | 658 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
| 659 intent.putExtra(Intent.EXTRA_SUBJECT, ""); |
| 660 intent.putExtra(Intent.EXTRA_TEXT, ""); |
| 661 intent.setType("text/plain"); |
709 return intent; | 662 return intent; |
710 } | 663 } |
711 | 664 |
712 /** | 665 /** |
713 * Gets the {@link ComponentName} of the app that was used to last share. | 666 * Gets the {@link ComponentName} of the app that was used to last share. |
714 */ | 667 */ |
715 @Nullable | 668 @Nullable |
716 public static ComponentName getLastShareComponentName() { | 669 public static ComponentName getLastShareComponentName() { |
717 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | 670 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); |
718 String packageName = preferences.getString(PACKAGE_NAME_KEY, null); | 671 String packageName = preferences.getString(PACKAGE_NAME_KEY, null); |
719 String className = preferences.getString(CLASS_NAME_KEY, null); | 672 String className = preferences.getString(CLASS_NAME_KEY, null); |
720 if (packageName == null || className == null) return null; | 673 if (packageName == null || className == null) return null; |
721 return new ComponentName(packageName, className); | 674 return new ComponentName(packageName, className); |
722 } | 675 } |
723 } | 676 } |
OLD | NEW |