diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index b1045bd4ec4..af6ca077c1d 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -234,7 +234,7 @@ private void showUnsupportedUrlDialog(final String url) { .setPositiveButton(R.string.open_in_browser, (dialog, which) -> ShareUtils.openUrlInBrowser(this, url)) .setNegativeButton(R.string.share, - (dialog, which) -> ShareUtils.shareText(this, "", url, false)) //no subject + (dialog, which) -> ShareUtils.shareText(this, "", url, "")) //no subject .setNeutralButton(R.string.cancel, null) .setOnDismissListener(dialog -> finish()) .show(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 784a1c3be7d..47257dd1f7e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -454,8 +454,8 @@ public void onClick(final View v) { break; case R.id.detail_controls_share: if (currentInfo != null) { - ShareUtils.shareText(requireContext(), - currentInfo.getName(), currentInfo.getUrl()); + ShareUtils.shareText(requireContext(), currentInfo.getName(), + currentInfo.getUrl(), currentInfo.getThumbnailUrl()); } break; case R.id.detail_controls_open_in_browser: diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index e02e18a8636..a94d6003239 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -203,7 +203,8 @@ public boolean onOptionsItemSelected(final MenuItem item) { break; case R.id.menu_item_share: if (currentInfo != null) { - ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl()); + ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(), + currentInfo.getAvatarUrl()); } break; default: diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 0e36d18c702..f7d3b34f8b3 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -251,7 +251,7 @@ public boolean onOptionsItemSelected(final MenuItem item) { ShareUtils.openUrlInBrowser(requireContext(), url); break; case R.id.menu_item_share: - ShareUtils.shareText(requireContext(), name, url); + ShareUtils.shareText(requireContext(), name, url, currentInfo.getThumbnailUrl()); break; case R.id.menu_item_bookmark: onBookmarkClicked(); diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt index 095c8dbc772..060e5066b58 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt @@ -293,7 +293,8 @@ class SubscriptionFragment : BaseStateFragment() { val actions = DialogInterface.OnClickListener { _, i -> when (i) { - 0 -> ShareUtils.shareText(requireContext(), selectedItem.name, selectedItem.url) + 0 -> ShareUtils.shareText(requireContext(), selectedItem.name, selectedItem.url, + selectedItem.thumbnailUrl) 1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url) 2 -> deleteChannel(selectedItem) } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index 29c9ac77b60..403848dd6e8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -313,7 +313,8 @@ private void buildItemPopupMenu(final PlayQueueItem item, final View view) { final MenuItem share = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 3, Menu.NONE, R.string.share); share.setOnMenuItemClickListener(menuItem -> { - shareText(getApplicationContext(), item.getTitle(), item.getUrl()); + shareText(getApplicationContext(), item.getTitle(), item.getUrl(), + item.getThumbnailUrl()); return true; }); diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 0c5dbbb6fe7..7600346ae46 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3593,7 +3593,8 @@ public void onClick(final View v) { } else if (v.getId() == binding.moreOptionsButton.getId()) { onMoreOptionsClicked(); } else if (v.getId() == binding.share.getId()) { - ShareUtils.shareText(context, getVideoTitle(), getVideoUrlAtCurrentTime()); + ShareUtils.shareText(context, getVideoTitle(), getVideoUrlAtCurrentTime(), + currentItem.getThumbnailUrl()); } else if (v.getId() == binding.playWithKodi.getId()) { onPlayWithKodiClicked(); } else if (v.getId() == binding.openInBrowser.getId()) { diff --git a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java index 4474d1811b3..682830741ae 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/ShareUtils.java @@ -8,6 +8,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Build; import android.widget.Toast; import androidx.core.content.ContextCompat; @@ -25,15 +26,15 @@ private ShareUtils() { * second param (a system chooser will be opened if there are multiple markets and no default) * and falls back to Google Play Store web URL if no app to handle the market scheme was found. *

- * It uses {@link ShareUtils#openIntentInApp(Context, Intent, boolean)} to open market scheme - * and {@link ShareUtils#openUrlInBrowser(Context, String, boolean)} to open Google Play Store + * It uses {@link #openIntentInApp(Context, Intent, boolean)} to open market scheme + * and {@link #openUrlInBrowser(Context, String, boolean)} to open Google Play Store * web URL with false for the boolean param. * * @param context the context to use * @param packageId the package id of the app to be installed */ public static void installApp(final Context context, final String packageId) { - // Try market:// scheme + // Try market scheme final boolean marketSchemeResult = openIntentInApp(context, new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + packageId)) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK), false); @@ -48,7 +49,7 @@ public static void installApp(final Context context, final String packageId) { * Open the url with the system default browser. *

* If no browser is set as default, fallbacks to - * {@link ShareUtils#openAppChooser(Context, Intent, boolean)} + * {@link #openAppChooser(Context, Intent, boolean)} * * @param context the context to use * @param url the url to browse @@ -56,7 +57,8 @@ public static void installApp(final Context context, final String packageId) { * for HTTP protocol or for the created intent * @return true if the URL can be opened or false if it cannot */ - public static boolean openUrlInBrowser(final Context context, final String url, + public static boolean openUrlInBrowser(final Context context, + final String url, final boolean httpDefaultBrowserTest) { final String defaultPackageName; final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)) @@ -96,9 +98,9 @@ public static boolean openUrlInBrowser(final Context context, final String url, * Open the url with the system default browser. *

* If no browser is set as default, fallbacks to - * {@link ShareUtils#openAppChooser(Context, Intent, boolean)} + * {@link #openAppChooser(Context, Intent, boolean)} *

- * This calls {@link ShareUtils#openUrlInBrowser(Context, String, boolean)} with true + * This calls {@link #openUrlInBrowser(Context, String, boolean)} with true * for the boolean parameter * * @param context the context to use @@ -113,19 +115,20 @@ public static boolean openUrlInBrowser(final Context context, final String url) * Open an intent with the system default app. *

* The intent can be of every type, excepted a web intent for which - * {@link ShareUtils#openUrlInBrowser(Context, String, boolean)} should be used. + * {@link #openUrlInBrowser(Context, String, boolean)} should be used. *

* If no app is set as default, fallbacks to - * {@link ShareUtils#openAppChooser(Context, Intent, boolean)}. + * {@link #openAppChooser(Context, Intent, boolean)}. *

* * @param context the context to use * @param intent the intent to open - * @param showToast the boolean to set if a toast is displayed to user when no app is installed + * @param showToast a boolean to set if a toast is displayed to user when no app is installed * to open the intent (true) or not (false) * @return true if the intent can be opened or false if it cannot be */ - public static boolean openIntentInApp(final Context context, final Intent intent, + public static boolean openIntentInApp(final Context context, + final Intent intent, final boolean showToast) { final String defaultPackageName = getDefaultAppPackageName(context, intent); @@ -178,6 +181,36 @@ private static void openAppChooser(final Context context, if (setTitleChooser) { chooserIntent.putExtra(Intent.EXTRA_TITLE, context.getString(R.string.open_with)); } + + // Migrate any clip data and flags from the original intent. + final int permFlags; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + permFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); + } else { + permFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + } + if (permFlags != 0) { + ClipData targetClipData = intent.getClipData(); + if (targetClipData == null && intent.getData() != null) { + final ClipData.Item item = new ClipData.Item(intent.getData()); + final String[] mimeTypes; + if (intent.getType() != null) { + mimeTypes = new String[] {intent.getType()}; + } else { + mimeTypes = new String[] {}; + } + targetClipData = new ClipData(null, mimeTypes, item); + } + if (targetClipData != null) { + chooserIntent.setClipData(targetClipData); + chooserIntent.addFlags(permFlags); + } + } context.startActivity(chooserIntent); } @@ -208,24 +241,45 @@ private static String getDefaultAppPackageName(final Context context, final Inte /** * Open the android share menu to share the current url. * - * @param context the context to use - * @param subject the url subject, typically the title - * @param url the url to share + * @param context the context to use + * @param subject the url subject, typically the title + * @param url the url to share + * @param imagePreviewUrl the image of the subject */ - public static void shareText(final Context context, final String subject, final String url) { - shareText(context, subject, url, true); + public static void shareText(final Context context, + final String subject, + final String url, + final String imagePreviewUrl) { + shareText(context, subject, url, imagePreviewUrl, true); } - + /** + * Open the android share sheet to share the current url. + * + * For Android 10+ users, a content preview is shown, which includes the title of the shared + * content. + * Support sharing the image of the content needs to done, if possible. + * + * @param context the context to use + * @param subject the url subject, typically the title + * @param url the url to share + * @param imagePreviewUrl the image of the subject + * @param showPreviewText show the subject as an extra title of the Android share sheet if true + */ public static void shareText(final Context context, final String subject, final String url, + final String imagePreviewUrl, final boolean showPreviewText) { final Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); - if (!subject.isEmpty() && showPreviewText) { - shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + if (!imagePreviewUrl.isEmpty() && !subject.isEmpty() && showPreviewText) { shareIntent.putExtra(Intent.EXTRA_TITLE, subject); + /* TODO: add the image of the content to Android share sheet with setClipData after + generating a content URI of this image, then use ClipData.newUri(the content + resolver, null, the content URI) and set the ClipData to the share intent with + shareIntent.setClipData(generated ClipData).*/ + //shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } shareIntent.putExtra(Intent.EXTRA_TEXT, url); diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java index 610f9f85237..a8ee1ebaaee 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamDialogEntry.java @@ -88,7 +88,8 @@ public enum StreamDialogEntry { }), share(R.string.share, (fragment, item) -> - ShareUtils.shareText(fragment.getContext(), item.getName(), item.getUrl())), + ShareUtils.shareText(fragment.getContext(), item.getName(), item.getUrl(), + item.getThumbnailUrl())), open_in_browser(R.string.open_in_browser, (fragment, item) -> ShareUtils.openUrlInBrowser(fragment.getContext(), item.getUrl())); diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index b31933dfdd2..44c20b5415c 100644 --- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java +++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java @@ -376,8 +376,11 @@ private void shareFile(Mission mission) { final Intent intent = new Intent(Intent.ACTION_CHOOSER); intent.putExtra(Intent.EXTRA_INTENT, shareIntent); - intent.putExtra(Intent.EXTRA_TITLE, mContext.getString(R.string.share_dialog_title)); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1) { + intent.putExtra(Intent.EXTRA_TITLE, mContext.getString(R.string.share_dialog_title)); + } intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); mContext.startActivity(intent); }