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.isShareDirectly()) { |
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 makeIntentAndShare(params, component); |
307 } else if (TargetChosenReceiver.isSupported()) { | 295 } else if (TargetChosenReceiver.isSupported()) { |
308 makeIntentAndShare(saveLastUsed, activity, title, text, url, offline Uri, screenshotUri, | 296 makeIntentAndShare(params, null); |
309 null, callback); | |
310 } else { | 297 } else { |
311 showShareDialog( | 298 showShareDialog(params); |
312 saveLastUsed, activity, title, text, url, offlineUri, screen shotUri, callback); | |
313 } | 299 } |
314 } | 300 } |
315 | 301 |
316 /** | 302 /** |
317 * Trigger the share action for the given image data. | 303 * Trigger the share action for the given image data. |
318 * @param activity The activity used to trigger the share action. | 304 * @param activity The activity used to trigger the share action. |
319 * @param jpegImageData The image data to be shared in jpeg format. | 305 * @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 | 306 * @param name When this is not null, it will share the image directly with the |
321 * {@link ComponentName} | 307 * {@link ComponentName} |
322 */ | 308 */ |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
433 fileUri = ApiCompatibilityUtils.getUriForImageCaptureFile(sa vedFile); | 419 fileUri = ApiCompatibilityUtils.getUriForImageCaptureFile(sa vedFile); |
434 } | 420 } |
435 callback.onResult(fileUri); | 421 callback.onResult(fileUri); |
436 } | 422 } |
437 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | 423 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
438 } | 424 } |
439 | 425 |
440 /** | 426 /** |
441 * Creates and shows a share intent picker dialog. | 427 * Creates and shows a share intent picker dialog. |
442 * | 428 * |
443 * @param saveLastUsed Whether to save the chosen activity for future direct sharing. | 429 * @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 */ | 430 */ |
455 private static void showShareDialog(final boolean saveLastUsed, final Activi ty activity, | 431 private static void showShareDialog(final ShareParams params) { |
456 final String title, final String text, final String url, final Uri o fflineUri, | 432 Activity activity = params.getActivity(); |
457 final Uri screenshotUri, @Nullable final TargetChosenCallback callba ck) { | 433 final TargetChosenCallback callback = params.getCallback(); |
458 Intent intent = getShareIntent(activity, title, text, url, null, null); | 434 Intent intent = getShareLinkAppCompatibilityIntent(); |
459 PackageManager manager = activity.getPackageManager(); | 435 PackageManager manager = activity.getPackageManager(); |
460 List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent , 0); | 436 List<ResolveInfo> resolveInfoList = manager.queryIntentActivities(intent , 0); |
461 assert resolveInfoList.size() > 0; | 437 assert resolveInfoList.size() > 0; |
462 if (resolveInfoList.size() == 0) return; | 438 if (resolveInfoList.size() == 0) return; |
463 Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator( manager)); | 439 Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator( manager)); |
464 | 440 |
465 final ShareDialogAdapter adapter = | 441 final ShareDialogAdapter adapter = |
466 new ShareDialogAdapter(activity, manager, resolveInfoList); | 442 new ShareDialogAdapter(activity, manager, resolveInfoList); |
467 AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style. AlertDialogTheme); | 443 AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style. AlertDialogTheme); |
468 builder.setTitle(activity.getString(R.string.share_link_chooser_title)); | 444 builder.setTitle(activity.getString(R.string.share_link_chooser_title)); |
469 builder.setAdapter(adapter, null); | 445 builder.setAdapter(adapter, null); |
470 | 446 |
471 // Need a mutable object to record whether the callback has been fired. | 447 // Need a mutable object to record whether the callback has been fired. |
472 final boolean[] callbackCalled = new boolean[1]; | 448 final boolean[] callbackCalled = new boolean[1]; |
473 | 449 |
474 final AlertDialog dialog = builder.create(); | 450 final AlertDialog dialog = builder.create(); |
475 dialog.show(); | 451 dialog.show(); |
476 dialog.getListView().setOnItemClickListener(new OnItemClickListener() { | 452 dialog.getListView().setOnItemClickListener(new OnItemClickListener() { |
477 @Override | 453 @Override |
478 public void onItemClick(AdapterView<?> parent, View view, int positi on, long id) { | 454 public void onItemClick(AdapterView<?> parent, View view, int positi on, long id) { |
479 ResolveInfo info = adapter.getItem(position); | 455 ResolveInfo info = adapter.getItem(position); |
480 ActivityInfo ai = info.activityInfo; | 456 ActivityInfo ai = info.activityInfo; |
481 ComponentName component = | 457 ComponentName component = |
482 new ComponentName(ai.applicationInfo.packageName, ai.nam e); | 458 new ComponentName(ai.applicationInfo.packageName, ai.nam e); |
459 | |
483 if (callback != null && !callbackCalled[0]) { | 460 if (callback != null && !callbackCalled[0]) { |
484 callback.onTargetChosen(component); | 461 callback.onTargetChosen(component); |
485 callbackCalled[0] = true; | 462 callbackCalled[0] = true; |
486 } | 463 } |
487 if (saveLastUsed) setLastShareComponentName(component); | 464 if (params.isSaveLastUsed()) setLastShareComponentName(component ); |
488 makeIntentAndShare(false, activity, title, text, url, offlineUri , screenshotUri, | 465 makeIntentAndShare(params, component); |
489 component, null); | |
490 dialog.dismiss(); | 466 dialog.dismiss(); |
491 } | 467 } |
492 }); | 468 }); |
493 | 469 |
494 if (callback != null) { | 470 if (callback != null) { |
495 dialog.setOnDismissListener(new OnDismissListener() { | 471 dialog.setOnDismissListener(new OnDismissListener() { |
496 @Override | 472 @Override |
497 public void onDismiss(DialogInterface dialog) { | 473 public void onDismiss(DialogInterface dialog) { |
498 if (!callbackCalled[0]) { | 474 if (!callbackCalled[0]) { |
499 callback.onCancel(); | 475 callback.onCancel(); |
500 callbackCalled[0] = true; | 476 callbackCalled[0] = true; |
501 } | 477 } |
502 } | 478 } |
503 }); | 479 }); |
504 } | 480 } |
505 | 481 |
506 if (sFakeIntentReceiverForTesting != null) { | 482 if (sFakeIntentReceiverForTesting != null) { |
507 sFakeIntentReceiverForTesting.onCustomChooserShown(dialog); | 483 sFakeIntentReceiverForTesting.onCustomChooserShown(dialog); |
508 } | 484 } |
509 } | 485 } |
510 | 486 |
511 /** | 487 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. | 488 Intent intent = getShareLinkIntent(params); |
513 * If there is no most recently used activity, it does nothing. | 489 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. | 490 intent.setComponent(component); |
515 * @param title Title of the page to be shared. | 491 if (intent.getComponent() != null) { |
516 * @param text Text to be shared. If both |text| and |url| are supplied, the y are concatenated | |
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 . | 492 // If a component was specified, there should not also be a callback . |
534 assert callback == null; | 493 assert params.getCallback() == null; |
535 fireIntent(activity, sharingIntent); | 494 fireIntent(params.getActivity(), intent); |
536 } else { | 495 } else { |
537 assert TargetChosenReceiver.isSupported(); | 496 assert TargetChosenReceiver.isSupported(); |
538 TargetChosenReceiver.sendChooserIntent(saveLastUsed, activity, shari ngIntent, callback); | 497 TargetChosenReceiver.sendChooserIntent( |
498 params.isSaveLastUsed(), params.getActivity(), intent, param s.getCallback()); | |
539 } | 499 } |
540 } | 500 } |
541 | 501 |
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 /** | 502 /** |
552 * Set the icon and the title for the menu item used for direct share. | 503 * Set the icon and the title for the menu item used for direct share. |
553 * | 504 * |
554 * @param activity Activity that is used to access the package manager. | 505 * @param activity Activity that is used to access the package manager. |
555 * @param item The menu item that is used for direct share | 506 * @param item The menu item that is used for direct share |
556 */ | 507 */ |
557 public static void configureDirectShareMenuItem(Activity activity, MenuItem item) { | 508 public static void configureDirectShareMenuItem(Activity activity, MenuItem item) { |
558 Intent shareIntent = getShareIntent(activity, "", "", "", null, null); | 509 Intent shareIntent = getShareLinkAppCompatibilityIntent(); |
559 Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activ ity, shareIntent); | 510 Pair<Drawable, CharSequence> directShare = getShareableIconAndName(activ ity, shareIntent); |
560 Drawable directShareIcon = directShare.first; | 511 Drawable directShareIcon = directShare.first; |
561 CharSequence directShareTitle = directShare.second; | 512 CharSequence directShareTitle = directShare.second; |
562 | 513 |
563 item.setIcon(directShareIcon); | 514 item.setIcon(directShareIcon); |
564 if (directShareTitle != null) { | 515 if (directShareTitle != null) { |
565 item.setTitle( | 516 item.setTitle( |
566 activity.getString(R.string.accessibility_menu_share_via, di rectShareTitle)); | 517 activity.getString(R.string.accessibility_menu_share_via, di rectShareTitle)); |
567 } | 518 } |
568 } | 519 } |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
642 @VisibleForTesting | 593 @VisibleForTesting |
643 public static void setLastShareComponentName(ComponentName component) { | 594 public static void setLastShareComponentName(ComponentName component) { |
644 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | 595 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); |
645 SharedPreferences.Editor editor = preferences.edit(); | 596 SharedPreferences.Editor editor = preferences.edit(); |
646 editor.putString(PACKAGE_NAME_KEY, component.getPackageName()); | 597 editor.putString(PACKAGE_NAME_KEY, component.getPackageName()); |
647 editor.putString(CLASS_NAME_KEY, component.getClassName()); | 598 editor.putString(CLASS_NAME_KEY, component.getClassName()); |
648 editor.apply(); | 599 editor.apply(); |
649 } | 600 } |
650 | 601 |
651 @VisibleForTesting | 602 @VisibleForTesting |
652 public static Intent getShareIntent(Activity activity, String title, String text, String url, | 603 public static Intent getShareLinkIntent(ShareParams params) { |
653 Uri offlineUri, Uri screenshotUri) { | 604 String url = params.getUrl(); |
605 String text = params.getText(); | |
654 if (!TextUtils.isEmpty(url)) { | 606 if (!TextUtils.isEmpty(url)) { |
655 url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); | 607 url = DomDistillerUrlUtils.getOriginalUrlFromDistillerUrl(url); |
656 if (!TextUtils.isEmpty(text)) { | 608 if (!TextUtils.isEmpty(text)) { |
657 // Concatenate text and URL with a space. | 609 // Concatenate text and URL with a space. |
658 text = text + " " + url; | 610 text = text + " " + url; |
659 } else { | 611 } else { |
660 text = url; | 612 text = url; |
661 } | 613 } |
662 } | 614 } |
663 | 615 |
664 Intent intent = new Intent(Intent.ACTION_SEND); | 616 Intent intent = new Intent(Intent.ACTION_SEND); |
665 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); | 617 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
666 intent.putExtra(Intent.EXTRA_SUBJECT, title); | 618 intent.putExtra(Intent.EXTRA_SUBJECT, params.getTitle()); |
667 intent.putExtra(Intent.EXTRA_TEXT, text); | 619 intent.putExtra(Intent.EXTRA_TEXT, text); |
668 intent.putExtra(EXTRA_TASK_ID, activity.getTaskId()); | 620 intent.putExtra(EXTRA_TASK_ID, params.getActivity().getTaskId()); |
669 | 621 |
622 Uri screenshotUri = params.getScreenshotUri(); | |
670 if (screenshotUri != null) { | 623 if (screenshotUri != null) { |
671 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | 624 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 | 625 // 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 | 626 // because adding Intent.FLAG_GRANT_READ_URI_PERMISSION doesn't work for |
676 // EXTRA_SHARE_SCREENSHOT_AS_STREAM. | 627 // EXTRA_SHARE_SCREENSHOT_AS_STREAM. |
677 intent.setClipData(ClipData.newRawUri("", screenshotUri)); | 628 intent.setClipData(ClipData.newRawUri("", screenshotUri)); |
678 intent.putExtra(EXTRA_SHARE_SCREENSHOT_AS_STREAM, screenshotUri); | 629 intent.putExtra(EXTRA_SHARE_SCREENSHOT_AS_STREAM, screenshotUri); |
679 } | 630 } |
680 if (offlineUri == null) { | 631 if (params.getOfflineUri() == null) { |
681 intent.setType("text/plain"); | 632 intent.setType("text/plain"); |
682 } else { | 633 } else { |
683 intent.setType("multipart/related"); | 634 intent.setType("multipart/related"); |
684 intent.putExtra(Intent.EXTRA_STREAM, offlineUri); | 635 intent.putExtra(Intent.EXTRA_STREAM, params.getOfflineUri()); |
685 } | 636 } |
686 return intent; | 637 return intent; |
687 } | 638 } |
688 | 639 |
689 /** | 640 /** |
690 * Creates an Intent to share an image. | 641 * Creates an Intent to share an image. |
691 * @param imageUri The Uri of the image. | 642 * @param imageUri The Uri of the image. |
692 * @return The Intent used to share the image. | 643 * @return The Intent used to share the image. |
693 */ | 644 */ |
694 public static Intent getShareImageIntent(Uri imageUri) { | 645 public static Intent getShareImageIntent(Uri imageUri) { |
695 Intent intent = new Intent(Intent.ACTION_SEND); | 646 Intent intent = new Intent(Intent.ACTION_SEND); |
696 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); | 647 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
697 intent.setType("image/jpeg"); | 648 intent.setType("image/jpeg"); |
698 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); | 649 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); |
699 intent.putExtra(Intent.EXTRA_STREAM, imageUri); | 650 intent.putExtra(Intent.EXTRA_STREAM, imageUri); |
700 return intent; | 651 return intent; |
701 } | 652 } |
702 | 653 |
703 private static Intent getDirectShareIntentForComponent(Activity activity, St ring title, | 654 /** |
704 String text, String url, Uri offlineUri, Uri screenshotUri, Componen tName component) { | 655 * Convenient method to create an Intent to retrieve all the apps support sh aring text. |
Yusuf
2017/06/02 20:07:46
Convenience
ltian
2017/06/02 23:44:39
Done.
| |
705 Intent intent = getShareIntent(activity, title, text, url, offlineUri, s creenshotUri); | 656 */ |
706 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT | 657 public static Intent getShareLinkAppCompatibilityIntent() { |
707 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); | 658 Intent intent = new Intent(Intent.ACTION_SEND); |
708 intent.setComponent(component); | 659 intent.addFlags(ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
660 intent.putExtra(Intent.EXTRA_SUBJECT, ""); | |
661 intent.putExtra(Intent.EXTRA_TEXT, ""); | |
662 intent.setType("text/plain"); | |
709 return intent; | 663 return intent; |
710 } | 664 } |
711 | 665 |
712 /** | 666 /** |
713 * Gets the {@link ComponentName} of the app that was used to last share. | 667 * Gets the {@link ComponentName} of the app that was used to last share. |
714 */ | 668 */ |
715 @Nullable | 669 @Nullable |
716 public static ComponentName getLastShareComponentName() { | 670 public static ComponentName getLastShareComponentName() { |
717 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); | 671 SharedPreferences preferences = ContextUtils.getAppSharedPreferences(); |
718 String packageName = preferences.getString(PACKAGE_NAME_KEY, null); | 672 String packageName = preferences.getString(PACKAGE_NAME_KEY, null); |
719 String className = preferences.getString(CLASS_NAME_KEY, null); | 673 String className = preferences.getString(CLASS_NAME_KEY, null); |
720 if (packageName == null || className == null) return null; | 674 if (packageName == null || className == null) return null; |
721 return new ComponentName(packageName, className); | 675 return new ComponentName(packageName, className); |
722 } | 676 } |
723 } | 677 } |
OLD | NEW |