From 1e63a8291579043659f86dbb46d4d63d3225c471 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 23 Sep 2018 16:42:56 +0200 Subject: preview media before sending them and allow sharing and attaching of multiple files --- src/main/AndroidManifest.xml | 12 +- .../pixart/messenger/persistance/FileBackend.java | 51 +++- .../services/ContactChooserTargetService.java | 6 +- .../pixart/messenger/ui/ConversationFragment.java | 224 ++++++---------- .../pixart/messenger/ui/ConversationsActivity.java | 15 +- .../de/pixart/messenger/ui/SearchActivity.java | 8 +- .../de/pixart/messenger/ui/SettingsActivity.java | 3 +- .../de/pixart/messenger/ui/ShareWithActivity.java | 297 +++------------------ .../messenger/ui/adapter/AccountAdapter.java | 8 +- .../messenger/ui/adapter/ConversationAdapter.java | 10 +- .../messenger/ui/adapter/ListItemAdapter.java | 8 +- .../messenger/ui/adapter/MediaPreviewAdapter.java | 211 +++++++++++++++ .../messenger/ui/forms/FormFieldWrapper.java | 8 +- .../de/pixart/messenger/ui/util/Attachment.java | 120 +++++++++ .../pixart/messenger/ui/util/AttachmentTool.java | 60 ----- .../java/de/pixart/messenger/ui/util/Color.java | 48 ---- .../java/de/pixart/messenger/ui/util/Drawable.java | 43 --- .../pixart/messenger/ui/util/StyledAttributes.java | 60 +++++ .../messenger/utils/IrregularUnicodeDetector.java | 4 +- .../res/drawable-hdpi/ic_android_black_48dp.png | Bin 0 -> 710 bytes .../res/drawable-hdpi/ic_android_white_48dp.png | Bin 0 -> 733 bytes .../res/drawable-hdpi/ic_archive_black_48dp.png | Bin 0 -> 560 bytes .../res/drawable-hdpi/ic_archive_white_48dp.png | Bin 0 -> 557 bytes .../drawable-hdpi/ic_description_black_48dp.png | Bin 0 -> 405 bytes .../drawable-hdpi/ic_description_white_48dp.png | Bin 0 -> 421 bytes src/main/res/drawable-hdpi/ic_event_black_48dp.png | Bin 0 -> 344 bytes src/main/res/drawable-hdpi/ic_event_white_48dp.png | Bin 0 -> 342 bytes .../res/drawable-hdpi/ic_headset_black_48dp.png | Bin 0 -> 857 bytes .../res/drawable-hdpi/ic_headset_white_48dp.png | Bin 0 -> 865 bytes src/main/res/drawable-hdpi/ic_mic_black_48dp.png | Bin 0 -> 866 bytes src/main/res/drawable-hdpi/ic_mic_white_48dp.png | Bin 0 -> 870 bytes .../res/drawable-hdpi/ic_person_black_48dp.png | Bin 0 -> 622 bytes .../res/drawable-hdpi/ic_person_white_48dp.png | Bin 0 -> 623 bytes src/main/res/drawable-hdpi/ic_room_black_48dp.png | Bin 0 -> 948 bytes src/main/res/drawable-hdpi/ic_room_white_48dp.png | Bin 0 -> 966 bytes .../res/drawable-mdpi/ic_android_black_48dp.png | Bin 0 -> 410 bytes .../res/drawable-mdpi/ic_android_white_48dp.png | Bin 0 -> 416 bytes .../res/drawable-mdpi/ic_archive_black_48dp.png | Bin 0 -> 309 bytes .../res/drawable-mdpi/ic_archive_white_48dp.png | Bin 0 -> 306 bytes .../drawable-mdpi/ic_description_black_48dp.png | Bin 0 -> 247 bytes .../drawable-mdpi/ic_description_white_48dp.png | Bin 0 -> 257 bytes src/main/res/drawable-mdpi/ic_event_black_48dp.png | Bin 0 -> 228 bytes src/main/res/drawable-mdpi/ic_event_white_48dp.png | Bin 0 -> 221 bytes .../res/drawable-mdpi/ic_headset_black_48dp.png | Bin 0 -> 466 bytes .../res/drawable-mdpi/ic_headset_white_48dp.png | Bin 0 -> 473 bytes src/main/res/drawable-mdpi/ic_mic_black_48dp.png | Bin 0 -> 487 bytes src/main/res/drawable-mdpi/ic_mic_white_48dp.png | Bin 0 -> 489 bytes .../res/drawable-mdpi/ic_person_black_48dp.png | Bin 0 -> 359 bytes .../res/drawable-mdpi/ic_person_white_48dp.png | Bin 0 -> 351 bytes src/main/res/drawable-mdpi/ic_room_black_48dp.png | Bin 0 -> 524 bytes src/main/res/drawable-mdpi/ic_room_white_48dp.png | Bin 0 -> 537 bytes .../res/drawable-xhdpi/ic_android_black_48dp.png | Bin 0 -> 838 bytes .../res/drawable-xhdpi/ic_android_white_48dp.png | Bin 0 -> 852 bytes .../res/drawable-xhdpi/ic_archive_black_48dp.png | Bin 0 -> 607 bytes .../res/drawable-xhdpi/ic_archive_white_48dp.png | Bin 0 -> 620 bytes .../drawable-xhdpi/ic_description_black_48dp.png | Bin 0 -> 460 bytes .../drawable-xhdpi/ic_description_white_48dp.png | Bin 0 -> 485 bytes .../res/drawable-xhdpi/ic_event_black_48dp.png | Bin 0 -> 435 bytes .../res/drawable-xhdpi/ic_event_white_48dp.png | Bin 0 -> 439 bytes .../res/drawable-xhdpi/ic_headset_black_48dp.png | Bin 0 -> 1006 bytes .../res/drawable-xhdpi/ic_headset_white_48dp.png | Bin 0 -> 1017 bytes src/main/res/drawable-xhdpi/ic_mic_black_48dp.png | Bin 0 -> 1002 bytes src/main/res/drawable-xhdpi/ic_mic_white_48dp.png | Bin 0 -> 1008 bytes .../res/drawable-xhdpi/ic_person_black_48dp.png | Bin 0 -> 751 bytes .../res/drawable-xhdpi/ic_person_white_48dp.png | Bin 0 -> 738 bytes src/main/res/drawable-xhdpi/ic_room_black_48dp.png | Bin 0 -> 1093 bytes src/main/res/drawable-xhdpi/ic_room_white_48dp.png | Bin 0 -> 1121 bytes .../res/drawable-xxhdpi/ic_android_black_48dp.png | Bin 0 -> 1548 bytes .../res/drawable-xxhdpi/ic_android_white_48dp.png | Bin 0 -> 1602 bytes .../res/drawable-xxhdpi/ic_archive_black_48dp.png | Bin 0 -> 1073 bytes .../res/drawable-xxhdpi/ic_archive_white_48dp.png | Bin 0 -> 1084 bytes .../drawable-xxhdpi/ic_description_black_48dp.png | Bin 0 -> 824 bytes .../drawable-xxhdpi/ic_description_white_48dp.png | Bin 0 -> 861 bytes .../res/drawable-xxhdpi/ic_event_black_48dp.png | Bin 0 -> 787 bytes .../res/drawable-xxhdpi/ic_event_white_48dp.png | Bin 0 -> 800 bytes .../res/drawable-xxhdpi/ic_headset_black_48dp.png | Bin 0 -> 1867 bytes .../res/drawable-xxhdpi/ic_headset_white_48dp.png | Bin 0 -> 1896 bytes src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png | Bin 0 -> 1846 bytes src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png | Bin 0 -> 1866 bytes .../res/drawable-xxhdpi/ic_person_black_48dp.png | Bin 0 -> 1351 bytes .../res/drawable-xxhdpi/ic_person_white_48dp.png | Bin 0 -> 1360 bytes .../res/drawable-xxhdpi/ic_room_black_48dp.png | Bin 0 -> 2010 bytes .../res/drawable-xxhdpi/ic_room_white_48dp.png | Bin 0 -> 2071 bytes .../res/drawable-xxxhdpi/ic_android_black_48dp.png | Bin 0 -> 1868 bytes .../res/drawable-xxxhdpi/ic_android_white_48dp.png | Bin 0 -> 1951 bytes .../res/drawable-xxxhdpi/ic_archive_black_48dp.png | Bin 0 -> 1219 bytes .../res/drawable-xxxhdpi/ic_archive_white_48dp.png | Bin 0 -> 1243 bytes .../drawable-xxxhdpi/ic_description_black_48dp.png | Bin 0 -> 1050 bytes .../drawable-xxxhdpi/ic_description_white_48dp.png | Bin 0 -> 1087 bytes .../res/drawable-xxxhdpi/ic_event_black_48dp.png | Bin 0 -> 1061 bytes .../res/drawable-xxxhdpi/ic_event_white_48dp.png | Bin 0 -> 1053 bytes .../res/drawable-xxxhdpi/ic_headset_black_48dp.png | Bin 0 -> 2162 bytes .../res/drawable-xxxhdpi/ic_headset_white_48dp.png | Bin 0 -> 2214 bytes .../res/drawable-xxxhdpi/ic_mic_black_48dp.png | Bin 0 -> 2108 bytes .../res/drawable-xxxhdpi/ic_mic_white_48dp.png | Bin 0 -> 2141 bytes .../res/drawable-xxxhdpi/ic_person_black_48dp.png | Bin 0 -> 1552 bytes .../res/drawable-xxxhdpi/ic_person_white_48dp.png | Bin 0 -> 1552 bytes .../res/drawable-xxxhdpi/ic_room_black_48dp.png | Bin 0 -> 2283 bytes .../res/drawable-xxxhdpi/ic_room_white_48dp.png | Bin 0 -> 2439 bytes src/main/res/layout/fragment_conversation.xml | 26 +- src/main/res/layout/media_preview.xml | 28 ++ src/main/res/values/attrs.xml | 10 + src/main/res/values/defaults.xml | 1 - src/main/res/values/dimens.xml | 1 + src/main/res/values/strings.xml | 2 - src/main/res/values/themes.xml | 20 ++ src/main/res/xml/preferences.xml | 5 - 107 files changed, 682 insertions(+), 607 deletions(-) create mode 100644 src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java create mode 100644 src/main/java/de/pixart/messenger/ui/util/Attachment.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/Color.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/Drawable.java create mode 100644 src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java create mode 100644 src/main/res/drawable-hdpi/ic_android_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_android_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_archive_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_archive_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_description_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_description_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_event_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_event_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_headset_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_headset_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_mic_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_mic_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_person_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_person_white_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_room_black_48dp.png create mode 100644 src/main/res/drawable-hdpi/ic_room_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_android_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_android_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_archive_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_archive_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_description_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_description_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_event_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_event_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_headset_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_headset_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_mic_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_mic_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_person_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_person_white_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_room_black_48dp.png create mode 100644 src/main/res/drawable-mdpi/ic_room_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_android_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_android_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_archive_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_archive_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_description_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_description_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_event_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_event_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_headset_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_headset_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_mic_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_mic_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_person_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_person_white_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_room_black_48dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_room_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_android_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_android_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_description_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_description_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_event_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_event_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_person_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_person_white_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_room_black_48dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_room_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png create mode 100644 src/main/res/layout/media_preview.xml (limited to 'src/main') diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 79b621878..f9a50b2a1 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -214,18 +214,10 @@ android:launchMode="singleTop"> - - - - - - - - - + - + uris, long max) { + public static boolean allFilesUnderSize(Context context, List attachments, long max) { if (max <= 0) { Log.d(Config.LOGTAG, "server did not report max file size for http upload"); return true; //exception to be compatible with HTTP Upload < v0.2 } - for (Uri uri : uris) { - String mime = context.getContentResolver().getType(uri); + for (Attachment attachment : attachments) { + if (attachment.getType() != Attachment.Type.FILE) { + continue; + } + String mime = attachment.getMime(); if (mime != null && mime.startsWith("video/")) { try { - Dimensions dimensions = FileBackend.getVideoDimensions(context, uri); + Dimensions dimensions = FileBackend.getVideoDimensions(context, attachment.getUri()); if (dimensions.getMin() >= 720) { Log.d(Config.LOGTAG, "do not consider video file with min width larger or equal than 720 for size check"); continue; @@ -190,7 +194,7 @@ public class FileBackend { //ignore and fall through } } - if (FileBackend.getFileSize(context, uri) > max) { + if (FileBackend.getFileSize(context, attachment.getUri()) > max) { Log.d(Config.LOGTAG, "not all files are under " + max + " bytes. suggesting falling back to jingle"); return false; } @@ -554,6 +558,21 @@ public class FileBackend { return paint; } + private Bitmap cropCenterSquareVideo(Uri uri, int size) { + MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever(); + Bitmap frame; + try { + metadataRetriever.setDataSource(mXmppConnectionService, uri); + frame = metadataRetriever.getFrameAtTime(0); + metadataRetriever.release(); + return cropCenterSquare(frame, size); + } catch (Exception e) { + frame = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + frame.eraseColor(0xff000000); + return frame; + } + } + private Bitmap getVideoPreview(File file, int size) throws IOException { MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever(); Bitmap frame; @@ -1041,6 +1060,28 @@ public class FileBackend { return getVideoDimensions(metadataRetriever); } + public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) { + final LruCache cache = mXmppConnectionService.getBitmapCache(); + Bitmap bitmap = cache.get(attachment.getUuid().toString()); + if (bitmap != null || cacheOnly) { + return bitmap; + } + Log.d(Config.LOGTAG, "attachment mime=" + attachment.getMime()); + if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) { + bitmap = cropCenterSquareVideo(attachment.getUri(), size); + drawOverlay(bitmap, R.drawable.play_video, 0.75f); + } else { + bitmap = cropCenterSquare(attachment.getUri(), size); + if ("image/gif".equals(attachment.getMime())) { + Bitmap withGifOverlay = bitmap.copy(Bitmap.Config.ARGB_8888, true); + drawOverlay(withGifOverlay, R.drawable.play_gif, 1.0f); + bitmap.recycle(); + bitmap = withGifOverlay; + } + } + cache.put(attachment.getUuid().toString(), bitmap); + return bitmap; + } private static Dimensions getVideoDimensions(Context context, Uri uri) throws NotAVideoFile { MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever(); diff --git a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java index d20f9c1f3..4c0aec3ca 100644 --- a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java +++ b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java @@ -17,7 +17,7 @@ import java.util.ArrayList; import java.util.List; import de.pixart.messenger.entities.Conversation; -import de.pixart.messenger.ui.ShareWithActivity; +import de.pixart.messenger.ui.ConversationsActivity; @TargetApi(Build.VERSION_CODES.M) public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection { @@ -41,7 +41,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements return chooserTargets; } mXmppConnectionService.populateWithOrderedConversations(conversations, false); - final ComponentName componentName = new ComponentName(this, ShareWithActivity.class); + final ComponentName componentName = new ComponentName(this, ConversationsActivity.class); final int pixel = (int) (48 * getResources().getDisplayMetrics().density); for (Conversation conversation : conversations) { if (conversation.sentMessagesCount() == 0) { @@ -51,7 +51,7 @@ public class ContactChooserTargetService extends ChooserTargetService implements final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel)); final float score = 1 - (1.0f / MAX_TARGETS) * chooserTargets.size(); final Bundle extras = new Bundle(); - extras.putString("uuid", conversation.getUuid()); + extras.putString(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid()); chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras)); if (chooserTargets.size() >= MAX_TARGETS) { break; diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index def4b8a69..451ac6bde 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -15,8 +15,6 @@ import android.content.IntentSender.SendIntentException; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.databinding.DataBindingUtil; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -27,7 +25,6 @@ import android.provider.MediaStore; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.StringRes; -import android.support.media.ExifInterface; import android.support.v13.view.inputmethod.InputConnectionCompat; import android.support.v13.view.inputmethod.InputContentInfoCompat; import android.support.v7.app.AlertDialog; @@ -54,16 +51,12 @@ import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import net.java.otr4j.session.SessionStatus; -import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -98,9 +91,10 @@ import de.pixart.messenger.http.HttpDownloadConnection; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.MessageArchiveService; import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.ui.adapter.MediaPreviewAdapter; import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.util.ActivityResult; -import de.pixart.messenger.ui.util.AttachmentTool; +import de.pixart.messenger.ui.util.Attachment; import de.pixart.messenger.ui.util.ConversationMenuConfigurator; import de.pixart.messenger.ui.util.DateSeparator; import de.pixart.messenger.ui.util.EditMessageActionModeCallback; @@ -113,7 +107,6 @@ import de.pixart.messenger.ui.util.SendButtonAction; import de.pixart.messenger.ui.util.SendButtonTool; import de.pixart.messenger.ui.util.ShareUtil; import de.pixart.messenger.ui.widget.EditMessage; -import de.pixart.messenger.utils.FileUtils; import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.utils.MessageUtils; import de.pixart.messenger.utils.NickValidityChecker; @@ -129,7 +122,6 @@ import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.XmppActivity.EXTRA_ACCOUNT; import static de.pixart.messenger.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION; -import static de.pixart.messenger.ui.util.SendButtonAction.TEXT; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard; import static de.pixart.messenger.xmpp.Patches.ENCRYPTION_EXCEPTIONS; @@ -179,6 +171,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke private Toast messageLoaderToast; private ConversationsActivity activity; private boolean reInitRequiredOnStart = true; + private MediaPreviewAdapter mediaPreviewAdapter; private SimpleFingerGestures gesturesDetector = new SimpleFingerGestures(); @@ -851,111 +844,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } public void attachEditorContentToConversation(Uri uri) { - this.attachFileToConversation(conversation, uri, null); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uri, Attachment.Type.FILE)); + toggleInputMethod(); } - private void attachImageToConversation(Conversation conversation, Uri uri, boolean sendAsIs) { + private void attachImageToConversation(Conversation conversation, Uri uri) { if (conversation == null) { return; } - if (sendAsIs) { - sendImage(conversation, uri); - return; - } - final Conversation conversation_preview = conversation; - final Uri uri_preview = uri; - Bitmap bitmap = null; - try { - bitmap = BitmapFactory.decodeFile(FileUtils.getPath(activity, uri)); - } catch (Exception e) { - e.printStackTrace(); - } - File file = null; - ExifInterface exif = null; - int orientation = 0; - try { - file = new File(FileUtils.getPath(activity, uri)); - } catch (Exception e) { - e.printStackTrace(); - } - if (file != null) { - try { - exif = new ExifInterface(file.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - orientation = exif != null ? exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) : 0; - } - Log.d(Config.LOGTAG, "EXIF: " + orientation); - Bitmap rotated_image = null; - Log.d(Config.LOGTAG, "Rotate image"); - try { - rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation); - } catch (Exception e) { - e.printStackTrace(); - } - if (rotated_image != null) { - int scaleSize = 600; - int originalWidth = rotated_image.getWidth(); - int originalHeight = rotated_image.getHeight(); - int newWidth = -1; - int newHeight = -1; - float multFactor; - if (originalHeight > originalWidth) { - newHeight = scaleSize; - multFactor = (float) originalWidth / (float) originalHeight; - newWidth = (int) (newHeight * multFactor); - } else if (originalWidth > originalHeight) { - newWidth = scaleSize; - multFactor = (float) originalHeight / (float) originalWidth; - newHeight = (int) (newWidth * multFactor); - } else if (originalHeight == originalWidth) { - newHeight = scaleSize; - newWidth = scaleSize; - } - Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px"); - Bitmap preview = null; - - try { - preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false); - } catch (Exception e) { - e.printStackTrace(); - } - if (preview != null) { - ImageView ImagePreview = new ImageView(activity); - LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); - ImagePreview.setLayoutParams(vp); - ImagePreview.setMaxWidth(newWidth); - ImagePreview.setMaxHeight(newHeight); - ImagePreview.setPadding(5, 5, 5, 5); - ImagePreview.setImageBitmap(preview); - getActivity().runOnUiThread(() -> { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setView(ImagePreview); - builder.setTitle(R.string.send_image); - builder.setPositiveButton(R.string.ok, (dialog, which) -> sendImage(conversation_preview, uri_preview)); - builder.setOnCancelListener(dialog -> mPendingImageUris.clear()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - builder.setOnDismissListener(dialog -> mPendingImageUris.clear()); - } - AlertDialog alertDialog = builder.create(); - alertDialog.show(); - }); - } else { - getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show()); - } - } else { - getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show()); - } - } - - private void sendImage(Conversation conversation, Uri uri) { final Toast prepareFileToast = Toast.makeText(getActivity(), getText(R.string.preparing_image), Toast.LENGTH_LONG); prepareFileToast.show(); activity.delegateUriPermissionsToService(uri); activity.xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() { - @Override public void userInputRequried(PendingIntent pi, Message object) { hidePrepareFileToast(prepareFileToast); @@ -981,6 +882,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void sendMessage() { + if (mediaPreviewAdapter.hasAttachments()) { + commitAttachments(); + return; + } final String body = binding.textinput.getText().toString(); final Conversation conversation = this.conversation; if (body.length() == 0 || conversation == null) { @@ -1086,7 +991,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void handlePositiveActivityResult(int requestCode, final Intent data) { - final String type = data == null ? null : data.getType(); switch (requestCode) { case REQUEST_TRUST_KEYS_TEXT: final String body = binding.textinput.getText().toString(); @@ -1098,51 +1002,32 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke selectPresenceToAttachFile(choice); break; case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - final List imageUris = AttachmentTool.extractUriFromIntent(data); - final int ImageUrisCount = imageUris.size(); - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching image - number of uris: " + ImageUrisCount); - if (ImageUrisCount == 1) { - Uri uri = imageUris.get(0); - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE"); - attachImageToConversation(conversation, uri, false); - } else { - for (Iterator i = imageUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES"); - attachImagesToConversation(conversation, i.next()); - } - } + final List imageUris = Attachment.extractAttachments(getActivity(), data, Attachment.Type.IMAGE); + mediaPreviewAdapter.addMediaPreviews(imageUris); + toggleInputMethod(); break; case ATTACHMENT_CHOICE_TAKE_FROM_CAMERA: final Uri takePhotoUri = pendingTakePhotoUri.pop(); - final Uri takeVideoUri = pendingTakeVideoUri.pop(); if (takePhotoUri != null) { - attachPhotoToConversation(conversation, takePhotoUri); - } else if (takeVideoUri != null) { - attachFileToConversation(conversation, takeVideoUri, type); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), takePhotoUri, Attachment.Type.IMAGE)); + toggleInputMethod(); } else { - Log.d(Config.LOGTAG, "lost take uri. unable to to attach"); + Log.d(Config.LOGTAG, "lost take photo uri. unable to to attach"); } break; case ATTACHMENT_CHOICE_CHOOSE_FILE: case ATTACHMENT_CHOICE_RECORD_VOICE: - final List fileUris = AttachmentTool.extractUriFromIntent(data); - final PresenceSelector.OnPresenceSelected callback = () -> { - for (Iterator i = fileUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE"); - attachFileToConversation(conversation, i.next(), type); - } - }; - if (conversation == null || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(getActivity(), fileUris, getMaxHttpUploadSize(conversation))) { - callback.onPresenceSelected(); - } else { - activity.selectPresence(conversation, callback); - } + final Attachment.Type type = requestCode == ATTACHMENT_CHOICE_RECORD_VOICE ? Attachment.Type.RECORDING : Attachment.Type.FILE; + final List fileUris = Attachment.extractAttachments(getActivity(), data, type); + mediaPreviewAdapter.addMediaPreviews(fileUris); + toggleInputMethod(); break; case ATTACHMENT_CHOICE_LOCATION: double latitude = data.getDoubleExtra("latitude", 0); double longitude = data.getDoubleExtra("longitude", 0); Uri geo = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude)); - attachLocationToConversation(conversation, geo); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), geo, Attachment.Type.LOCATION)); + toggleInputMethod(); break; case REQUEST_INVITE_TO_CONVERSATION: XmppActivity.ConferenceInvite invite = XmppActivity.ConferenceInvite.parse(data); @@ -1156,6 +1041,38 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } + private void commitAttachments() { + final List attachments = mediaPreviewAdapter.getAttachments(); + final PresenceSelector.OnPresenceSelected callback = () -> { + for (Iterator i = attachments.iterator(); i.hasNext(); i.remove()) { + final Attachment attachment = i.next(); + if (attachment.getType() == Attachment.Type.LOCATION) { + attachLocationToConversation(conversation, attachment.getUri()); + } else if (attachment.getType() == Attachment.Type.IMAGE) { + Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching image to conversations. CHOOSE_IMAGE"); + attachImageToConversation(conversation, attachment.getUri()); + } else { + Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE/RECORD_VIDEO"); + attachFileToConversation(conversation, attachment.getUri(), attachment.getMime()); + } + } + mediaPreviewAdapter.notifyDataSetChanged(); + toggleInputMethod(); + }; + if (conversation == null || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(getActivity(), attachments, getMaxHttpUploadSize(conversation))) { + callback.onPresenceSelected(); + } else { + activity.selectPresence(conversation, callback); + } + } + + public void toggleInputMethod() { + boolean hasAttachments = mediaPreviewAdapter.hasAttachments(); + binding.textinput.setVisibility(hasAttachments ? View.GONE : View.VISIBLE); + binding.mediaPreview.setVisibility(hasAttachments ? View.VISIBLE : View.GONE); + updateSendButton(); + } + private void handleNegativeActivityResult(int requestCode) { switch (requestCode) { //nothing to do for now @@ -1253,6 +1170,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke binding.messagesView.setOnScrollListener(mOnScrollListener); binding.messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); + mediaPreviewAdapter = new MediaPreviewAdapter(this); + binding.mediaPreview.setAdapter(mediaPreviewAdapter); messageListAdapter = new MessageAdapter((XmppActivity) getActivity(), this.messageList); messageListAdapter.setOnContactPictureClicked(message -> { String fingerprint; @@ -1871,6 +1790,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke case ATTACHMENT_CHOICE_CHOOSE_FILE: chooser = true; intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setAction(Intent.ACTION_GET_CONTENT); break; @@ -2361,6 +2281,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final String nick = extras.getString(ConversationsActivity.EXTRA_NICK); final boolean asQuote = extras.getBoolean(ConversationsActivity.EXTRA_AS_QUOTE); final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false); + final List uris = extractUris(extras); + if (uris != null && uris.size() > 0) { + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris)); + toggleInputMethod(); + return; + } if (nick != null) { if (pm) { Jid jid = conversation.getJid(); @@ -2389,6 +2315,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } + private List extractUris(Bundle extras) { + final List uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM); + if (uris != null) { + return uris; + } + final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM); + if (uri != null) { + return Collections.singletonList(uri); + } else { + return null; + } + } + private boolean showBlockSubmenu(View view) { final Jid jid = conversation.getJid(); if (jid.getLocal() == null) { @@ -2603,11 +2542,17 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } public void updateSendButton() { + boolean hasAttachments = mediaPreviewAdapter != null && mediaPreviewAdapter.hasAttachments(); boolean useSendButtonToIndicateStatus = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("send_button_status", getResources().getBoolean(R.bool.send_button_status)); final Conversation c = this.conversation; final Presence.Status status; final String text = this.binding.textinput == null ? "" : this.binding.textinput.getText().toString(); - SendButtonAction action = SendButtonTool.getAction(getActivity(), c, text); + final SendButtonAction action; + if (hasAttachments) { + action = SendButtonAction.TEXT; + } else { + action = SendButtonTool.getAction(getActivity(), c, text); + } if (useSendButtonToIndicateStatus && c.getAccount().getStatus() == Account.State.ONLINE) { if (activity.xmppConnectionService != null && activity.xmppConnectionService.getMessageArchiveService().isCatchingUp(c)) { status = Presence.Status.OFFLINE; @@ -2619,9 +2564,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } else { status = Presence.Status.OFFLINE; } - if (action.toString().equals("CHOOSE_ATTACHMENT") && !activity.xmppConnectionService.getAttachmentChoicePreference()) { - action = TEXT; - } this.binding.textSendButton.setTag(action); this.binding.textSendButton.setImageResource(SendButtonTool.getSendButtonImageResource(getActivity(), action, status)); } diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java index 11d4cc31f..05cfe3ab4 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java @@ -63,6 +63,7 @@ import net.java.otr4j.session.SessionStatus; import org.openintents.openpgp.util.OpenPgpApi; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -111,6 +112,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio public static final String ACTION_DESTROY_MUC = "de.pixart.messenger.DESTROY_MUC"; public static final int REQUEST_OPEN_MESSAGE = 0x9876; public static final int REQUEST_PLAY_PAUSE = 0x5432; + private static List VIEW_AND_SHARE_ACTIONS = Arrays.asList( + ACTION_VIEW_CONVERSATION, + Intent.ACTION_SEND, + Intent.ACTION_SEND_MULTIPLE + ); private boolean showLastSeen = false; @@ -126,8 +132,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio private boolean mActivityPaused = true; private AtomicBoolean mRedirectInProcess = new AtomicBoolean(false); - private static boolean isViewIntent(Intent i) { - return i != null && ACTION_VIEW_CONVERSATION.equals(i.getAction()) && i.hasExtra(EXTRA_CONVERSATION); + private static boolean isViewOrShareIntent(Intent i) { + Log.d(Config.LOGTAG, "action: " + (i == null ? null : i.getAction())); + return i != null && VIEW_AND_SHARE_ACTIONS.contains(i.getAction()) && i.hasExtra(EXTRA_CONVERSATION); } private static Intent createLauncherIntent(Context context) { @@ -451,7 +458,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } else { intent = savedInstanceState.getParcelable("intent"); } - if (isViewIntent(intent)) { + if (isViewOrShareIntent(intent)) { pendingViewIntent.push(intent); setIntent(createLauncherIntent(this)); } @@ -626,7 +633,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio @Override protected void onNewIntent(final Intent intent) { - if (isViewIntent(intent)) { + if (isViewOrShareIntent(intent)) { if (xmppConnectionService != null) { processViewIntent(intent); } else { diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java index cc3172670..9ea9e7592 100644 --- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java @@ -56,7 +56,7 @@ import de.pixart.messenger.services.MessageSearchTask; import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.interfaces.OnSearchResultsAvailable; import de.pixart.messenger.ui.util.ChangeWatcher; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.ui.util.DateSeparator; import de.pixart.messenger.ui.util.Drawable; import de.pixart.messenger.ui.util.ListViewUtils; @@ -213,12 +213,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc private void changeBackground(boolean hasSearch, boolean hasResults) { if (hasSearch) { if (hasResults) { - binding.searchResults.setBackgroundColor(Color.get(this, R.attr.color_background_secondary)); + binding.searchResults.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_secondary)); } else { - binding.searchResults.setBackground(Drawable.get(this, R.attr.activity_background_no_results)); + binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_no_results)); } } else { - binding.searchResults.setBackground(Drawable.get(this, R.attr.activity_background_search)); + binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_search)); } } diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index 0e4b5d391..c9c6006de 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -38,7 +38,6 @@ import de.pixart.messenger.crypto.OmemoSetting; import de.pixart.messenger.entities.Account; import de.pixart.messenger.services.ExportLogsService; import de.pixart.messenger.services.MemorizingTrustManager; -import de.pixart.messenger.ui.util.Color; import de.pixart.messenger.utils.TimeframeUtils; import rocks.xmpp.addr.Jid; @@ -85,7 +84,7 @@ public class SettingsActivity extends XmppActivity implements fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit(); } mSettingsFragment.setActivityIntent(getIntent()); - getWindow().getDecorView().setBackgroundColor(Color.get(this, R.attr.color_background_secondary)); + getWindow().getDecorView().setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_secondary)); setSupportActionBar(findViewById(R.id.toolbar)); configureActionBar(getSupportActionBar()); } diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java index a820378a3..da3d2ff8f 100644 --- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java @@ -1,6 +1,5 @@ package de.pixart.messenger.ui; -import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -12,116 +11,32 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; -import java.net.URLConnection; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Conversation; -import de.pixart.messenger.entities.Message; -import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.adapter.ConversationAdapter; -import de.pixart.messenger.ui.util.PresenceSelector; -import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; -import static java.lang.String.format; public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate { private static final int REQUEST_STORAGE_PERMISSION = 0x733f32; - private boolean mReturnToPrevious = false; + private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; private Conversation mPendingConversation = null; - - @Override - public void onConversationUpdate() { - refreshUi(); - } - - private class Share { - public List uris = new ArrayList<>(); - public boolean image; - public String account; - public String contact; - public String text; - public String uuid; - public boolean multiple = false; - public String type; - } - private Share share; - - private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; - private RecyclerView mListView; private ConversationAdapter mAdapter; private List mConversations = new ArrayList<>(); - private Toast mToast; - private AtomicInteger attachmentCounter = new AtomicInteger(0); - - private UiInformableCallback attachFileCallback = new UiInformableCallback() { - - @Override - public void inform(final String text) { - runOnUiThread(() -> replaceToast(text)); - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - // TODO Auto-generated method stub - - } - - @Override - public void success(final Message message) { - runOnUiThread(() -> { - if (attachmentCounter.decrementAndGet() <= 0) { - int resId; - if (share.image && share.multiple) { - resId = R.string.shared_images_with_x; - } else if (share.image) { - resId = R.string.shared_image_with_x; - } else { - resId = R.string.shared_file_with_x; - } - Conversation conversation = (Conversation) message.getConversation(); - replaceToast(getString(resId, conversation.getName())); - if (mReturnToPrevious) { - finish(); - } else { - switchToConversation(conversation); - } - } - }); - } - - @Override - public void error(final int errorCode, Message object) { - runOnUiThread(() -> { - replaceToast(getString(errorCode)); - if (attachmentCounter.decrementAndGet() <= 0) { - finish(); - } - }); - } - }; - - protected void hideToast() { - if (mToast != null) { - mToast.cancel(); - } - } - protected void replaceToast(String msg) { - hideToast(); - mToast = Toast.makeText(this, msg, Toast.LENGTH_LONG); - mToast.show(); + @Override + public void onConversationUpdate() { + refreshUi(); } protected void onActivityResult(int requestCode, int resultCode, final Intent data) { @@ -160,20 +75,16 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer super.onCreate(savedInstanceState); boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); new EmojiService(this).init(useBundledEmoji); - setContentView(R.layout.activity_share_with); - setSupportActionBar(findViewById(R.id.toolbar)); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(false); getSupportActionBar().setHomeButtonEnabled(false); } - setTitle(getString(R.string.title_activity_sharewith)); - - mListView = findViewById(R.id.choose_conversation_list); + RecyclerView mListView = findViewById(R.id.choose_conversation_list); mAdapter = new ConversationAdapter(this, this.mConversations); - mListView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); + mListView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mListView.setAdapter(mAdapter); mAdapter.setConversationClickListener((view, conversation) -> share(conversation)); this.share = new Share(); @@ -203,57 +114,28 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer if (intent == null) { return; } - this.mReturnToPrevious = getBooleanPreference("return_to_previous", R.bool.return_to_previous); final String type = intent.getType(); final String action = intent.getAction(); - Log.d(Config.LOGTAG, "action: " + action + ", type:" + type); - share.uuid = intent.getStringExtra("uuid"); if (Intent.ACTION_SEND.equals(action)) { - final String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); final String text = intent.getStringExtra(Intent.EXTRA_TEXT); final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); if (type != null && uri != null && (text == null || !type.equals("text/plain"))) { this.share.uris.clear(); this.share.uris.add(uri); - this.share.image = type.startsWith("image/") || isImage(uri); - this.share.type = type; } else { - if (subject != null) { - this.share.text = format("[%s]%n%s", subject, text); - } else { - this.share.text = text; - } + this.share.text = text; } } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { - this.share.image = type != null && type.startsWith("image/"); - if (!this.share.image) { - return; - } this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); } if (xmppConnectionServiceBound) { - if (share.uuid != null) { - share(); - } else { - xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); - } - } - - } - - protected boolean isImage(Uri uri) { - try { - String guess = URLConnection.guessContentTypeFromName(uri.toString()); - return (guess != null && guess.startsWith("image/")); - } catch (final StringIndexOutOfBoundsException ignored) { - return false; + xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); } } @Override void onBackendConnected() { - if (xmppConnectionServiceBound && share != null - && ((share.contact != null && share.account != null) || share.uuid != null)) { + if (xmppConnectionServiceBound && share != null && ((share.contact != null && share.account != null))) { share(); return; } @@ -262,28 +144,19 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer private void share() { final Conversation conversation; - if (share.uuid != null) { - conversation = xmppConnectionService.findConversationByUuid(share.uuid); - if (conversation == null) { - return; - } - } else { - Account account; - try { - account = xmppConnectionService.findAccountByJid(Jid.of(share.account)); - } catch (final IllegalArgumentException e) { - account = null; - } - if (account == null) { - return; - } - - try { - conversation = xmppConnectionService - .findOrCreateConversation(account, Jid.of(share.contact), false, true); - } catch (final IllegalArgumentException e) { - return; - } + Account account; + try { + account = xmppConnectionService.findAccountByJid(Jid.of(share.account)); + } catch (final IllegalArgumentException e) { + account = null; + } + if (account == null) { + return; + } + try { + conversation = xmppConnectionService.findOrCreateConversation(account, Jid.of(share.contact), false, true); + } catch (final IllegalArgumentException e) { + return; } share(conversation); } @@ -293,110 +166,18 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer mPendingConversation = conversation; return; } - final Account account = conversation.getAccount(); - final XmppConnection connection = account.getXmppConnection(); - final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize(); - mListView.setEnabled(false); - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) { - if (share.uuid == null) { - showInstallPgpDialog(); - } else { - Toast.makeText(this, R.string.openkeychain_not_installed, Toast.LENGTH_SHORT).show(); - finish(); - } - return; + Intent intent = new Intent(this, ConversationsActivity.class); + intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid()); + if (share.uris.size() > 0) { + intent.setAction(Intent.ACTION_SEND_MULTIPLE); + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else if (share.text != null) { + intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION); + intent.putExtra(ConversationsActivity.EXTRA_TEXT, share.text); } - if (share.uris.size() != 0) { - PresenceSelector.OnPresenceSelected callback = () -> { - attachmentCounter.set(share.uris.size()); - if (share.image) { - Log.d(Config.LOGTAG, "ShareWithActivity share() image " + share.uris.size() + " uri(s) " + share.uris.toString()); - share.multiple = share.uris.size() > 1; - replaceToast(getString(share.multiple ? R.string.preparing_images : R.string.preparing_image)); - for (Iterator i = share.uris.iterator(); i.hasNext(); i.remove()) { - final Uri uri = i.next(); - delegateUriPermissionsToService(uri); - xmppConnectionService.attachImageToConversation(conversation, uri, attachFileCallback); - } - } else { - Log.d(Config.LOGTAG, "ShareWithActivity share() file " + share.uris.size() + " uri(s) " + share.uris.toString()); - replaceToast(getString(R.string.preparing_file)); - final Uri uri = share.uris.get(0); - delegateUriPermissionsToService(uri); - xmppConnectionService.attachFileToConversation(conversation, uri, share.type, attachFileCallback); - finish(); - } - }; - if (account.httpUploadAvailable() - && ((share.image && !neverCompressPictures()) - || conversation.getMode() == Conversation.MODE_MULTI - || FileBackend.allFilesUnderSize(this, share.uris, max)) - && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) { - callback.onPresenceSelected(); - } else { - selectPresence(conversation, callback); - } - } else { - if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty()) { - final PresenceSelector.OnPresenceSelected callback = new PresenceSelector.OnPresenceSelected() { - - private void finishAndSend(Message message) { - replaceToast(getString(R.string.shared_text_with_x, conversation.getName())); - finish(); - } - - private UiCallback messageEncryptionCallback = new UiCallback() { - @Override - public void success(final Message message) { - runOnUiThread(() -> finishAndSend(message)); - } - - @Override - public void error(final int errorCode, Message object) { - runOnUiThread(() -> { - replaceToast(getString(errorCode)); - finish(); - }); - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - finish(); - } - }; - - @Override - public void onPresenceSelected() { - - final int encryption = conversation.getNextEncryption(); - - Message message = new Message(conversation,share.text, encryption); - - Log.d(Config.LOGTAG,"on presence selected encrpytion="+encryption); - - if (encryption == Message.ENCRYPTION_PGP) { - replaceToast(getString(R.string.encrypting_message)); - xmppConnectionService.getPgpEngine().encrypt(message,messageEncryptionCallback); - return; - } - - if (encryption == Message.ENCRYPTION_OTR) { - message.setCounterpart(conversation.getNextCounterpart()); - } - xmppConnectionService.sendMessage(message); - finishAndSend(message); - } - }; - if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) { - selectPresence(conversation, callback); - } else { - callback.onPresenceSelected(); - } - } else { - switchToConversation(conversation, this.share.text, true); - } - } - + startActivity(intent); + finish(); } public void refreshUiReal() { @@ -404,12 +185,10 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer mAdapter.notifyDataSetChanged(); } - @Override - public void onBackPressed() { - if (attachmentCounter.get() >= 1) { - replaceToast(getString(R.string.sharing_files_please_wait)); - } else { - super.onBackPressed(); - } + private class Share { + public String account; + public String contact; + public String text; + ArrayList uris = new ArrayList<>(); } } \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java index 01b432d65..e37ea6121 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java @@ -24,7 +24,7 @@ import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.ui.ManageAccountActivity; import de.pixart.messenger.ui.XmppActivity; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.utils.UIHelper; public class AccountAdapter extends ArrayAdapter { @@ -65,14 +65,14 @@ public class AccountAdapter extends ArrayAdapter { statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: - statusView.setTextColor(Color.get(activity, R.attr.TextColorOnline)); + statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline)); break; case DISABLED: case CONNECTING: - statusView.setTextColor(Color.get(activity, android.R.attr.textColorSecondary)); + statusView.setTextColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary)); break; default: - statusView.setTextColor(Color.get(activity, R.attr.TextColorError)); + statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError)); break; } final SwitchCompat tglAccountState = view.findViewById(R.id.tgl_account_status); diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java index 36cae0eb3..55dd6d65f 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -31,7 +31,7 @@ import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.Transferable; import de.pixart.messenger.ui.ConversationFragment; import de.pixart.messenger.ui.XmppActivity; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.ui.widget.UnreadCountCustomView; import de.pixart.messenger.utils.EmojiWrapper; import de.pixart.messenger.utils.IrregularUnicodeDetector; @@ -97,9 +97,9 @@ public class ConversationAdapter extends RecyclerView.Adapter { } } if (offline) { - viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(StyledAttributes.get(activity, R.attr.text_Color_Main)); viewHolder.name.setAlpha(INACTIVE_ALPHA); viewHolder.jid.setAlpha(INACTIVE_ALPHA); viewHolder.avatar.setAlpha(INACTIVE_ALPHA); viewHolder.tags.setAlpha(INACTIVE_ALPHA); } else { if (ShowPresenceColoredNames()) { - viewHolder.name.setTextColor(color != 0 ? color : Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(color != 0 ? color : StyledAttributes.get(activity, R.attr.text_Color_Main)); } else { - viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(StyledAttributes.get(activity, R.attr.text_Color_Main)); } viewHolder.name.setAlpha(ACTIVE_ALPHA); viewHolder.jid.setAlpha(ACTIVE_ALPHA); diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java new file mode 100644 index 000000000..bbcaa5dc6 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java @@ -0,0 +1,211 @@ +package de.pixart.messenger.ui.adapter; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Resources; +import android.databinding.DataBindingUtil; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.MediaPreviewBinding; +import de.pixart.messenger.ui.ConversationFragment; +import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.Attachment; +import de.pixart.messenger.ui.util.StyledAttributes; + +public class MediaPreviewAdapter extends RecyclerView.Adapter { + + private final List mediaPreviews = new ArrayList<>(); + + private final ConversationFragment conversationFragment; + + public MediaPreviewAdapter(ConversationFragment fragment) { + this.conversationFragment = fragment; + } + + private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final Attachment oldAttachment = bitmapWorkerTask.attachment; + if (oldAttachment == null || !oldAttachment.equals(attachment)) { + 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; + } + + @NonNull + @Override + public MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + MediaPreviewBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media_preview, parent, false); + return new MediaPreviewViewHolder(binding); + } + + @Override + public void onBindViewHolder(@NonNull MediaPreviewViewHolder holder, int position) { + final Context context = conversationFragment.getActivity(); + final Attachment attachment = mediaPreviews.get(position); + if (attachment.renderThumbnail()) { + holder.binding.mediaPreview.setImageAlpha(255); + loadPreview(attachment, holder.binding.mediaPreview); + } else { + cancelPotentialWork(attachment, holder.binding.mediaPreview); + holder.binding.mediaPreview.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary)); + holder.binding.mediaPreview.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255)); + final @AttrRes int attr; + if (attachment.getType() == Attachment.Type.LOCATION) { + attr = R.attr.media_preview_location; + } else if (attachment.getType() == Attachment.Type.RECORDING) { + attr = R.attr.media_preview_recording; + } else { + final String mime = attachment.getMime(); + if (mime == null) { + attr = R.attr.media_preview_file; + } else if (mime.startsWith("audio/")) { + attr = R.attr.media_preview_audio; + } else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) { + attr = R.attr.media_preview_calendar; + } else if (mime.equals("text/x-vcard")) { + attr = R.attr.media_preview_contact; + } else if (mime.equals("application/vnd.android.package-archive")) { + attr = R.attr.media_preview_app; + } else if (mime.equals("application/zip") || mime.equals("application/rar")) { + attr = R.attr.media_preview_archive; + } else { + attr = R.attr.media_preview_file; + } + } + holder.binding.mediaPreview.setImageDrawable(StyledAttributes.getDrawable(context, attr)); + } + holder.binding.deleteButton.setOnClickListener(v -> { + int pos = mediaPreviews.indexOf(attachment); + mediaPreviews.remove(pos); + notifyItemRemoved(pos); + conversationFragment.toggleInputMethod(); + }); + } + + public void addMediaPreviews(List attachments) { + this.mediaPreviews.addAll(attachments); + notifyDataSetChanged(); + } + + private void loadPreview(Attachment attachment, ImageView imageView) { + if (cancelPotentialWork(attachment, imageView)) { + XmppActivity activity = (XmppActivity) conversationFragment.getActivity(); + final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), true); + if (bm != null) { + cancelPotentialWork(attachment, imageView); + imageView.setImageBitmap(bm); + imageView.setBackgroundColor(0x00000000); + } else { + imageView.setBackgroundColor(0xff333333); + imageView.setImageDrawable(null); + final BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final AsyncDrawable asyncDrawable = new AsyncDrawable(conversationFragment.getActivity().getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(attachment); + } catch (final RejectedExecutionException ignored) { + } + } + } + } + + @Override + public int getItemCount() { + return mediaPreviews.size(); + } + + public boolean hasAttachments() { + return mediaPreviews.size() > 0; + } + + public List getAttachments() { + return mediaPreviews; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); + } + + BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + class MediaPreviewViewHolder extends RecyclerView.ViewHolder { + + private final MediaPreviewBinding binding; + + MediaPreviewViewHolder(MediaPreviewBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + class BitmapWorkerTask extends AsyncTask { + private final WeakReference imageViewReference; + private Attachment attachment = null; + + BitmapWorkerTask(ImageView imageView) { + imageViewReference = new WeakReference<>(imageView); + } + + @Override + protected Bitmap doInBackground(Attachment... params) { + Activity activity = conversationFragment.getActivity(); + if (activity instanceof XmppActivity) { + final XmppActivity xmppActivity = (XmppActivity) activity; + this.attachment = params[0]; + return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false); + } else { + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null && !isCancelled()) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java index adcc11809..0dfd86327 100644 --- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java +++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java @@ -1,7 +1,6 @@ package de.pixart.messenger.ui.forms; import android.content.Context; -import android.support.v4.content.ContextCompat; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; @@ -11,6 +10,7 @@ import android.view.View; import java.util.List; import de.pixart.messenger.R; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.xmpp.forms.Field; public abstract class FormFieldWrapper { @@ -18,9 +18,9 @@ public abstract class FormFieldWrapper { protected final Context context; protected final Field field; protected final View view; - protected OnFormFieldValuesEdited onFormFieldValuesEditedListener; + OnFormFieldValuesEdited onFormFieldValuesEditedListener; - protected FormFieldWrapper(Context context, Field field) { + FormFieldWrapper(Context context, Field field) { this.context = context; this.field = field; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -58,7 +58,7 @@ public abstract class FormFieldWrapper { int start = label.length(); int end = label.length() + 2; spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0); - spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, R.color.accent)), start, end, 0); + spannableString.setSpan(new ForegroundColorSpan(StyledAttributes.getColor(context, R.color.accent)), start, end, 0); } return spannableString; } diff --git a/src/main/java/de/pixart/messenger/ui/util/Attachment.java b/src/main/java/de/pixart/messenger/ui/util/Attachment.java new file mode 100644 index 000000000..8c1d0c75e --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/Attachment.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018, Daniel Gultsch All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.pixart.messenger.ui.util; + +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import de.pixart.messenger.Config; +import de.pixart.messenger.utils.MimeUtils; + +public class Attachment { + private final Uri uri; + private final Type type; + private final UUID uuid; + private final String mime; + private Attachment(Uri uri, Type type, String mime) { + this.uri = uri; + this.type = type; + this.mime = mime; + this.uuid = UUID.randomUUID(); + } + + public static List of(final Context context, Uri uri, Type type) { + final String mime = type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri); + return Collections.singletonList(new Attachment(uri, type, mime)); + } + + public static List of(final Context context, List uris) { + List attachments = new ArrayList<>(); + for (Uri uri : uris) { + final String mime = MimeUtils.guessMimeTypeFromUri(context, uri); + attachments.add(new Attachment(uri, mime != null && mime.startsWith("image/") ? Type.IMAGE : Type.FILE, mime)); + } + return attachments; + } + + public static List extractAttachments(final Context context, final Intent intent, Type type) { + List uris = new ArrayList<>(); + if (intent == null) { + return uris; + } + final String contentType = intent.getType(); + final Uri data = intent.getData(); + if (data == null) { + final ClipData clipData = intent.getClipData(); + if (clipData != null) { + for (int i = 0; i < clipData.getItemCount(); ++i) { + final Uri uri = clipData.getItemAt(i).getUri(); + Log.d(Config.LOGTAG, "uri=" + uri + " contentType=" + contentType); + final String mime = contentType != null ? contentType : MimeUtils.guessMimeTypeFromUri(context, uri); + Log.d(Config.LOGTAG, "mime=" + mime); + uris.add(new Attachment(uri, type, mime)); + } + } + } else { + final String mime = contentType != null ? contentType : MimeUtils.guessMimeTypeFromUri(context, data); + uris.add(new Attachment(data, type, mime)); + } + return uris; + } + + public String getMime() { + return mime; + } + + public Type getType() { + return type; + } + + public boolean renderThumbnail() { + return type == Type.IMAGE || (type == Type.FILE && mime != null && (mime.startsWith("video/") || mime.startsWith("image/"))); + } + + public Uri getUri() { + return uri; + } + + public UUID getUuid() { + return uuid; + } + + public enum Type { + FILE, IMAGE, LOCATION, RECORDING + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java b/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java deleted file mode 100644 index c56f27ebf..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - -import android.annotation.SuppressLint; -import android.content.ClipData; -import android.content.Intent; -import android.net.Uri; - -import java.util.ArrayList; -import java.util.List; - -public class AttachmentTool { - @SuppressLint("NewApi") - public static List extractUriFromIntent(final Intent intent) { - List uris = new ArrayList<>(); - if (intent == null) { - return uris; - } - final Uri uri = intent.getData(); - if (uri == null) { - final ClipData clipData = intent.getClipData(); - if (clipData != null) { - for (int i = 0; i < clipData.getItemCount(); ++i) { - uris.add(clipData.getItemAt(i).getUri()); - } - } - } else { - uris.add(uri); - } - return uris; - } -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/Color.java b/src/main/java/de/pixart/messenger/ui/util/Color.java deleted file mode 100644 index 58ce6a465..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/Color.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - - -import android.content.Context; -import android.content.res.TypedArray; -import android.support.annotation.AttrRes; -import android.support.annotation.ColorInt; - -public class Color { - - public static @ColorInt - int get(Context context, @AttrRes int attr) { - TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr}); - int color = typedArray.getColor(0, 0); - typedArray.recycle(); - return color; - } - -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/Drawable.java b/src/main/java/de/pixart/messenger/ui/util/Drawable.java deleted file mode 100644 index 5e0c770d4..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/Drawable.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - -import android.content.Context; -import android.content.res.TypedArray; -import android.support.annotation.AttrRes; - -public class Drawable { - public static android.graphics.drawable.Drawable get(Context context, @AttrRes int id) { - TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); - android.graphics.drawable.Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java b/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java new file mode 100644 index 000000000..cd4ad3e49 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, Daniel Gultsch All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.pixart.messenger.ui.util; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.AttrRes; +import android.support.annotation.ColorInt; + +public class StyledAttributes { + public static android.graphics.drawable.Drawable getDrawable(Context context, @AttrRes int id) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); + android.graphics.drawable.Drawable drawable = typedArray.getDrawable(0); + typedArray.recycle(); + return drawable; + } + + public static float getFloat(Context context, @AttrRes int id) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); + float value = typedArray.getFloat(0, 0f); + typedArray.recycle(); + return value; + } + + public static @ColorInt + int getColor(Context context, @AttrRes int attr) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr}); + int color = typedArray.getColor(0, 0); + typedArray.recycle(); + return color; + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java b/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java index b54f8d074..c0c8ca770 100644 --- a/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java +++ b/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java @@ -50,7 +50,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import de.pixart.messenger.R; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import rocks.xmpp.addr.Jid; public class IrregularUnicodeDetector { @@ -73,7 +73,7 @@ public class IrregularUnicodeDetector { } public static Spannable style(Context context, Jid jid) { - return style(jid, Color.get(context, R.attr.color_warning)); + return style(jid, StyledAttributes.get(context, R.attr.color_warning)); } private static Spannable style(Jid jid, @ColorInt int color) { diff --git a/src/main/res/drawable-hdpi/ic_android_black_48dp.png b/src/main/res/drawable-hdpi/ic_android_black_48dp.png new file mode 100644 index 000000000..38f6c05e8 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_android_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_android_white_48dp.png b/src/main/res/drawable-hdpi/ic_android_white_48dp.png new file mode 100644 index 000000000..13108b25b Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_android_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_archive_black_48dp.png b/src/main/res/drawable-hdpi/ic_archive_black_48dp.png new file mode 100644 index 000000000..1faf474cd Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_archive_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_archive_white_48dp.png b/src/main/res/drawable-hdpi/ic_archive_white_48dp.png new file mode 100644 index 000000000..462016ade Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_archive_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_description_black_48dp.png b/src/main/res/drawable-hdpi/ic_description_black_48dp.png new file mode 100644 index 000000000..193ac1011 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_description_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_description_white_48dp.png b/src/main/res/drawable-hdpi/ic_description_white_48dp.png new file mode 100644 index 000000000..bfb5baa97 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_description_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_event_black_48dp.png b/src/main/res/drawable-hdpi/ic_event_black_48dp.png new file mode 100644 index 000000000..5cd32f4c2 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_event_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_event_white_48dp.png b/src/main/res/drawable-hdpi/ic_event_white_48dp.png new file mode 100644 index 000000000..653d15b2b Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_event_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_headset_black_48dp.png b/src/main/res/drawable-hdpi/ic_headset_black_48dp.png new file mode 100644 index 000000000..8a97fe7ec Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_headset_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_headset_white_48dp.png b/src/main/res/drawable-hdpi/ic_headset_white_48dp.png new file mode 100644 index 000000000..a4810f277 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_headset_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_mic_black_48dp.png b/src/main/res/drawable-hdpi/ic_mic_black_48dp.png new file mode 100644 index 000000000..4ed4168cd Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_mic_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_mic_white_48dp.png b/src/main/res/drawable-hdpi/ic_mic_white_48dp.png new file mode 100644 index 000000000..2d729e773 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_mic_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_person_black_48dp.png b/src/main/res/drawable-hdpi/ic_person_black_48dp.png new file mode 100644 index 000000000..7401e00b5 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_person_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_person_white_48dp.png b/src/main/res/drawable-hdpi/ic_person_white_48dp.png new file mode 100644 index 000000000..667c220dd Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_person_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_room_black_48dp.png b/src/main/res/drawable-hdpi/ic_room_black_48dp.png new file mode 100644 index 000000000..2ebc54430 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_room_black_48dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_room_white_48dp.png b/src/main/res/drawable-hdpi/ic_room_white_48dp.png new file mode 100644 index 000000000..29d2981b2 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_room_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_android_black_48dp.png b/src/main/res/drawable-mdpi/ic_android_black_48dp.png new file mode 100644 index 000000000..4f25964be Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_android_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_android_white_48dp.png b/src/main/res/drawable-mdpi/ic_android_white_48dp.png new file mode 100644 index 000000000..032fe2781 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_android_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_archive_black_48dp.png b/src/main/res/drawable-mdpi/ic_archive_black_48dp.png new file mode 100644 index 000000000..e9d5ed1b4 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_archive_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_archive_white_48dp.png b/src/main/res/drawable-mdpi/ic_archive_white_48dp.png new file mode 100644 index 000000000..82be16407 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_archive_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_description_black_48dp.png b/src/main/res/drawable-mdpi/ic_description_black_48dp.png new file mode 100644 index 000000000..7bc6a6846 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_description_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_description_white_48dp.png b/src/main/res/drawable-mdpi/ic_description_white_48dp.png new file mode 100644 index 000000000..9759bde29 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_description_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_event_black_48dp.png b/src/main/res/drawable-mdpi/ic_event_black_48dp.png new file mode 100644 index 000000000..8266743b4 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_event_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_event_white_48dp.png b/src/main/res/drawable-mdpi/ic_event_white_48dp.png new file mode 100644 index 000000000..2fb9a7a2c Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_event_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_headset_black_48dp.png b/src/main/res/drawable-mdpi/ic_headset_black_48dp.png new file mode 100644 index 000000000..36b513948 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_headset_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_headset_white_48dp.png b/src/main/res/drawable-mdpi/ic_headset_white_48dp.png new file mode 100644 index 000000000..798e229c7 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_headset_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_mic_black_48dp.png b/src/main/res/drawable-mdpi/ic_mic_black_48dp.png new file mode 100644 index 000000000..9c7caa295 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_mic_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_mic_white_48dp.png b/src/main/res/drawable-mdpi/ic_mic_white_48dp.png new file mode 100644 index 000000000..80d851e95 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_mic_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_person_black_48dp.png b/src/main/res/drawable-mdpi/ic_person_black_48dp.png new file mode 100644 index 000000000..76858f701 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_person_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_person_white_48dp.png b/src/main/res/drawable-mdpi/ic_person_white_48dp.png new file mode 100644 index 000000000..496699985 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_person_white_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_room_black_48dp.png b/src/main/res/drawable-mdpi/ic_room_black_48dp.png new file mode 100644 index 000000000..e60454537 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_room_black_48dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_room_white_48dp.png b/src/main/res/drawable-mdpi/ic_room_white_48dp.png new file mode 100644 index 000000000..8c51d01ad Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_room_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_android_black_48dp.png b/src/main/res/drawable-xhdpi/ic_android_black_48dp.png new file mode 100644 index 000000000..9f85a42bf Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_android_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_android_white_48dp.png b/src/main/res/drawable-xhdpi/ic_android_white_48dp.png new file mode 100644 index 000000000..913ebd69f Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_android_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png b/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png new file mode 100644 index 000000000..6c383ee81 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_archive_white_48dp.png b/src/main/res/drawable-xhdpi/ic_archive_white_48dp.png new file mode 100644 index 000000000..dbbf9f870 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_archive_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_description_black_48dp.png b/src/main/res/drawable-xhdpi/ic_description_black_48dp.png new file mode 100644 index 000000000..0da5f7798 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_description_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_description_white_48dp.png b/src/main/res/drawable-xhdpi/ic_description_white_48dp.png new file mode 100644 index 000000000..e1b8e9aec Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_description_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_event_black_48dp.png b/src/main/res/drawable-xhdpi/ic_event_black_48dp.png new file mode 100644 index 000000000..8e43444b7 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_event_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_event_white_48dp.png b/src/main/res/drawable-xhdpi/ic_event_white_48dp.png new file mode 100644 index 000000000..be533de42 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_event_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_headset_black_48dp.png b/src/main/res/drawable-xhdpi/ic_headset_black_48dp.png new file mode 100644 index 000000000..7f85eb8bb Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_headset_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_headset_white_48dp.png b/src/main/res/drawable-xhdpi/ic_headset_white_48dp.png new file mode 100644 index 000000000..b815a1944 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_headset_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png b/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png new file mode 100644 index 000000000..e39b8170c Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png b/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png new file mode 100644 index 000000000..ec4405d44 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_person_black_48dp.png b/src/main/res/drawable-xhdpi/ic_person_black_48dp.png new file mode 100644 index 000000000..2751da525 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_person_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_person_white_48dp.png b/src/main/res/drawable-xhdpi/ic_person_white_48dp.png new file mode 100644 index 000000000..6a77a6a38 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_person_white_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_room_black_48dp.png b/src/main/res/drawable-xhdpi/ic_room_black_48dp.png new file mode 100644 index 000000000..9d46eb841 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_room_black_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_room_white_48dp.png b/src/main/res/drawable-xhdpi/ic_room_white_48dp.png new file mode 100644 index 000000000..b7a61a2f5 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_room_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_android_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_android_black_48dp.png new file mode 100644 index 000000000..61b806d38 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_android_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_android_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_android_white_48dp.png new file mode 100644 index 000000000..c3bf2bdc3 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_android_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png new file mode 100644 index 000000000..0155c8104 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png new file mode 100644 index 000000000..5a18abe12 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_description_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_description_black_48dp.png new file mode 100644 index 000000000..9ddffa1f3 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_description_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_description_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_description_white_48dp.png new file mode 100644 index 000000000..b42cec8eb Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_description_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_event_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_event_black_48dp.png new file mode 100644 index 000000000..3eb5d3073 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_event_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_event_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_event_white_48dp.png new file mode 100644 index 000000000..44ead726f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_event_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png new file mode 100644 index 000000000..2a8236f35 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png new file mode 100644 index 000000000..2b6b2644f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png new file mode 100644 index 000000000..44ef2fb4a Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png new file mode 100644 index 000000000..0fc42bfad Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_person_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_person_black_48dp.png new file mode 100644 index 000000000..0001aee6b Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_person_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_person_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_person_white_48dp.png new file mode 100644 index 000000000..32fe521d0 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_person_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_room_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_room_black_48dp.png new file mode 100644 index 000000000..e83824a17 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_room_black_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_room_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_room_white_48dp.png new file mode 100644 index 000000000..dd8eb11d3 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_room_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png new file mode 100644 index 000000000..1d1492b08 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png new file mode 100644 index 000000000..d040e8639 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png new file mode 100644 index 000000000..4dd5e44ab Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png new file mode 100644 index 000000000..5ac82a8df Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png new file mode 100644 index 000000000..f405b2931 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png new file mode 100644 index 000000000..1d32d8841 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png new file mode 100644 index 000000000..038df57d5 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png new file mode 100644 index 000000000..1937048aa Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png new file mode 100644 index 000000000..9a9a1b7f6 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png new file mode 100644 index 000000000..ba48f28a1 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png new file mode 100644 index 000000000..f2247b7ff Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png new file mode 100644 index 000000000..e9079bb3b Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png new file mode 100644 index 000000000..6d377123a Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png new file mode 100644 index 000000000..5bb65aed1 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png new file mode 100644 index 000000000..51d418cf7 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png new file mode 100644 index 000000000..0f7de3f5d Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png differ diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index 3ddcd69c7..acf42aafe 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -14,6 +14,7 @@ android:layout_height="match_parent" android:layout_above="@+id/snackbar" android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:background="?attr/color_background_tertiary" android:divider="@null" @@ -53,7 +54,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_alignParentLeft="true"> + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"> + + diff --git a/src/main/res/layout/media_preview.xml b/src/main/res/layout/media_preview.xml new file mode 100644 index 000000000..8849e7b4a --- /dev/null +++ b/src/main/res/layout/media_preview.xml @@ -0,0 +1,28 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml index e89ed2944..b10a13815 100644 --- a/src/main/res/values/attrs.xml +++ b/src/main/res/values/attrs.xml @@ -42,6 +42,15 @@ + + + + + + + + + @@ -90,6 +99,7 @@ + diff --git a/src/main/res/values/defaults.xml b/src/main/res/values/defaults.xml index 4867a8e95..e213ea773 100644 --- a/src/main/res/values/defaults.xml +++ b/src/main/res/values/defaults.xml @@ -86,7 +86,6 @@ true true recent - false false false 144 diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml index 50f955c89..c420781ea 100644 --- a/src/main/res/values/dimens.xml +++ b/src/main/res/values/dimens.xml @@ -10,6 +10,7 @@ 224dp 16dp + 80dp 4dp 8dp diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index aefe978c0..e5261bb44 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -712,8 +712,6 @@ %s have read up to this point %s has read up to this point %1$s and %2$d more have read up to this point - Quick Sharing - Immediately return to previous activity instead of opening the conversation after sharing something This chat is unencrypted, for security reasons you should activate message encryption by using the lock icon. The preferable encryption is OMEMO. Warn if chat is unencrypted If message encryption is available, you should use it. If you don\'t use message encryption, show a warning message inside the chat. diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml index 0e58f26a6..628963db2 100644 --- a/src/main/res/values/themes.xml +++ b/src/main/res/values/themes.xml @@ -65,6 +65,15 @@ @drawable/ic_send_voice_offline + @drawable/ic_description_black_48dp + @drawable/ic_mic_black_48dp + @drawable/ic_headset_black_48dp + @drawable/ic_room_black_48dp + @drawable/ic_person_black_48dp + @drawable/ic_android_black_48dp + @drawable/ic_event_black_48dp + @drawable/ic_archive_black_48dp + @drawable/ic_group_add_white_24dp @drawable/ic_person_add_white_24dp @drawable/ic_cancel_black_24dp @@ -106,6 +115,7 @@ 16dp 16dp 0.54 + 0.70 @drawable/ic_attach_camera @drawable/ic_attach_document @@ -219,10 +229,20 @@ @color/primary_dark 0.7 + 0.7 24dp 16dp + @drawable/ic_description_white_48dp + @drawable/ic_mic_white_48dp + @drawable/ic_headset_white_48dp + @drawable/ic_room_white_48dp + @drawable/ic_person_white_48dp + @drawable/ic_android_white_48dp + @drawable/ic_event_white_48dp + @drawable/ic_archive_white_48dp + @drawable/ic_group_add_white_24dp @drawable/ic_person_add_white_24dp @drawable/ic_cancel_white_24dp diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index d6241a5ff..819fcded9 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -203,11 +203,6 @@ android:key="video_compression" android:summary="@string/pref_video_compression_summary" android:title="@string/pref_video_compression" /> -