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.Bitmap;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.squareup.picasso.Cache;
|
import com.squareup.picasso.Cache;
|
||||||
import com.squareup.picasso.LruCache;
|
import com.squareup.picasso.LruCache;
|
||||||
import com.squareup.picasso.OkHttp3Downloader;
|
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,
|
public static void loadNotificationIcon(final String url,
|
||||||
final Consumer<Bitmap> bitmapConsumer) {
|
final Consumer<Bitmap> bitmapConsumer) {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.schabi.newpipe.util.external_communication;
|
package org.schabi.newpipe.util.external_communication;
|
||||||
|
|
||||||
|
import static org.schabi.newpipe.MainActivity.DEBUG;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
|
@ -7,17 +9,28 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.BuildConfig;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.util.PicassoHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
public final class ShareUtils {
|
public final class ShareUtils {
|
||||||
|
private static final String TAG = ShareUtils.class.getSimpleName();
|
||||||
|
|
||||||
private ShareUtils() {
|
private ShareUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +244,11 @@ public final class ShareUtils {
|
||||||
/**
|
/**
|
||||||
* Open the android share sheet to share a content.
|
* 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
|
* For Android 10+ users, a content preview is shown, which includes the title of the shared
|
||||||
* content.
|
* content and an image preview the content, if its URL is not null or empty and its
|
||||||
* Support sharing the image of the content needs to done, if possible.
|
* corresponding image is in the image cache.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
* @param context the context to use
|
* @param context the context to use
|
||||||
* @param title the title of the content
|
* @param title the title of the content
|
||||||
|
@ -252,13 +267,20 @@ public final class ShareUtils {
|
||||||
shareIntent.putExtra(Intent.EXTRA_SUBJECT, title);
|
shareIntent.putExtra(Intent.EXTRA_SUBJECT, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: add the image of the content to Android share sheet with setClipData after
|
// Content preview in the share sheet has been added in Android 10, so it's not needed to
|
||||||
generating a content URI of this image, then use ClipData.newUri(the content resolver,
|
// set a content preview which will be never displayed
|
||||||
null, the content URI) and set the ClipData to the share intent with
|
// See https://developer.android.com/training/sharing/send#adding-rich-content-previews
|
||||||
shareIntent.setClipData(generated ClipData).
|
// If loading of images has been disabled, don't try to generate a content preview
|
||||||
if (!imagePreviewUrl.isEmpty()) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
//shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
&& !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);
|
openAppChooser(context, shareIntent, false);
|
||||||
}
|
}
|
||||||
|
@ -266,11 +288,11 @@ public final class ShareUtils {
|
||||||
/**
|
/**
|
||||||
* Open the android share sheet to share a content.
|
* 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>
|
* <p>
|
||||||
* This calls {@link #shareText(Context, String, String, String)} with an empty string for the
|
* 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 context the context to use
|
||||||
* @param title the title of the content
|
* @param title the title of the content
|
||||||
|
@ -301,4 +323,81 @@ public final class ShareUtils {
|
||||||
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, text));
|
clipboardManager.setPrimaryClip(ClipData.newPlainText(null, text));
|
||||||
Toast.makeText(context, R.string.msg_copied, Toast.LENGTH_SHORT).show();
|
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