Recycler.java revision 30523f5908dc28f7686bbc1363b20ea77ee29ab9
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.mms.util; 18 19import com.android.mms.MmsConfig; 20import com.android.mms.ui.MessageUtils; 21import com.android.mms.ui.MessagingPreferenceActivity; 22import com.google.android.mms.util.SqliteWrapper; 23 24import android.content.ContentResolver; 25import android.content.ContentUris; 26import android.content.Context; 27import android.content.SharedPreferences; 28import android.database.Cursor; 29import android.net.Uri; 30import android.preference.PreferenceManager; 31import android.provider.BaseColumns; 32import android.provider.Telephony; 33import android.provider.Telephony.Mms; 34import android.provider.Telephony.Sms; 35import android.provider.Telephony.Sms.Conversations; 36import android.util.Log; 37 38/** 39 * The recycler is responsible for deleting old messages. 40 */ 41public abstract class Recycler { 42 private static final boolean LOCAL_DEBUG = false; 43 private static final String TAG = "Recycler"; 44 45 // Default preference values 46 private static final boolean DEFAULT_AUTO_DELETE = false; 47 48 private static Recycler sSmsRecycler; 49 private static Recycler sMmsRecycler; 50 51 public static Recycler getSmsRecycler() { 52 if (sSmsRecycler == null) { 53 sSmsRecycler = new SmsRecycler(); 54 } 55 return sSmsRecycler; 56 } 57 58 public static Recycler getMmsRecycler() { 59 if (sMmsRecycler == null) { 60 sMmsRecycler = new MmsRecycler(); 61 } 62 return sMmsRecycler; 63 } 64 65 public static boolean checkForThreadsOverLimit(Context context) { 66 Recycler smsRecycler = getSmsRecycler(); 67 Recycler mmsRecycler = getMmsRecycler(); 68 69 return smsRecycler.anyThreadOverLimit(context) || mmsRecycler.anyThreadOverLimit(context); 70 } 71 72 public void deleteOldMessages(Context context) { 73 if (LOCAL_DEBUG) { 74 Log.v(TAG, "Recycler.deleteOldMessages this: " + this); 75 } 76 if (!isAutoDeleteEnabled(context)) { 77 return; 78 } 79 80 Cursor cursor = getAllThreads(context); 81 int limit = getMessageLimit(context); 82 try { 83 while (cursor.moveToNext()) { 84 long threadId = getThreadId(cursor); 85 deleteMessagesForThread(context, threadId, limit); 86 } 87 } finally { 88 cursor.close(); 89 } 90 } 91 92 public void deleteOldMessagesByThreadId(Context context, long threadId) { 93 if (LOCAL_DEBUG) { 94 Log.v(TAG, "Recycler.deleteOldMessagesByThreadId this: " + this + 95 " threadId: " + threadId); 96 } 97 if (!isAutoDeleteEnabled(context)) { 98 return; 99 } 100 101 deleteMessagesForThread(context, threadId, getMessageLimit(context)); 102 } 103 104 public static boolean isAutoDeleteEnabled(Context context) { 105 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 106 return prefs.getBoolean(MessagingPreferenceActivity.AUTO_DELETE, 107 DEFAULT_AUTO_DELETE); 108 } 109 110 abstract public int getMessageLimit(Context context); 111 112 abstract public void setMessageLimit(Context context, int limit); 113 114 public int getMessageMinLimit() { 115 return MmsConfig.getMinMessageCountPerThread(); 116 } 117 118 public int getMessageMaxLimit() { 119 return MmsConfig.getMaxMessageCountPerThread(); 120 } 121 122 abstract protected long getThreadId(Cursor cursor); 123 124 abstract protected Cursor getAllThreads(Context context); 125 126 abstract protected void deleteMessagesForThread(Context context, long threadId, int keep); 127 128 abstract protected void dumpMessage(Cursor cursor, Context context); 129 130 abstract protected boolean anyThreadOverLimit(Context context); 131 132 static class SmsRecycler extends Recycler { 133 private static final String[] ALL_SMS_THREADS_PROJECTION = { 134 Telephony.Sms.Conversations.THREAD_ID, Telephony.Sms.Conversations.MESSAGE_COUNT 135 }; 136 137 private static final int ID = 0; 138 private static final int MESSAGE_COUNT = 1; 139 140 static private final String[] SMS_MESSAGE_PROJECTION = new String[] { 141 BaseColumns._ID, 142 Conversations.THREAD_ID, 143 Sms.ADDRESS, 144 Sms.BODY, 145 Sms.DATE, 146 Sms.READ, 147 Sms.TYPE, 148 Sms.STATUS, 149 }; 150 151 // The indexes of the default columns which must be consistent 152 // with above PROJECTION. 153 static private final int COLUMN_ID = 0; 154 static private final int COLUMN_THREAD_ID = 1; 155 static private final int COLUMN_SMS_ADDRESS = 2; 156 static private final int COLUMN_SMS_BODY = 3; 157 static private final int COLUMN_SMS_DATE = 4; 158 static private final int COLUMN_SMS_READ = 5; 159 static private final int COLUMN_SMS_TYPE = 6; 160 static private final int COLUMN_SMS_STATUS = 7; 161 162 private final String MAX_SMS_MESSAGES_PER_THREAD = "MaxSmsMessagesPerThread"; 163 164 public int getMessageLimit(Context context) { 165 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 166 return prefs.getInt(MAX_SMS_MESSAGES_PER_THREAD, 167 MmsConfig.getDefaultSMSMessagesPerThread()); 168 } 169 170 public void setMessageLimit(Context context, int limit) { 171 SharedPreferences.Editor editPrefs = 172 PreferenceManager.getDefaultSharedPreferences(context).edit(); 173 editPrefs.putInt(MAX_SMS_MESSAGES_PER_THREAD, limit); 174 editPrefs.commit(); 175 } 176 177 protected long getThreadId(Cursor cursor) { 178 return cursor.getLong(ID); 179 } 180 181 protected Cursor getAllThreads(Context context) { 182 ContentResolver resolver = context.getContentResolver(); 183 Cursor cursor = SqliteWrapper.query(context, resolver, 184 Telephony.Sms.Conversations.CONTENT_URI, 185 ALL_SMS_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER); 186 187 return cursor; 188 } 189 190 protected void deleteMessagesForThread(Context context, long threadId, int keep) { 191 if (LOCAL_DEBUG) { 192 Log.v(TAG, "SMS: deleteMessagesForThread"); 193 } 194 ContentResolver resolver = context.getContentResolver(); 195 Cursor cursor = SqliteWrapper.query(context, resolver, 196 ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId), 197 SMS_MESSAGE_PROJECTION, 198 "locked=0", 199 null, "date DESC"); // get in newest to oldest order 200 201 int count = cursor.getCount(); 202 int numberToDelete = count - keep; 203 if (LOCAL_DEBUG) { 204 Log.v(TAG, "SMS: deleteMessagesForThread keep: " + keep + 205 " count: " + count + 206 " numberToDelete: " + numberToDelete); 207 } 208 if (numberToDelete <= 0) { 209 return; 210 } 211 try { 212 // Move to the keep limit and then delete everything older than that one. 213 cursor.move(keep); 214 long latestDate = cursor.getLong(COLUMN_SMS_DATE); 215 216 long cntDeleted = SqliteWrapper.delete(context, resolver, 217 ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId), 218 "locked=0 AND date<" + latestDate, 219 null); 220 if (LOCAL_DEBUG) { 221 Log.v(TAG, "SMS: deleteMessagesForThread cntDeleted: " + cntDeleted); 222 } 223 } finally { 224 cursor.close(); 225 } 226 } 227 228 protected void dumpMessage(Cursor cursor, Context context) { 229 long date = cursor.getLong(COLUMN_SMS_DATE); 230 String dateStr = MessageUtils.formatTimeStampString(context, date, true); 231 if (LOCAL_DEBUG) { 232 Log.v(TAG, "Recycler message " + 233 "\n address: " + cursor.getString(COLUMN_SMS_ADDRESS) + 234 "\n body: " + cursor.getString(COLUMN_SMS_BODY) + 235 "\n date: " + dateStr + 236 "\n date: " + date + 237 "\n read: " + cursor.getInt(COLUMN_SMS_READ)); 238 } 239 } 240 241 @Override 242 protected boolean anyThreadOverLimit(Context context) { 243 Cursor cursor = getAllThreads(context); 244 int limit = getMessageLimit(context); 245 try { 246 while (cursor.moveToNext()) { 247 long threadId = getThreadId(cursor); 248 ContentResolver resolver = context.getContentResolver(); 249 Cursor msgs = SqliteWrapper.query(context, resolver, 250 ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId), 251 SMS_MESSAGE_PROJECTION, 252 "locked=0", 253 null, "date DESC"); // get in newest to oldest order 254 255 if (msgs.getCount() >= limit) { 256 return true; 257 } 258 } 259 } finally { 260 cursor.close(); 261 } 262 return false; 263 } 264 } 265 266 static class MmsRecycler extends Recycler { 267 private static final String[] ALL_MMS_THREADS_PROJECTION = { 268 "thread_id", "count(*) as msg_count" 269 }; 270 271 private static final int ID = 0; 272 private static final int MESSAGE_COUNT = 1; 273 274 static private final String[] MMS_MESSAGE_PROJECTION = new String[] { 275 BaseColumns._ID, 276 Conversations.THREAD_ID, 277 Mms.DATE, 278 }; 279 280 // The indexes of the default columns which must be consistent 281 // with above PROJECTION. 282 static private final int COLUMN_ID = 0; 283 static private final int COLUMN_THREAD_ID = 1; 284 static private final int COLUMN_MMS_DATE = 2; 285 static private final int COLUMN_MMS_READ = 3; 286 287 private final String MAX_MMS_MESSAGES_PER_THREAD = "MaxMmsMessagesPerThread"; 288 289 public int getMessageLimit(Context context) { 290 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); 291 return prefs.getInt(MAX_MMS_MESSAGES_PER_THREAD, 292 MmsConfig.getDefaultMMSMessagesPerThread()); 293 } 294 295 public void setMessageLimit(Context context, int limit) { 296 SharedPreferences.Editor editPrefs = 297 PreferenceManager.getDefaultSharedPreferences(context).edit(); 298 editPrefs.putInt(MAX_MMS_MESSAGES_PER_THREAD, limit); 299 editPrefs.commit(); 300 } 301 302 protected long getThreadId(Cursor cursor) { 303 return cursor.getLong(ID); 304 } 305 306 protected Cursor getAllThreads(Context context) { 307 ContentResolver resolver = context.getContentResolver(); 308 Cursor cursor = SqliteWrapper.query(context, resolver, 309 Uri.withAppendedPath(Telephony.Mms.CONTENT_URI, "threads"), 310 ALL_MMS_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER); 311 312 return cursor; 313 } 314 315 protected void deleteMessagesForThread(Context context, long threadId, int keep) { 316 if (LOCAL_DEBUG) { 317 Log.v(TAG, "MMS: deleteMessagesForThread"); 318 } 319 ContentResolver resolver = context.getContentResolver(); 320 Cursor cursor = SqliteWrapper.query(context, resolver, 321 Telephony.Mms.CONTENT_URI, 322 MMS_MESSAGE_PROJECTION, 323 "thread_id=" + threadId + " AND locked=0", 324 null, "date DESC"); // get in newest to oldest order 325 326 int count = cursor.getCount(); 327 int numberToDelete = count - keep; 328 if (LOCAL_DEBUG) { 329 Log.v(TAG, "MMS: deleteMessagesForThread keep: " + keep + 330 " count: " + count + 331 " numberToDelete: " + numberToDelete); 332 } 333 if (numberToDelete <= 0) { 334 return; 335 } 336 try { 337 // Move to the keep limit and then delete everything older than that one. 338 cursor.move(keep); 339 long latestDate = cursor.getLong(COLUMN_MMS_DATE); 340 341 long cntDeleted = SqliteWrapper.delete(context, resolver, 342 Telephony.Mms.CONTENT_URI, 343 "thread_id=" + threadId + " AND locked=0 AND date<" + latestDate, 344 null); 345 if (LOCAL_DEBUG) { 346 Log.v(TAG, "MMS: deleteMessagesForThread cntDeleted: " + cntDeleted); 347 } 348 } finally { 349 cursor.close(); 350 } 351 } 352 353 protected void dumpMessage(Cursor cursor, Context context) { 354 long id = cursor.getLong(COLUMN_ID); 355 if (LOCAL_DEBUG) { 356 Log.v(TAG, "Recycler message " + 357 "\n id: " + id 358 ); 359 } 360 } 361 362 @Override 363 protected boolean anyThreadOverLimit(Context context) { 364 Cursor cursor = getAllThreads(context); 365 int limit = getMessageLimit(context); 366 try { 367 while (cursor.moveToNext()) { 368 long threadId = getThreadId(cursor); 369 ContentResolver resolver = context.getContentResolver(); 370 Cursor msgs = SqliteWrapper.query(context, resolver, 371 Telephony.Mms.CONTENT_URI, 372 MMS_MESSAGE_PROJECTION, 373 "thread_id=" + threadId + " AND locked=0", 374 null, "date DESC"); // get in newest to oldest order 375 376 if (msgs.getCount() >= limit) { 377 return true; 378 } 379 } 380 } finally { 381 cursor.close(); 382 } 383 return false; 384 } 385 } 386 387} 388 389 390