From 475e29b622ef5c87e3a3b3a36020bf99958467cd Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 29 Jan 2017 20:06:28 +0100 Subject: configurable local message retention period. --- src/main/java/de/pixart/messenger/Config.java | 2 + .../de/pixart/messenger/entities/Conversation.java | 14 +++++ .../de/pixart/messenger/parser/MessageParser.java | 10 +++- .../messenger/persistance/DatabaseBackend.java | 7 +++ .../messenger/services/MessageArchiveService.java | 11 ++-- .../messenger/services/XmppConnectionService.java | 67 +++++++++++++++++----- .../pixart/messenger/ui/ConversationActivity.java | 7 --- .../pixart/messenger/ui/ConversationFragment.java | 11 +--- .../de/pixart/messenger/ui/SettingsActivity.java | 3 + .../messenger/ui/adapter/MessageAdapter.java | 11 +++- src/main/res/values-de/strings.xml | 7 +++ src/main/res/values/arrays.xml | 20 +++++++ src/main/res/values/strings.xml | 3 + src/main/res/xml/preferences.xml | 12 ++-- 14 files changed, 141 insertions(+), 44 deletions(-) diff --git a/src/main/java/de/pixart/messenger/Config.java b/src/main/java/de/pixart/messenger/Config.java index fcd5948b0..eef639e40 100644 --- a/src/main/java/de/pixart/messenger/Config.java +++ b/src/main/java/de/pixart/messenger/Config.java @@ -125,6 +125,8 @@ public final class Config { public static final ChatState DEFAULT_CHATSTATE = ChatState.ACTIVE; public static final int TYPING_TIMEOUT = 5; + public static final int EXPIRY_INTERVAL = 30 * 60 * 1000; // 30 minutes + public static final String UPDATE_URL = BuildConfig.UPDATE_URL; public static final long UPDATE_CHECK_TIMER = 24 * 60 * 60; // in seconds diff --git a/src/main/java/de/pixart/messenger/entities/Conversation.java b/src/main/java/de/pixart/messenger/entities/Conversation.java index 0928c2ebf..526c60f6c 100644 --- a/src/main/java/de/pixart/messenger/entities/Conversation.java +++ b/src/main/java/de/pixart/messenger/entities/Conversation.java @@ -20,7 +20,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Locale; +import java.util.concurrent.atomic.AtomicBoolean; import de.pixart.messenger.Config; import de.pixart.messenger.crypto.PgpDecryptionService; @@ -90,6 +92,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl private String mLastReceivedOtrMessageId = null; private String mFirstMamReference = null; private Message correctingMessage; + public AtomicBoolean messagesLoaded = new AtomicBoolean(true); public boolean hasMessagesLeftOnServer() { return messagesLeftOnServer; @@ -943,6 +946,17 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl account.getPgpDecryptionService().decrypt(messages); } + public void expireOldMessages(long timestamp) { + synchronized (this.messages) { + for (ListIterator iterator = this.messages.listIterator(); iterator.hasNext(); ) { + if (iterator.next().getTimeSent() < timestamp) { + iterator.remove(); + } + } + untieMessages(); + } + } + public void sort() { synchronized (this.messages) { Collections.sort(this.messages, new Comparator() { diff --git a/src/main/java/de/pixart/messenger/parser/MessageParser.java b/src/main/java/de/pixart/messenger/parser/MessageParser.java index ec341eb37..b69b5312e 100644 --- a/src/main/java/de/pixart/messenger/parser/MessageParser.java +++ b/src/main/java/de/pixart/messenger/parser/MessageParser.java @@ -520,6 +520,12 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } + long deletionDate = mXmppConnectionService.getAutomaticMessageDeletionDate(); + if (deletionDate != 0 && message.getTimeSent() < deletionDate) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": skipping message from " + message.getCounterpart().toString() + " because it was sent prior to our deletion date"); + return; + } + boolean checkForDuplicates = query != null || (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay")) || message.getType() == Message.TYPE_PRIVATE; @@ -565,9 +571,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece conversation.endOtrIfNeeded(); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { - mXmppConnectionService.databaseBackend.createMessage(message); - } + mXmppConnectionService.databaseBackend.createMessage(message); final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); if (message.trusted() && message.treatAsDownloadable() != Message.Decision.NEVER && manager.getAutoAcceptFileSize() > 0) { manager.createNewDownloadConnection(message); diff --git a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java index bb4652308..b40452f1a 100644 --- a/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java +++ b/src/main/java/de/pixart/messenger/persistance/DatabaseBackend.java @@ -774,6 +774,13 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } + public boolean expireOldMessages(long timestamp) { + String where = Message.TIME_SENT + " 0; + } + public Pair getLastMessageReceived(Account account) { Cursor cursor = null; try { diff --git a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java index 4a1fe2a87..003f12991 100644 --- a/src/main/java/de/pixart/messenger/services/MessageArchiveService.java +++ b/src/main/java/de/pixart/messenger/services/MessageArchiveService.java @@ -56,6 +56,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { startCatchup = lastClearDate.first; reference = null; } + startCatchup = Math.max(startCatchup,mXmppConnectionService.getAutomaticMessageDeletionDate()); long endCatchup = account.getXmppConnection().getLastSessionEstablished(); final Query query; if (startCatchup == 0) { @@ -107,12 +108,14 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { public Query query(Conversation conversation, long start, long end) { synchronized (this.queries) { - if (start > end) { - return null; - } final Query query = new Query(conversation, start, end, PagingOrder.REVERSE); if (start == 0) { query.reference = conversation.getFirstMamReference(); + Log.d(Config.LOGTAG, "setting mam reference"); + } + query.start = Math.max(start, mXmppConnectionService.getAutomaticMessageDeletionDate()); + if (start > end) { + return null; } this.queries.add(query); this.execute(query); @@ -222,7 +225,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { query.getConversation().setFirstMamReference(first == null ? null : first.getContent()); } if (complete || relevant == null || abort) { - final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() == 0; + final boolean done = (complete || query.getMessageCount() == 0) && query.getStart() <= mXmppConnectionService.getAutomaticMessageDeletionDate(); this.finalizeQuery(query, done); Log.d(Config.LOGTAG, query.getAccount().getJid().toBareJid() + ": finished mam after " + query.getTotalCount() + " messages. messages left=" + Boolean.toString(!done)); if (query.getWith() == null && query.getMessageCount() > 0) { diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 1c7118f0f..55b87074a 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -69,6 +69,7 @@ import java.util.ListIterator; import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; import de.duenndns.ssl.MemorizingTrustManager; import de.pixart.messenger.Config; @@ -300,6 +301,7 @@ public class XmppConnectionService extends Service { } }; private int keyStatusUpdatedListenerCount = 0; + private AtomicLong mLastExpiryRun = new AtomicLong(0); private SecureRandom mRandom; private LruCache, ServiceDiscoveryResult> discoCache = new LruCache<>(20); private OnStatusChanged statusListener = new OnStatusChanged() { @@ -518,7 +520,6 @@ public class XmppConnectionService extends Service { if (!progressTracker.contains(p) && p != 100 && p != 0) { progressTracker.add(p); if (informableCallback != null) { - informableCallback.inform(getString(R.string.transcoding_video_progress, p)); } } @@ -560,7 +561,6 @@ public class XmppConnectionService extends Service { } else { processAsFile(); } - } }); } @@ -715,6 +715,9 @@ public class XmppConnectionService extends Service { } } } + if (SystemClock.elapsedRealtime() - mLastExpiryRun.get() >= Config.EXPIRY_INTERVAL) { + expireOldMessages(); + } return START_STICKY; } @@ -925,6 +928,33 @@ public class XmppConnectionService extends Service { } } + private void expireOldMessages() { + expireOldMessages(false); + } + + public void expireOldMessages(final boolean resetHasMessagesLeftOnServer) { + mLastExpiryRun.set(SystemClock.elapsedRealtime()); + mDatabaseExecutor.execute(new Runnable() { + @Override + public void run() { + long timestamp = getAutomaticMessageDeletionDate(); + if (timestamp > 0) { + databaseBackend.expireOldMessages(timestamp); + synchronized (XmppConnectionService.this.conversations) { + for (Conversation conversation : XmppConnectionService.this.conversations) { + conversation.expireOldMessages(timestamp); + if (resetHasMessagesLeftOnServer) { + conversation.messagesLoaded.set(true); + conversation.setHasMessagesLeftOnServer(true); + } + } + } + updateConversationUi(); + } + } + }); + } + public boolean hasInternetConnection() { ConnectivityManager cm = (ConnectivityManager) getApplicationContext() .getSystemService(Context.CONNECTIVITY_SERVICE); @@ -1338,12 +1368,10 @@ public class XmppConnectionService extends Service { if (addToConversation) { conversation.add(message); } - if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) { - if (saveInDb) { - databaseBackend.createMessage(message); - } else if (message.edited()) { - databaseBackend.updateMessage(message, message.getEditedId()); - } + if (saveInDb) { + databaseBackend.createMessage(message); + } else if (message.edited()) { + databaseBackend.updateMessage(message, message.getEditedId()); } updateConversationUi(); } @@ -1453,6 +1481,12 @@ public class XmppConnectionService extends Service { Runnable runnable = new Runnable() { @Override public void run() { + long deletionDate = getAutomaticMessageDeletionDate(); + mLastExpiryRun.set(SystemClock.elapsedRealtime()); + if (deletionDate > 0) { + Log.d(Config.LOGTAG, "deleting messages that are older than " + AbstractGenerator.getTimestamp(deletionDate)); + databaseBackend.expireOldMessages(deletionDate); + } Log.d(Config.LOGTAG, "restoring roster"); for (Account account : accounts) { databaseBackend.readRoster(account.getRoster()); @@ -1624,8 +1658,10 @@ public class XmppConnectionService extends Service { MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp); if (query != null) { query.setCallback(callback); + callback.informUser(R.string.fetching_history_from_server); + } else { + callback.informUser(R.string.not_fetching_history_retention_period); } - callback.informUser(R.string.fetching_history_from_server); } } } @@ -3152,6 +3188,15 @@ public class XmppConnectionService extends Service { .getDefaultSharedPreferences(getApplicationContext()); } + public long getAutomaticMessageDeletionDate() { + try { + final long timeout = Long.parseLong(getPreferences().getString(SettingsActivity.AUTOMATIC_MESSAGE_DELETION, "0")) * 1000; + return timeout == 0 ? timeout : System.currentTimeMillis() - timeout; + } catch (NumberFormatException e) { + return 0; + } + } + public boolean confirmMessages() { return getPreferences().getBoolean("confirm_messages", true); } @@ -3164,10 +3209,6 @@ public class XmppConnectionService extends Service { return getPreferences().getBoolean("chat_states", true); } - public boolean saveEncryptedMessages() { - return !getPreferences().getBoolean("dont_save_encrypted", false); - } - private boolean respectAutojoin() { return getPreferences().getBoolean("autojoin", true); } diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java index bc9c7ea1b..0dfd7bc14 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java @@ -2137,11 +2137,4 @@ public class ConversationActivity extends XmppActivity startActivity(intent); } } - - public void setMessagesLoaded() { - if (mConversationFragment != null) { - mConversationFragment.setMessagesLoaded(); - mConversationFragment.updateMessages(); - } - } } diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index c515aeb29..5b17e7179 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -118,7 +118,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private RelativeLayout snackbar; private TextView snackbarMessage; private TextView snackbarAction; - private boolean messagesLoaded = true; private Toast messageLoaderToast; private OnScrollListener mOnScrollListener = new OnScrollListener() { @@ -133,14 +132,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { synchronized (ConversationFragment.this.messageList) { - if (firstVisibleItem < 5 && messagesLoaded && messageList.size() > 0) { + if (firstVisibleItem < 5 && conversation != null && conversation.messagesLoaded.compareAndSet(true,false) && messageList.size() > 0) { long timestamp; if (messageList.get(0).getType() == Message.TYPE_STATUS && messageList.size() >= 2) { timestamp = messageList.get(1).getTimeSent(); } else { timestamp = messageList.get(0).getTimeSent(); } - messagesLoaded = false; activity.xmppConnectionService.loadMoreMessages(conversation, timestamp, new XmppConnectionService.OnMoreMessagesLoaded() { @Override public void onMoreMessagesLoaded(final int c, Conversation conversation) { @@ -169,7 +167,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa messageListAdapter.notifyDataSetChanged(); int pos = Math.max(getIndexOf(uuid, messageList), 0); messagesView.setSelectionFromTop(pos, pxOffset); - messagesLoaded = true; if (messageLoaderToast != null) { messageLoaderToast.cancel(); } @@ -387,10 +384,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa private ConversationActivity activity; private Message selectedMessage; - public void setMessagesLoaded() { - this.messagesLoaded = true; - } - private void sendMessage() { final String body = mEditMessage.getText().toString(); if (body.length() == 0 || this.conversation == null) { @@ -937,7 +930,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa messageListAdapter.updatePreferences(); this.messagesView.setAdapter(messageListAdapter); updateMessages(); - this.messagesLoaded = true; + this.conversation.messagesLoaded.set(true); synchronized (this.messageList) { final Message first = conversation.getFirstUnreadMessage(); final int bottom = Math.max(0, this.messageList.size() - 1); diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index 7c3131a0f..6624266f7 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -40,6 +40,7 @@ public class SettingsActivity extends XmppActivity implements public static final String TREAT_VIBRATE_AS_SILENT = "treat_vibrate_as_silent"; public static final String MANUALLY_CHANGE_PRESENCE = "manually_change_presence"; public static final String BLIND_TRUST_BEFORE_VERIFICATION = "btbv"; + public static final String AUTOMATIC_MESSAGE_DELETION = "automatic_message_deletion"; public static final int REQUEST_WRITE_LOGS = 0xbf8701; private SettingsFragment mSettingsFragment; @@ -263,6 +264,8 @@ public class SettingsActivity extends XmppActivity implements reconnectAccounts(); } else if (name.equals("use_tor")) { reconnectAccounts(); + } else if (name.equals(AUTOMATIC_MESSAGE_DELETION)) { + xmppConnectionService.expireOldMessages(true); } } 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 2f99b6b99..385fa184b 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/MessageAdapter.java @@ -62,6 +62,7 @@ import de.pixart.messenger.entities.Message; import de.pixart.messenger.entities.Message.FileParams; import de.pixart.messenger.entities.Transferable; import de.pixart.messenger.persistance.FileBackend; +import de.pixart.messenger.services.MessageArchiveService; import de.pixart.messenger.services.NotificationService; import de.pixart.messenger.ui.ConversationActivity; import de.pixart.messenger.ui.ShowFullscreenMessageActivity; @@ -679,9 +680,13 @@ public class MessageAdapter extends ArrayAdapter implements CopyTextVie if (timestamp == 0) { timestamp = System.currentTimeMillis(); } - activity.setMessagesLoaded(); - activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp); - Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show(); + conversation.messagesLoaded.set(true); + MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp); + if (query != null) { + Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(activity, R.string.not_fetching_history_retention_period, Toast.LENGTH_SHORT).show(); + } } @Override diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index f3af24fa5..b3f808120 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -681,5 +681,12 @@ Gerät misstrauen Bist du sicher, dass du die Verifizierung dieses Gerätes misstrauen möchtest? Dieses Gerät und Nachrichten von dem Gerät werden als unverifiziert markiert. Video wird komprimiert (%s%% fertiggestellt) + Nachricht wird verschlüsselt + Nachrichten automatisch löschen + Nachrichten von diesel Gerät nach dem eingestellten Zeitfenster automatisch löschen. + 24 Studnen + 30 Tage + 6 Monate + 7 Tage diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 821fd7068..a4b20e3ab 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -84,4 +84,24 @@ @string/presence_dnd + + 0 + 86400 + 604800 + 2592000 + 15811200 + + + + @string/never + @string/timeout_24_hours + @string/timeout_7_days + @string/timeout_30_days + @string/timeout_6_months + + + 24 hours + 7 days + 30 days + 6 months diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 9b40b97c3..738b2a441 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -727,4 +727,7 @@ Reply Encrypting message Compressing video (%s%% completed) + Automatically delete messages from this device that are older than the configured time frame. + Automatic message deletion + Overstepping local retention period. diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 92078afc0..05d4551fb 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -225,11 +225,13 @@ android:key="btbv" android:title="@string/pref_blind_trust_before_verification" android:summary="@string/pref_blind_trust_before_verification_summary" /> - +