diff options
author | steckbrief <steckbrief@chefmail.de> | 2017-02-06 10:01:13 +0100 |
---|---|---|
committer | steckbrief <steckbrief@chefmail.de> | 2017-02-06 10:01:13 +0100 |
commit | 754de6bb0449a577d2bb9c28cca6adf0ef9554f6 (patch) | |
tree | 279b405d94e0d86d10ed94bd34d919457944ead6 /src/main/java/de/thedevstack/conversationsplus/utils | |
parent | cd633f13b8d7327e47994bb5a000f0c0b7089e7f (diff) |
relates FS#241: Implementation of http download based on okhttp
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/utils')
6 files changed, 283 insertions, 42 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java index 77c313f7..95bd60dc 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java @@ -175,7 +175,7 @@ public final class FileUtils { return null; } - String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase(); + String filename = FileUtils.getFilenameFromPath(path).toLowerCase(); final String lastPart = FileUtils.getLastExtension(filename); @@ -254,6 +254,19 @@ public final class FileUtils { return true; } + public static boolean isImage(String mimeType) { + return null != mimeType && mimeType.startsWith("image/"); + } + + public static String getFilenameFromPath(String path) { + String filename = null; + if (null != path && !path.isEmpty()) { + filename = path.substring(path.lastIndexOf('/') + 1); + } + + return filename; + } + private FileUtils() { // Utility class - do not instantiate } diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java index 0b8ace95..eee18cc9 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java @@ -1,25 +1,38 @@ package de.thedevstack.conversationsplus.utils; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.media.ExifInterface; import android.net.Uri; +import android.os.AsyncTask; +import android.util.DisplayMetrics; import android.util.LruCache; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.util.concurrent.RejectedExecutionException; import de.thedevstack.android.logcat.Logging; +import de.thedevstack.conversationsplus.ConversationsPlusApplication; import de.thedevstack.conversationsplus.exceptions.ImageResizeException; import de.thedevstack.conversationsplus.Config; import de.thedevstack.conversationsplus.R; import de.thedevstack.conversationsplus.entities.Message; import de.thedevstack.conversationsplus.persistance.FileBackend; +import de.thedevstack.conversationsplus.utils.ui.ViewUtil; /** * This util provides @@ -373,6 +386,129 @@ public final class ImageUtil { return inSampleSize; } + static class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> { + private final WeakReference<ImageView> imageViewReference; + private final WeakReference<TextView> alternativeViewReference; + private final boolean setSize; + private Message message = null; + + public BitmapWorkerTask(ImageView imageView, TextView alternativeView, boolean setSize) { + imageViewReference = new WeakReference<>(imageView); + alternativeViewReference = new WeakReference<>(alternativeView); + this.setSize = setSize; + } + + @Override + protected Bitmap doInBackground(Message... params) { + message = params[0]; + try { + DisplayMetrics metrics = ConversationsPlusApplication.getAppContext().getResources().getDisplayMetrics(); + return ImageUtil.getThumbnail(message, (int) (metrics.density * 288), false); + } catch (FileNotFoundException e) { + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + final ImageView imageView = imageViewReference.get(); + final TextView alternativeView = alternativeViewReference.get(); + if (bitmap != null) { + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + if (setSize) { + imageView.setLayoutParams(new LinearLayout.LayoutParams( + bitmap.getWidth(), bitmap.getHeight())); + } + ViewUtil.gone(alternativeView); + ViewUtil.visible(imageView); + } + } else { + ViewUtil.visible(alternativeView); + alternativeView.setText(message.getBody()); // TODO Should be the same as MessageAdapter.displayText... or display preview of ConversationAdapter + ViewUtil.gone(imageView); + } + } + } + + public static void loadBitmap(Message message, ImageView imageView, TextView alternativeView, boolean setSize) { + Bitmap bm; + try { + DisplayMetrics metrics = ConversationsPlusApplication.getAppContext().getResources().getDisplayMetrics(); + bm = ImageUtil.getThumbnail(message,(int) (metrics.density * 288), true); + } catch (FileNotFoundException e) { + bm = null; + } + + if (bm != null) { + imageView.setImageBitmap(bm); + imageView.setBackgroundColor(0x00000000); + if (setSize) { + imageView.setLayoutParams(new LinearLayout.LayoutParams( + bm.getWidth(), bm.getHeight())); + } + + ViewUtil.gone(alternativeView); + ViewUtil.visible(imageView); + } else { + if (cancelPotentialWork(message, imageView)) { + imageView.setBackgroundColor(0xff333333); + imageView.setImageDrawable(null); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView, alternativeView, setSize); + final AsyncDrawable asyncDrawable = new AsyncDrawable(ConversationsPlusApplication.getAppContext().getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + ViewUtil.gone(alternativeView); + ViewUtil.visible(imageView); + try { + task.execute(message); + } catch (final RejectedExecutionException ignored) { + } + } + } + } + + public static boolean cancelPotentialWork(Message message, + ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final Message oldMessage = bitmapWorkerTask.message; + if (oldMessage == null || message != oldMessage) { + bitmapWorkerTask.cancel(true); + } else { + return false; + } + } + return true; + } + + private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncDrawable) { + final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; + + public AsyncDrawable(Resources res, Bitmap bitmap, + BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<>( + bitmapWorkerTask); + } + + public BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + private ImageUtil() { // Static helper class } diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java index acd73575..e0d627b9 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java @@ -27,6 +27,14 @@ public final class MessageUtil { if (null == message) { return; } + + // Ensure that for every message the fileParams are set after calling this method + FileParams fileParams = message.getFileParams(); + if (null == fileParams) { + fileParams = new FileParams(); + message.setFileParams(fileParams); + } + String body = message.getBody(); /** * there are a few cases where spaces result in an unwanted behavior, e.g. @@ -37,24 +45,27 @@ public final class MessageUtil { return; } - FileParams fileParams = message.getFileParams(); - if (null == fileParams) { - fileParams = new FileParams(); - message.setFileParams(fileParams); - } - try { URL url = new URL(body); if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { message.setTreatAsDownloadable(Message.Decision.NEVER); return; } + String extension = FileUtils.getRelevantExtension(url); if (message.isHttpUploaded()) { fileParams.setUrl(url.toString()); - message.setTreatAsDownloadable(Message.Decision.MUST); + if (null != extension + && (Transferable.WELL_KNOWN_EXTENSIONS.contains(extension.toLowerCase()) || Transferable.VALID_IMAGE_EXTENSIONS.contains(extension.toLowerCase()))) { + message.setTreatAsDownloadable(Message.Decision.MUST); + } else { + message.setTreatAsDownloadable(Message.Decision.NEVER); + fileParams.setFileStatus(FileStatus.UNDEFINED); + } + + extractFilename(message, url.toString()); return; } - String extension = FileUtils.getRelevantExtension(url); + if (extension == null) { message.setTreatAsDownloadable(Message.Decision.NEVER); return; @@ -65,23 +76,100 @@ public final class MessageUtil { if (MimeUtils.guessMimeTypeFromExtension(extension) != null) { message.setTreatAsDownloadable(Message.Decision.MUST); fileParams.setKeyAndIv(ivAndKey); - fileParams.setUrl(url.toString()); } else { message.setTreatAsDownloadable(Message.Decision.NEVER); } } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension) || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) { message.setTreatAsDownloadable(Message.Decision.SHOULD); - fileParams.setUrl(url.toString()); } else { message.setTreatAsDownloadable(Message.Decision.NEVER); } + if (message.treatAsDownloadable() == Message.Decision.MUST + || message.treatAsDownloadable() == Message.Decision.SHOULD) { + fileParams.setUrl(url.toString()); + extractFilename(message, url.toString()); + } } catch (MalformedURLException e) { message.setTreatAsDownloadable(Message.Decision.NEVER); } } + private static void extractFilename(Message message, String url) { + String originalFilename = FileUtils.getFilenameFromPath(url); + final String lowerCaseFilename = originalFilename.toLowerCase(); + final String lastPart = FileUtils.getLastExtension(lowerCaseFilename); + + detectAndSetEncryption(lastPart, message); + + String filenameExtension; + if (!lastPart.isEmpty() && Transferable.VALID_CRYPTO_EXTENSIONS.contains(lastPart)) { + filenameExtension = FileUtils.getSecondToLastExtension(lowerCaseFilename); + originalFilename = originalFilename.replace("." + lastPart, ""); + } else { + filenameExtension = lastPart; + } + message.setRelativeFilePath(message.getUuid() + "." + filenameExtension); + + message.getFileParams().setOriginalFilename(originalFilename); + } + + private static void detectAndSetEncryption(String lastPart, Message message) { + if (!lastPart.isEmpty() && ("pgp".equals(lastPart) || "gpg".equals(lastPart))) { + message.setEncryption(Message.ENCRYPTION_PGP); + } else if (message.getEncryption() != Message.ENCRYPTION_OTR + && message.getEncryption() != Message.ENCRYPTION_AXOLOTL) { + message.setEncryption(Message.ENCRYPTION_NONE); + } else if ((message.getEncryption() == Message.ENCRYPTION_OTR + || message.getEncryption() == Message.ENCRYPTION_AXOLOTL) + && message.getFileParams() != null && message.getFileParams().getKey() == null) { + // If an encryption is set for the message, but no key given -> decryption not possible + message.setEncryption(Message.ENCRYPTION_NONE); + } + } + + /** + * Checks if an attached file is an image or not. + * Prerequisite for calling this method: The check if a file is attached is done before. + * @param message + * @return + */ + public static boolean isAttachedFileAnImage(Message message) { + final FileParams fileParams = message.getFileParams(); + String mimeType = (null != fileParams) ? fileParams.getMimeType() : null; + return message.getType() == Message.TYPE_IMAGE + || (fileParams != null && fileParams.getWidth() > 0) + || (null != mimeType && mimeType.startsWith("image/")); + } + + public static boolean isTypeFileAndDecrypted(Message message) { + return message.getType() == Message.TYPE_FILE + && message.getEncryption() != Message.ENCRYPTION_PGP + && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED; + } + + public static boolean isDecrypted(Message message) { + return message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED; + } + + /** + * Checks if the file status of an message is <b>NOT</b> one of: + * <ul> + * <li>FileStatus#NOT_FOUND</li> + * <li>FileStatus#DELETED</li> + * </ul> + * The file status does not guarantee that a file is really available or not. + * @param message + * @return + */ + public static boolean mayFileRemoteAvailable(Message message) { + FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null; + return null != fileStatus + && FileStatus.NOT_FOUND != fileStatus + && FileStatus.DELETED != fileStatus; + } + public static boolean needsDownload(Message message) { FileStatus fileStatus = (null != message.getFileParams()) ? message.getFileParams().getFileStatus() : null; return (null == fileStatus diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java index a2ed6d81..e3680b81 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java @@ -291,31 +291,28 @@ public class UIHelper { } public static String getHumanReadableFileSize(long size) { - String filesize; - if (size > (1.5 * 1024 * 1024)) { - filesize = size / (1024 * 1024)+ " MiB"; - } else if (size > 0) { - filesize = size / 1024 + " KiB"; - } else { - filesize = null; - } + return getHumanReadableDetailedFileSize(size, 1); + } - return filesize; + public static String getHumanReadableDetailedFileSize(long filesize) { + String size = getHumanReadableDetailedFileSize(filesize, 2); + return null == size ? "?" : size; } - public static String getHumanReadableDetailedFileSize(long filesize) { + public static String getHumanReadableDetailedFileSize(long filesize, int precision) { if (0 > filesize) { - return "?"; + return null; } double size = Double.valueOf(filesize); String[] sizes = {" bytes", " Kb", " Mb", " Gb", " Tb"}; int i = 0; - while (1023 < size) { + while (1023 < size && i < sizes.length - 1) { size /= 1024d; ++i; } + BigDecimal readableSize = new BigDecimal(size); - readableSize = readableSize.setScale(2, BigDecimal.ROUND_HALF_UP); - return readableSize.doubleValue() + sizes[i]; + readableSize = readableSize.setScale(precision, BigDecimal.ROUND_HALF_UP); + return ((0 == i) ? String.valueOf(readableSize.intValue()) : String.valueOf(readableSize.doubleValue())) + sizes[i]; } } diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java index 27a269f2..faa9a5ed 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java @@ -4,6 +4,8 @@ import android.support.annotation.StringRes; import android.view.View; import android.widget.TextView; +import de.thedevstack.conversationsplus.ConversationsPlusColors; + /** * */ @@ -61,7 +63,7 @@ public final class TextViewUtil extends ViewUtil { public static void setColorEnabledAndTextResId(TextView tv, Integer color, Boolean enabled, @StringRes Integer resid) { if (null != color) { - tv.setTextColor(color); + tv.setTextColor(ConversationsPlusColors.byId(color)); } if (enabled != null) { diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/ui/ViewUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/ui/ViewUtil.java index 77422587..170a6401 100644 --- a/src/main/java/de/thedevstack/conversationsplus/utils/ui/ViewUtil.java +++ b/src/main/java/de/thedevstack/conversationsplus/utils/ui/ViewUtil.java @@ -10,30 +10,35 @@ import android.view.View; public class ViewUtil { public static <T extends View> T visible(View parentView, @IdRes int textViewId) { - T tv = (T) parentView.findViewById(textViewId); - if (null != tv) { - tv.setVisibility(View.VISIBLE); - } - - return tv; + return ViewUtil.visible((T) parentView.findViewById(textViewId)); } public static <T extends View> T invisible(View parentView, @IdRes int textViewId) { - T tv = (T) parentView.findViewById(textViewId); - if (null != tv) { - tv.setVisibility(View.INVISIBLE); - } - - return tv; + return ViewUtil.invisible((T) parentView.findViewById(textViewId)); } public static <T extends View> T gone(View parentView, @IdRes int textViewId) { - T tv = (T) parentView.findViewById(textViewId); - if (null != tv) { - tv.setVisibility(View.GONE); + return ViewUtil.gone((T) parentView.findViewById(textViewId)); + } + + public static <T extends View> T gone(T view) { + if (null != view) { + view.setVisibility(View.GONE); } + return view; + } - return tv; + public static <T extends View> T visible(T view) { + if (null != view) { + view.setVisibility(View.VISIBLE); + } + return view; } + public static <T extends View> T invisible(T view) { + if (null != view) { + view.setVisibility(View.INVISIBLE); + } + return view; + } } |