Skip to content

Commit fe43ef6

Browse files
committed
Support for auto-deleting old messages beyond a certain conversation thread length.
1 parent a185750 commit fe43ef6

File tree

7 files changed

+319
-16
lines changed

7 files changed

+319
-16
lines changed

res/values/strings.xml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@
3939
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">You need to have entered your passphrase before importing keys...</string>
4040
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">You need to have entered your passphrase before managing keys...</string>
4141
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">You haven\'t set a passphrase yet!</string>
42-
42+
<string name="ApplicationPreferencesActivity_conversation_length_limit">Conversation length limit</string>
43+
<string name="ApplicationPreferencesActivity_messages_per_conversation">messages per conversation</string>
44+
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Delete all old messages now?</string>
45+
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Are you sure you would like to immediately trim all conversation threads to the %s most recent messages?</string>
46+
<string name="ApplicationPreferencesActivity_delete">Delete</string>
47+
4348
<!-- AttachmentTypeSelectorAdapter -->
4449

4550
<string name="AttachmentTypeSelectorAdapter_picture">Picture</string>
@@ -377,6 +382,17 @@
377382
<string name="preferences__mmsc_url_required">MMSC URL (Required)</string>
378383
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (Optional)</string>
379384
<string name="preferences__mms_proxy_port_optional">MMS Proxy Port (Optional)</string>
385+
<string name="preferences__delivery_reports">Delivery Reports</string>
386+
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Request a delivery report for each SMS message you send</string>
387+
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Request a delivery report for each MMS message you send</string>
388+
<string name="preferences__mms_delivery_reports">MMS delivery reports</string>
389+
<string name="preferences__sms_delivery_reports">SMS delivery reports</string>
390+
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automatically delete older messages once a conversation thread exceeds a specified length</string>
391+
<string name="preferences__delete_old_messages">Delete old messages</string>
392+
<string name="preferences__storage">Storage</string>
393+
<string name="preferences__conversation_length_limit">Conversation length limit</string>
394+
<string name="preferences__trim_all_threads_now">Trim all threads now</string>
395+
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Scan through all conversation threads and enforce conversation length limits</string>
380396

381397

382398
<!-- **************************************** -->

res/xml/preferences.xml

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,36 @@
1414

1515
</PreferenceCategory>
1616

17-
<PreferenceCategory android:title="Delivery Reports">
17+
<PreferenceCategory android:title="@string/preferences__delivery_reports">
1818
<CheckBoxPreference android:defaultValue="false"
1919
android:key="pref_delivery_report_sms"
20-
android:summary="Request a delivery report for each SMS message you send"
21-
android:title="SMS delivery reports" />
20+
android:summary="@string/preferences__request_a_delivery_report_for_each_sms_message_you_send"
21+
android:title="@string/preferences__sms_delivery_reports" />
2222

2323
<CheckBoxPreference android:defaultValue="false"
2424
android:key="pref_delivery_report_mms"
25-
android:summary="Request a delivery report for each MMS message you send"
25+
android:summary="@string/preferences__request_a_delivery_report_for_each_mms_message_you_send"
2626
android:enabled="false"
27-
android:title="MMS delivery reports" />
27+
android:title="@string/preferences__mms_delivery_reports" />
28+
</PreferenceCategory>
29+
30+
<PreferenceCategory android:title="@string/preferences__storage">
31+
<CheckBoxPreference android:defaultValue="false"
32+
android:key="pref_trim_threads"
33+
android:summary="@string/preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length"
34+
android:title="@string/preferences__delete_old_messages" />
35+
36+
<EditTextPreference android:defaultValue="500"
37+
android:key="pref_trim_length"
38+
android:title="@string/preferences__conversation_length_limit"
39+
android:inputType="number"
40+
android:dependency="pref_trim_threads" />
41+
42+
<Preference android:key="pref_trim_now"
43+
android:title="@string/preferences__trim_all_threads_now"
44+
android:summary="@string/preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits"
45+
android:dependency="pref_trim_threads" />
46+
2847
</PreferenceCategory>
2948

3049
<PreferenceCategory android:title="@string/preferences__input_settings">

src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.thoughtcrime.securesms;
1818

1919
import android.app.Activity;
20+
import android.app.AlertDialog;
21+
import android.content.DialogInterface;
2022
import android.content.Intent;
2123
import android.content.SharedPreferences;
2224
import android.net.Uri;
@@ -25,6 +27,7 @@
2527
import android.preference.Preference;
2628
import android.preference.PreferenceManager;
2729
import android.provider.ContactsContract;
30+
import android.util.Log;
2831
import android.widget.Toast;
2932

3033
import com.actionbarsherlock.app.SherlockPreferenceActivity;
@@ -38,6 +41,7 @@
3841
import org.thoughtcrime.securesms.service.KeyCachingService;
3942
import org.thoughtcrime.securesms.util.Dialogs;
4043
import org.thoughtcrime.securesms.util.MemoryCleaner;
44+
import org.thoughtcrime.securesms.util.Trimmer;
4145

4246
import java.util.List;
4347

@@ -83,6 +87,10 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
8387
public static final String SMS_DELIVERY_REPORT_PREF = "pref_delivery_report_sms";
8488
public static final String MMS_DELIVERY_REPORT_PREF = "pref_delivery_report_mms";
8589

90+
public static final String THREAD_TRIM_ENABLED = "pref_trim_threads";
91+
public static final String THREAD_TRIM_LENGTH = "pref_trim_length";
92+
public static final String THREAD_TRIM_NOW = "pref_trim_now";
93+
8694
@Override
8795
protected void onCreate(Bundle icicle) {
8896
super.onCreate(icicle);
@@ -104,6 +112,10 @@ protected void onCreate(Bundle icicle) {
104112
.setOnPreferenceClickListener(new ManageIdentitiesClickListener());
105113
this.findPreference(CHANGE_PASSPHRASE_PREF)
106114
.setOnPreferenceClickListener(new ChangePassphraseClickListener());
115+
this.findPreference(THREAD_TRIM_NOW)
116+
.setOnPreferenceClickListener(new TrimNowClickListener());
117+
this.findPreference(THREAD_TRIM_LENGTH)
118+
.setOnPreferenceChangeListener(new TrimLengthValidationListener());
107119
}
108120

109121
@Override
@@ -159,20 +171,16 @@ private void initializeEditTextSummary(final EditTextPreference preference) {
159171
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
160172
@Override
161173
public boolean onPreferenceChange(Preference pref, Object newValue) {
162-
preference.setSummary(newValue == null ? "Not set" : (String)newValue);
174+
preference.setSummary(newValue == null ? "Not set" : ((String)newValue));
163175
return true;
164176
}
165177
});
166178
}
167179

168180
private void initializeEditTextSummaries() {
169-
final EditTextPreference mmscUrlPreference = (EditTextPreference)this.findPreference(MMSC_HOST_PREF);
170-
final EditTextPreference mmsProxyHostPreference = (EditTextPreference)this.findPreference(MMSC_PROXY_HOST_PREF);
171-
final EditTextPreference mmsProxyPortPreference = (EditTextPreference)this.findPreference(MMSC_PROXY_PORT_PREF);
172-
173-
initializeEditTextSummary(mmscUrlPreference);
174-
initializeEditTextSummary(mmsProxyHostPreference);
175-
initializeEditTextSummary(mmsProxyPortPreference);
181+
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_HOST_PREF));
182+
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_PROXY_HOST_PREF));
183+
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_PROXY_PORT_PREF));
176184
}
177185

178186
private void initializeIdentitySelection() {
@@ -330,4 +338,56 @@ public boolean onPreferenceClick(Preference preference) {
330338
}
331339
}
332340

341+
private class TrimNowClickListener implements Preference.OnPreferenceClickListener {
342+
@Override
343+
public boolean onPreferenceClick(Preference preference) {
344+
final int threadLengthLimit = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(ApplicationPreferencesActivity.this)
345+
.getString(THREAD_TRIM_LENGTH, "500"));
346+
347+
AlertDialog.Builder builder = new AlertDialog.Builder(ApplicationPreferencesActivity.this);
348+
builder.setTitle(R.string.ApplicationPreferencesActivity_delete_all_old_messages_now);
349+
builder.setMessage(String.format(getString(R.string.ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages),
350+
threadLengthLimit));
351+
builder.setPositiveButton(R.string.ApplicationPreferencesActivity_delete,
352+
new DialogInterface.OnClickListener() {
353+
@Override
354+
public void onClick(DialogInterface dialog, int which) {
355+
Trimmer.trimAllThreads(ApplicationPreferencesActivity.this, threadLengthLimit);
356+
}
357+
});
358+
359+
builder.setNegativeButton(android.R.string.cancel, null);
360+
builder.show();
361+
362+
return true;
363+
}
364+
}
365+
366+
private class TrimLengthValidationListener implements Preference.OnPreferenceChangeListener {
367+
368+
public TrimLengthValidationListener() {
369+
EditTextPreference preference = (EditTextPreference)findPreference(THREAD_TRIM_LENGTH);
370+
preference.setSummary(preference.getText() + " " + getString(R.string.ApplicationPreferencesActivity_messages_per_conversation));
371+
}
372+
373+
@Override
374+
public boolean onPreferenceChange(Preference preference, Object newValue) {
375+
if (newValue == null || ((String)newValue).trim().length() == 0) {
376+
return false;
377+
}
378+
379+
try {
380+
Integer.parseInt((String)newValue);
381+
} catch (NumberFormatException nfe) {
382+
Log.w("ApplicationPreferencesActivity", nfe);
383+
return false;
384+
}
385+
386+
preference.setSummary((String)newValue + " " +
387+
getString(R.string.ApplicationPreferencesActivity_messages_per_conversation));
388+
return true;
389+
}
390+
391+
}
392+
333393
}

src/org/thoughtcrime/securesms/database/MmsDatabase.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.thoughtcrime.securesms.recipients.RecipientFactory;
2929
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
3030
import org.thoughtcrime.securesms.recipients.Recipients;
31+
import org.thoughtcrime.securesms.util.Trimmer;
3132

3233
import ws.com.google.android.mms.InvalidHeaderValueException;
3334
import ws.com.google.android.mms.MmsException;
@@ -343,6 +344,7 @@ public long insertMessageReceived(NotificationInd notification) {
343344
notifyConversationListeners(threadId);
344345
DatabaseFactory.getThreadDatabase(context).update(threadId);
345346
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
347+
Trimmer.trimThread(context, threadId);
346348

347349
return messageId;
348350
} catch (RecipientFormattingException rfe) {
@@ -364,6 +366,8 @@ public long insertMessageSent(SendReq sendRequest, long threadId, boolean isSecu
364366

365367
long messageId = insertMediaMessage(sendRequest, contentValues);
366368
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
369+
Trimmer.trimThread(context, threadId);
370+
367371
return messageId;
368372
}
369373

@@ -427,6 +431,35 @@ public void deleteThread(long threadId) {
427431
}
428432
}
429433

434+
/*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) {
435+
date = date / 1000;
436+
Cursor cursor = null;
437+
438+
try {
439+
SQLiteDatabase db = databaseHelper.getReadableDatabase();
440+
String where = THREAD_ID + " = ? AND (CASE " + MESSAGE_BOX;
441+
442+
for (int outgoingType : Types.OUTGOING_MAILBOX_TYPES) {
443+
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
444+
}
445+
446+
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
447+
448+
Log.w("MmsDatabase", "Executing trim query: " + where);
449+
cursor = db.query(TABLE_NAME, new String[] {ID}, where, new String[] {threadId+""}, null, null, null);
450+
451+
while (cursor != null && cursor.moveToNext()) {
452+
Log.w("MmsDatabase", "Trimming: " + cursor.getLong(0));
453+
delete(cursor.getLong(0));
454+
}
455+
456+
} finally {
457+
if (cursor != null)
458+
cursor.close();
459+
}
460+
}
461+
462+
430463
public void deleteAllThreads() {
431464
DatabaseFactory.getPartDatabase(context).deleteAllParts();
432465
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
@@ -553,12 +586,23 @@ public static class Types {
553586
public static final int DOWNLOAD_SOFT_FAILURE = 4;
554587
public static final int DOWNLOAD_HARD_FAILURE = 5;
555588

589+
public static final int[] OUTGOING_MAILBOX_TYPES = {Types.MESSAGE_BOX_OUTBOX,
590+
Types.MESSAGE_BOX_SENT,
591+
Types.MESSAGE_BOX_SECURE_OUTBOX,
592+
Types.MESSAGE_BOX_SENT_FAILED,
593+
Types.MESSAGE_BOX_SECURE_SENT};
594+
556595
public static boolean isSecureMmsBox(long mailbox) {
557596
return mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SECURE_SENT || mailbox == Types.MESSAGE_BOX_SECURE_INBOX;
558597
}
559598

560599
public static boolean isOutgoingMmsBox(long mailbox) {
561-
return mailbox == Types.MESSAGE_BOX_OUTBOX || mailbox == Types.MESSAGE_BOX_SENT || mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SENT_FAILED || mailbox == Types.MESSAGE_BOX_SECURE_SENT;
600+
for (int outgoingMailboxType : OUTGOING_MAILBOX_TYPES) {
601+
if (mailbox == outgoingMailboxType)
602+
return true;
603+
}
604+
605+
return false;
562606
}
563607

564608
public static boolean isPendingMmsBox(long mailbox) {

src/org/thoughtcrime/securesms/database/SmsDatabase.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import org.thoughtcrime.securesms.recipients.Recipient;
2929
import org.thoughtcrime.securesms.recipients.Recipients;
30+
import org.thoughtcrime.securesms.util.Trimmer;
3031

3132
import java.util.ArrayList;
3233
import java.util.List;
@@ -112,6 +113,8 @@ private long insertMessageReceived(SmsMessage message, String body, long type, l
112113
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
113114
DatabaseFactory.getThreadDatabase(context).update(threadId);
114115
notifyConversationListeners(threadId);
116+
Trimmer.trimThread(context, threadId);
117+
115118
return messageId;
116119
}
117120

@@ -235,6 +238,8 @@ public long insertMessageSent(String address, long threadId, String body, long d
235238
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
236239
DatabaseFactory.getThreadDatabase(context).update(threadId);
237240
notifyConversationListeners(threadId);
241+
Trimmer.trimThread(context, threadId);
242+
238243
return messageId;
239244
}
240245

@@ -276,6 +281,18 @@ public void deleteMessage(long messageId) {
276281
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
277282
}
278283

284+
/*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) {
285+
SQLiteDatabase db = databaseHelper.getWritableDatabase();
286+
String where = THREAD_ID + " = ? AND (CASE " + TYPE;
287+
288+
for (int outgoingType : Types.OUTGOING_MESSAGE_TYPES) {
289+
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
290+
}
291+
292+
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
293+
294+
db.delete(TABLE_NAME, where, new String[] {threadId+""});
295+
}
279296

280297
/*package*/ void deleteThreads(Set<Long> threadIds) {
281298
SQLiteDatabase db = databaseHelper.getWritableDatabase();
@@ -348,12 +365,21 @@ public static class Types {
348365
public static final int DECRYPT_IN_PROGRESS_TYPE = 47; // Messages are in the process of being asymmetricaly decrypted.
349366
public static final int NO_SESSION_TYPE = 48; // Messages were received with async encryption but there is no session yet.
350367

368+
public static final int[] OUTGOING_MESSAGE_TYPES = {SENT_TYPE, SENT_PENDING, ENCRYPTING_TYPE,
369+
ENCRYPTED_OUTBOX_TYPE, SECURE_SENT_TYPE,
370+
FAILED_TYPE};
371+
351372
public static boolean isFailedMessageType(long type) {
352373
return type == FAILED_TYPE;
353374
}
354375

355376
public static boolean isOutgoingMessageType(long type) {
356-
return type == SENT_TYPE || type == SENT_PENDING || type == ENCRYPTING_TYPE || type == ENCRYPTED_OUTBOX_TYPE || type == SECURE_SENT_TYPE || type == FAILED_TYPE;
377+
for (int outgoingType : OUTGOING_MESSAGE_TYPES) {
378+
if (type == outgoingType)
379+
return true;
380+
}
381+
382+
return false;
357383
}
358384

359385
public static boolean isPendingMessageType(long type) {

0 commit comments

Comments
 (0)