aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/utils')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/FileUtils.java15
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ImageUtil.java136
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/MessageUtil.java106
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UIHelper.java23
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ui/TextViewUtil.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/ui/ViewUtil.java35
6 files changed, 280 insertions, 39 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());
+ 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 ViewUtil.invisible((T) parentView.findViewById(textViewId));
}
- return tv;
+ public static <T extends View> T gone(View parentView, @IdRes int textViewId) {
+ return ViewUtil.gone((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);
+ 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;
+ }
}