1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mms.ui; 19 20 import com.android.mms.R; 21 import com.google.android.mms.util.SqliteWrapper; 22 import com.android.mms.transaction.MessagingNotification; 23 24 import android.app.Activity; 25 import android.app.AlertDialog; 26 27 import android.content.AsyncQueryHandler; 28 import android.content.ContentResolver; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.content.DialogInterface.OnClickListener; 32 import android.database.ContentObserver; 33 import android.database.Cursor; 34 import android.database.sqlite.SQLiteException; 35 import android.net.Uri; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.provider.Telephony.Sms; 39 import android.provider.Telephony.Threads; 40 import android.telephony.SmsManager; 41 import android.util.Log; 42 import android.view.ContextMenu; 43 import android.view.Menu; 44 import android.view.MenuItem; 45 import android.view.View; 46 import android.view.Window; 47 import android.widget.AdapterView; 48 import android.widget.ListView; 49 import android.widget.TextView; 50 51 /** 52 * Displays a list of the SMS messages stored on the ICC. 53 */ 54 public class ManageSimMessages extends Activity 55 implements View.OnCreateContextMenuListener { 56 private static final Uri ICC_URI = Uri.parse("content://sms/icc"); 57 private static final String TAG = "ManageSimMessages"; 58 private static final int MENU_COPY_TO_PHONE_MEMORY = 0; 59 private static final int MENU_DELETE_FROM_SIM = 1; 60 private static final int MENU_VIEW = 2; 61 private static final int OPTION_MENU_DELETE_ALL = 0; 62 63 private static final int SHOW_LIST = 0; 64 private static final int SHOW_EMPTY = 1; 65 private static final int SHOW_BUSY = 2; 66 private int mState; 67 68 69 private ContentResolver mContentResolver; 70 private Cursor mCursor = null; 71 private ListView mSimList; 72 private TextView mMessage; 73 private MessageListAdapter mListAdapter = null; 74 private AsyncQueryHandler mQueryHandler = null; 75 76 public static final int SIM_FULL_NOTIFICATION_ID = 234; 77 78 private final ContentObserver simChangeObserver = 79 new ContentObserver(new Handler()) { 80 @Override 81 public void onChange(boolean selfUpdate) { 82 refreshMessageList(); 83 } 84 }; 85 86 @Override onCreate(Bundle icicle)87 protected void onCreate(Bundle icicle) { 88 super.onCreate(icicle); 89 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 90 91 mContentResolver = getContentResolver(); 92 mQueryHandler = new QueryHandler(mContentResolver, this); 93 setContentView(R.layout.sim_list); 94 mSimList = (ListView) findViewById(R.id.messages); 95 mMessage = (TextView) findViewById(R.id.empty_message); 96 97 init(); 98 } 99 100 @Override onNewIntent(Intent intent)101 protected void onNewIntent(Intent intent) { 102 setIntent(intent); 103 104 init(); 105 } 106 init()107 private void init() { 108 MessagingNotification.cancelNotification(getApplicationContext(), 109 SIM_FULL_NOTIFICATION_ID); 110 111 updateState(SHOW_BUSY); 112 startQuery(); 113 } 114 115 private class QueryHandler extends AsyncQueryHandler { 116 private final ManageSimMessages mParent; 117 QueryHandler( ContentResolver contentResolver, ManageSimMessages parent)118 public QueryHandler( 119 ContentResolver contentResolver, ManageSimMessages parent) { 120 super(contentResolver); 121 mParent = parent; 122 } 123 124 @Override onQueryComplete( int token, Object cookie, Cursor cursor)125 protected void onQueryComplete( 126 int token, Object cookie, Cursor cursor) { 127 mCursor = cursor; 128 if (mCursor != null) { 129 if (!mCursor.moveToFirst()) { 130 // Let user know the SIM is empty 131 updateState(SHOW_EMPTY); 132 } else if (mListAdapter == null) { 133 mListAdapter = new MessageListAdapter( 134 mParent, mCursor, mSimList, false, 135 Threads.COMMON_THREAD); 136 mSimList.setAdapter(mListAdapter); 137 mSimList.setOnCreateContextMenuListener(mParent); 138 updateState(SHOW_LIST); 139 } else { 140 mListAdapter.changeCursor(mCursor); 141 updateState(SHOW_LIST); 142 } 143 startManagingCursor(mCursor); 144 registerSimChangeObserver(); 145 } 146 } 147 } 148 startQuery()149 private void startQuery() { 150 try { 151 mQueryHandler.startQuery(0, null, ICC_URI, null, null, null, null); 152 } catch (SQLiteException e) { 153 SqliteWrapper.checkSQLiteException(this, e); 154 } 155 } 156 refreshMessageList()157 private void refreshMessageList() { 158 updateState(SHOW_BUSY); 159 if (mCursor != null) { 160 stopManagingCursor(mCursor); 161 mCursor.close(); 162 } 163 startQuery(); 164 } 165 166 @Override onCreateContextMenu( ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)167 public void onCreateContextMenu( 168 ContextMenu menu, View v, 169 ContextMenu.ContextMenuInfo menuInfo) { 170 menu.add(0, MENU_COPY_TO_PHONE_MEMORY, 0, 171 R.string.sim_copy_to_phone_memory); 172 menu.add(0, MENU_DELETE_FROM_SIM, 0, R.string.sim_delete); 173 174 // TODO: Enable this once viewMessage is written. 175 // menu.add(0, MENU_VIEW, 0, R.string.sim_view); 176 } 177 178 @Override onContextItemSelected(MenuItem item)179 public boolean onContextItemSelected(MenuItem item) { 180 AdapterView.AdapterContextMenuInfo info; 181 try { 182 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 183 } catch (ClassCastException exception) { 184 Log.e(TAG, "Bad menuInfo.", exception); 185 return false; 186 } 187 188 final Cursor cursor = (Cursor) mListAdapter.getItem(info.position); 189 190 switch (item.getItemId()) { 191 case MENU_COPY_TO_PHONE_MEMORY: 192 copyToPhoneMemory(cursor); 193 return true; 194 case MENU_DELETE_FROM_SIM: 195 confirmDeleteDialog(new OnClickListener() { 196 public void onClick(DialogInterface dialog, int which) { 197 updateState(SHOW_BUSY); 198 deleteFromSim(cursor); 199 } 200 }, R.string.confirm_delete_SIM_message); 201 return true; 202 case MENU_VIEW: 203 viewMessage(cursor); 204 return true; 205 } 206 return super.onContextItemSelected(item); 207 } 208 209 @Override onResume()210 public void onResume() { 211 super.onResume(); 212 registerSimChangeObserver(); 213 } 214 215 @Override onPause()216 public void onPause() { 217 super.onPause(); 218 mContentResolver.unregisterContentObserver(simChangeObserver); 219 } 220 registerSimChangeObserver()221 private void registerSimChangeObserver() { 222 mContentResolver.registerContentObserver( 223 ICC_URI, true, simChangeObserver); 224 } 225 copyToPhoneMemory(Cursor cursor)226 private void copyToPhoneMemory(Cursor cursor) { 227 String address = cursor.getString( 228 cursor.getColumnIndexOrThrow("address")); 229 String body = cursor.getString(cursor.getColumnIndexOrThrow("body")); 230 Long date = cursor.getLong(cursor.getColumnIndexOrThrow("date")); 231 232 try { 233 if (isIncomingMessage(cursor)) { 234 Sms.Inbox.addMessage(mContentResolver, address, body, null, date, true); 235 } else { 236 Sms.Sent.addMessage(mContentResolver, address, body, null, date); 237 } 238 } catch (SQLiteException e) { 239 SqliteWrapper.checkSQLiteException(this, e); 240 } 241 } 242 isIncomingMessage(Cursor cursor)243 private boolean isIncomingMessage(Cursor cursor) { 244 int messageStatus = cursor.getInt( 245 cursor.getColumnIndexOrThrow("status")); 246 247 return (messageStatus == SmsManager.STATUS_ON_ICC_READ) || 248 (messageStatus == SmsManager.STATUS_ON_ICC_UNREAD); 249 } 250 deleteFromSim(Cursor cursor)251 private void deleteFromSim(Cursor cursor) { 252 String messageIndexString = 253 cursor.getString(cursor.getColumnIndexOrThrow("index_on_icc")); 254 Uri simUri = ICC_URI.buildUpon().appendPath(messageIndexString).build(); 255 256 SqliteWrapper.delete(this, mContentResolver, simUri, null, null); 257 } 258 deleteAllFromSim()259 private void deleteAllFromSim() { 260 Cursor cursor = (Cursor) mListAdapter.getCursor(); 261 262 if (cursor != null) { 263 if (cursor.moveToFirst()) { 264 int count = cursor.getCount(); 265 266 for (int i = 0; i < count; ++i) { 267 deleteFromSim(cursor); 268 cursor.moveToNext(); 269 } 270 } 271 } 272 } 273 274 @Override onPrepareOptionsMenu(Menu menu)275 public boolean onPrepareOptionsMenu(Menu menu) { 276 menu.clear(); 277 278 if ((null != mCursor) && (mCursor.getCount() > 0) && mState == SHOW_LIST) { 279 menu.add(0, OPTION_MENU_DELETE_ALL, 0, R.string.menu_delete_messages).setIcon( 280 android.R.drawable.ic_menu_delete); 281 } 282 283 return true; 284 } 285 286 @Override onOptionsItemSelected(MenuItem item)287 public boolean onOptionsItemSelected(MenuItem item) { 288 switch (item.getItemId()) { 289 case OPTION_MENU_DELETE_ALL: 290 confirmDeleteDialog(new OnClickListener() { 291 public void onClick(DialogInterface dialog, int which) { 292 updateState(SHOW_BUSY); 293 deleteAllFromSim(); 294 } 295 }, R.string.confirm_delete_all_SIM_messages); 296 break; 297 } 298 299 return true; 300 } 301 confirmDeleteDialog(OnClickListener listener, int messageId)302 private void confirmDeleteDialog(OnClickListener listener, int messageId) { 303 AlertDialog.Builder builder = new AlertDialog.Builder(this); 304 builder.setTitle(R.string.confirm_dialog_title); 305 builder.setIcon(android.R.drawable.ic_dialog_alert); 306 builder.setCancelable(true); 307 builder.setPositiveButton(R.string.yes, listener); 308 builder.setNegativeButton(R.string.no, null); 309 builder.setMessage(messageId); 310 311 builder.show(); 312 } 313 updateState(int state)314 private void updateState(int state) { 315 if (mState == state) { 316 return; 317 } 318 319 mState = state; 320 switch (state) { 321 case SHOW_LIST: 322 mSimList.setVisibility(View.VISIBLE); 323 mMessage.setVisibility(View.GONE); 324 setTitle(getString(R.string.sim_manage_messages_title)); 325 setProgressBarIndeterminateVisibility(false); 326 break; 327 case SHOW_EMPTY: 328 mSimList.setVisibility(View.GONE); 329 mMessage.setVisibility(View.VISIBLE); 330 setTitle(getString(R.string.sim_manage_messages_title)); 331 setProgressBarIndeterminateVisibility(false); 332 break; 333 case SHOW_BUSY: 334 mSimList.setVisibility(View.GONE); 335 mMessage.setVisibility(View.GONE); 336 setTitle(getString(R.string.refreshing)); 337 setProgressBarIndeterminateVisibility(true); 338 break; 339 default: 340 Log.e(TAG, "Invalid State"); 341 } 342 } 343 viewMessage(Cursor cursor)344 private void viewMessage(Cursor cursor) { 345 // TODO: Add this. 346 } 347 } 348 349