diff options
15 files changed, 181 insertions, 75 deletions
diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index eef639e40..8b095a149 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -94,6 +94,7 @@ public final class Config { public static boolean PUT_AUTH_TAG_INTO_KEY = false; public static final int MAX_DISPLAY_MESSAGE_CHARS = 4096; + public static final int MAX_STORAGE_MESSAGE_CHARS = 1024 * 1024 * 1024; public static final boolean ExportLogs = true; // automatically export logs public static final int ExportLogs_Hour = 4; //valid values from 0 to 23 diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java index 526c60f6c..4a7b45088 100644 --- a/src/main/java/de/pixart/messenger/entities/Conversation.java +++ b/src/main/java/de/pixart/messenger/entities/Conversation.java @@ -994,6 +994,21 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } } + public int failedCount() { + synchronized (this.messages) { + int count = 0; + for (int i = this.messages.size() - 1; i >= 0; --i) { + Message message = this.messages.get(i); + if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) && message.getEncryption() != Message.ENCRYPTION_PGP) { + if (message.getStatus() == Message.STATUS_SEND_FAILED){ + ++count; + } + } + } + return count; + } + } + public class Smp { public static final int STATUS_NONE = 0; public static final int STATUS_CONTACT_REQUESTED = 1; diff --git a/src/main/java/de/pixart/messenger/entities/Message.java b/src/main/java/de/pixart/messenger/entities/Message.java index 8ac7735d6..0936c86c8 100644 --- a/src/main/java/de/pixart/messenger/entities/Message.java +++ b/src/main/java/de/pixart/messenger/entities/Message.java @@ -9,6 +9,7 @@ import java.net.URL; import de.pixart.messenger.Config; import de.pixart.messenger.crypto.axolotl.FingerprintStatus; +import de.pixart.messenger.http.AesGcmURLStreamHandler; import de.pixart.messenger.utils.CryptoHelper; import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.MimeUtils; @@ -154,6 +155,8 @@ public class Message extends AbstractEntity { } } catch (InvalidJidException e) { jid = null; + } catch (IllegalStateException e) { + return null; // message too long? } Jid trueCounterpart; try { @@ -217,7 +220,7 @@ public class Message extends AbstractEntity { } else { values.put(TRUE_COUNTERPART, trueCounterpart.toPreppedString()); } - values.put(BODY, body); + values.put(BODY, body.length() > Config.MAX_STORAGE_MESSAGE_CHARS ? body.substring(0,Config.MAX_STORAGE_MESSAGE_CHARS) : body); values.put(TIME_SENT, timeSent); values.put(ENCRYPTION, encryption); values.put(STATUS, status); @@ -657,6 +660,12 @@ public class Message extends AbstractEntity { } try { URL url = new URL(body); + String ref = url.getRef(); + final String protocol = url.getProtocol(); + final boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}"); + if (AesGcmURLStreamHandler.PROTOCOL_NAME.equalsIgnoreCase(protocol) && encrypted) { + return Decision.MUST; + } if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) { return Decision.NEVER; } else if (oob) { @@ -666,8 +675,6 @@ public class Message extends AbstractEntity { if (extension == null) { return Decision.NEVER; } - String ref = url.getRef(); - boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}"); if (encrypted) { return Decision.MUST; diff --git a/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandler.java b/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandler.java new file mode 100644 index 000000000..64188c5db --- /dev/null +++ b/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandler.java @@ -0,0 +1,17 @@ +package de.pixart.messenger.http; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; + + +public class AesGcmURLStreamHandler extends URLStreamHandler { + + public static final String PROTOCOL_NAME = "aesgcm"; + + @Override + protected URLConnection openConnection(URL url) throws IOException { + return new URL("https"+url.toString().substring(url.getProtocol().length())).openConnection(); + } +} diff --git a/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandlerFactory.java b/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandlerFactory.java new file mode 100644 index 000000000..b5edd2f5a --- /dev/null +++ b/src/main/java/de/pixart/messenger/http/AesGcmURLStreamHandlerFactory.java @@ -0,0 +1,15 @@ +package de.pixart.messenger.http; + +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; + +public class AesGcmURLStreamHandlerFactory implements URLStreamHandlerFactory { + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + if (AesGcmURLStreamHandler.PROTOCOL_NAME.equals(protocol)) { + return new AesGcmURLStreamHandler(); + } else { + return null; + } + } +} diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index b40452f1a..ef961f6a3 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -55,7 +55,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static DatabaseBackend instance = null; public static final String DATABASE_NAME = "history"; - public static final int DATABASE_VERSION = 34; + public static final int DATABASE_VERSION = 35; private static String CREATE_CONTATCS_STATEMENT = "create table " + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, " @@ -145,6 +145,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { private static String START_TIMES_TABLE = "start_times"; private static String CREATE_START_TIMES_TABLE = "create table " + START_TIMES_TABLE + " (timestamp NUMBER);"; private static String CREATE_MESSAGE_TIME_INDEX = "create INDEX message_time_index ON " + Message.TABLENAME + "(" + Message.TIME_SENT + ")"; + private static String CREATE_MESSAGE_CONVERSATION_INDEX = "create INDEX message_conversation_index ON " + Message.TABLENAME + "(" + Message.CONVERSATION + ")"; private DatabaseBackend(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -195,6 +196,7 @@ public class DatabaseBackend extends SQLiteOpenHelper { + ") ON DELETE CASCADE);"); db.execSQL(CREATE_MESSAGE_TIME_INDEX); + db.execSQL(CREATE_MESSAGE_CONVERSATION_INDEX); db.execSQL(CREATE_CONTATCS_STATEMENT); db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT); db.execSQL(CREATE_SESSIONS_STATEMENT); @@ -393,6 +395,9 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL(CREATE_MESSAGE_TIME_INDEX); // do nothing else at this point because we have seperated videos, images, audios and other files in different directories } + if (oldVersion < 35 && newVersion >= 35) { + db.execSQL(CREATE_MESSAGE_CONVERSATION_INDEX); + } } private static ContentValues createFingerprintStatusContentValues(FingerprintStatus.Trust trust, boolean active) { @@ -597,8 +602,10 @@ public class DatabaseBackend extends SQLiteOpenHelper { cursor.moveToLast(); do { Message message = Message.fromCursor(cursor); - message.setConversation(conversation); - list.add(message); + if (message != null) { + message.setConversation(conversation); + list.add(message); + } } while (cursor.moveToPrevious()); } cursor.close(); diff --git a/src/main/java/de/pixart/messenger/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java index d7948d651..61d8a921d 100644 --- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java +++ b/src/main/java/de/pixart/messenger/services/ExportLogsService.java @@ -119,6 +119,8 @@ public class ExportLogsService extends Service { BufferedWriter bw = null; try { for (Message message : mDatabaseBackend.getMessagesIterable(conversation)) { + if (message == null) + continue; if (message.getType() == Message.TYPE_TEXT || message.hasFileOnRemoteHost()) { String date = simpleDateFormat.format(new Date(message.getTimeSent())); if (bw == null) { diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 55b87074a..729079f2a 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -99,6 +99,7 @@ import de.pixart.messenger.generator.IqGenerator; import de.pixart.messenger.generator.MessageGenerator; import de.pixart.messenger.generator.PresenceGenerator; import de.pixart.messenger.http.HttpConnectionManager; +import de.pixart.messenger.http.AesGcmURLStreamHandlerFactory; import de.pixart.messenger.parser.AbstractParser; import de.pixart.messenger.parser.IqParser; import de.pixart.messenger.parser.MessageParser; @@ -143,9 +144,14 @@ import de.pixart.messenger.xmpp.stanzas.IqPacket; import de.pixart.messenger.xmpp.stanzas.MessagePacket; import de.pixart.messenger.xmpp.stanzas.PresencePacket; import me.leolin.shortcutbadger.ShortcutBadger; +import java.net.URL; public class XmppConnectionService extends Service { + static { + URL.setURLStreamHandlerFactory(new AesGcmURLStreamHandlerFactory()); + } + public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations"; public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground"; @@ -1717,6 +1723,7 @@ public class XmppConnectionService extends Service { return conversation; } conversation = databaseBackend.findConversation(account, jid); + final boolean loadMessagesFromDb; if (conversation != null) { conversation.setStatus(Conversation.STATUS_AVAILABLE); conversation.setAccount(account); @@ -1727,8 +1734,8 @@ public class XmppConnectionService extends Service { conversation.setMode(Conversation.MODE_SINGLE); conversation.setContactJid(jid.toBareJid()); } - conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); - this.databaseBackend.updateConversation(conversation); + databaseBackend.updateConversation(conversation); + loadMessagesFromDb = conversation.messagesLoaded.compareAndSet(true,false); } else { String conversationName; Contact contact = account.getRoster().getContact(jid); @@ -1744,20 +1751,31 @@ public class XmppConnectionService extends Service { conversation = new Conversation(conversationName, account, jid.toBareJid(), Conversation.MODE_SINGLE); } - this.databaseBackend.createConversation(conversation); + loadMessagesFromDb = false; } - if (account.getXmppConnection() != null - && account.getXmppConnection().getFeatures().mam() - && !muc) { - if (query == null) { - this.mMessageArchiveService.query(conversation); - } else { - if (query.getConversation() == null) { - this.mMessageArchiveService.query(conversation, query.getStart()); + final Conversation c = conversation; + mDatabaseExecutor.execute(new Runnable() { + @Override + public void run() { + if (loadMessagesFromDb) { + c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE)); + updateConversationUi(); + c.messagesLoaded.set(true); + } + if (account.getXmppConnection() != null + && account.getXmppConnection().getFeatures().mam() + && !muc) { + if (query == null) { + mMessageArchiveService.query(c); + } else { + if (query.getConversation() == null) { + mMessageArchiveService.query(c, query.getStart()); + } + } } + checkDeletedFiles(c); } - } - checkDeletedFiles(conversation); + }); this.conversations.add(conversation); updateConversationUi(); return conversation; diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index 7bc1a3208..db9e3981d 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -141,8 +141,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() { @Override - public void onMoreMessagesLoaded(final int c, Conversation conversation) { + public void onMoreMessagesLoaded(final int c, final Conversation conversation) { if (ConversationFragment.this.conversation != conversation) { + conversation.messagesLoaded.set(true); return; } activity.runOnUiThread(new Runnable() { @@ -170,6 +171,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa if (messageLoaderToast != null) { messageLoaderToast.cancel(); } + conversation.messagesLoaded.set(true); } }); } @@ -1283,7 +1285,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private boolean showLoadMoreMessages(final Conversation c) { final boolean mam = hasMamSupport(c); final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService(); - return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.hasMessagesLeftOnServer() && !service.queryInProgress(c))); + return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.messagesLoaded.get() && c.hasMessagesLeftOnServer() && !service.queryInProgress(c))); } private boolean hasMamSupport(final Conversation c) { 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 600ac47eb..f17b7a2ab 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -22,6 +22,7 @@ import java.util.concurrent.RejectedExecutionException; import de.pixart.messenger.R; import de.pixart.messenger.entities.Conversation; import de.pixart.messenger.entities.Message; +import de.pixart.messenger.entities.Transferable; import de.pixart.messenger.ui.ConversationActivity; import de.pixart.messenger.ui.XmppActivity; import de.pixart.messenger.utils.UIHelper; @@ -86,9 +87,10 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { ImageView imagePreview = (ImageView) view.findViewById(R.id.conversation_lastimage); ImageView notificationStatus = (ImageView) view.findViewById(R.id.notification_status); TextView mUnread = (TextView) view.findViewById(R.id.conversation_unread); + TextView mFailed = (TextView) view.findViewById(R.id.conversation_failed); Message message = conversation.getLatestMessage(); int unreadcount = conversation.unreadCount(); - String mimeType = message.getMimeType(); + int failedcount = conversation.failedCount(); if (!conversation.isRead()) { convName.setTypeface(null, Typeface.BOLD); @@ -96,61 +98,44 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> { convName.setTypeface(null, Typeface.NORMAL); } - - /*if (message.getTransferable() == null - || message.getTransferable().getStatus() != Transferable.STATUS_DELETED) { - if (mimeType != null && message.getMimeType().startsWith("video/")) { - mLastMessage.setVisibility(View.GONE); - imagePreview.setVisibility(View.VISIBLE); - activity.loadVideoPreview(message, imagePreview); - } else if (message.getFileParams().width > 0) { - mLastMessage.setVisibility(View.GONE); - imagePreview.setVisibility(View.VISIBLE); - activity.loadBitmap(message, imagePreview); - } else { - Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message); - mLastMessage.setVisibility(View.VISIBLE); - imagePreview.setVisibility(View.GONE); - mLastMessage.setText(preview.first); - if (preview.second) { - if (conversation.isRead()) { - mLastMessage.setTypeface(null, Typeface.ITALIC); - } else { - mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - } - } else { - if (conversation.isRead()) { - mLastMessage.setTypeface(null, Typeface.NORMAL); - } else { - mLastMessage.setTypeface(null, Typeface.BOLD); - } - } - } - } else {*/ - Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message); - mLastMessage.setVisibility(View.VISIBLE); - imagePreview.setVisibility(View.GONE); - mLastMessage.setText(preview.first); if (unreadcount > 0) { mUnread.setVisibility(View.VISIBLE); mUnread.setText(String.valueOf(unreadcount)); } else { mUnread.setVisibility(View.GONE); } - if (preview.second) { - if (conversation.isRead()) { - mLastMessage.setTypeface(null, Typeface.ITALIC); - } else { - mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC); - } + if (failedcount > 0) { + mFailed.setVisibility(View.VISIBLE); + mFailed.setText(String.valueOf(failedcount)); } else { - if (conversation.isRead()) { - mLastMessage.setTypeface(null, Typeface.NORMAL); + mFailed.setVisibility(View.GONE); + } + + if (message.getFileParams().width > 0 + && (message.getTransferable() == null + || message.getTransferable().getStatus() != Transferable.STATUS_DELETED)) { + mLastMessage.setVisibility(View.GONE); + imagePreview.setVisibility(View.VISIBLE); + activity.loadBitmap(message, imagePreview); + } else { + Pair<String, Boolean> preview = UIHelper.getMessagePreview(activity, message); + mLastMessage.setVisibility(View.VISIBLE); + imagePreview.setVisibility(View.GONE); + mLastMessage.setText(preview.first); + if (preview.second) { + if (conversation.isRead()) { + mLastMessage.setTypeface(null, Typeface.ITALIC); + } else { + mLastMessage.setTypeface(null, Typeface.BOLD_ITALIC); + } } else { - mLastMessage.setTypeface(null, Typeface.BOLD); + if (conversation.isRead()) { + mLastMessage.setTypeface(null, Typeface.NORMAL); + } else { + mLastMessage.setTypeface(null, Typeface.BOLD); + } } } - //} long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0); if (muted_till == Long.MAX_VALUE) { diff --git a/src/main/java/de/pixart/messenger/utils/MimeUtils.java b/src/main/java/de/pixart/messenger/utils/MimeUtils.java index bdcd82bae..0e52e283f 100644 --- a/src/main/java/de/pixart/messenger/utils/MimeUtils.java +++ b/src/main/java/de/pixart/messenger/utils/MimeUtils.java @@ -505,7 +505,12 @@ public final class MimeUtils { public static String guessMimeTypeFromUri(Context context, Uri uri) { // try the content resolver - String mimeType = context.getContentResolver().getType(uri); + String mimeType; + try { + mimeType = context.getContentResolver().getType(uri); + } catch (Throwable throwable) { + mimeType = null; + } // try the extension if (mimeType == null && uri.getPath() != null) { String path = uri.getPath(); diff --git a/src/main/res/drawable/rounded_rectangle_primary.xml b/src/main/res/drawable/rounded_rectangle_primary.xml new file mode 100644 index 000000000..3a32aa8e5 --- /dev/null +++ b/src/main/res/drawable/rounded_rectangle_primary.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + +<item> + <shape android:shape="rectangle"> + <padding + android:bottom="2dp" + android:left="2dp" + android:right="2dp" + android:top="2dp" /> + <solid android:color="@color/primary" /> + <corners android:radius="6dp" /> + </shape> +</item> + +</layer-list>
\ No newline at end of file diff --git a/src/main/res/drawable/rounded_rectangle.xml b/src/main/res/drawable/rounded_rectangle_red.xml index fab83eda9..a6cb17d2b 100644 --- a/src/main/res/drawable/rounded_rectangle.xml +++ b/src/main/res/drawable/rounded_rectangle_red.xml @@ -8,7 +8,7 @@ android:left="2dp" android:right="2dp" android:top="2dp" /> - <solid android:color="@color/primary" /> + <solid android:color="@color/red800" /> <corners android:radius="6dp" /> </shape> </item> diff --git a/src/main/res/layout/conversation_list_row.xml b/src/main/res/layout/conversation_list_row.xml index 195a14621..16d611ce9 100644 --- a/src/main/res/layout/conversation_list_row.xml +++ b/src/main/res/layout/conversation_list_row.xml @@ -46,13 +46,13 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/conversation_lastwrapper" - android:layout_toLeftOf="@+id/conversation_unread" android:paddingRight="4dp" android:singleLine="true" android:text="Awesome groupchat" android:textColor="@color/black87" android:textSize="?attr/TextSizeHeadline" - android:typeface="sans" /> + android:typeface="sans" + android:layout_toLeftOf="@+id/conversation_failed" /> <RelativeLayout android:id="@+id/conversation_lastwrapper" @@ -110,18 +110,34 @@ android:text="100" android:textColor="@color/grey50" android:textSize="?attr/TextSizeBody" - android:background="@drawable/rounded_rectangle" + android:background="@drawable/rounded_rectangle_primary" + android:visibility="gone" + android:paddingLeft="4dp" + android:paddingRight="4dp" + android:paddingBottom="2dp" + android:layout_margin="1dp" /> + <TextView + android:id="@+id/conversation_failed" + android:layout_toLeftOf="@+id/conversation_unread" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignBaseline="@+id/conversation_name" + android:text="100" + android:textColor="@color/grey50" + android:textSize="?attr/TextSizeBody" + android:background="@drawable/rounded_rectangle_red" android:visibility="gone" android:paddingLeft="4dp" android:paddingRight="4dp" - android:paddingBottom="2dp" /> + android:paddingBottom="2dp" + android:layout_margin="1dp" /> <TextView android:id="@+id/conversation_lastupdate" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignBaseline="@+id/conversation_name" android:layout_alignParentRight="true" + android:layout_alignBaseline="@+id/conversation_name" android:paddingLeft="4dp" android:gravity="right" android:text="23:42" diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 4df0cc10b..4ac341344 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -729,7 +729,7 @@ <string name="transcoding_video_progress">Compressing video (%s%% completed)</string> <string name="pref_automatically_delete_messages_description">Automatically delete messages from this device that are older than the configured time frame.</string> <string name="pref_automatically_delete_messages">Automatic message deletion</string> - <string name="not_fetching_history_retention_period">Overstepping local retention period.</string> + <string name="not_fetching_history_retention_period">Not fetching messages due to local retention period.</string> <string name="timeout_24_hours">24 hours</string> <string name="timeout_7_days">7 days</string> <string name="timeout_30_days">30 days</string> |