Merge pull request #8143 from TiA4f8R/image-preview-sharesheet
Add image preview of the content shared where possible in Android share sheet (for Android 10+ devices only)
This commit is contained in:
commit
8c870cd3ca
|
@ -7,6 +7,8 @@ import android.content.Context;
|
|||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.squareup.picasso.Cache;
|
||||
import com.squareup.picasso.LruCache;
|
||||
import com.squareup.picasso.OkHttp3Downloader;
|
||||
|
@ -158,6 +160,11 @@ public final class PicassoHelper {
|
|||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Bitmap getImageFromCacheIfPresent(final String imageUrl) {
|
||||
// URLs in the internal cache finish with \n so we need to add \n to image URLs
|
||||
return picassoCache.get(imageUrl + "\n");
|
||||
}
|
||||
|
||||
public static void loadNotificationIcon(final String url,
|
||||
final Consumer<Bitmap> bitmapConsumer) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.schabi.newpipe.util.external_communication;
|
||||
|
||||
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
|
@ -7,17 +9,28 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import org.schabi.newpipe.BuildConfig;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.PicassoHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
public final class ShareUtils {
|
||||
private static final String TAG = ShareUtils.class.getSimpleName();
|
||||
|
||||
private ShareUtils() {
|
||||
}
|
||||
|
||||
|
@ -231,9 +244,11 @@ public final class ShareUtils {
|
|||
/**
|
||||
* Open the android share sheet to share a content.
|
||||
*
|
||||
* <p>
|
||||
* 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.
|
||||
* content and an image preview the content, if its URL is not null or empty and its
|
||||
* corresponding image is in the image cache.
|
||||
* </p>
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param title the title of the content
|
||||
|
@ -252,13 +267,20 @@ public final class ShareUtils {
|
|||
shareIntent.putExtra(Intent.EXTRA_SUBJECT, title);
|
||||
}
|
||||
|
||||
/* 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).
|
||||
if (!imagePreviewUrl.isEmpty()) {
|
||||
//shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}*/
|
||||
// Content preview in the share sheet has been added in Android 10, so it's not needed to
|
||||
// set a content preview which will be never displayed
|
||||
// See https://developer.android.com/training/sharing/send#adding-rich-content-previews
|
||||
// If loading of images has been disabled, don't try to generate a content preview
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& !TextUtils.isEmpty(imagePreviewUrl)
|
||||
&& PicassoHelper.getShouldLoadImages()) {
|
||||
|
||||
final ClipData clipData = generateClipDataForImagePreview(context, imagePreviewUrl);
|
||||
if (clipData != null) {
|
||||
shareIntent.setClipData(clipData);
|
||||
shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
openAppChooser(context, shareIntent, false);
|
||||
}
|
||||
|
@ -266,11 +288,11 @@ public final class ShareUtils {
|
|||
/**
|
||||
* Open the android share sheet to share a content.
|
||||
*
|
||||
* For Android 10+ users, a content preview is shown, which includes the title of the shared
|
||||
* content.
|
||||
* <p>
|
||||
* This calls {@link #shareText(Context, String, String, String)} with an empty string for the
|
||||
* imagePreviewUrl parameter.
|
||||
* {@code imagePreviewUrl} parameter. This method should be used when the shared content has no
|
||||
* preview thumbnail.
|
||||
* </p>
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param title the title of the content
|
||||
|
@ -301,4 +323,81 @@ public final class ShareUtils {
|
|||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, text));
|
||||
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a {@link ClipData} with the image of the content shared, if it's in the app cache.
|
||||
*
|
||||
* <p>
|
||||
* In order not to worry about network issues (timeouts, DNS issues, low connection speed, ...)
|
||||
* when sharing a content, only images in the {@link com.squareup.picasso.LruCache LruCache}
|
||||
* used by the Picasso library inside {@link PicassoHelper} are used as preview images. If the
|
||||
* thumbnail image is not in the cache, no {@link ClipData} will be generated and {@code null}
|
||||
* will be returned.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* In order to display the image in the content preview of the Android share sheet, an URI of
|
||||
* the content, accessible and readable by other apps has to be generated, so a new file inside
|
||||
* the application cache will be generated, named {@code android_share_sheet_image_preview.jpg}
|
||||
* (if a file under this name already exists, it will be overwritten). The thumbnail will be
|
||||
* compressed in JPEG format, with a {@code 90} compression level.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Note that if an exception occurs when generating the {@link ClipData}, {@code null} is
|
||||
* returned.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This method will call {@link PicassoHelper#getImageFromCacheIfPresent(String)} to get the
|
||||
* thumbnail of the content in the {@link com.squareup.picasso.LruCache LruCache} used by
|
||||
* the Picasso library inside {@link PicassoHelper}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Using the result of this method when sharing has only an effect on the system share sheet (if
|
||||
* OEMs didn't change Android system standard behavior) on Android API 29 and higher.
|
||||
* </p>
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param thumbnailUrl the URL of the content thumbnail
|
||||
* @return a {@link ClipData} of the content thumbnail, or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
private static ClipData generateClipDataForImagePreview(
|
||||
@NonNull final Context context,
|
||||
@NonNull final String thumbnailUrl) {
|
||||
try {
|
||||
final Bitmap bitmap = PicassoHelper.getImageFromCacheIfPresent(thumbnailUrl);
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Save the image in memory to the application's cache because we need a URI to the
|
||||
// image to generate a ClipData which will show the share sheet, and so an image file
|
||||
final Context applicationContext = context.getApplicationContext();
|
||||
final String appFolder = applicationContext.getCacheDir().getAbsolutePath();
|
||||
final File thumbnailPreviewFile = new File(appFolder
|
||||
+ "/android_share_sheet_image_preview.jpg");
|
||||
|
||||
// Any existing file will be overwritten with FileOutputStream
|
||||
final FileOutputStream fileOutputStream = new FileOutputStream(thumbnailPreviewFile);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
|
||||
fileOutputStream.close();
|
||||
|
||||
final ClipData clipData = ClipData.newUri(applicationContext.getContentResolver(), "",
|
||||
FileProvider.getUriForFile(applicationContext,
|
||||
BuildConfig.APPLICATION_ID + ".provider",
|
||||
thumbnailPreviewFile));
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "ClipData successfully generated for Android share sheet: " + clipData);
|
||||
}
|
||||
return clipData;
|
||||
|
||||
} catch (final Exception e) {
|
||||
Log.w(TAG, "Error when setting preview image for share sheet", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue