aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Schneppe <kriztan@users.noreply.github.com>2018-09-27 18:42:25 +0200
committerGitHub <noreply@github.com>2018-09-27 18:42:25 +0200
commit4f4a52f068f3b3956aca5934531a48d1f90d7974 (patch)
treeda24e0f840730a75f2b3dab69a3b5f12025391be
parent1816092e68584b5794303578b6af6d621ffc58d4 (diff)
parentf4ac1f68feac3e3b19ab5932ede9d79dd14bff27 (diff)
Merge branch 'master' into bubble-into-avatar
-rw-r--r--src/main/AndroidManifest.xml16
-rw-r--r--src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java5
-rw-r--r--src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java24
-rw-r--r--src/main/java/de/pixart/messenger/persistance/FileBackend.java124
-rw-r--r--src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java120
-rw-r--r--src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java6
-rw-r--r--src/main/java/de/pixart/messenger/services/ExportLogsService.java4
-rw-r--r--src/main/java/de/pixart/messenger/services/XmppConnectionService.java37
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java27
-rw-r--r--src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java41
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationFragment.java498
-rw-r--r--src/main/java/de/pixart/messenger/ui/ConversationsActivity.java22
-rw-r--r--src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java77
-rw-r--r--src/main/java/de/pixart/messenger/ui/RecordingActivity.java2
-rw-r--r--src/main/java/de/pixart/messenger/ui/SearchActivity.java9
-rw-r--r--src/main/java/de/pixart/messenger/ui/SettingsActivity.java76
-rw-r--r--src/main/java/de/pixart/messenger/ui/ShareWithActivity.java297
-rw-r--r--src/main/java/de/pixart/messenger/ui/UpdaterActivity.java4
-rw-r--r--src/main/java/de/pixart/messenger/ui/WelcomeActivity.java14
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java8
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java184
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java8
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java226
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java188
-rw-r--r--src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java51
-rw-r--r--src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java8
-rw-r--r--src/main/java/de/pixart/messenger/ui/interfaces/OnMediaLoaded.java10
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/Attachment.java166
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java60
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java5
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/Drawable.java43
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/GridManager.java76
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/PendingItem.java6
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java17
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java27
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java (renamed from src/main/java/de/pixart/messenger/ui/util/Color.java)18
-rw-r--r--src/main/java/de/pixart/messenger/ui/util/ViewUtil.java72
-rw-r--r--src/main/java/de/pixart/messenger/ui/widget/SquareFrameLayout.java25
-rw-r--r--src/main/java/de/pixart/messenger/utils/Compatibility.java10
-rw-r--r--src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java4
-rw-r--r--src/main/res/drawable-hdpi/ic_android_black_48dp.pngbin0 -> 710 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_android_white_48dp.pngbin0 -> 733 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_archive_black_48dp.pngbin0 -> 560 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_archive_white_48dp.pngbin0 -> 557 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_attach_videocam.pngbin0 -> 222 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_contact_white_24dp.pngbin0 -> 324 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_description_black_48dp.pngbin0 -> 405 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_description_white_48dp.pngbin0 -> 421 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_event_black_48dp.pngbin0 -> 344 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_event_white_48dp.pngbin0 -> 342 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_headset_black_48dp.pngbin0 -> 857 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_headset_white_48dp.pngbin0 -> 865 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_help_black_48dp.pngbin0 -> 1176 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_help_white_48dp.pngbin0 -> 1177 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_mic_black_48dp.pngbin0 -> 866 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_mic_white_48dp.pngbin0 -> 870 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_person_black_48dp.pngbin0 -> 622 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_person_white_48dp.pngbin0 -> 623 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_room_black_48dp.pngbin0 -> 948 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_room_white_48dp.pngbin0 -> 966 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_send_videocam_away.pngbin0 -> 342 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_send_videocam_dnd.pngbin0 -> 397 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_send_videocam_online.pngbin0 -> 389 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_android_black_48dp.pngbin0 -> 410 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_android_white_48dp.pngbin0 -> 416 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_archive_black_48dp.pngbin0 -> 309 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_archive_white_48dp.pngbin0 -> 306 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_attach_videocam.pngbin0 -> 137 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_contact_white_24dp.pngbin0 -> 201 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_description_black_48dp.pngbin0 -> 247 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_description_white_48dp.pngbin0 -> 257 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_event_black_48dp.pngbin0 -> 228 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_event_white_48dp.pngbin0 -> 221 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_headset_black_48dp.pngbin0 -> 466 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_headset_white_48dp.pngbin0 -> 473 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_help_black_48dp.pngbin0 -> 656 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_help_white_48dp.pngbin0 -> 659 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_mic_black_48dp.pngbin0 -> 487 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_mic_white_48dp.pngbin0 -> 489 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_person_black_48dp.pngbin0 -> 359 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_person_white_48dp.pngbin0 -> 351 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_room_black_48dp.pngbin0 -> 524 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_room_white_48dp.pngbin0 -> 537 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_send_videocam_away.pngbin0 -> 217 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_send_videocam_dnd.pngbin0 -> 242 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_send_videocam_online.pngbin0 -> 245 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_android_black_48dp.pngbin0 -> 838 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_android_white_48dp.pngbin0 -> 852 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_archive_black_48dp.pngbin0 -> 607 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_archive_white_48dp.pngbin0 -> 620 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_attach_videocam.pngbin0 -> 202 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_contact_white_24dp.pngbin0 -> 370 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_description_black_48dp.pngbin0 -> 460 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_description_white_48dp.pngbin0 -> 485 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_event_black_48dp.pngbin0 -> 435 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_event_white_48dp.pngbin0 -> 439 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_headset_black_48dp.pngbin0 -> 1006 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_headset_white_48dp.pngbin0 -> 1017 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_help_black_48dp.pngbin0 -> 1406 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_help_white_48dp.pngbin0 -> 1426 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_mic_black_48dp.pngbin0 -> 1002 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_mic_white_48dp.pngbin0 -> 1008 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_person_black_48dp.pngbin0 -> 751 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_person_white_48dp.pngbin0 -> 738 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_room_black_48dp.pngbin0 -> 1093 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_room_white_48dp.pngbin0 -> 1121 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_send_videocam_away.pngbin0 -> 307 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_send_videocam_dnd.pngbin0 -> 325 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_send_videocam_online.pngbin0 -> 318 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_android_black_48dp.pngbin0 -> 1548 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_android_white_48dp.pngbin0 -> 1602 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_archive_black_48dp.pngbin0 -> 1073 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_archive_white_48dp.pngbin0 -> 1084 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_attach_videocam.pngbin0 -> 294 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_contact_white_24dp.pngbin0 -> 647 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_description_black_48dp.pngbin0 -> 824 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_description_white_48dp.pngbin0 -> 861 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_event_black_48dp.pngbin0 -> 787 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_event_white_48dp.pngbin0 -> 800 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_headset_black_48dp.pngbin0 -> 1867 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_headset_white_48dp.pngbin0 -> 1896 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_help_black_48dp.pngbin0 -> 2613 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_help_white_48dp.pngbin0 -> 2702 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_mic_black_48dp.pngbin0 -> 1846 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_mic_white_48dp.pngbin0 -> 1866 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_person_black_48dp.pngbin0 -> 1351 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_person_white_48dp.pngbin0 -> 1360 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_room_black_48dp.pngbin0 -> 2010 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_room_white_48dp.pngbin0 -> 2071 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_send_videocam_away.pngbin0 -> 609 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.pngbin0 -> 667 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_send_videocam_online.pngbin0 -> 654 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_android_black_48dp.pngbin0 -> 1868 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_android_white_48dp.pngbin0 -> 1951 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.pngbin0 -> 1219 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.pngbin0 -> 1243 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_attach_videocam.pngbin0 -> 405 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_contact_white_24dp.pngbin0 -> 756 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_description_black_48dp.pngbin0 -> 1050 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_description_white_48dp.pngbin0 -> 1087 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_event_black_48dp.pngbin0 -> 1061 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_event_white_48dp.pngbin0 -> 1053 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.pngbin0 -> 2162 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.pngbin0 -> 2214 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_help_black_48dp.pngbin0 -> 2945 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_help_white_48dp.pngbin0 -> 3067 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.pngbin0 -> 2108 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.pngbin0 -> 2141 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_person_black_48dp.pngbin0 -> 1552 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_person_white_48dp.pngbin0 -> 1552 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_room_black_48dp.pngbin0 -> 2283 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_room_white_48dp.pngbin0 -> 2439 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_send_videocam_away.pngbin0 -> 630 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.pngbin0 -> 663 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_send_videocam_online.pngbin0 -> 643 bytes
-rw-r--r--src/main/res/layout/activity_choose_contact.xml2
-rw-r--r--src/main/res/layout/activity_contact_details.xml54
-rw-r--r--src/main/res/layout/activity_manage_accounts.xml2
-rw-r--r--src/main/res/layout/activity_media_browser.xml24
-rw-r--r--src/main/res/layout/activity_muc_details.xml46
-rw-r--r--src/main/res/layout/activity_start_conversation.xml2
-rw-r--r--src/main/res/layout/conversation_list_row.xml4
-rw-r--r--src/main/res/layout/fragment_conversation.xml37
-rw-r--r--src/main/res/layout/media.xml16
-rw-r--r--src/main/res/layout/media_preview.xml28
-rw-r--r--src/main/res/menu/choose_attachment.xml7
-rw-r--r--src/main/res/menu/fragment_conversation.xml44
-rw-r--r--src/main/res/values-h360dp/dimens.xml4
-rw-r--r--src/main/res/values-h500dp/dimens.xml4
-rw-r--r--src/main/res/values-w384dp/dimens.xml4
-rw-r--r--src/main/res/values-w585dp/dimens.xml3
-rw-r--r--src/main/res/values/arrays.xml6
-rw-r--r--src/main/res/values/attrs.xml12
-rw-r--r--src/main/res/values/defaults.xml1
-rw-r--r--src/main/res/values/dimens.xml4
-rw-r--r--src/main/res/values/strings.xml9
-rw-r--r--src/main/res/values/themes.xml30
-rw-r--r--src/main/res/xml/preferences.xml5
179 files changed, 1893 insertions, 1080 deletions
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 79b621878..bfdb2ec3d 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -214,18 +214,10 @@
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEND" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="text/plain" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.SEND" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="*/*" />
- </intent-filter>
- <intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
+
<category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="image/*" />
+ <data android:mimeType="*/*" />
</intent-filter>
<meta-data
@@ -283,6 +275,10 @@
android:theme="@style/Base.Theme.AppCompat" />
<activity android:name=".ui.MemorizingActivity" />
+ <activity
+ android:name=".ui.MediaBrowserActivity"
+ android:label="@string/media_browser" />
+
<service android:name=".services.ExportLogsService" />
<service
android:name=".services.ContactChooserTargetService"
diff --git a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
index 0adbbe51f..f9b2539c3 100644
--- a/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/de/pixart/messenger/crypto/axolotl/XmppAxolotlMessage.java
@@ -21,6 +21,7 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import de.pixart.messenger.Config;
+import de.pixart.messenger.utils.Compatibility;
import de.pixart.messenger.xml.Element;
import rocks.xmpp.addr.Jid;
@@ -177,7 +178,7 @@ public class XmppAxolotlMessage {
try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
@@ -293,7 +294,7 @@ public class XmppAxolotlMessage {
key = newKey;
}
- Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
index 94710b86e..365f8fa87 100644
--- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java
@@ -35,6 +35,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import de.pixart.messenger.Config;
@@ -787,6 +788,29 @@ public class DatabaseBackend extends SQLiteOpenHelper {
};
}
+ public List<FilePath> getRelativeFilePaths(String account, Jid jid, int limit) {
+ SQLiteDatabase db = this.getReadableDatabase();
+ final String SQL = "select uuid,relativeFilePath from messages where type in (1,2) and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
+ final String[] args = {account, jid.toEscapedString(), jid.toEscapedString() + "/%"};
+ Cursor cursor = db.rawQuery(SQL + (limit > 0 ? " limit " + String.valueOf(limit) : ""), args);
+ List<FilePath> filesPaths = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ filesPaths.add(new FilePath(cursor.getString(0), cursor.getString(1)));
+ }
+ cursor.close();
+ return filesPaths;
+ }
+
+ public static class FilePath {
+ public final UUID uuid;
+ public final String path;
+
+ private FilePath(String uuid, String path) {
+ this.uuid = UUID.fromString(uuid);
+ this.path = path;
+ }
+ }
+
public Conversation findConversation(final Account account, final Jid contactJid) {
SQLiteDatabase db = this.getReadableDatabase();
String[] selectionArgs = {account.getUuid(),
diff --git a/src/main/java/de/pixart/messenger/persistance/FileBackend.java b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
index 0c3028feb..822fa6318 100644
--- a/src/main/java/de/pixart/messenger/persistance/FileBackend.java
+++ b/src/main/java/de/pixart/messenger/persistance/FileBackend.java
@@ -46,6 +46,7 @@ import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -56,6 +57,7 @@ import de.pixart.messenger.R;
import de.pixart.messenger.entities.DownloadableFile;
import de.pixart.messenger.entities.Message;
import de.pixart.messenger.services.XmppConnectionService;
+import de.pixart.messenger.ui.util.Attachment;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.ExifHelper;
import de.pixart.messenger.utils.FileUtils;
@@ -80,8 +82,8 @@ public class FileBackend {
}
private void createNoMedia() {
- final File nomedia_files = new File(getConversationsDirectory("Files", true) + ".nomedia");
- final File nomedia_audios = new File(getConversationsDirectory("Audios", true) + ".nomedia");
+ final File nomedia_files = new File(getConversationsDirectory("Files") + ".nomedia");
+ final File nomedia_audios = new File(getConversationsDirectory("Audios") + ".nomedia");
if (!nomedia_files.exists()) {
try {
nomedia_files.createNewFile();
@@ -99,8 +101,8 @@ public class FileBackend {
}
public void updateMediaScanner(File file) {
- if (file.getAbsolutePath().startsWith(getConversationsDirectory("Images", true))
- || file.getAbsolutePath().startsWith(getConversationsDirectory("Videos", true))) {
+ if (file.getAbsolutePath().startsWith(getConversationsDirectory("Images"))
+ || file.getAbsolutePath().startsWith(getConversationsDirectory("Videos"))) {
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(file));
mXmppConnectionService.sendBroadcast(intent);
@@ -129,13 +131,13 @@ public class FileBackend {
file = new DownloadableFile(path);
} else {
if (mime != null && mime.startsWith("image")) {
- file = new DownloadableFile(getConversationsDirectory("Images", true) + path);
+ file = new DownloadableFile(getConversationsDirectory("Images") + path);
} else if (mime != null && mime.startsWith("video")) {
- file = new DownloadableFile(getConversationsDirectory("Videos", true) + path);
+ file = new DownloadableFile(getConversationsDirectory("Videos") + path);
} else if (mime != null && mime.startsWith("audio")) {
- file = new DownloadableFile(getConversationsDirectory("Audios", true) + path);
+ file = new DownloadableFile(getConversationsDirectory("Audios") + path);
} else {
- file = new DownloadableFile(getConversationsDirectory("Files", true) + path);
+ file = new DownloadableFile(getConversationsDirectory("Files") + path);
}
}
return file;
@@ -151,7 +153,7 @@ public class FileBackend {
}
final DownloadableFile file = getFileForPath(path, message.getMimeType());
if (encrypted) {
- return new DownloadableFile(getConversationsDirectory("Files", true) + file.getName() + ".pgp");
+ return new DownloadableFile(getConversationsDirectory("Files") + file.getName() + ".pgp");
} else {
return file;
}
@@ -172,16 +174,19 @@ public class FileBackend {
}
}
- public static boolean allFilesUnderSize(Context context, List<Uri> uris, long max) {
+ public static boolean allFilesUnderSize(Context context, List<Attachment> 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 +195,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;
}
@@ -198,31 +203,43 @@ public class FileBackend {
return true;
}
- public static String getDirectoryName(final String type, final boolean isMedia) {
- String media = "";
- if (isMedia) {
- media = "Media/Pix-Art Messenger ";
+ public List<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) {
+ List<Attachment> attachments = new ArrayList<>();
+ for (DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) {
+ final String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path));
+ Log.d(Config.LOGTAG, "mime=" + mime);
+ File file = getFileForPath(relativeFilePath.path, mime);
+ if (file.exists()) {
+ attachments.add(Attachment.of(relativeFilePath.uuid, file, mime));
+ } else {
+ Log.d(Config.LOGTAG, "file " + file.getAbsolutePath() + " doesn't exist");
+ }
}
- if (type == "null" || type == null) {
- return "/Pix-Art Messenger/";
+ return attachments;
+ }
+
+ public static String getConversationsDirectory(final String type) {
+ if (type.equalsIgnoreCase("null") || type == null) {
+ return getAppMediaDirectory() + "Pix-Art Messenger" + "/";
} else {
- return "/Pix-Art Messenger" + "/" + media + type + "/";
+ return getAppMediaDirectory() + "Pix-Art Messenger" + " " + type + "/";
}
}
- public static String getConversationsDirectory(final String type, final boolean isMedia) {
- String DirName = null;
- if (type != "null" || type != null) {
- DirName = type;
- }
- String path = Environment.getExternalStorageDirectory().getAbsolutePath() + getDirectoryName(DirName, isMedia);
- File createFolders = new File(path);
- if (!createFolders.exists()) {
- Log.d(Config.LOGTAG, "creating directory " + createFolders);
- createFolders.mkdirs();
- }
- return path;
+ public static String getAppMediaDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "Pix-Art Messenger" + "/Media/";
+ }
+ public static String getBackupDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "Pix-Art Messenger" + "/Database/";
+ }
+
+ public static String getAppLogsDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "Pix-Art Messenger" + "/Chats/";
+ }
+
+ public static String getAppUpdateDirectory() {
+ return Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "Pix-Art Messenger" + "/Update/";
}
private Bitmap resize(final Bitmap originalBitmap, int size) throws IOException {
@@ -294,7 +311,7 @@ public class FileBackend {
Log.d(Config.LOGTAG, "File path = null");
return false;
}
- if (path.contains(getDirectoryName("null", true))) {
+ if (path.contains(getConversationsDirectory("null"))) {
Log.d(Config.LOGTAG, "File " + path + " is in our directory");
return true;
}
@@ -554,6 +571,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 +1073,30 @@ public class FileBackend {
return getVideoDimensions(metadataRetriever);
}
+ public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) {
+ final String key = "attachment_" + attachment.getUuid().toString() + "_" + String.valueOf(size);
+ final LruCache<String, Bitmap> cache = mXmppConnectionService.getBitmapCache();
+ Bitmap bitmap = cache.get(key);
+ if (bitmap != null || cacheOnly) {
+ return bitmap;
+ }
+ 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;
+ }
+ }
+ if (bitmap != null) {
+ cache.put(key, 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/AbstractConnectionManager.java b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
index 1c4c6ca19..65711ef42 100644
--- a/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
+++ b/src/main/java/de/pixart/messenger/services/AbstractConnectionManager.java
@@ -1,85 +1,39 @@
package de.pixart.messenger.services;
-import android.Manifest;
import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.util.Log;
import android.util.Pair;
-import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.modes.AEADBlockCipher;
-import org.bouncycastle.crypto.modes.GCMBlockCipher;
-import org.bouncycastle.crypto.params.AEADParameters;
-import org.bouncycastle.crypto.params.KeyParameter;
-
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
-import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import de.pixart.messenger.Config;
import de.pixart.messenger.R;
import de.pixart.messenger.entities.DownloadableFile;
+import de.pixart.messenger.utils.Compatibility;
public class AbstractConnectionManager {
- protected XmppConnectionService mXmppConnectionService;
+ private static final String KEYTYPE = "AES";
+ private static final String CIPHERMODE = "AES/GCM/NoPadding";
+ private static final String PROVIDER = "BC";
private static final int UI_REFRESH_THRESHOLD = 250;
private static final AtomicLong LAST_UI_UPDATE_CALL = new AtomicLong(0);
+ protected XmppConnectionService mXmppConnectionService;
public AbstractConnectionManager(XmppConnectionService service) {
this.mXmppConnectionService = service;
}
- public XmppConnectionService getXmppConnectionService() {
- return this.mXmppConnectionService;
- }
-
- public long getAutoAcceptFileSize() {
- long defaultValue_wifi = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_wifi);
- long defaultValue_mobile = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_mobile);
- long defaultValue_roaming = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_roaming);
-
- String config = "0";
- if (mXmppConnectionService.isWIFI()) {
- config = this.mXmppConnectionService.getPreferences().getString(
- "auto_accept_file_size_wifi", String.valueOf(defaultValue_wifi));
- } else if (mXmppConnectionService.isMobile()) {
- config = this.mXmppConnectionService.getPreferences().getString(
- "auto_accept_file_size_mobile", String.valueOf(defaultValue_mobile));
- } else if (mXmppConnectionService.isMobileRoaming()) {
- config = this.mXmppConnectionService.getPreferences().getString(
- "auto_accept_file_size_roaming", String.valueOf(defaultValue_roaming));
- }
- try {
- return Long.parseLong(config);
- } catch (NumberFormatException e) {
- return defaultValue_mobile;
- }
- }
-
- public boolean hasStoragePermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- return mXmppConnectionService.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
- } else {
- return true;
- }
- }
-
public static Pair<InputStream, Integer> createInputStream(DownloadableFile file, boolean gcm) throws FileNotFoundException {
FileInputStream is;
int size;
@@ -90,15 +44,15 @@ public class AbstractConnectionManager {
}
try {
if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(true, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- InputStream cis = new org.bouncycastle.crypto.io.CipherInputStream(is, cipher);
- return new Pair<>(cis, cipher.getOutputSize(size));
+ Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
+ return new Pair<>(new CipherInputStream(is, cipher), cipher.getOutputSize(size));
} else {
IvParameterSpec ips = new IvParameterSpec(file.getIv());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted input stream");
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(file.getKey(), KEYTYPE), ips);
return new Pair<>(new CipherInputStream(is, cipher), (size / 16 + 1) * 16);
}
} catch (Exception e) {
@@ -126,27 +80,53 @@ public class AbstractConnectionManager {
}
try {
if (gcm) {
- AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
- cipher.init(false, new AEADParameters(new KeyParameter(file.getKey()), 128, file.getIv()));
- return new org.bouncycastle.crypto.io.CipherOutputStream(os, cipher);
+ Cipher cipher = Compatibility.twentyTwo() ? Cipher.getInstance(CIPHERMODE) : Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(file.getKey(), KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(file.getIv());
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+ return new CipherOutputStream(os, cipher);
} else {
IvParameterSpec ips = new IvParameterSpec(file.getIv());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), "AES"), ips);
- Log.d(Config.LOGTAG, "opening encrypted output stream");
+ cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(file.getKey(), KEYTYPE), ips);
return new CipherOutputStream(os, cipher);
}
- } catch (InvalidKeyException e) {
- return null;
- } catch (NoSuchAlgorithmException e) {
- return null;
- } catch (NoSuchPaddingException e) {
- return null;
- } catch (InvalidAlgorithmParameterException e) {
- return null;
+ } catch (Exception e) {
+ throw new AssertionError(e);
}
}
+ public XmppConnectionService getXmppConnectionService() {
+ return this.mXmppConnectionService;
+ }
+
+ public long getAutoAcceptFileSize() {
+ long defaultValue_wifi = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_wifi);
+ long defaultValue_mobile = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_mobile);
+ long defaultValue_roaming = this.getXmppConnectionService().getResources().getInteger(R.integer.auto_accept_filesize_roaming);
+
+ String config = "0";
+ if (mXmppConnectionService.isWIFI()) {
+ config = this.mXmppConnectionService.getPreferences().getString(
+ "auto_accept_file_size_wifi", String.valueOf(defaultValue_wifi));
+ } else if (mXmppConnectionService.isMobile()) {
+ config = this.mXmppConnectionService.getPreferences().getString(
+ "auto_accept_file_size_mobile", String.valueOf(defaultValue_mobile));
+ } else if (mXmppConnectionService.isMobileRoaming()) {
+ config = this.mXmppConnectionService.getPreferences().getString(
+ "auto_accept_file_size_roaming", String.valueOf(defaultValue_roaming));
+ }
+ try {
+ return Long.parseLong(config);
+ } catch (NumberFormatException e) {
+ return defaultValue_mobile;
+ }
+ }
+
+ public boolean hasStoragePermission() {
+ return Compatibility.hasStoragePermission(mXmppConnectionService);
+ }
+
public void updateConversationUi(boolean force) {
synchronized (LAST_UI_UPDATE_CALL) {
if (force || SystemClock.elapsedRealtime() - LAST_UI_UPDATE_CALL.get() >= UI_REFRESH_THRESHOLD) {
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/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
index b0da108ba..e66873acc 100644
--- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java
+++ b/src/main/java/de/pixart/messenger/services/ExportLogsService.java
@@ -41,7 +41,7 @@ import static de.pixart.messenger.ui.SettingsActivity.USE_MULTI_ACCOUNTS;
public class ExportLogsService extends XmppConnectionService {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
- private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory("Chats", false) + "%s";
+ private static final String DIRECTORY_STRING_FORMAT = FileBackend.getAppLogsDirectory() + "%s";
private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n";
private static AtomicBoolean running = new AtomicBoolean(false);
boolean ReadableLogsEnabled = false;
@@ -171,7 +171,7 @@ public class ExportLogsService extends XmppConnectionService {
// Get hold of the db:
FileInputStream InputFile = new FileInputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
// Set the output folder on the SDcard
- File directory = new File(FileBackend.getConversationsDirectory("Database", false));
+ File directory = new File(FileBackend.getBackupDirectory());
// Create the folder if it doesn't exist:
if (!directory.exists()) {
boolean directory_created = directory.mkdirs();
diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
index b6d23668c..525354957 100644
--- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
+++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java
@@ -116,6 +116,7 @@ import de.pixart.messenger.persistance.FileBackend;
import de.pixart.messenger.ui.SettingsActivity;
import de.pixart.messenger.ui.UiCallback;
import de.pixart.messenger.ui.interfaces.OnAvatarPublication;
+import de.pixart.messenger.ui.interfaces.OnMediaLoaded;
import de.pixart.messenger.ui.interfaces.OnSearchResultsAvailable;
import de.pixart.messenger.utils.Compatibility;
import de.pixart.messenger.utils.ConversationsFileObserver;
@@ -431,7 +432,6 @@ public class XmppConnectionService extends Service {
public void stopForcingForegroundNotification() {
mForceForegroundService.set(false);
toggleForegroundService();
- mNotificationService.dismissForcedForegroundNotification();
}
public boolean areMessagesInitialized() {
@@ -727,6 +727,7 @@ public class XmppConnectionService extends Service {
}
private boolean processAccountState(Account account, boolean interactive, boolean isUiAction, boolean isAccountPushed, HashSet<Account> pingCandidates) {
+ storeNumberOfAccounts(this.getAccounts().size());
boolean pingNow = false;
if (account.getStatus().isAttemptReconnect()) {
if (!hasInternetConnection()) {
@@ -802,6 +803,14 @@ public class XmppConnectionService extends Service {
return pingNow;
}
+ private void storeNumberOfAccounts(int accounts) {
+ //write No of accounts to file
+ final SharedPreferences.Editor editor = getPreferences().edit();
+ Log.d(Config.LOGTAG, "Number of accounts is " + accounts);
+ editor.putInt(SettingsActivity.NUMBER_OF_ACCOUNTS, accounts);
+ editor.apply();
+ }
+
public boolean isDataSaverDisabled() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
@@ -1126,7 +1135,7 @@ public class XmppConnectionService extends Service {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
startContactObserver();
}
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
+ if (Compatibility.hasStoragePermission(this)) {
Log.d(Config.LOGTAG, "starting file observer");
new Thread(fileObserver::startWatching).start();
}
@@ -1224,19 +1233,21 @@ public class XmppConnectionService extends Service {
}
public void toggleForegroundService() {
- if (mForceForegroundService.get() || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) {
+ final boolean status;
+ if (mForceForegroundService.get() || (Compatibility.keepForegroundService(this)/* && hasEnabledAccounts()*/)) {
startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification());
- Log.d(Config.LOGTAG, "started foreground service");
+ status = true;
} else {
stopForeground(true);
- Log.d(Config.LOGTAG, "stopped foreground service");
+ status = false;
}
+ mNotificationService.dismissForcedForegroundNotification(); //if the channel was changed the previous call might fail
+ Log.d(Config.LOGTAG, "ForegroundService: " + (status ? "on" : "off"));
}
@Override
public void onTaskRemoved(final Intent rootIntent) {
super.onTaskRemoved(rootIntent);
- //TODO check for accounts enabled
if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) || mForceForegroundService.get()) {
Log.d(Config.LOGTAG, "ignoring onTaskRemoved because foreground service is activated");
} else {
@@ -2661,7 +2672,7 @@ public class XmppConnectionService extends Service {
private boolean hasEnabledAccounts() {
if (this.accounts == null) {
- return true; // set to true if accounts could not be fetched - used for notifications
+ return false; // set to false if accounts could not be fetched - used for notifications
}
for (Account account : this.accounts) {
if (account.isEnabled()) {
@@ -2671,6 +2682,18 @@ public class XmppConnectionService extends Service {
return false;
}
+ public void getAttachments(final Conversation conversation, int limit, final OnMediaLoaded onMediaLoaded) {
+ getAttachments(conversation.getAccount(), conversation.getJid().asBareJid(), limit, onMediaLoaded);
+ }
+
+ public void getAttachments(final Account account, final Jid jid, final int limit, final OnMediaLoaded onMediaLoaded) {
+ getAttachments(account.getUuid(), jid.asBareJid(), limit, onMediaLoaded);
+ }
+
+ public void getAttachments(final String account, final Jid jid, final int limit, final OnMediaLoaded onMediaLoaded) {
+ new Thread(() -> onMediaLoaded.onMediaLoaded(fileBackend.convertToAttachments(databaseBackend.getRelativeFilePaths(account, jid, limit)))).start();
+ }
+
public void persistSelfNick(MucOptions.User self) {
final Conversation conversation = self.getConversation();
Jid full = self.getFullJid();
diff --git a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
index 38479b459..070914bb8 100644
--- a/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ConferenceDetailsActivity.java
@@ -32,6 +32,7 @@ import org.openintents.openpgp.util.OpenPgpUtils;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -49,9 +50,14 @@ import de.pixart.messenger.entities.MucOptions.User;
import de.pixart.messenger.services.XmppConnectionService;
import de.pixart.messenger.services.XmppConnectionService.OnConversationUpdate;
import de.pixart.messenger.services.XmppConnectionService.OnMucRosterUpdate;
+import de.pixart.messenger.ui.adapter.MediaAdapter;
+import de.pixart.messenger.ui.interfaces.OnMediaLoaded;
+import de.pixart.messenger.ui.util.Attachment;
+import de.pixart.messenger.ui.util.GridManager;
import de.pixart.messenger.ui.util.MucDetailsContextMenuHelper;
import de.pixart.messenger.ui.util.MyLinkify;
import de.pixart.messenger.ui.util.SoftKeyboardUtils;
+import de.pixart.messenger.utils.Compatibility;
import de.pixart.messenger.utils.EmojiWrapper;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
import de.pixart.messenger.utils.StringUtils;
@@ -64,7 +70,7 @@ import rocks.xmpp.addr.Jid;
import static de.pixart.messenger.entities.Bookmark.printableValue;
import static de.pixart.messenger.utils.StringUtils.changed;
-public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher {
+public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher, OnMediaLoaded {
public static final String ACTION_VIEW_MUC = "view_muc";
private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
private Conversation mConversation;
@@ -96,6 +102,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
}
};
private ActivityMucDetailsBinding binding;
+ private MediaAdapter mMediaAdapter;
private String uuid = null;
private User mSelectedUser = null;
@@ -319,6 +326,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
this.binding.mucEditTitle.addTextChangedListener(this);
this.binding.mucEditSubject.addTextChangedListener(this);
this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject));
+ mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
+ this.binding.media.setAdapter(mMediaAdapter);
+ GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
}
@Override
@@ -328,6 +338,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
if (this.mTheme != theme) {
recreate();
}
+ binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE);
}
@Override
@@ -475,6 +486,15 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
return true;
}
+ @Override
+ public void onMediaLoaded(List<Attachment> attachments) {
+ runOnUiThread(() -> {
+ int limit = GridManager.getCurrentColumnCount(binding.media);
+ mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit, attachments.size())));
+ binding.mediaWrapper.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE);
+ });
+ }
+
protected void saveAsBookmark() {
xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName());
updateView();
@@ -503,6 +523,11 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
if (uuid != null) {
this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
if (this.mConversation != null) {
+ if (Compatibility.hasStoragePermission(this)) {
+ final int limit = GridManager.getCurrentColumnCount(this.binding.media);
+ xmppConnectionService.getAttachments(this.mConversation, limit, this);
+ this.binding.showMedia.setOnClickListener((v) -> MediaBrowserActivity.launch(this, mConversation));
+ }
updateView();
}
}
diff --git a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
index 5dce3fca2..1dfc80e3c 100644
--- a/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/ContactDetailsActivity.java
@@ -30,6 +30,8 @@ import android.widget.Toast;
import org.openintents.openpgp.util.OpenPgpUtils;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -45,6 +47,11 @@ import de.pixart.messenger.entities.Conversation;
import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.services.XmppConnectionService.OnAccountUpdate;
import de.pixart.messenger.services.XmppConnectionService.OnRosterUpdate;
+import de.pixart.messenger.ui.adapter.MediaAdapter;
+import de.pixart.messenger.ui.interfaces.OnMediaLoaded;
+import de.pixart.messenger.ui.util.Attachment;
+import de.pixart.messenger.ui.util.GridManager;
+import de.pixart.messenger.utils.Compatibility;
import de.pixart.messenger.utils.CryptoHelper;
import de.pixart.messenger.utils.IrregularUnicodeDetector;
import de.pixart.messenger.utils.MenuDoubleTabUtil;
@@ -57,12 +64,13 @@ import de.pixart.messenger.xmpp.OnUpdateBlocklist;
import de.pixart.messenger.xmpp.XmppConnection;
import rocks.xmpp.addr.Jid;
-public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
+public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnMediaLoaded {
public static final String ACTION_VIEW_CONTACT = "view_contact";
private Contact contact;
private Conversation mConversation;
ActivityContactDetailsBinding binding;
+ private MediaAdapter mMediaAdapter;
private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
@Override
@@ -260,6 +268,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
this.mNotifyStatusButton = findViewById(R.id.notification_status_button);
this.mNotifyStatusButton.setOnClickListener(this.mNotifyStatusClickListener);
this.mNotifyStatusText = findViewById(R.id.notification_status_text);
+ mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
+ this.binding.media.setAdapter(mMediaAdapter);
+ GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
}
@Override
@@ -279,6 +290,8 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
this.showLastSeen = preferences.getBoolean("last_activity", false);
}
+ binding.mediaWrapper.setVisibility(Compatibility.hasStoragePermission(this) ? View.VISIBLE : View.GONE);
+ mMediaAdapter.setAttachments(Collections.emptyList());
}
@Override
@@ -552,12 +565,20 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
}
final AxolotlService axolotlService = contact.getAccount().getAxolotlService();
if (Config.supportOmemo() && axolotlService != null) {
+ final Collection<XmppAxolotlSession> sessions = axolotlService.findSessionsForContact(contact);
+ boolean anyActive = false;
+ for(XmppAxolotlSession session : sessions) {
+ anyActive = session.getTrust().isActive();
+ if (anyActive) {
+ break;
+ }
+ }
boolean skippedInactive = false;
boolean showsInactive = false;
- for (final XmppAxolotlSession session : axolotlService.findSessionsForContact(contact)) {
+ for (final XmppAxolotlSession session : sessions) {
final FingerprintStatus trust = session.getTrust();
hasKeys |= !trust.isCompromised();
- if (!trust.isActive()) {
+ if (!trust.isActive() && anyActive) {
if (showInactiveOmemo) {
showsInactive = true;
} else {
@@ -657,6 +678,11 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
processFingerprintVerification(mPendingFingerprintVerificationUri);
mPendingFingerprintVerificationUri = null;
}
+ if (Compatibility.hasStoragePermission(this)) {
+ final int limit = GridManager.getCurrentColumnCount(this.binding.media);
+ xmppConnectionService.getAttachments(account, contact.getJid().asBareJid(), limit, this);
+ this.binding.showMedia.setOnClickListener((v) -> MediaBrowserActivity.launch(this, contact));
+ }
populateView();
}
}
@@ -676,4 +702,13 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show();
}
}
+
+ @Override
+ public void onMediaLoaded(List<Attachment> attachments) {
+ runOnUiThread(() -> {
+ int limit = GridManager.getCurrentColumnCount(binding.media);
+ mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit, attachments.size())));
+ binding.mediaWrapper.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE);
+ });
+ }
}
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java
index 0d4610c8e..d326058e7 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;
@@ -139,55 +131,56 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
public static final int REQUEST_DECRYPT_PGP = 0x0202;
public static final int REQUEST_ENCRYPT_MESSAGE = 0x0207;
public static final int REQUEST_TRUST_KEYS_TEXT = 0x0208;
- public static final int REQUEST_TRUST_KEYS_MENU = 0x0209;
+ public static final int REQUEST_TRUST_KEYS_ATTACHMENTS = 0x0209;
public static final int REQUEST_START_DOWNLOAD = 0x0210;
public static final int REQUEST_ADD_EDITOR_CONTENT = 0x0211;
public static final int ATTACHMENT_CHOICE = 0x0300;
public static final int ATTACHMENT_CHOICE_CHOOSE_IMAGE = 0x0301;
- public static final int ATTACHMENT_CHOICE_TAKE_FROM_CAMERA = 0x0302;
+ public static final int ATTACHMENT_CHOICE_TAKE_PHOTO = 0x0302;
public static final int ATTACHMENT_CHOICE_CHOOSE_FILE = 0x0303;
public static final int ATTACHMENT_CHOICE_RECORD_VOICE = 0x0304;
public static final int ATTACHMENT_CHOICE_LOCATION = 0x0305;
public static final int ATTACHMENT_CHOICE_CHOOSE_VIDEO = 0x0306;
+ public static final int ATTACHMENT_CHOICE_RECORD_VIDEO = 0x0307;
public static final int ATTACHMENT_CHOICE_INVALID = 0x0399;
public static final String RECENTLY_USED_QUICK_ACTION = "recently_used_quick_action";
public static final String STATE_CONVERSATION_UUID = ConversationFragment.class.getName() + ".uuid";
public static final String STATE_SCROLL_POSITION = ConversationFragment.class.getName() + ".scroll_position";
- public static final String STATE_PHOTO_URI = ConversationFragment.class.getName() + ".take_photo_uri";
- public static final String STATE_VIDEO_URI = ConversationFragment.class.getName() + ".take_video_uri";
+ public static final String STATE_PHOTO_URI = ConversationFragment.class.getName() + ".media_previews";
+ public static final String STATE_MEDIA_PREVIEWS = ConversationFragment.class.getName() + ".take_photo_uri";
private static final String STATE_LAST_MESSAGE_UUID = "state_last_message_uuid";
private final List<Message> messageList = new ArrayList<>();
- final private List<Uri> mPendingImageUris = new ArrayList<>();
- private String lastMessageUuid = null;
- public Uri mPendingEditorContent = null;
private final PendingItem<ActivityResult> postponedActivityResult = new PendingItem<>();
private final PendingItem<String> pendingConversationsUuid = new PendingItem<>();
+ private final PendingItem<ArrayList<Attachment>> pendingMediaPreviews = new PendingItem<>();
private final PendingItem<Bundle> pendingExtras = new PendingItem<>();
private final PendingItem<Uri> pendingTakePhotoUri = new PendingItem<>();
private final PendingItem<Uri> pendingTakeVideoUri = new PendingItem<>();
private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>();
private final PendingItem<String> pendingLastMessageUuid = new PendingItem<>();
private final PendingItem<Message> pendingMessage = new PendingItem<>();
- protected MessageAdapter messageListAdapter;
- private Conversation conversation;
+ public Uri mPendingEditorContent = null;
public FragmentConversationBinding binding;
+ protected MessageAdapter messageListAdapter;
protected Message lastHistoryMessage = null;
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, dd. MMM yyyy", Locale.getDefault());
+ private String lastMessageUuid = null;
+ private Conversation conversation;
private Toast messageLoaderToast;
private ConversationsActivity activity;
- private boolean reInitRequiredOnStart = true;
-
- private SimpleFingerGestures gesturesDetector = new SimpleFingerGestures();
-
+ private Menu mOptionsMenu;
protected OnClickListener clickToVerify = new OnClickListener() {
@Override
public void onClick(View v) {
activity.verifyOtrSessionDialog(conversation, v);
}
};
+ private boolean reInitRequiredOnStart = true;
+ private MediaPreviewAdapter mediaPreviewAdapter;
+ private SimpleFingerGestures gesturesDetector = new SimpleFingerGestures();
private OnClickListener clickToMuc = new OnClickListener() {
@Override
@@ -466,7 +459,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
switch (action) {
case CHOOSE_ATTACHMENT:
choose_attachment(v);
- case TAKE_FROM_CAMERA:
+ case TAKE_PHOTO:
+ case RECORD_VIDEO:
case SEND_LOCATION:
case RECORD_VOICE:
case CHOOSE_PICTURE:
@@ -494,32 +488,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
};
-
- @SuppressLint("RestrictedApi")
- private void choose_attachment(View v) {
- SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(activity);
- final boolean hideVoice = p.getBoolean("show_record_voice_btn", activity.getResources().getBoolean(R.bool.show_record_voice_btn));
- PopupMenu popup = new PopupMenu(activity, v);
- popup.inflate(R.menu.choose_attachment);
- Menu menu = popup.getMenu();
- ConversationMenuConfigurator.configureQuickShareAttachmentMenu(conversation, menu, hideVoice);
- popup.setOnMenuItemClickListener(attachmentItem -> {
- switch (attachmentItem.getItemId()) {
- case R.id.attach_choose_picture:
- case R.id.attach_take_picture:
- case R.id.attach_choose_file:
- case R.id.attach_record_voice:
- case R.id.attach_location:
- handleAttachmentSelection(attachmentItem);
- default:
- return false;
- }
- });
- MenuPopupHelper menuHelper = new MenuPopupHelper(getActivity(), (MenuBuilder) menu, v);
- menuHelper.setForceShowIcon(true);
- menuHelper.show();
- }
-
private View.OnLongClickListener mSendButtonLongListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -651,6 +619,32 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
+ @SuppressLint("RestrictedApi")
+ private void choose_attachment(View v) {
+ SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(activity);
+ final boolean hideVoice = p.getBoolean("show_record_voice_btn", activity.getResources().getBoolean(R.bool.show_record_voice_btn));
+ PopupMenu popup = new PopupMenu(activity, v);
+ popup.inflate(R.menu.choose_attachment);
+ Menu menu = popup.getMenu();
+ ConversationMenuConfigurator.configureQuickShareAttachmentMenu(conversation, menu, hideVoice);
+ popup.setOnMenuItemClickListener(attachmentItem -> {
+ switch (attachmentItem.getItemId()) {
+ case R.id.attach_choose_picture:
+ case R.id.attach_take_picture:
+ case R.id.attach_record_video:
+ case R.id.attach_choose_file:
+ case R.id.attach_record_voice:
+ case R.id.attach_location:
+ handleAttachmentSelection(attachmentItem);
+ default:
+ return false;
+ }
+ });
+ MenuPopupHelper menuHelper = new MenuPopupHelper(getActivity(), (MenuBuilder) menu, v);
+ menuHelper.setForceShowIcon(true);
+ menuHelper.show();
+ }
+
private void toggleScrollDownButton() {
toggleScrollDownButton(binding.messagesView);
}
@@ -851,111 +845,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<Message>() {
-
@Override
public void userInputRequried(PendingIntent pi, Message object) {
hidePrepareFileToast(prepareFileToast);
@@ -981,6 +883,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) {
@@ -1021,10 +927,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
protected boolean trustKeysIfNeeded(int requestCode) {
- return trustKeysIfNeeded(requestCode, ATTACHMENT_CHOICE_INVALID);
- }
-
- protected boolean trustKeysIfNeeded(int requestCode, int attachmentChoice) {
AxolotlService axolotlService = conversation.getAccount().getAxolotlService();
final List<Jid> targets = axolotlService.getCryptoTargets(conversation);
boolean hasUnaccepted = !conversation.getAcceptedCryptoTargets().containsAll(targets);
@@ -1042,7 +944,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
intent.putExtra("contacts", contacts);
intent.putExtra(EXTRA_ACCOUNT, conversation.getAccount().getJid().asBareJid().toString());
- intent.putExtra("choice", attachmentChoice);
intent.putExtra("conversation", conversation.getUuid());
startActivityForResult(intent, requestCode);
return true;
@@ -1086,63 +987,43 @@ 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();
Message message = new Message(conversation, body, conversation.getNextEncryption());
sendMessage(message);
break;
- case REQUEST_TRUST_KEYS_MENU:
- int choice = data.getIntExtra("choice", ATTACHMENT_CHOICE_INVALID);
- selectPresenceToAttachFile(choice);
+ case REQUEST_TRUST_KEYS_ATTACHMENTS:
+ commitAttachments();
break;
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
- final List<Uri> 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<Uri> 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<Attachment> imageUris = Attachment.extractAttachments(getActivity(), data, Attachment.Type.IMAGE);
+ mediaPreviewAdapter.addMediaPreviews(imageUris);
+ toggleInputMethod();
break;
- case ATTACHMENT_CHOICE_TAKE_FROM_CAMERA:
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
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_VIDEO:
case ATTACHMENT_CHOICE_RECORD_VOICE:
- final List<Uri> fileUris = AttachmentTool.extractUriFromIntent(data);
- final PresenceSelector.OnPresenceSelected callback = () -> {
- for (Iterator<Uri> 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<Attachment> 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,9 +1037,51 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
+ private void commitAttachments() {
+ if (conversation.getNextEncryption() == Message.ENCRYPTION_AXOLOTL && trustKeysIfNeeded(REQUEST_TRUST_KEYS_ATTACHMENTS)) {
+ return;
+ }
+ final List<Attachment> attachments = mediaPreviewAdapter.getAttachments();
+ final PresenceSelector.OnPresenceSelected callback = () -> {
+ for (Iterator<Attachment> 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);
+ if (mOptionsMenu != null) {
+ ConversationMenuConfigurator.configureAttachmentMenu(conversation, mOptionsMenu, activity.xmppConnectionService.getAttachmentChoicePreference(), hasAttachments);
+ }
+ updateSendButton();
+ }
+
private void handleNegativeActivityResult(int requestCode) {
switch (requestCode) {
- //nothing to do for now
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ if (pendingTakePhotoUri.clear()) {
+ Log.d(Config.LOGTAG, "cleared pending photo uri after negative activity result");
+ }
+ break;
}
}
@@ -1202,27 +1125,38 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ mOptionsMenu = menu;
+ boolean hasAttachments = mediaPreviewAdapter != null && mediaPreviewAdapter.hasAttachments();
menuInflater.inflate(R.menu.fragment_conversation, menu);
final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
final MenuItem menuNeedHelp = menu.findItem(R.id.action_create_issue);
final MenuItem menuSearchUpdates = menu.findItem(R.id.action_check_updates);
final MenuItem menuArchiveChat = menu.findItem(R.id.action_archive_chat);
+ final MenuItem menuGroupDetails = menu.findItem(R.id.action_group_details);
+ final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
if (conversation != null) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
menuInviteContact.setVisible(true);
menuArchiveChat.setTitle(R.string.action_end_conversation_muc);
+ menuGroupDetails.setVisible(true);
+ menuContactDetails.setVisible(false);
} else {
menuInviteContact.setVisible(false);
menuArchiveChat.setTitle(R.string.action_end_conversation);
+ menuGroupDetails.setVisible(false);
+ menuContactDetails.setVisible(true);
}
menuNeedHelp.setVisible(true);
menuSearchUpdates.setVisible(false);
- ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu, activity.xmppConnectionService.getAttachmentChoicePreference());
+ ConversationMenuConfigurator.configureAttachmentMenu(conversation, menu, activity.xmppConnectionService.getAttachmentChoicePreference(), hasAttachments);
ConversationMenuConfigurator.configureEncryptionMenu(conversation, menu);
} else {
menuNeedHelp.setVisible(false);
menuSearchUpdates.setVisible(true);
+ menuInviteContact.setVisible(false);
+ menuGroupDetails.setVisible(false);
+ menuContactDetails.setVisible(false);
}
super.onCreateOptionsMenu(menu, menuInflater);
}
@@ -1235,7 +1169,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
binding.textinput.addTextChangedListener(new StylingHelper.MessageEditorStyler(binding.textinput));
binding.textinput.setOnEditorActionListener(mEditorActionListener);
binding.textinput.setRichContentListener(new String[]{"image/*"}, mEditorContentListener);
- binding.textinput.setBackgroundResource(activity.isDarkTheme() ? R.drawable.message_bubble_sent_blue_dark : R.drawable.message_bubble_sent_blue);
+ binding.textinput.setBackgroundResource(messageInputBubble());
binding.textSendButton.setOnClickListener(this.mSendButtonListener);
binding.textSendButton.setOnLongClickListener(this.mSendButtonLongListener);
@@ -1244,6 +1178,9 @@ 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);
+ binding.mediaPreview.setBackgroundResource(messageInputBubble());
messageListAdapter = new MessageAdapter((XmppActivity) getActivity(), this.messageList);
messageListAdapter.setOnContactPictureClicked(message -> {
String fingerprint;
@@ -1439,7 +1376,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (m.isFileOrImage() && !deleted) {
String path = m.getRelativeFilePath();
Log.d(Config.LOGTAG, "Path = " + path);
- if (path == null || !path.startsWith("/") || path.contains(FileBackend.getConversationsDirectory("null", false))) {
+ if (path == null || !path.startsWith("/") || path.contains(FileBackend.getConversationsDirectory("null"))) {
deleteFile.setVisible(true);
deleteFile.setTitle(activity.getString(R.string.delete_x_file, UIHelper.getFileDescriptionString(activity, m)));
}
@@ -1513,6 +1450,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
break;
case R.id.attach_choose_picture:
case R.id.attach_take_picture:
+ case R.id.attach_record_video:
case R.id.attach_choose_file:
case R.id.attach_record_voice:
case R.id.attach_location:
@@ -1541,6 +1479,15 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
case R.id.action_clear_history:
clearHistoryDialog(conversation);
break;
+ case R.id.action_group_details:
+ Intent intent = new Intent(activity, ConferenceDetailsActivity.class);
+ intent.setAction(ConferenceDetailsActivity.ACTION_VIEW_MUC);
+ intent.putExtra("uuid", conversation.getUuid());
+ startActivity(intent);
+ break;
+ case R.id.action_contact_details:
+ activity.switchToContactDetails(conversation.getContact());
+ break;
case R.id.action_block:
case R.id.action_unblock:
final Activity activity = getActivity();
@@ -1560,7 +1507,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
attachFile(ATTACHMENT_CHOICE_CHOOSE_IMAGE);
break;
case R.id.attach_take_picture:
- attachFile(ATTACHMENT_CHOICE_TAKE_FROM_CAMERA);
+ attachFile(ATTACHMENT_CHOICE_TAKE_PHOTO);
+ break;
+ case R.id.attach_record_video:
+ attachFile(ATTACHMENT_CHOICE_RECORD_VIDEO);
break;
case R.id.attach_choose_file:
attachFile(ATTACHMENT_CHOICE_CHOOSE_FILE);
@@ -1620,7 +1570,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO)) {
return;
}
- } else if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_FROM_CAMERA) {
+ } else if (attachmentChoice == ATTACHMENT_CHOICE_TAKE_PHOTO || attachmentChoice == ATTACHMENT_CHOICE_RECORD_VIDEO) {
if (!hasPermissions(attachmentChoice, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)) {
return;
}
@@ -1684,9 +1634,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
activity.showInstallPgpDialog();
}
} else {
- if (encryption != Message.ENCRYPTION_AXOLOTL || !trustKeysIfNeeded(REQUEST_TRUST_KEYS_MENU, attachmentChoice)) {
- selectPresenceToAttachFile(attachmentChoice);
- }
+ selectPresenceToAttachFile(attachmentChoice);
}
}
@@ -1806,7 +1754,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
final int encryption = conversation.getNextEncryption();
final Account account = conversation.getAccount();
final PresenceSelector.OnPresenceSelected callback = () -> {
- final Intent intent = new Intent();
+ Intent intent = new Intent();
boolean chooser = false;
switch (attachmentChoice) {
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
@@ -1823,46 +1771,29 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setAction(Intent.ACTION_GET_CONTENT);
break;
- case ATTACHMENT_CHOICE_TAKE_FROM_CAMERA:
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setTitle(getString(R.string.attach_take_from_camera));
- builder.setNegativeButton(getString(R.string.action_take_photo),
- (dialog, which) -> {
- final Uri uri = activity.xmppConnectionService.getFileBackend().getTakePhotoUri();
- pendingTakePhotoUri.push(uri);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
- startActivityForResult(intent, attachmentChoice);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- });
- builder.setPositiveButton(getString(R.string.action_take_video),
- (dialog, which) -> {
- final Uri uri = activity.xmppConnectionService.getFileBackend().getTakeVideoUri();
- pendingTakeVideoUri.push(uri);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
- intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
- startActivityForResult(intent, attachmentChoice);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- });
- builder.create().show();
+ case ATTACHMENT_CHOICE_RECORD_VIDEO:
+ intent.setAction(MediaStore.ACTION_VIDEO_CAPTURE);
+ break;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ final Uri uri = activity.xmppConnectionService.getFileBackend().getTakePhotoUri();
+ pendingTakePhotoUri.push(uri);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+ intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
break;
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;
case ATTACHMENT_CHOICE_RECORD_VOICE:
- startActivityForResult(new Intent(getActivity(), RecordingActivity.class), attachmentChoice);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
+ intent = new Intent(getActivity(), RecordingActivity.class);
break;
case ATTACHMENT_CHOICE_LOCATION:
- startActivityForResult(new Intent(getActivity(), ShareLocationActivity.class), attachmentChoice);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
+ intent = new Intent(getActivity(), ShareLocationActivity.class);
break;
}
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
@@ -1871,8 +1802,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
startActivityForResult(
Intent.createChooser(intent, getString(R.string.perform_action_with)),
attachmentChoice);
+ activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
} else {
startActivityForResult(intent, attachmentChoice);
+ activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
}
}
};
@@ -2079,23 +2012,32 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
@Override
+ public void startActivityForResult(Intent intent, int requestCode) {
+ final Activity activity = getActivity();
+ if (activity instanceof ConversationsActivity) {
+ ((ConversationsActivity) activity).clearPendingViewIntent();
+ }
+ super.startActivityForResult(intent, requestCode);
+ }
+
+ @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (conversation != null) {
outState.putString(STATE_CONVERSATION_UUID, conversation.getUuid());
outState.putString(STATE_LAST_MESSAGE_UUID, lastMessageUuid);
- final Uri PhotoUri = pendingTakePhotoUri.peek();
- final Uri VideoUri = pendingTakeVideoUri.peek();
- if (PhotoUri != null) {
- outState.putString(STATE_PHOTO_URI, PhotoUri.toString());
- }
- if (VideoUri != null) {
- outState.putString(STATE_VIDEO_URI, VideoUri.toString());
+ final Uri uri = pendingTakePhotoUri.peek();
+ if (uri != null) {
+ outState.putString(STATE_PHOTO_URI, uri.toString());
}
final ScrollState scrollState = getScrollPosition();
if (scrollState != null) {
outState.putParcelable(STATE_SCROLL_POSITION, scrollState);
}
+ final ArrayList<Attachment> attachments = mediaPreviewAdapter.getAttachments();
+ if (attachments.size() > 0) {
+ outState.putParcelableArrayList(STATE_MEDIA_PREVIEWS, attachments);
+ }
}
}
@@ -2106,18 +2048,18 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
return;
}
String uuid = savedInstanceState.getString(STATE_CONVERSATION_UUID);
+ ArrayList<Attachment> attachments = savedInstanceState.getParcelableArrayList(STATE_MEDIA_PREVIEWS);
pendingLastMessageUuid.push(savedInstanceState.getString(STATE_LAST_MESSAGE_UUID, null));
if (uuid != null) {
QuickLoader.set(uuid);
this.pendingConversationsUuid.push(uuid);
+ if (attachments != null && attachments.size() > 0) {
+ this.pendingMediaPreviews.push(attachments);
+ }
String takePhotoUri = savedInstanceState.getString(STATE_PHOTO_URI);
if (takePhotoUri != null) {
pendingTakePhotoUri.push(Uri.parse(takePhotoUri));
}
- String takeVideoUri = savedInstanceState.getString(STATE_VIDEO_URI);
- if (takeVideoUri != null) {
- pendingTakeVideoUri.push(Uri.parse(takeVideoUri));
- }
pendingScrollState.push(savedInstanceState.getParcelable(STATE_SCROLL_POSITION));
}
}
@@ -2151,10 +2093,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
if (this.conversation != null) {
final String msg = this.binding.textinput.getText().toString();
- final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
- if (this.conversation.getStatus() != Conversation.STATUS_ARCHIVED && participating && this.conversation.setNextMessage(msg)) {
- this.activity.xmppConnectionService.updateConversation(this.conversation);
- }
+ storeNextMessage(msg);
updateChatState(this.conversation, msg);
this.activity.xmppConnectionService.getNotificationService().setOpenConversation(null);
}
@@ -2176,17 +2115,17 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
Log.d(Config.LOGTAG, "ConversationFragment.saveMessageDraftStopAudioPlayer()");
final String msg = this.binding.textinput.getText().toString();
- final boolean participating = previousConversation.getMode() == Conversational.MODE_SINGLE || previousConversation.getMucOptions().participating();
- if (participating && previousConversation.setNextMessage(msg)) {
- activity.xmppConnectionService.updateConversation(previousConversation);
- }
+ storeNextMessage(msg);
updateChatState(this.conversation, msg);
messageListAdapter.stopAudioPlayer();
+ mediaPreviewAdapter.clearPreviews();
+ toggleInputMethod();
}
public void reInit(Conversation conversation, Bundle extras) {
QuickLoader.set(conversation.getUuid());
this.saveMessageDraftStopAudioPlayer();
+ this.clearPending();
if (this.reInit(conversation, extras != null)) {
if (extras != null) {
processExtras(extras);
@@ -2343,6 +2282,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<Uri> 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();
@@ -2371,6 +2316,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
+ private List<Uri> extractUris(Bundle extras) {
+ final List<Uri> 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) {
@@ -2551,10 +2509,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
this.binding.textinput.append(conversation.getDraftMessage());
conversation.setDraftMessage(null);
}
- final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
- if (participating && conversation.setNextMessage(this.binding.textinput.getText().toString())) {
- activity.xmppConnectionService.databaseBackend.updateConversation(conversation);
- }
+ storeNextMessage();
updateChatMsgHint();
SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(activity);
final boolean prefScrollToBottom = p.getBoolean("scroll_to_bottom", activity.getResources().getBoolean(R.bool.scroll_to_bottom));
@@ -2566,6 +2521,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
}
}
+ private boolean storeNextMessage() {
+ return storeNextMessage(this.binding.textinput.getText().toString());
+ }
+
+ private boolean storeNextMessage(String msg) {
+ final boolean participating = conversation.getMode() == Conversational.MODE_SINGLE || conversation.getMucOptions().participating();
+ if (this.conversation.getStatus() != Conversation.STATUS_ARCHIVED && participating && this.conversation.setNextMessage(msg)) {
+ this.activity.xmppConnectionService.updateConversation(this.conversation);
+ return true;
+ }
+ return false;
+ }
+
public void doneSendingPgpMessage() {
mSendingPgpMessage.set(false);
}
@@ -2585,11 +2553,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;
@@ -2601,9 +2575,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));
}
@@ -2925,6 +2896,9 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
if (status == Account.State.ONLINE && conversation.setOutgoingChatState(Config.DEFAULT_CHATSTATE)) {
service.sendChatState(conversation);
}
+ if (storeNextMessage()) {
+ activity.onConversationsListItemUpdated();
+ }
updateSendButton();
}
@@ -3084,23 +3058,33 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke
reInit(conversation);
ScrollState scrollState = pendingScrollState.pop();
String lastMessageUuid = pendingLastMessageUuid.pop();
+ List<Attachment> attachments = pendingMediaPreviews.pop();
if (scrollState != null) {
setScrollPosition(scrollState, lastMessageUuid);
}
+ if (attachments != null && attachments.size() > 0) {
+ Log.d(Config.LOGTAG, "had attachments on restore");
+ mediaPreviewAdapter.addMediaPreviews(attachments);
+ toggleInputMethod();
+ }
return true;
}
private void clearPending() {
- if (postponedActivityResult.pop() != null) {
+ if (postponedActivityResult.clear()) {
Log.e(Config.LOGTAG, "cleared pending intent with unhandled result left");
}
- pendingScrollState.pop();
- if (pendingTakePhotoUri.pop() != null) {
+ if (pendingScrollState.clear()) {
+ Log.e(Config.LOGTAG,"cleared scroll state");
+ }
+ if (pendingTakePhotoUri.clear()) {
Log.e(Config.LOGTAG, "cleared pending photo uri");
}
}
-
+ private int messageInputBubble() {
+ return activity.isDarkTheme() ? R.drawable.message_bubble_sent_blue_dark : R.drawable.message_bubble_sent_blue;
+ }
public Conversation getConversation() {
return conversation;
diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java
index 11d4cc31f..8d8e5386a 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<String> 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));
}
@@ -483,6 +490,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
@Override
public void onConversationSelected(Conversation conversation) {
+ clearPendingViewIntent();
if (ConversationFragment.getConversation(this) == conversation) {
Log.d(Config.LOGTAG, "ignore onConversationSelected() because conversation is already open");
return;
@@ -490,6 +498,12 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio
openConversation(conversation, null);
}
+ public void clearPendingViewIntent() {
+ if (pendingViewIntent.clear()) {
+ Log.e(Config.LOGTAG, "cleared pending view intent");
+ }
+ }
+
private void displayToast(final String msg) {
runOnUiThread(() -> Toast.makeText(ConversationsActivity.this, msg, Toast.LENGTH_SHORT).show());
}
@@ -626,7 +640,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/MediaBrowserActivity.java b/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java
new file mode 100644
index 000000000..4b32b42ab
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/MediaBrowserActivity.java
@@ -0,0 +1,77 @@
+package de.pixart.messenger.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+
+import java.util.List;
+
+import de.pixart.messenger.R;
+import de.pixart.messenger.databinding.ActivityMediaBrowserBinding;
+import de.pixart.messenger.entities.Account;
+import de.pixart.messenger.entities.Contact;
+import de.pixart.messenger.entities.Conversation;
+import de.pixart.messenger.ui.adapter.MediaAdapter;
+import de.pixart.messenger.ui.interfaces.OnMediaLoaded;
+import de.pixart.messenger.ui.util.Attachment;
+import de.pixart.messenger.ui.util.GridManager;
+import rocks.xmpp.addr.Jid;
+
+
+public class MediaBrowserActivity extends XmppActivity implements OnMediaLoaded {
+
+ private ActivityMediaBrowserBinding binding;
+
+ private MediaAdapter mMediaAdapter;
+
+ public static void launch(Context context, Contact contact) {
+ launch(context, contact.getAccount(), contact.getJid().asBareJid().toEscapedString());
+ }
+
+ public static void launch(Context context, Conversation conversation) {
+ launch(context, conversation.getAccount(), conversation.getJid().asBareJid().toEscapedString());
+ }
+
+ private static void launch(Context context, Account account, String jid) {
+ final Intent intent = new Intent(context, MediaBrowserActivity.class);
+ intent.putExtra("account", account.getUuid());
+ intent.putExtra("jid", jid);
+ context.startActivity(intent);
+ }
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.binding = DataBindingUtil.setContentView(this, R.layout.activity_media_browser);
+ setSupportActionBar((Toolbar) binding.toolbar);
+ configureActionBar(getSupportActionBar());
+ mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
+ this.binding.media.setAdapter(mMediaAdapter);
+ GridManager.setupLayoutManager(this, this.binding.media, R.dimen.browser_media_size);
+
+ }
+
+ @Override
+ protected void refreshUiReal() {
+
+ }
+
+ @Override
+ void onBackendConnected() {
+ Intent intent = getIntent();
+ String account = intent == null ? null : intent.getStringExtra("account");
+ String jid = intent == null ? null : intent.getStringExtra("jid");
+ if (account != null && jid != null) {
+ xmppConnectionService.getAttachments(account, Jid.of(jid), 0, this);
+ }
+ }
+
+ @Override
+ public void onMediaLoaded(List<Attachment> attachments) {
+ runOnUiThread(() -> {
+ mMediaAdapter.setAttachments(attachments);
+ });
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
index 455bbf3c3..31dc1681a 100644
--- a/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/RecordingActivity.java
@@ -127,7 +127,7 @@ public class RecordingActivity extends Activity implements View.OnClickListener
private static File generateOutputFilename(Context context) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US);
- return new File(FileBackend.getConversationsDirectory("Audios", true) + "/"
+ return new File(FileBackend.getConversationsDirectory("Audios")
+ dateFormat.format(new Date())
+ ".m4a");
}
diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
index cc3172670..e9ba1d426 100644
--- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java
@@ -56,12 +56,11 @@ 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.DateSeparator;
-import de.pixart.messenger.ui.util.Drawable;
import de.pixart.messenger.ui.util.ListViewUtils;
import de.pixart.messenger.ui.util.PendingItem;
import de.pixart.messenger.ui.util.ShareUtil;
+import de.pixart.messenger.ui.util.StyledAttributes;
import de.pixart.messenger.utils.FtsUtils;
import de.pixart.messenger.utils.MessageUtils;
@@ -213,12 +212,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 4bf9d26cc..1c0992cf1 100644
--- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java
@@ -38,7 +38,7 @@ 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.ui.util.StyledAttributes;
import de.pixart.messenger.utils.TimeframeUtils;
import rocks.xmpp.addr.Jid;
@@ -60,6 +60,7 @@ public class SettingsActivity extends XmppActivity implements
public static final String USE_BUNDLED_EMOJIS = "use_bundled_emoji";
public static final String USE_MULTI_ACCOUNTS = "use_multi_accounts";
public static final String QUICK_SHARE_ATTACHMENT_CHOICE = "quick_share_attachment_choice";
+ public static final String NUMBER_OF_ACCOUNTS = "number_of_accounts";
public static final int REQUEST_WRITE_LOGS = 0xbf8701;
Preference multiAccountPreference;
@@ -75,6 +76,7 @@ public class SettingsActivity extends XmppActivity implements
super.onCreate(savedInstanceState);
this.mTheme = findTheme();
setTheme(this.mTheme);
+ updateTheme();
setContentView(R.layout.activity_settings);
FragmentManager fm = getFragmentManager();
mSettingsFragment = (SettingsFragment) fm.findFragmentById(R.id.settings_content);
@@ -83,7 +85,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());
}
@@ -96,12 +98,11 @@ public class SettingsActivity extends XmppActivity implements
@Override
public void onStart() {
super.onStart();
- updateTheme();
-
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);
multiAccountPreference = mSettingsFragment.findPreference("enable_multi_accounts");
if (multiAccountPreference != null) {
isMultiAccountChecked = ((CheckBoxPreference) multiAccountPreference).isChecked();
+ handleMultiAccountChanges();
}
BundledEmojiPreference = mSettingsFragment.findPreference("use_bundled_emoji");
@@ -112,7 +113,7 @@ public class SettingsActivity extends XmppActivity implements
QuickShareAttachmentChoicePreference = mSettingsFragment.findPreference("quick_share_attachment_choice");
if (QuickShareAttachmentChoicePreference != null) {
QuickShareAttachmentChoicePreference.setOnPreferenceChangeListener((preference, newValue) -> {
- recreate();
+ refreshUiReal();
return true;
});
isQuickShareAttachmentChoiceChecked = ((CheckBoxPreference) QuickShareAttachmentChoicePreference).isChecked();
@@ -287,21 +288,20 @@ public class SettingsActivity extends XmppActivity implements
Log.d(Config.LOGTAG, "Multi account checkbox checked: " + isMultiAccountChecked);
if (isMultiAccountChecked) {
enableMultiAccountsPreference.setEnabled(false);
- if (xmppConnectionService != null) {
- final List<Account> accounts = xmppConnectionService.getAccounts();
- Log.d(Config.LOGTAG, "Disabled multi account: Number of accounts " + accounts.size());
- if (accounts.size() > 1) {
- Log.d(Config.LOGTAG, "Disabled multi account not possible because you have more than one account");
+ int accounts = getNumberOfAccounts();
+ Log.d(Config.LOGTAG, "Disabled multi account: Number of accounts " + accounts);
+ if (accounts > 1) {
+ Log.d(Config.LOGTAG, "Disabling multi account not possible because you have more than one account");
enableMultiAccountsPreference.setEnabled(false);
} else {
- Log.d(Config.LOGTAG, "Disabled multi account possible because you have one account");
+ Log.d(Config.LOGTAG, "Disabling multi account possible because you have only one account");
enableMultiAccountsPreference.setEnabled(true);
+ enableMultiAccountsPreference.setOnPreferenceClickListener(preference -> {
+ refreshUiReal();
+ return true;
+ });
}
} else {
- Log.d(Config.LOGTAG, "Disabled multi account not possible because XmppConnectionService == null");
- enableMultiAccountsPreference.setEnabled(false);
- }
- } else {
enableMultiAccountsPreference.setEnabled(true);
enableMultiAccountsPreference.setOnPreferenceClickListener(preference -> {
enableMultiAccounts();
@@ -314,7 +314,7 @@ public class SettingsActivity extends XmppActivity implements
private void updateTheme() {
final int theme = findTheme();
if (this.mTheme != theme) {
- recreate();
+ refreshUiReal();
}
}
@@ -478,12 +478,17 @@ public class SettingsActivity extends XmppActivity implements
SharedPreferences multiaccount_prefs = getApplicationContext().getSharedPreferences(USE_MULTI_ACCOUNTS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = multiaccount_prefs.edit();
editor.putString("BackupPW", pw1);
- editor.commit();
+ boolean passwordstored = editor.commit();
+ Log.d(Config.LOGTAG, "saving multiaccount password " + passwordstored);
+ if (passwordstored) {
+ recreate();
+ } else {
+ handleMultiAccountChanges();
+ }
}
})
.setNegativeButton(R.string.cancel, null);
alertDialogBuilder.create().show();
-
}
@@ -558,6 +563,39 @@ public class SettingsActivity extends XmppActivity implements
}
public void refreshUiReal() {
- //nothing to do. This Activity doesn't implement any listeners
+ recreate();
+ handleMultiAccountChanges();
+ }
+
+ private void handleMultiAccountChanges() {
+ multiAccountPreference = mSettingsFragment.findPreference("enable_multi_accounts");
+ if (multiAccountPreference != null) {
+ //check if password = null
+ final SharedPreferences multiaccount_prefs = getApplicationContext().getSharedPreferences(USE_MULTI_ACCOUNTS, Context.MODE_PRIVATE);
+ if (multiaccount_prefs != null && multiaccount_prefs.getString("BackupPW", null) == null) {
+ Log.d(Config.LOGTAG, "uncheck multiaccount because password = null");
+ if (multiAccountPreference != null) {
+ ((CheckBoxPreference) multiAccountPreference).setChecked(false);
+ }
+ }
+ //if multiAccountDisabled reset password
+ final Preference enableMultiAccountsPreference = mSettingsFragment.findPreference("enable_multi_accounts");
+ if (enableMultiAccountsPreference != null && !isMultiAccountChecked) {
+ SharedPreferences.Editor editor = multiaccount_prefs.edit();
+ editor.putString("BackupPW", null);
+ if (editor.commit()) {
+ Log.d(Config.LOGTAG, "resetting multiaccount password because multiaccount = unchecked");
+ } else {
+ Log.d(Config.LOGTAG, "resetting multiaccount password failed");
+ }
+ }
+ }
+ }
+
+ private int getNumberOfAccounts() {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ int NumberOfAccounts = preferences.getInt(NUMBER_OF_ACCOUNTS, 0);
+ Log.d(Config.LOGTAG, "Get number of accounts from file: " + NumberOfAccounts);
+ return NumberOfAccounts;
}
}
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<Uri> 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<Conversation> mConversations = new ArrayList<>();
- private Toast mToast;
- private AtomicInteger attachmentCounter = new AtomicInteger(0);
-
- private UiInformableCallback<Message> attachFileCallback = new UiInformableCallback<Message>() {
-
- @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<Uri> 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<Message> messageEncryptionCallback = new UiCallback<Message>() {
- @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<Uri> uris = new ArrayList<>();
}
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java b/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
index 959060101..9e35d7ebf 100644
--- a/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/UpdaterActivity.java
@@ -114,7 +114,7 @@ public class UpdaterActivity extends XmppActivity {
store = null;
}
//delete old downloaded localVersion files
- File dir = new File(FileBackend.getConversationsDirectory("Update", false));
+ File dir = new File(FileBackend.getAppUpdateDirectory());
if (dir.isDirectory()) {
String[] children = dir.list();
for (String aChildren : children) {
@@ -291,7 +291,7 @@ public class UpdaterActivity extends XmppActivity {
private class DownloadTask extends AsyncTask<String, Integer, String> {
- File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + FileBackend.getDirectoryName("Update", false));
+ File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + FileBackend.getAppUpdateDirectory());
File file = new File(dir, FileName);
private Context context;
private PowerManager.WakeLock mWakeLock;
diff --git a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
index 9d5b76c1c..e2bf9d8e9 100644
--- a/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
+++ b/src/main/java/de/pixart/messenger/ui/WelcomeActivity.java
@@ -193,7 +193,7 @@ public class WelcomeActivity extends XmppActivity {
private boolean BackupAvailable() {
// Set the folder on the SDcard
- File filePath = new File(FileBackend.getConversationsDirectory("Database", false) + "database.db.crypt");
+ File filePath = new File(FileBackend.getBackupDirectory() + "database.db.crypt");
Log.d(Config.LOGTAG, "DB Path: " + filePath.toString());
if (filePath.exists()) {
Log.d(Config.LOGTAG, "DB Path existing");
@@ -206,11 +206,11 @@ public class WelcomeActivity extends XmppActivity {
private void checkDatabase(String DecryptionKey) throws IOException {
// Set the folder on the SDcard
- File directory = new File(FileBackend.getConversationsDirectory("Database", false));
+ File directory = new File(FileBackend.getBackupDirectory());
// Set the input file stream up:
- FileInputStream InputFile = new FileInputStream(directory.getPath() + "/database.db.crypt");
+ FileInputStream InputFile = new FileInputStream(directory.getPath() + "database.db.crypt");
// Temp output for DB checks
- File TempFile = new File(directory.getPath() + "/database.bak");
+ File TempFile = new File(directory.getPath() + "database.bak");
FileOutputStream OutputTemp = new FileOutputStream(TempFile);
try {
@@ -286,11 +286,11 @@ public class WelcomeActivity extends XmppActivity {
// Set location for the db:
final OutputStream OutputFile = new FileOutputStream(this.getDatabasePath(DatabaseBackend.DATABASE_NAME));
// Set the folder on the SDcard
- File directory = new File(FileBackend.getConversationsDirectory("Database", false));
+ File directory = new File(FileBackend.getBackupDirectory());
// Set the input file stream up:
- final InputStream InputFile = new FileInputStream(directory.getPath() + "/database.bak");
+ final InputStream InputFile = new FileInputStream(directory.getPath() + "database.bak");
//set temp file
- File TempFile = new File(directory.getPath() + "/database.bak");
+ File TempFile = new File(directory.getPath() + "database.bak");
// Transfer bytes from the input file to the output file
byte[] buffer = new byte[1024];
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<Account> {
@@ -65,14 +65,14 @@ public class AccountAdapter extends ArrayAdapter<Account> {
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 5294ba7cd..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<ConversationAdapte
}
if (conversation == ConversationFragment.getConversation(activity)) {
- viewHolder.frame.setBackgroundColor(Color.get(activity, R.attr.color_background_tertiary));
+ viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_tertiary));
} else {
- viewHolder.frame.setBackgroundColor(Color.get(activity,R.attr.color_background_primary));
+ viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_secondary));
}
Message message = conversation.getLatestMessage();
@@ -250,11 +250,11 @@ public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapte
viewHolder.name.setTextColor(ContextCompat.getColor(activity, R.color.notavailable));
break;
default:
- viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main));
+ viewHolder.name.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main));
break;
}
} else {
- viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main));
+ viewHolder.name.setTextColor(StyledAttributes.getColor(activity, R.attr.text_Color_Main));
}
if (activity.xmppConnectionService.indicateReceived()) {
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
index 9f4a52d95..2e0bfccc6 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/KnownHostsAdapter.java
@@ -7,8 +7,6 @@ import android.widget.Filter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Locale;
public class KnownHostsAdapter extends ArrayAdapter<String> {
@@ -17,34 +15,31 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
- if (constraint != null) {
- ArrayList<String> suggestions = new ArrayList<>();
- final String[] split = constraint.toString().split("@");
- if (split.length == 1) {
- for (String domain : domains) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- } else if (split.length == 2) {
- for (String domain : domains) {
- if (domain.contentEquals(split[1])) {
- suggestions.clear();
- break;
- } else if (domain.contains(split[1])) {
- suggestions.add(split[0].toLowerCase(Locale
- .getDefault()) + "@" + domain);
- }
- }
- } else {
+ final ArrayList<String> suggestions = new ArrayList<>();
+ final String[] split = constraint == null ? new String[0] : constraint.toString().split("@");
+ if (split.length == 1) {
+ final String local = split[0].toLowerCase(Locale.ENGLISH);
+ for (String domain : domains) {
+ suggestions.add(local + "@" + domain);
+ }
+ } else if (split.length == 2) {
+ final String localPart = split[0].toLowerCase(Locale.ENGLISH);
+ final String domainPart = split[1].toLowerCase(Locale.ENGLISH);
+ if (domains.contains(domainPart)) {
return new FilterResults();
}
- FilterResults filterResults = new FilterResults();
- filterResults.values = suggestions;
- filterResults.count = suggestions.size();
- return filterResults;
+ for (String domain : domains) {
+ if (domain.contains(domainPart)) {
+ suggestions.add(localPart + "@" + domain);
+ }
+ }
} else {
return new FilterResults();
}
+ FilterResults filterResults = new FilterResults();
+ filterResults.values = suggestions;
+ filterResults.count = suggestions.size();
+ return filterResults;
}
@Override
@@ -52,9 +47,7 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
ArrayList filteredList = (ArrayList) results.values;
if (results.count > 0) {
clear();
- for (Object c : filteredList) {
- add((String) c);
- }
+ addAll(filteredList);
notifyDataSetChanged();
}
}
@@ -62,140 +55,7 @@ public class KnownHostsAdapter extends ArrayAdapter<String> {
public KnownHostsAdapter(Context context, int viewResourceId, Collection<String> mKnownHosts) {
super(context, viewResourceId, new ArrayList<>());
-
- if (mKnownHosts == null) {
- domains = new ArrayList<>();
- } else {
- domains = new ArrayList<>(mKnownHosts);
- }
-
- HashSet<String> hashSet = new HashSet<>();
-
- // get servers from https://conversations.im/compliance/
- new Thread(() -> {
- domains.add("pix-art.de");
- domains.add("conversations.im");
- domains.add("jabber.cat");
- domains.add("jabjab.de");
- domains.add("im.koderoot.net");
- domains.add("riotcat.org");
- domains.add("magicbroccoli.de");
- domains.add("kode.im");
- domains.add("jabber-germany.de");
- domains.add("simplewire.de");
- domains.add("suchat.org");
- domains.add("jabber.at");
- domains.add("trashserver.net");
- domains.add("wiuwiu.de");
- domains.add("5222.de");
- domains.add("dismail.de");
- domains.add("chat.sum7.eu");
- domains.add("xmpp.zone");
- domains.add("libranet.de");
- domains.add("laborversuch.de");
- domains.add("creep.im");
- domains.add("jabber.systemausfall.org");
- domains.add("jabber.hot-chilli.net");
- domains.add("jabber.fr");
- domains.add("jabber.de");
- domains.add("draugr.de");
- domains.add("elaon.de");
- domains.add("high-way.me");
- domains.add("jabber.rwth-aachen.de");
- domains.add("deshalbfrei.org");
- domains.add("mail.de");
- domains.add("bommboo.de");
- domains.add("jabber.systemli.org");
- domains.add("jabb.im");
- domains.add("mailbox.org");
- domains.add("hot-chilli.net");
- domains.add("jabberpl.org");
- domains.add("chinwag.im");
- domains.add("tchncs.de");
- domains.add("zsim.de");
- domains.add("patchcord.be");
- domains.add("gajim.org");
- domains.add("talker.to");
- domains.add("pimux.de");
- domains.add("jabber.home.vdlinde.org");
- domains.add("im.apinc.org");
- domains.add("chatme.im");
- domains.add("fusselkater.org");
- domains.add("datenknoten.me");
- domains.add("fysh.in");
- domains.add("jabber.chaos-darmstadt.de");
- domains.add("yax.im");
- domains.add("neko.im");
- domains.add("jabberzac.org");
- domains.add("xmpp.is");
- domains.add("home.zom.im");
- domains.add("jabber.ccc.de");
- domains.add("jwchat.org");
- domains.add("kdetalk.net");
- domains.add("kde.org");
- domains.add("riseup.net");
- domains.add("ruhr-uni-bochum.de");
- domains.add("njs.netlab.cz");
- domains.add("schokokeks.org");
- domains.add("jabber.cz");
- domains.add("ubuntu-jabber.de");
- domains.add("xabber.de");
- domains.add("ubuntu-jabber.net");
- domains.add("jabber.ru");
- domains.add("darknet.nz");
- domains.add("movim.eu");
- domains.add("404.city");
- domains.add("igniterealtime.org");
- domains.add("kapsi.fi");
- domains.add("jabbel.net");
- domains.add("joindiaspora.com");
- domains.add("alpha-labs.net");
- domains.add("xmppnet.de");
- domains.add("hoth.one");
- domains.add("blah.im");
- domains.add("xmpp.jp");
- domains.add("jabber.uni-mainz.de");
- domains.add("richim.org");
- domains.add("tigase.im");
- domains.add("jappix.com");
- domains.add("member.fsf.org");
- domains.add("jabber.rueckgr.at");
- domains.add("swissjabber.ch");
- domains.add("twattle.net");
- domains.add("jabber.calyxinstitute.org");
- domains.add("sapo.pt");
- domains.add("uprod.biz");
- domains.add("krautspace.de");
- domains.add("kraut.space");
- domains.add("null.pm");
- domains.add("anonymitaet-im-inter.net");
- domains.add("0nl1ne.at");
- domains.add("linuxlovers.at");
- domains.add("jabber.org");
- domains.add("jabber.no-sense.net");
- domains.add("swissjabber.eu");
- domains.add("swissjabber.org");
- domains.add("swissjabber.de");
- domains.add("swissjabber.li");
- domains.add("jabber.no");
- domains.add("cypherpunks.it");
- domains.add("adastra.re");
- domains.add("jabber-br.org");
- domains.add("einfachjabber.de");
- domains.add("jabber.smash-net.org");
- domains.add("freifunk.im");
- domains.add("openmailbox.org");
- domains.add("jabber.otr.im");
- domains.add("evil.im");
- domains.add("xmpp.slack.com");
- domains.add("chat.hipchat.com");
- domains.add("googlemail.com");
-
- hashSet.addAll(domains);
- domains.clear();
- domains.addAll(hashSet);
- Collections.sort(domains, String::compareToIgnoreCase);
- }).start();
+ domains = new ArrayList<>(mKnownHosts);
}
public KnownHostsAdapter(Context context, int viewResourceId) {
diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
index 64d432041..17479cf99 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/ListItemAdapter.java
@@ -27,7 +27,7 @@ import de.pixart.messenger.databinding.ContactBinding;
import de.pixart.messenger.entities.ListItem;
import de.pixart.messenger.ui.SettingsActivity;
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.IrregularUnicodeDetector;
import de.pixart.messenger.utils.UIHelper;
import rocks.xmpp.addr.Jid;
@@ -105,16 +105,16 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
}
}
if (offline) {
- viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main));
+ viewHolder.name.setTextColor(StyledAttributes.getColor(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.getColor(activity, R.attr.text_Color_Main));
} else {
- viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main));
+ viewHolder.name.setTextColor(StyledAttributes.getColor(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/MediaAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java
new file mode 100644
index 000000000..0472c6d11
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaAdapter.java
@@ -0,0 +1,226 @@
+package de.pixart.messenger.ui.adapter;
+
+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.DimenRes;
+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.Arrays;
+import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
+
+import de.pixart.messenger.R;
+import de.pixart.messenger.databinding.MediaBinding;
+import de.pixart.messenger.ui.XmppActivity;
+import de.pixart.messenger.ui.util.Attachment;
+import de.pixart.messenger.ui.util.StyledAttributes;
+import de.pixart.messenger.ui.util.ViewUtil;
+
+public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHolder> {
+
+ private static final List<String> DOCUMENT_MIMES = Arrays.asList(
+ "application/pdf",
+ "application/vnd.oasis.opendocument.text",
+ "application/msword",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "text/x-tex",
+ "text/plain"
+ );
+
+ private final ArrayList<Attachment> attachments = new ArrayList<>();
+
+ private final XmppActivity activity;
+
+ private int mediaSize = 0;
+
+ public MediaAdapter(XmppActivity activity, @DimenRes int mediaSize) {
+ this.activity = activity;
+ this.mediaSize = Math.round(activity.getResources().getDimension(mediaSize));
+ }
+
+ public static void setMediaSize(RecyclerView recyclerView, int mediaSize) {
+ RecyclerView.Adapter adapter = recyclerView.getAdapter();
+ if (adapter instanceof MediaAdapter) {
+ ((MediaAdapter) adapter).setMediaSize(mediaSize);
+ }
+ }
+
+ private static @AttrRes
+ int getImageAttr(Attachment attachment) {
+ 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_unknown;
+ } 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 if (DOCUMENT_MIMES.contains(mime)) {
+ attr = R.attr.media_preview_document;
+ } else {
+ attr = R.attr.media_preview_unknown;
+ }
+ }
+ return attr;
+ }
+
+ public static void renderPreview(Context context, Attachment attachment, ImageView imageView) {
+ imageView.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
+ imageView.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255));
+ imageView.setImageDrawable(StyledAttributes.getDrawable(context, getImageAttr(attachment)));
+ }
+
+ 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 MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+ MediaBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media, parent, false);
+ return new MediaViewHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
+ final Attachment attachment = attachments.get(position);
+ if (attachment.renderThumbnail()) {
+ holder.binding.media.setImageAlpha(255);
+ loadPreview(attachment, holder.binding.media);
+ } else {
+ cancelPotentialWork(attachment, holder.binding.media);
+ renderPreview(activity, attachment, holder.binding.media);
+ }
+ holder.binding.media.setOnClickListener(v -> ViewUtil.view(activity, attachment));
+ }
+
+ public void setAttachments(List<Attachment> attachments) {
+ this.attachments.clear();
+ this.attachments.addAll(attachments);
+ notifyDataSetChanged();
+ }
+
+ private void setMediaSize(int mediaSize) {
+ this.mediaSize = mediaSize;
+ }
+
+ private void loadPreview(Attachment attachment, ImageView imageView) {
+ if (cancelPotentialWork(attachment, imageView)) {
+ final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment, mediaSize, 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(activity.getResources(), null, task);
+ imageView.setImageDrawable(asyncDrawable);
+ try {
+ task.execute(attachment);
+ } catch (final RejectedExecutionException ignored) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return attachments.size();
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+ AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+ super(res, bitmap);
+ bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+ }
+
+ BitmapWorkerTask getBitmapWorkerTask() {
+ return bitmapWorkerTaskReference.get();
+ }
+ }
+
+ class MediaViewHolder extends RecyclerView.ViewHolder {
+
+ private final MediaBinding binding;
+
+ MediaViewHolder(MediaBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+ }
+
+ class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
+ private final WeakReference<ImageView> imageViewReference;
+ private Attachment attachment = null;
+
+ BitmapWorkerTask(ImageView imageView) {
+ imageViewReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ protected Bitmap doInBackground(Attachment... params) {
+ this.attachment = params[0];
+ return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, mediaSize, false);
+ }
+
+ @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/adapter/MediaPreviewAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java
new file mode 100644
index 000000000..ae4c42816
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java
@@ -0,0 +1,188 @@
+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.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;
+
+public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
+
+ private final ArrayList<Attachment> 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);
+ MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview);
+ }
+ holder.binding.deleteButton.setOnClickListener(v -> {
+ int pos = mediaPreviews.indexOf(attachment);
+ mediaPreviews.remove(pos);
+ notifyItemRemoved(pos);
+ conversationFragment.toggleInputMethod();
+ });
+ }
+
+ public void addMediaPreviews(List<Attachment> 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 ArrayList<Attachment> getAttachments() {
+ return mediaPreviews;
+ }
+
+ public void clearPreviews() {
+ this.mediaPreviews.clear();
+ }
+
+ static class AsyncDrawable extends BitmapDrawable {
+ private final WeakReference<BitmapWorkerTask> 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<Attachment, Void, Bitmap> {
+ private final WeakReference<ImageView> 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/adapter/MessageAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
index b720375e7..b13ccb910 100644
--- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
+++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java
@@ -2,11 +2,9 @@ package de.pixart.messenger.ui.adapter;
import android.Manifest;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -29,7 +27,6 @@ import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Base64;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
@@ -72,11 +69,11 @@ import de.pixart.messenger.services.MessageArchiveService;
import de.pixart.messenger.services.NotificationService;
import de.pixart.messenger.ui.ConversationFragment;
import de.pixart.messenger.ui.ConversationsActivity;
-import de.pixart.messenger.ui.ShowFullscreenMessageActivity;
import de.pixart.messenger.ui.XmppActivity;
import de.pixart.messenger.ui.text.DividerSpan;
import de.pixart.messenger.ui.text.QuoteSpan;
import de.pixart.messenger.ui.util.MyLinkify;
+import de.pixart.messenger.ui.util.ViewUtil;
import de.pixart.messenger.ui.widget.ClickableMovementMethod;
import de.pixart.messenger.ui.widget.CopyTextView;
import de.pixart.messenger.ui.widget.ListSelectionManager;
@@ -498,7 +495,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} else {
viewHolder.messageBody.setTextAppearance(getContext(), R.style.TextAppearance_Conversations_Body1);
}
- viewHolder.messageBody.setHighlightColor(darkBackground ? type == SENT ? ContextCompat.getColor(activity, R.color.black26) : ContextCompat.getColor(activity, R.color.grey800) : ContextCompat.getColor(activity, R.color.grey500));
+ viewHolder.messageBody.setHighlightColor(darkBackground ? type == SENT ? ContextCompat.getColor(activity, R.color.accent) : ContextCompat.getColor(activity, R.color.accent) : ContextCompat.getColor(activity, R.color.accent));
viewHolder.messageBody.setTypeface(null, Typeface.NORMAL);
if (message.getBody() != null) {
final String nick = UIHelper.getMessageDisplayName(message);
@@ -1047,52 +1044,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
return;
}
String mime = file.getMimeType();
- if (mime.startsWith("image/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("image", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- } else if (mime.startsWith("video/")) {
- Intent intent = new Intent(getContext(), ShowFullscreenMessageActivity.class);
- intent.putExtra("video", Uri.fromFile(file));
- try {
- activity.startActivity(intent);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- return;
- } catch (ActivityNotFoundException e) {
- //ignored
- }
- }
- Intent openIntent = new Intent(Intent.ACTION_VIEW);
if (mime == null) {
mime = "*/*";
}
- Uri uri;
- try {
- uri = FileBackend.getUriForFile(activity, file);
- } catch (SecurityException e) {
- Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
- Toast.makeText(activity, activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
- return;
- }
- openIntent.setDataAndType(uri, mime);
- openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- PackageManager manager = activity.getPackageManager();
- List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
- if (info.size() == 0) {
- openIntent.setDataAndType(uri,"*/*");
- }
- try {
- getContext().startActivity(openIntent);
- activity.overridePendingTransition(R.animator.fade_in, R.animator.fade_out);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
- }
+ ViewUtil.view(activity, file, mime);
}
public void showLocation(Message message) {
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/interfaces/OnMediaLoaded.java b/src/main/java/de/pixart/messenger/ui/interfaces/OnMediaLoaded.java
new file mode 100644
index 000000000..0441be96d
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/interfaces/OnMediaLoaded.java
@@ -0,0 +1,10 @@
+package de.pixart.messenger.ui.interfaces;
+
+import java.util.List;
+
+import de.pixart.messenger.ui.util.Attachment;
+
+public interface OnMediaLoaded {
+
+ void onMediaLoaded(List<Attachment> attachments);
+} \ No newline at end of file
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..96159c1bd
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/util/Attachment.java
@@ -0,0 +1,166 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.io.File;
+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 implements Parcelable {
+ public static final Creator<Attachment> CREATOR = new Parcelable.Creator<Attachment>() {
+ @Override
+ public Attachment createFromParcel(Parcel in) {
+ return new Attachment(in);
+ }
+
+ @Override
+ public Attachment[] newArray(int size) {
+ return new Attachment[size];
+ }
+ };
+ private final Uri uri;
+ private final Type type;
+ private final UUID uuid;
+ private final String mime;
+
+ Attachment(Parcel in) {
+ uri = in.readParcelable(Uri.class.getClassLoader());
+ mime = in.readString();
+ uuid = UUID.fromString(in.readString());
+ type = Type.valueOf(in.readString());
+ }
+
+ private Attachment(UUID uuid, Uri uri, Type type, String mime) {
+ this.uri = uri;
+ this.type = type;
+ this.mime = mime;
+ this.uuid = uuid;
+ }
+
+ private Attachment(Uri uri, Type type, String mime) {
+ this.uri = uri;
+ this.type = type;
+ this.mime = mime;
+ this.uuid = UUID.randomUUID();
+ }
+
+ public static List<Attachment> 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<Attachment> of(final Context context, List<Uri> uris) {
+ List<Attachment> 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 Attachment of(UUID uuid, final File file, String mime) {
+ return new Attachment(uuid, Uri.fromFile(file), mime != null && (mime.startsWith("image/") || mime.startsWith("video/")) ? Type.IMAGE : Type.FILE, mime);
+ }
+
+ public static List<Attachment> extractAttachments(final Context context, final Intent intent, Type type) {
+ List<Attachment> 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;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(uri, flags);
+ dest.writeString(mime);
+ dest.writeString(uuid.toString());
+ dest.writeString(type.toString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ 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<Uri> extractUriFromIntent(final Intent intent) {
- List<Uri> 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/ConversationMenuConfigurator.java b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
index 5c86b4a1b..edca88f78 100644
--- a/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
+++ b/src/main/java/de/pixart/messenger/ui/util/ConversationMenuConfigurator.java
@@ -65,12 +65,13 @@ public class ConversationMenuConfigurator {
menu.findItem(R.id.attach_location).setVisible(locationAvailable);
}
- public static void configureAttachmentMenu(@NonNull Conversation conversation, Menu menu, Boolean Quick_share_attachment_choice) {
+ public static void configureAttachmentMenu(@NonNull Conversation conversation, Menu menu, Boolean Quick_share_attachment_choice, boolean hasAttachments) {
final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
- if (Quick_share_attachment_choice) {
+ if (Quick_share_attachment_choice && !hasAttachments) {
menuAttach.setVisible(false);
return;
}
+
final boolean visible;
if (conversation.getMode() == Conversation.MODE_MULTI) {
visible = conversation.getAccount().httpUploadAvailable() && conversation.getMucOptions().participating();
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/GridManager.java b/src/main/java/de/pixart/messenger/ui/util/GridManager.java
new file mode 100644
index 000000000..ae615b051
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/util/GridManager.java
@@ -0,0 +1,76 @@
+package de.pixart.messenger.ui.util;
+
+import android.content.Context;
+import android.support.annotation.DimenRes;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.ViewTreeObserver;
+
+import de.pixart.messenger.Config;
+import de.pixart.messenger.ui.adapter.MediaAdapter;
+
+public class GridManager {
+
+ public static void setupLayoutManager(final Context context, RecyclerView recyclerView, @DimenRes int desiredSize) {
+ int maxWidth = context.getResources().getDisplayMetrics().widthPixels;
+ ColumnInfo columnInfo = calculateColumnCount(context, maxWidth, desiredSize);
+ Log.d(Config.LOGTAG, "preliminary count=" + columnInfo.count);
+ MediaAdapter.setMediaSize(recyclerView, columnInfo.width);
+ recyclerView.setLayoutManager(new GridLayoutManager(context, columnInfo.count));
+ recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ final int availableWidth = recyclerView.getMeasuredWidth();
+ if (availableWidth == 0) {
+ Log.e(Config.LOGTAG, "GridManager: available width was 0; probably because layout was hidden");
+ return;
+ }
+ final ColumnInfo columnInfo = calculateColumnCount(context, recyclerView.getMeasuredWidth(), desiredSize);
+ Log.d(Config.LOGTAG, "final count " + columnInfo.count);
+ if (recyclerView.getAdapter().getItemCount() != 0) {
+ Log.e(Config.LOGTAG, "adapter already has items; just go with it now");
+ return;
+ }
+ setupLayoutManagerInternal(recyclerView, columnInfo);
+ MediaAdapter.setMediaSize(recyclerView, columnInfo.width);
+ }
+ });
+ }
+
+ private static void setupLayoutManagerInternal(RecyclerView recyclerView, final ColumnInfo columnInfo) {
+ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ if (layoutManager instanceof GridLayoutManager) {
+ ((GridLayoutManager) layoutManager).setSpanCount(columnInfo.count);
+ }
+ }
+
+ private static ColumnInfo calculateColumnCount(Context context, int availableWidth, @DimenRes int desiredSize) {
+ final float desiredWidth = context.getResources().getDimension(desiredSize);
+ final int columns = Math.round(availableWidth / desiredWidth);
+ final int realWidth = availableWidth / columns;
+ Log.d(Config.LOGTAG, "desired=" + desiredWidth + " real=" + realWidth);
+ return new ColumnInfo(columns, realWidth);
+ }
+
+ public static int getCurrentColumnCount(RecyclerView recyclerView) {
+ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
+ if (layoutManager instanceof GridLayoutManager) {
+ return ((GridLayoutManager) layoutManager).getSpanCount();
+ } else {
+ return 0;
+ }
+ }
+
+ public static class ColumnInfo {
+ private final int count;
+ private final int width;
+
+ private ColumnInfo(int count, int width) {
+ this.count = count;
+ this.width = width;
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/util/PendingItem.java b/src/main/java/de/pixart/messenger/ui/util/PendingItem.java
index eaddf3a8c..d06f5bfc9 100644
--- a/src/main/java/de/pixart/messenger/ui/util/PendingItem.java
+++ b/src/main/java/de/pixart/messenger/ui/util/PendingItem.java
@@ -46,4 +46,10 @@ public class PendingItem<T> {
public synchronized T peek() {
return item;
}
+
+ public synchronized boolean clear() {
+ boolean notNull = this.item != null;
+ this.item = null;
+ return notNull;
+ }
} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java b/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java
index 9f9fabc5a..c7feb2105 100644
--- a/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java
+++ b/src/main/java/de/pixart/messenger/ui/util/SendButtonAction.java
@@ -33,10 +33,11 @@ import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE;
import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_CHOOSE_IMAGE;
import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_LOCATION;
import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_RECORD_VOICE;
-import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_TAKE_FROM_CAMERA;
+import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_RECORD_VIDEO;
+import static de.pixart.messenger.ui.ConversationFragment.ATTACHMENT_CHOICE_TAKE_PHOTO;
public enum SendButtonAction {
- TEXT, TAKE_FROM_CAMERA, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE, CHOOSE_ATTACHMENT;
+ TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE, RECORD_VIDEO, CHOOSE_ATTACHMENT;
public static SendButtonAction valueOfOrDefault(String setting, SendButtonAction text) {
try {
@@ -52,8 +53,10 @@ public enum SendButtonAction {
return SEND_LOCATION;
case ATTACHMENT_CHOICE_RECORD_VOICE:
return RECORD_VOICE;
- case ATTACHMENT_CHOICE_TAKE_FROM_CAMERA:
- return TAKE_FROM_CAMERA;
+ case ATTACHMENT_CHOICE_RECORD_VIDEO:
+ return RECORD_VIDEO;
+ case ATTACHMENT_CHOICE_TAKE_PHOTO:
+ return TAKE_PHOTO;
case ATTACHMENT_CHOICE_CHOOSE_IMAGE:
return CHOOSE_PICTURE;
case ATTACHMENT_CHOICE:
@@ -65,8 +68,10 @@ public enum SendButtonAction {
public int toChoice() {
switch (this) {
- case TAKE_FROM_CAMERA:
- return ATTACHMENT_CHOICE_TAKE_FROM_CAMERA;
+ case TAKE_PHOTO:
+ return ATTACHMENT_CHOICE_TAKE_PHOTO;
+ case RECORD_VIDEO:
+ return ATTACHMENT_CHOICE_RECORD_VIDEO;
case SEND_LOCATION:
return ATTACHMENT_CHOICE_LOCATION;
case RECORD_VOICE:
diff --git a/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java b/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java
index b4b0ecc28..96bcad183 100644
--- a/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java
+++ b/src/main/java/de/pixart/messenger/ui/util/SendButtonTool.java
@@ -44,7 +44,6 @@ import de.pixart.messenger.utils.UIHelper;
public class SendButtonTool {
public static SendButtonAction getAction(Activity activity, Conversation c, String text) {
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
final boolean empty = text.length() == 0;
final boolean conference = c.getMode() == Conversation.MODE_MULTI;
if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) {
@@ -57,14 +56,14 @@ public class SendButtonTool {
}
} else {
if (empty) {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
if (conference && c.getNextCounterpart() != null) {
return SendButtonAction.CANCEL;
} else {
- boolean quickShareChoice = preferences.getBoolean(SettingsActivity.QUICK_SHARE_ATTACHMENT_CHOICE, activity.getResources().getBoolean(R.bool.quick_share_attachment_choice));
String setting = preferences.getString("quick_action", activity.getResources().getString(R.string.quick_action));
- if (quickShareChoice && AttachmentsVisible(c)) {
+ if (quickShareChoice(activity) && AttachmentsVisible(c)) {
return SendButtonAction.CHOOSE_ATTACHMENT;
- } else if (quickShareChoice && !AttachmentsVisible(c)) {
+ } else if (quickShareChoice(activity) && !AttachmentsVisible(c)) {
return SendButtonAction.TEXT;
} else {
if (!setting.equals("none") && UIHelper.receivedLocationQuestion(c.getLatestMessage())) {
@@ -106,7 +105,20 @@ public class SendButtonTool {
default:
return getThemeResource(activity, R.attr.ic_send_text_offline, R.drawable.ic_send_text_offline);
}
- case TAKE_FROM_CAMERA:
+ case RECORD_VIDEO:
+ switch (status) {
+ case CHAT:
+ case ONLINE:
+ return R.drawable.ic_send_videocam_online;
+ case AWAY:
+ return R.drawable.ic_send_videocam_away;
+ case XA:
+ case DND:
+ return R.drawable.ic_send_videocam_dnd;
+ default:
+ return getThemeResource(activity, R.attr.ic_send_videocam_offline, R.drawable.ic_send_videocam_offline);
+ }
+ case TAKE_PHOTO:
switch (status) {
case CHAT:
case ONLINE:
@@ -198,4 +210,9 @@ public class SendButtonTool {
return res;
}
+
+ public static boolean quickShareChoice(Activity activity) {
+ final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
+ return preferences.getBoolean(SettingsActivity.QUICK_SHARE_ATTACHMENT_CHOICE, activity.getResources().getBoolean(R.bool.quick_share_attachment_choice));
+ }
} \ 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/StyledAttributes.java
index 58ce6a465..cd4ad3e49 100644
--- a/src/main/java/de/pixart/messenger/ui/util/Color.java
+++ b/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java
@@ -35,14 +35,26 @@ import android.content.res.TypedArray;
import android.support.annotation.AttrRes;
import android.support.annotation.ColorInt;
-public class Color {
+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 get(Context context, @AttrRes int attr) {
+ 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/ui/util/ViewUtil.java b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java
new file mode 100644
index 000000000..b1905479b
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/util/ViewUtil.java
@@ -0,0 +1,72 @@
+package de.pixart.messenger.ui.util;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.List;
+
+import de.pixart.messenger.Config;
+import de.pixart.messenger.R;
+import de.pixart.messenger.persistance.FileBackend;
+import de.pixart.messenger.ui.ShowFullscreenMessageActivity;
+
+public class ViewUtil {
+
+ public static void view(Context context, Attachment attachment) {
+ File file = new File(attachment.getUri().getPath());
+ final String mime = attachment.getMime() == null ? "*/*" : attachment.getMime();
+ view(context, file, mime);
+ }
+
+ public static void view(Context context, File file, String mime) {
+ Uri uri;
+ try {
+ uri = FileBackend.getUriForFile(context, file);
+ } catch (SecurityException e) {
+ Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
+ Toast.makeText(context, context.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
+ return;
+ }
+ // use internal viewer for images and videos
+ if (mime.startsWith("image/")) {
+ Intent intent = new Intent(context, ShowFullscreenMessageActivity.class);
+ intent.putExtra("image", Uri.fromFile(file));
+ try {
+ context.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ } else if (mime.startsWith("video/")) {
+ Intent intent = new Intent(context, ShowFullscreenMessageActivity.class);
+ intent.putExtra("video", Uri.fromFile(file));
+ try {
+ context.startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException e) {
+ //ignored
+ }
+ } else {
+ Intent openIntent = new Intent(Intent.ACTION_VIEW);
+ openIntent.setDataAndType(uri, mime);
+ openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ PackageManager manager = context.getPackageManager();
+ List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
+ if (info.size() == 0) {
+ openIntent.setDataAndType(uri, "*/*");
+ }
+ try {
+ context.startActivity(openIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/ui/widget/SquareFrameLayout.java b/src/main/java/de/pixart/messenger/ui/widget/SquareFrameLayout.java
new file mode 100644
index 000000000..4d2fcf92d
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/widget/SquareFrameLayout.java
@@ -0,0 +1,25 @@
+package de.pixart.messenger.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class SquareFrameLayout extends FrameLayout {
+ public SquareFrameLayout(Context context) {
+ super(context);
+ }
+
+ public SquareFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareFrameLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ //noinspection SuspiciousNameCombination
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/de/pixart/messenger/utils/Compatibility.java b/src/main/java/de/pixart/messenger/utils/Compatibility.java
index 95d7b4ef2..3ebc7c4f6 100644
--- a/src/main/java/de/pixart/messenger/utils/Compatibility.java
+++ b/src/main/java/de/pixart/messenger/utils/Compatibility.java
@@ -2,12 +2,14 @@ package de.pixart.messenger.utils;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.support.annotation.BoolRes;
+import android.support.v4.content.ContextCompat;
import java.util.Arrays;
import java.util.Collections;
@@ -26,10 +28,18 @@ public class Compatibility {
"vibrate_on_notification");
private static final List<String> UNUESD_SETTINGS_PRE_TWENTYSIX = Collections.singletonList("more_notification_settings");
+ public static boolean hasStoragePermission(Context context) {
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ }
+
public static boolean twentySix() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
+ public static boolean twentyTwo() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
+ }
+
private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) {
return getPreferences(context).getBoolean(name, context.getResources().getBoolean(res));
}
diff --git a/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java b/src/main/java/de/pixart/messenger/utils/IrregularUnicodeDetector.java
index b54f8d074..3cd715a4c 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.getColor(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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_android_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_android_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_archive_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_archive_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_attach_videocam.png b/src/main/res/drawable-hdpi/ic_attach_videocam.png
new file mode 100644
index 000000000..f95861633
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_attach_videocam.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_contact_white_24dp.png b/src/main/res/drawable-hdpi/ic_contact_white_24dp.png
new file mode 100644
index 000000000..7c046b21c
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_contact_white_24dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_description_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_description_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_event_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_event_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_headset_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_headset_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_help_black_48dp.png b/src/main/res/drawable-hdpi/ic_help_black_48dp.png
new file mode 100644
index 000000000..b2ea7914a
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_help_black_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_help_white_48dp.png b/src/main/res/drawable-hdpi/ic_help_white_48dp.png
new file mode 100644
index 000000000..6827c3fac
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_help_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_mic_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_mic_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_person_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_person_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_room_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_room_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_send_videocam_away.png b/src/main/res/drawable-hdpi/ic_send_videocam_away.png
new file mode 100644
index 000000000..950db9d9e
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_send_videocam_away.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_send_videocam_dnd.png b/src/main/res/drawable-hdpi/ic_send_videocam_dnd.png
new file mode 100644
index 000000000..8a3588678
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_send_videocam_dnd.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_send_videocam_online.png b/src/main/res/drawable-hdpi/ic_send_videocam_online.png
new file mode 100644
index 000000000..ac5af607f
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_send_videocam_online.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_android_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_android_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_archive_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_archive_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_attach_videocam.png b/src/main/res/drawable-mdpi/ic_attach_videocam.png
new file mode 100644
index 000000000..66aa2fe49
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_attach_videocam.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_contact_white_24dp.png b/src/main/res/drawable-mdpi/ic_contact_white_24dp.png
new file mode 100644
index 000000000..c61747dc2
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_contact_white_24dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_description_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_description_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_event_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_event_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_headset_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_headset_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_help_black_48dp.png b/src/main/res/drawable-mdpi/ic_help_black_48dp.png
new file mode 100644
index 000000000..3678f6f23
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_help_black_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_help_white_48dp.png b/src/main/res/drawable-mdpi/ic_help_white_48dp.png
new file mode 100644
index 000000000..68e4b1563
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_help_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_mic_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_mic_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_person_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_person_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_room_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_room_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_send_videocam_away.png b/src/main/res/drawable-mdpi/ic_send_videocam_away.png
new file mode 100644
index 000000000..55399ac8d
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_send_videocam_away.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_send_videocam_dnd.png b/src/main/res/drawable-mdpi/ic_send_videocam_dnd.png
new file mode 100644
index 000000000..608947d59
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_send_videocam_dnd.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_send_videocam_online.png b/src/main/res/drawable-mdpi/ic_send_videocam_online.png
new file mode 100644
index 000000000..90a152504
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_send_videocam_online.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_android_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_android_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_archive_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_archive_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_attach_videocam.png b/src/main/res/drawable-xhdpi/ic_attach_videocam.png
new file mode 100644
index 000000000..5f6054b81
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_attach_videocam.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_contact_white_24dp.png b/src/main/res/drawable-xhdpi/ic_contact_white_24dp.png
new file mode 100644
index 000000000..eedda1b40
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_contact_white_24dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_description_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_description_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_event_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_event_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_headset_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_headset_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_help_black_48dp.png b/src/main/res/drawable-xhdpi/ic_help_black_48dp.png
new file mode 100644
index 000000000..066c88bbe
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_help_black_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_help_white_48dp.png b/src/main/res/drawable-xhdpi/ic_help_white_48dp.png
new file mode 100644
index 000000000..805ea1efb
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_help_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_mic_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_mic_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_person_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_person_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_room_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_room_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_videocam_away.png b/src/main/res/drawable-xhdpi/ic_send_videocam_away.png
new file mode 100644
index 000000000..69e340800
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_send_videocam_away.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_videocam_dnd.png b/src/main/res/drawable-xhdpi/ic_send_videocam_dnd.png
new file mode 100644
index 000000000..e7f784d8b
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_send_videocam_dnd.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_send_videocam_online.png b/src/main/res/drawable-xhdpi/ic_send_videocam_online.png
new file mode 100644
index 000000000..7cda9e6f0
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_send_videocam_online.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_android_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_android_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_archive_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_archive_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_attach_videocam.png b/src/main/res/drawable-xxhdpi/ic_attach_videocam.png
new file mode 100644
index 000000000..e74d2a776
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_attach_videocam.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_contact_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_contact_white_24dp.png
new file mode 100644
index 000000000..91e0d9a6b
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_contact_white_24dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_description_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_description_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_event_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_event_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_headset_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_headset_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_help_black_48dp.png b/src/main/res/drawable-xxhdpi/ic_help_black_48dp.png
new file mode 100644
index 000000000..2f8af6e41
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_help_black_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_help_white_48dp.png b/src/main/res/drawable-xxhdpi/ic_help_white_48dp.png
new file mode 100644
index 000000000..1a3e87205
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_help_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_mic_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_mic_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_person_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_person_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_room_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_room_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_videocam_away.png b/src/main/res/drawable-xxhdpi/ic_send_videocam_away.png
new file mode 100644
index 000000000..ce04c7790
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_send_videocam_away.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.png b/src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.png
new file mode 100644
index 000000000..24890da09
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_send_videocam_dnd.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_send_videocam_online.png b/src/main/res/drawable-xxhdpi/ic_send_videocam_online.png
new file mode 100644
index 000000000..079cdef19
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_send_videocam_online.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_android_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_android_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_archive_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_archive_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_attach_videocam.png b/src/main/res/drawable-xxxhdpi/ic_attach_videocam.png
new file mode 100644
index 000000000..02fddb640
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_attach_videocam.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_contact_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_contact_white_24dp.png
new file mode 100644
index 000000000..f36920906
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_contact_white_24dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_description_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_description_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_event_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_event_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_headset_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_headset_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_help_black_48dp.png b/src/main/res/drawable-xxxhdpi/ic_help_black_48dp.png
new file mode 100644
index 000000000..45e2e7b21
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_help_black_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_help_white_48dp.png b/src/main/res/drawable-xxxhdpi/ic_help_white_48dp.png
new file mode 100644
index 000000000..dc996ae81
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_help_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_mic_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_mic_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_person_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_person_white_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_room_black_48dp.png
Binary files 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
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_room_white_48dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_videocam_away.png b/src/main/res/drawable-xxxhdpi/ic_send_videocam_away.png
new file mode 100644
index 000000000..7e517e6be
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_send_videocam_away.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.png
new file mode 100644
index 000000000..3077ee233
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_send_videocam_dnd.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_send_videocam_online.png b/src/main/res/drawable-xxxhdpi/ic_send_videocam_online.png
new file mode 100644
index 000000000..0baef17f4
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_send_videocam_online.png
Binary files differ
diff --git a/src/main/res/layout/activity_choose_contact.xml b/src/main/res/layout/activity_choose_contact.xml
index f720864a3..26f1c7494 100644
--- a/src/main/res/layout/activity_choose_contact.xml
+++ b/src/main/res/layout/activity_choose_contact.xml
@@ -13,7 +13,7 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/color_background_primary">
+ android:background="?attr/color_background_secondary">
<ListView
android:id="@+id/choose_contact_list"
diff --git a/src/main/res/layout/activity_contact_details.xml b/src/main/res/layout/activity_contact_details.xml
index dd3ff2c31..a6a720995 100644
--- a/src/main/res/layout/activity_contact_details.xml
+++ b/src/main/res/layout/activity_contact_details.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
@@ -102,8 +101,7 @@
android:layout_marginBottom="4dp"
android:layout_marginLeft="-2dp"
android:layout_marginTop="4dp"
- android:orientation="horizontal">
- </com.wefika.flowlayout.FlowLayout>
+ android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>
<TextView
android:id="@+id/details_lastseen"
@@ -235,6 +233,52 @@
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
+ android:id="@+id/media_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/media"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="-2dp"
+ android:layout_marginStart="-2dp"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/card_padding_list"
+ android:paddingEnd="@dimen/card_padding_regular"
+ android:paddingStart="@dimen/card_padding_regular"
+ android:paddingTop="@dimen/card_padding_regular" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/show_media"
+ style="@style/Widget.Conversations.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="0dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/view_media"
+ android:textColor="?attr/colorAccent" />
+ </LinearLayout>
+ </LinearLayout>
+ </android.support.v7.widget.CardView>
+
+ <android.support.v7.widget.CardView
android:id="@+id/keys_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -254,7 +298,7 @@
android:layout_height="wrap_content"
android:divider="?android:dividerHorizontal"
android:orientation="vertical"
- android:padding="@dimen/card_padding_list"></LinearLayout>
+ android:padding="@dimen/card_padding_list" />
<LinearLayout
android:layout_width="wrap_content"
diff --git a/src/main/res/layout/activity_manage_accounts.xml b/src/main/res/layout/activity_manage_accounts.xml
index e80aa01e0..2dc266bf3 100644
--- a/src/main/res/layout/activity_manage_accounts.xml
+++ b/src/main/res/layout/activity_manage_accounts.xml
@@ -2,7 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:background="?attr/color_background_primary"
+ android:background="?attr/color_background_secondary"
android:orientation="vertical">
<include layout="@layout/toolbar" />
diff --git a/src/main/res/layout/activity_media_browser.xml b/src/main/res/layout/activity_media_browser.xml
new file mode 100644
index 000000000..149f2c164
--- /dev/null
+++ b/src/main/res/layout/activity_media_browser.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?attr/color_background_primary"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/toolbar"
+ layout="@layout/toolbar" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/media"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:orientation="horizontal"
+ android:padding="2dp"
+ android:scrollbars="vertical" />
+
+ </LinearLayout>
+</layout> \ No newline at end of file
diff --git a/src/main/res/layout/activity_muc_details.xml b/src/main/res/layout/activity_muc_details.xml
index 2958e3efe..b46ad8032 100644
--- a/src/main/res/layout/activity_muc_details.xml
+++ b/src/main/res/layout/activity_muc_details.xml
@@ -276,6 +276,52 @@
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
+ android:id="@+id/media_wrapper"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:layout_marginLeft="@dimen/activity_horizontal_margin"
+ android:layout_marginRight="@dimen/activity_horizontal_margin"
+ android:layout_marginTop="@dimen/activity_vertical_margin">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/media"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="-2dp"
+ android:layout_marginStart="-2dp"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/card_padding_list"
+ android:paddingEnd="@dimen/card_padding_regular"
+ android:paddingStart="@dimen/card_padding_regular"
+ android:paddingTop="@dimen/card_padding_regular" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/show_media"
+ style="@style/Widget.Conversations.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="0dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/view_media"
+ android:textColor="?attr/colorAccent" />
+ </LinearLayout>
+ </LinearLayout>
+ </android.support.v7.widget.CardView>
+
+ <android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin"
diff --git a/src/main/res/layout/activity_start_conversation.xml b/src/main/res/layout/activity_start_conversation.xml
index 1cb95b456..7adb8714a 100644
--- a/src/main/res/layout/activity_start_conversation.xml
+++ b/src/main/res/layout/activity_start_conversation.xml
@@ -29,7 +29,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/tab_layout"
- android:background="?attr/color_background_primary" />
+ android:background="?attr/color_background_secondary" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml
index 5fce5c1f2..cbd884ae2 100644
--- a/src/main/res/layout/conversation_list_row.xml
+++ b/src/main/res/layout/conversation_list_row.xml
@@ -8,12 +8,12 @@
android:id="@+id/frame"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:background="?attr/color_background_primary">
+ android:background="?attr/color_background_secondary">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:background="?attr/color_background_secondary"
+ android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:padding="8dp">
diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml
index d081b6543..16f48bde2 100644
--- a/src/main/res/layout/fragment_conversation.xml
+++ b/src/main/res/layout/fragment_conversation.xml
@@ -5,7 +5,7 @@
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/color_background_secondary"
+ android:background="?attr/color_background_tertiary"
android:clickable="false">
<ListView
@@ -14,8 +14,9 @@
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_secondary"
+ android:background="?attr/color_background_tertiary"
android:divider="@null"
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"
@@ -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">
<RelativeLayout
android:id="@+id/textsend"
@@ -61,7 +63,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- android:background="?attr/color_background_secondary"
+ android:background="?attr/color_background_tertiary"
android:clickable="true"
android:paddingBottom="2dp"
android:paddingLeft="2dp"
@@ -73,18 +75,34 @@
android:layout_height="48dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
- android:background="?attr/color_background_secondary"
+ android:background="?attr/color_background_tertiary"
android:contentDescription="@string/attach_record_voice"
android:src="@drawable/ic_send_voice_offline"
android:visibility="gone" />
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/media_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@+id/textSendButton"
+ android:background="@drawable/message_bubble_sent_blue"
+ android:orientation="horizontal"
+ android:paddingTop="8dp"
+ android:requiresFadingEdge="horizontal"
+ android:visibility="gone"
+ app:layoutManager="android.support.v7.widget.LinearLayoutManager"
+ tools:listitem="@layout/media_preview"></android.support.v7.widget.RecyclerView>
+
<de.pixart.messenger.ui.widget.EditMessage
android:id="@+id/textinput"
style="@style/Widget.Conversations.EditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/recordVoiceButton"
android:layout_toLeftOf="@+id/textSendButton"
android:layout_toRightOf="@+id/recordVoiceButton"
+ android:layout_toStartOf="@+id/textSendButton"
android:background="@drawable/message_bubble_sent_blue"
android:ems="10"
android:imeOptions="flagNoExtractUi|actionSend"
@@ -105,9 +123,10 @@
android:id="@+id/textSendButton"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
- android:background="?attr/color_background_secondary"
+ android:background="?attr/color_background_tertiary"
android:contentDescription="@string/send_message"
android:src="@drawable/ic_send_text_offline" />
</RelativeLayout>
@@ -130,15 +149,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/snackbar_action"
+ android:layout_toStartOf="@+id/snackbar_action"
android:paddingLeft="24dp"
+ android:paddingStart="24dp"
android:textAppearance="@style/TextAppearance.Conversations.Body1.OnDark" />
<TextView
android:id="@+id/snackbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:paddingBottom="16dp"
@@ -166,8 +189,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:paddingLeft="24dp"
+ android:paddingStart="24dp"
android:textColor="@color/realblack"
android:textSize="?attr/TextSizeBody" />
</RelativeLayout>
diff --git a/src/main/res/layout/media.xml b/src/main/res/layout/media.xml
new file mode 100644
index 000000000..f95ddeda3
--- /dev/null
+++ b/src/main/res/layout/media.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <de.pixart.messenger.ui.widget.SquareFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="2dp">
+
+ <ImageView
+ android:id="@+id/media"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/black54"
+ android:scaleType="centerInside" />
+ </de.pixart.messenger.ui.widget.SquareFrameLayout>
+</layout> \ No newline at end of file
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/media_preview"
+ android:layout_width="@dimen/media_preview_size"
+ android:layout_height="@dimen/media_preview_size"
+ android:layout_centerInParent="true"
+ android:layout_margin="12dp"
+ android:background="@color/black54"
+ android:scaleType="center" />
+
+ <ImageButton
+ android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:alpha="?attr/delete_icon_alpha"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:src="?attr/icon_cancel" />
+ </RelativeLayout>
+</layout> \ No newline at end of file
diff --git a/src/main/res/menu/choose_attachment.xml b/src/main/res/menu/choose_attachment.xml
index ae6f47a95..a9a57404d 100644
--- a/src/main/res/menu/choose_attachment.xml
+++ b/src/main/res/menu/choose_attachment.xml
@@ -14,7 +14,12 @@
<item
android:id="@+id/attach_take_picture"
android:icon="?attr/ic_attach_camera"
- android:title="@string/attach_take_from_camera" />
+ android:title="@string/action_take_photo" />
+
+ <item
+ android:id="@+id/attach_record_video"
+ android:icon="?attr/ic_attach_videocam"
+ android:title="@string/action_take_video" />
<item
android:id="@+id/attach_record_voice"
android:icon="?attr/ic_attach_record"
diff --git a/src/main/res/menu/fragment_conversation.xml b/src/main/res/menu/fragment_conversation.xml
index d67bf5253..088501bcb 100644
--- a/src/main/res/menu/fragment_conversation.xml
+++ b/src/main/res/menu/fragment_conversation.xml
@@ -5,8 +5,8 @@
android:id="@+id/action_security"
android:icon="?attr/icon_not_secure"
android:orderInCategory="20"
- app:showAsAction="always"
- android:title="@string/action_secure" >
+ android:title="@string/action_secure"
+ app:showAsAction="always">
<menu>
<group android:checkableBehavior="single">
<item
@@ -28,8 +28,8 @@
android:id="@+id/action_attach_file"
android:icon="?attr/icon_new_attachment"
android:orderInCategory="30"
- app:showAsAction="always"
- android:title="@string/attach_file" >
+ android:title="@string/attach_file"
+ app:showAsAction="always">
<menu>
<item
@@ -45,7 +45,13 @@
<item
android:id="@+id/attach_take_picture"
android:icon="?attr/ic_attach_camera"
- android:title="@string/attach_take_from_camera" />
+ android:title="@string/action_take_photo" />
+
+ <item
+ android:id="@+id/attach_record_video"
+ android:icon="?attr/ic_attach_videocam"
+ android:title="@string/action_take_video" />
+
<item
android:id="@+id/attach_record_voice"
android:icon="?attr/ic_attach_record"
@@ -60,21 +66,33 @@
<item
android:id="@+id/action_invite"
android:orderInCategory="45"
- app:showAsAction="never"
- android:title="@string/invite_contact" />
+ android:title="@string/invite_contact"
+ app:showAsAction="never" />
<item
android:id="@+id/action_clear_history"
android:orderInCategory="50"
- app:showAsAction="never"
- android:title="@string/action_clear_history" />
+ android:title="@string/action_clear_history"
+ app:showAsAction="never" />
<item
android:id="@+id/action_archive_chat"
android:orderInCategory="60"
- app:showAsAction="never"
- android:title="@string/action_end_conversation" />
+ android:title="@string/action_end_conversation"
+ app:showAsAction="never" />
<item
android:id="@+id/action_create_issue"
android:orderInCategory="100"
- app:showAsAction="never"
- android:title="@string/create_issue" />
+ android:title="@string/create_issue"
+ app:showAsAction="never" />
+ <item
+ android:id="@+id/action_group_details"
+ android:icon="@drawable/ic_group_white_24dp"
+ android:orderInCategory="100"
+ android:title="@string/action_group_details"
+ app:showAsAction="always" />
+ <item
+ android:id="@+id/action_contact_details"
+ android:icon="@drawable/ic_contact_white_24dp"
+ android:orderInCategory="100"
+ android:title="@string/action_contact_details"
+ app:showAsAction="always" />
</menu> \ No newline at end of file
diff --git a/src/main/res/values-h360dp/dimens.xml b/src/main/res/values-h360dp/dimens.xml
new file mode 100644
index 000000000..5e432c034
--- /dev/null
+++ b/src/main/res/values-h360dp/dimens.xml
@@ -0,0 +1,4 @@
+<resources>
+ <dimen name="publish_avatar_top_margin">16dp</dimen>
+ <dimen name="publish_avatar_size">128dp</dimen>
+</resources> \ No newline at end of file
diff --git a/src/main/res/values-h500dp/dimens.xml b/src/main/res/values-h500dp/dimens.xml
new file mode 100644
index 000000000..deea1124f
--- /dev/null
+++ b/src/main/res/values-h500dp/dimens.xml
@@ -0,0 +1,4 @@
+<resources>
+ <dimen name="publish_avatar_top_margin">24dp</dimen>
+ <dimen name="publish_avatar_size">192dp</dimen>
+</resources> \ No newline at end of file
diff --git a/src/main/res/values-w384dp/dimens.xml b/src/main/res/values-w384dp/dimens.xml
index 430845b1c..e9350addd 100644
--- a/src/main/res/values-w384dp/dimens.xml
+++ b/src/main/res/values-w384dp/dimens.xml
@@ -1,5 +1,9 @@
<resources>
<!-- 384dp is the screen width of the Nexus 4. Something like a Moto G is smaller but a Nexus 5X is larger -->
<!-- https://material.io/devices/ -->
+ <dimen name="fineprint_size">12sp</dimen>
+ <dimen name="swipe_handle_size">48dp</dimen>
<dimen name="audio_player_width">288dp</dimen>
+ <dimen name="avatar_on_details_screen_size">72dp</dimen>
+ <dimen name="media_size">64dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
</resources> \ No newline at end of file
diff --git a/src/main/res/values-w585dp/dimens.xml b/src/main/res/values-w585dp/dimens.xml
index 38ca33a11..067401781 100644
--- a/src/main/res/values-w585dp/dimens.xml
+++ b/src/main/res/values-w585dp/dimens.xml
@@ -1,3 +1,6 @@
<resources>
<dimen name="activity_horizontal_margin">32dp</dimen>
+ <dimen name="avatar_on_details_screen_size">96dp</dimen>
+ <dimen name="media_size">80dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
+ <dimen name="browser_media_size">128dp</dimen>
</resources>
diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml
index ff36fe0ae..93da9bac4 100644
--- a/src/main/res/values/arrays.xml
+++ b/src/main/res/values/arrays.xml
@@ -37,7 +37,8 @@
<string-array name="quick_actions">
<item>@string/none</item>
<item>@string/recently_used</item>
- <item>@string/attach_take_from_camera</item>
+ <item>@string/action_take_photo</item>
+ <item>@string/action_take_video</item>
<item>@string/attach_choose_picture</item>
<item>@string/attach_record_voice</item>
<item>@string/send_location</item>
@@ -46,7 +47,8 @@
<string-array name="quick_action_values">
<item>none</item>
<item>recent</item>
- <item>TAKE_FROM_CAMERA</item>
+ <item>TAKE_PHOTO</item>
+ <item>RECORD_VIDEO</item>
<item>CHOOSE_PICTURE</item>
<item>RECORD_VOICE</item>
<item>SEND_LOCATION</item>
diff --git a/src/main/res/values/attrs.xml b/src/main/res/values/attrs.xml
index e89ed2944..672cb00fb 100644
--- a/src/main/res/values/attrs.xml
+++ b/src/main/res/values/attrs.xml
@@ -42,6 +42,16 @@
<attr name="conversations_overview_background" format="reference|color" />
+ <attr name="media_preview_document" format="reference" />
+ <attr name="media_preview_recording" format="reference" />
+ <attr name="media_preview_audio" format="reference" />
+ <attr name="media_preview_location" format="reference" />
+ <attr name="media_preview_contact" format="reference" />
+ <attr name="media_preview_app" format="reference" />
+ <attr name="media_preview_calendar" format="reference" />
+ <attr name="media_preview_archive" format="reference" />
+ <attr name="media_preview_unknown" format="reference" />
+
<attr name="icon_add_group" format="reference" />
<attr name="icon_add_person" format="reference" />
<attr name="icon_cancel" format="reference" />
@@ -83,6 +93,7 @@
<attr name="ic_attach_photo" format="reference" />
<attr name="ic_attach_record" format="reference" />
<attr name="ic_attach_video" format="reference" />
+ <attr name="ic_attach_videocam" format="reference" />
<attr name="ic_file_apk" format="reference" />
<attr name="ic_file_pdf" format="reference" />
@@ -90,6 +101,7 @@
<attr name="ic_file_calendar" format="reference" />
<attr name="icon_alpha" format="float" />
+ <attr name="delete_icon_alpha" format="float" />
<attr name="popupOverlayStyle" format="reference" />
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 @@
<bool name="btbv">true</bool>
<bool name="send_button_status">true</bool>
<string name="quick_action">recent</string>
- <bool name="return_to_previous">false</bool>
<bool name="display_enter_key">false</bool>
<bool name="show_dynamic_tags">false</bool>
<integer name="grace_period">144</integer>
diff --git a/src/main/res/values/dimens.xml b/src/main/res/values/dimens.xml
index 7f0f561b1..526eb7d40 100644
--- a/src/main/res/values/dimens.xml
+++ b/src/main/res/values/dimens.xml
@@ -10,6 +10,9 @@
<dimen name="audio_player_width">224dp</dimen>
<dimen name="avatar_item_distance">16dp</dimen>
+ <dimen name="media_preview_size">80dp</dimen>
+ <dimen name="media_size">56dp</dimen> <!-- ideally not larger than avatar_on_details_screen -->
+ <dimen name="browser_media_size">96dp</dimen>
<dimen name="toolbar_elevation">4dp</dimen>
<dimen name="publish_avatar_top_margin">8dp</dimen>
@@ -24,6 +27,5 @@
<dimen name="scan_dot_size">8dp</dimen>
<dimen name="background_image_opacity">0.12</dimen>
-
<dimen name="rounded_image_border">8dp</dimen>
</resources>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 26a8a7112..62b415cf3 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -712,8 +712,6 @@
<string name="contacts_have_read_up_to_this_point">%s have read up to this point</string>
<string name="contact_has_read_up_to_this_point">%s has read up to this point</string>
<string name="contacts_and_n_more_have_read_up_to_this_point">%1$s and %2$d more have read up to this point</string>
- <string name="pref_return_to_previous">Quick Sharing</string>
- <string name="pref_return_to_previous_summary">Immediately return to previous activity instead of opening the conversation after sharing something</string>
<string name="conversation_unencrypted_hint">This chat is unencrypted, for security reasons you should activate message encryption by using the lock icon. The preferable encryption is OMEMO.</string>
<string name="pref_warn_unencrypted_chat">Warn if chat is unencrypted</string>
<string name="pref_warn_unencrypted_chat_summary">If message encryption is available, you should use it. If you don\'t use message encryption, show a warning message inside the chat.</string>
@@ -812,17 +810,20 @@
<string name="phone_book">Address book</string>
<string name="unable_to_save_recording">Unable to save recording</string>
<string name="foreground_service_channel_name">Foreground service</string>
- <string name="foreground_service_channel_description">This notification category is used to display a permanent notification indicating that Conversations is running.</string>
+ <string name="foreground_service_channel_description">This notification category is used to display a permanent notification indicating that Pix-Art Messenger is running.</string>
<string name="notification_group_status_information">Status Information</string>
<string name="error_channel_name">Connectivity Problems</string>
<string name="error_channel_description">This notification category is used to display a notification in case there is a problem connecting to an account.</string>
<string name="notification_group_messages">Messages</string>
<string name="messages_channel_name">Messages</string>
<string name="silent_messages_channel_name">Silent messages</string>
- <string name="silent_messages_channel_description">This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string>
+ <string name="silent_messages_channel_description">This notification category is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string>
<string name="pref_more_notification_settings">Notification Settings</string>
<string name="pref_more_notification_settings_summary">Importance, Sound, Vibrate</string>
<string name="video_compression_channel_name">Video compression</string>
<string name="backup_channel_name">Database backup</string>
<string name="app_update_channel_name">App update</string>
+ <string name="action_group_details">Group details</string>
+ <string name="view_media">View media</string>
+ <string name="media_browser">Media browser</string>
</resources>
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
index c17bb820f..50cf8e93a 100644
--- a/src/main/res/values/themes.xml
+++ b/src/main/res/values/themes.xml
@@ -65,6 +65,16 @@
</item>
<item name="ic_send_voice_offline" type="reference">@drawable/ic_send_voice_offline</item>
+ <item name="media_preview_document" type="reference">@drawable/ic_description_black_48dp</item>
+ <item name="media_preview_recording" type="reference">@drawable/ic_mic_black_48dp</item>
+ <item name="media_preview_audio" type="reference">@drawable/ic_headset_black_48dp</item>
+ <item name="media_preview_location" type="reference">@drawable/ic_room_black_48dp</item>
+ <item name="media_preview_contact" type="reference">@drawable/ic_person_black_48dp</item>
+ <item name="media_preview_app" type="reference">@drawable/ic_android_black_48dp</item>
+ <item name="media_preview_calendar" type="reference">@drawable/ic_event_black_48dp</item>
+ <item name="media_preview_archive" type="reference">@drawable/ic_archive_black_48dp</item>
+ <item name="media_preview_unknown" type="reference">@drawable/ic_help_black_48dp</item>
+
<item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item>
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_black_24dp</item>
@@ -106,8 +116,10 @@
<item name="dialog_horizontal_padding">16dp</item>
<item name="dialog_vertical_padding">16dp</item>
<item name="icon_alpha" type="float">0.54</item>
+ <item name="delete_icon_alpha" type="float">0.70</item>
<item name="ic_attach_camera" type="reference">@drawable/ic_attach_camera</item>
+ <item type="reference" name="ic_attach_videocam">@drawable/ic_attach_videocam</item>
<item name="ic_attach_document" type="reference">@drawable/ic_attach_document</item>
<item name="ic_attach_location" type="reference">@drawable/ic_attach_location</item>
<item name="ic_attach_photo" type="reference">@drawable/ic_attach_photo</item>
@@ -150,9 +162,9 @@
<item name="colorControlActivated">@color/accent</item>
<item name="text_Color_Main">@color/realwhite</item>
- <item name="color_background_primary">@color/grey800</item>
- <item name="color_background_secondary">@color/grey900</item>
- <item name="color_background_tertiary">@color/grey700</item>
+ <item name="color_background_primary">@color/grey700</item>
+ <item name="color_background_secondary">@color/grey800</item>
+ <item name="color_background_tertiary">@color/grey900</item>
<item name="color_warning">@color/red_a700</item>
<item name="TextColorOnline">@color/green500</item>
<item name="TextColorError">@color/red500</item>
@@ -210,6 +222,7 @@
</item>
<item name="ic_attach_camera" type="reference">@drawable/ic_attach_camera_white</item>
+ <item name="ic_attach_videocam" type="reference">@drawable/ic_attach_videocam_white</item>
<item name="ic_attach_video" type="reference">@drawable/ic_attach_videocam_white</item>
<item name="ic_attach_document" type="reference">@drawable/ic_attach_document_white</item>
<item name="ic_attach_location" type="reference">@drawable/ic_attach_location_white</item>
@@ -219,10 +232,21 @@
<item name="conversations_overview_background">@color/primary_dark</item>
<item name="icon_alpha" type="float">0.7</item>
+ <item name="delete_icon_alpha" type="float">0.7</item>
<item name="dialog_horizontal_padding">24dp</item>
<item name="dialog_vertical_padding">16dp</item>
+ <item name="media_preview_document" type="reference">@drawable/ic_description_white_48dp</item>
+ <item name="media_preview_recording" type="reference">@drawable/ic_mic_white_48dp</item>
+ <item name="media_preview_audio" type="reference">@drawable/ic_headset_white_48dp</item>
+ <item name="media_preview_location" type="reference">@drawable/ic_room_white_48dp</item>
+ <item name="media_preview_contact" type="reference">@drawable/ic_person_white_48dp</item>
+ <item name="media_preview_app" type="reference">@drawable/ic_android_white_48dp</item>
+ <item name="media_preview_calendar" type="reference">@drawable/ic_event_white_48dp</item>
+ <item name="media_preview_archive" type="reference">@drawable/ic_archive_white_48dp</item>
+ <item name="media_preview_unknown" type="reference">@drawable/ic_help_white_48dp</item>
+
<item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item>
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_white_24dp</item>
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" />
- <CheckBoxPreference
- android:defaultValue="@bool/return_to_previous"
- android:key="return_to_previous"
- android:summary="@string/pref_return_to_previous_summary"
- android:title="@string/pref_return_to_previous" />
</PreferenceScreen>
<!--status-->
<PreferenceScreen