display PDF previews

This commit is contained in:
Daniel Gultsch 2020-02-11 17:41:54 +01:00
parent 6acb15dd15
commit 2aee26c49a
3 changed files with 88 additions and 19 deletions

View File

@ -11,6 +11,7 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.pdf.PdfRenderer;
import android.media.MediaMetadataRetriever;
import android.media.MediaScannerConnection;
import android.net.Uri;
@ -25,6 +26,7 @@ import android.system.Os;
import android.system.StructStat;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LruCache;
@ -59,6 +61,7 @@ import eu.siacs.conversations.services.AttachFileToConversationRunnable;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.RecordingActivity;
import eu.siacs.conversations.ui.util.Attachment;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.ExifHelper;
import eu.siacs.conversations.utils.FileUtils;
@ -509,7 +512,6 @@ public class FileBackend {
}
public DownloadableFile getFileForPath(String path) {
return getFileForPath(path, MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(path)));
}
@ -818,7 +820,9 @@ public class FileBackend {
}
DownloadableFile file = getFile(message);
final String mime = file.getMimeType();
if (mime.startsWith("video/")) {
if ("application/pdf".equals(mime) && Compatibility.runsTwentyOne()) {
thumbnail = getPdfDocumentPreview(file, size);
} else if (mime.startsWith("video/")) {
thumbnail = getVideoPreview(file, size);
} else {
Bitmap fullsize = getFullsizeImagePreview(file, size);
@ -897,8 +901,8 @@ public class FileBackend {
}
}
private Bitmap getVideoPreview(File file, int size) {
MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
private Bitmap getVideoPreview(final File file, final int size) {
final MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
Bitmap frame;
try {
metadataRetriever.setDataSource(file.getAbsolutePath());
@ -913,6 +917,24 @@ public class FileBackend {
return frame;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Bitmap getPdfDocumentPreview(final File file, final int size) {
try {
final ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
final PdfRenderer pdfRenderer = new PdfRenderer(fileDescriptor);
final PdfRenderer.Page page = pdfRenderer.openPage(0);
Dimensions dimensions = scalePdfDimensions(new Dimensions(page.getHeight(), page.getWidth()));
final Bitmap rendered = Bitmap.createBitmap(dimensions.width, dimensions.height, Bitmap.Config.ARGB_8888);
page.render(rendered, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
return rendered;
} catch (IOException e) {
Log.d(Config.LOGTAG, "unable to render PDF document preview", e);
final Bitmap placeholder = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
placeholder.eraseColor(0xff000000);
return placeholder;
}
}
public Uri getTakePhotoUri() {
File file;
if (Config.ONLY_INTERNAL_STORAGE) {
@ -1210,14 +1232,22 @@ public class FileBackend {
final boolean image = message.getType() == Message.TYPE_IMAGE || (mime != null && mime.startsWith("image/"));
final boolean video = mime != null && mime.startsWith("video/");
final boolean audio = mime != null && mime.startsWith("audio/");
final boolean pdf = "application/pdf".equals(mime);
final StringBuilder body = new StringBuilder();
if (url != null) {
body.append(url.toString());
}
body.append('|').append(file.getSize());
if (image || video) {
if (image || video || (pdf && Compatibility.runsTwentyOne())) {
try {
Dimensions dimensions = image ? getImageDimensions(file) : getVideoDimensions(file);
final Dimensions dimensions;
if (video) {
dimensions = getVideoDimensions(file);
} else if (pdf && Compatibility.runsTwentyOne()) {
dimensions = getPdfDocumentDimensions(file);
} else {
dimensions = getImageDimensions(file);
}
if (dimensions.valid()) {
body.append('|').append(dimensions.width).append('|').append(dimensions.height);
}
@ -1264,6 +1294,45 @@ public class FileBackend {
return getVideoDimensions(metadataRetriever);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private Dimensions getPdfDocumentDimensions(final File file) {
final ParcelFileDescriptor fileDescriptor;
try {
fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
if (fileDescriptor == null) {
return new Dimensions(0, 0);
}
} catch (FileNotFoundException e) {
return new Dimensions(0, 0);
}
try {
final PdfRenderer pdfRenderer = new PdfRenderer(fileDescriptor);
final PdfRenderer.Page page = pdfRenderer.openPage(0);
final int height = page.getHeight();
final int width = page.getWidth();
page.close();
pdfRenderer.close();
return scalePdfDimensions(new Dimensions(height, width));
} catch (IOException e) {
Log.d(Config.LOGTAG, "unable to get dimensions for pdf document", e);
return new Dimensions(0, 0);
}
}
private Dimensions scalePdfDimensions(Dimensions in) {
final DisplayMetrics displayMetrics = mXmppConnectionService.getResources().getDisplayMetrics();
final int target = (int) (displayMetrics.density * 288);
final int w, h;
if (in.width <= in.height) {
w = Math.max((int) (in.width / ((double) in.height / target)), 1);
h = target;
} else {
w = target;
h = Math.max((int) (in.height / ((double) in.width / target)), 1);
}
return new Dimensions(h, w);
}
public Bitmap getAvatar(String avatar, int size) {
if (avatar == null) {
return null;
@ -1275,10 +1344,6 @@ public class FileBackend {
return bm;
}
public boolean isFileAvailable(Message message) {
return getFile(message).exists();
}
private static class Dimensions {
public final int width;
public final int height;

View File

@ -540,15 +540,15 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
this.audioPlayer.init(audioPlayer, message);
}
private void displayImageMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
private void displayMediaPreviewMessage(ViewHolder viewHolder, final Message message, final boolean darkBackground) {
toggleWhisperInfo(viewHolder, message, darkBackground);
viewHolder.download_button.setVisibility(View.GONE);
viewHolder.audioPlayer.setVisibility(View.GONE);
viewHolder.image.setVisibility(View.VISIBLE);
FileParams params = message.getFileParams();
double target = metrics.density * 288;
int scaledW;
int scaledH;
final FileParams params = message.getFileParams();
final double target = metrics.density * 288;
final int scaledW;
final int scaledH;
if (Math.max(params.height, params.width) * metrics.density <= target) {
scaledW = (int) (params.width * metrics.density);
scaledH = (int) (params.height * metrics.density);
@ -747,7 +747,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
}
} else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
if (message.getFileParams().width > 0 && message.getFileParams().height > 0) {
displayImageMessage(viewHolder, message, darkBackground);
displayMediaPreviewMessage(viewHolder, message, darkBackground);
} else if (message.getFileParams().runtime > 0) {
displayAudioMessage(viewHolder, message, darkBackground);
} else {

View File

@ -38,14 +38,18 @@ public class Compatibility {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
public static boolean runsTwentySix() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
public static boolean runsTwentyOne() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
public static boolean runsTwentyFour() {
private static boolean runsTwentyFour() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
public static boolean runsTwentySix() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
public static boolean twentyEight() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}