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 android.app.ActionBar; 21 import android.app.Activity; 22 import android.app.AlertDialog; 23 import android.content.AsyncQueryHandler; 24 import android.content.ContentResolver; 25 import android.content.DialogInterface; 26 import android.content.DialogInterface.OnClickListener; 27 import android.content.Intent; 28 import android.database.ContentObserver; 29 import android.database.Cursor; 30 import android.database.sqlite.SQLiteException; 31 import android.database.sqlite.SqliteWrapper; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.provider.Telephony.Sms; 36 import android.telephony.SmsManager; 37 import android.util.Log; 38 import android.view.ContextMenu; 39 import android.view.Menu; 40 import android.view.MenuItem; 41 import android.view.View; 42 import android.view.Window; 43 import android.widget.AdapterView; 44 import android.widget.ListView; 45 import android.widget.TextView; 46 47 import com.android.mms.R; 48 import com.android.mms.transaction.MessagingNotification; 49 50 /** 51 * Displays a list of the SMS messages stored on the ICC. 52 */ 53 public class ManageSimMessages extends Activity 54 implements View.OnCreateContextMenuListener { 55 private static final Uri ICC_URI = Uri.parse("content://sms/icc"); 56 private static final String TAG = "ManageSimMessages"; 57 private static final int MENU_COPY_TO_PHONE_MEMORY = 0; 58 private static final int MENU_DELETE_FROM_SIM = 1; 59 private static final int MENU_VIEW = 2; 60 private static final int OPTION_MENU_DELETE_ALL = 0; 61 62 private static final int SHOW_LIST = 0; 63 private static final int SHOW_EMPTY = 1; 64 private static final int SHOW_BUSY = 2; 65 private int mState; 66 67 68 private ContentResolver mContentResolver; 69 private Cursor mCursor = null; 70 private ListView mSimList; 71 private TextView mMessage; 72 private MessageListAdapter mListAdapter = null; 73 private AsyncQueryHandler mQueryHandler = null; 74 75 public static final int SIM_FULL_NOTIFICATION_ID = 234; 76 77 private final ContentObserver simChangeObserver = 78 new ContentObserver(new Handler()) { 79 @Override 80 public void onChange(boolean selfUpdate) { 81 refreshMessageList(); 82 } 83 }; 84 85 @Override 86 protected void onCreate(Bundle icicle) { 87 super.onCreate(icicle); 88 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 89 90 mContentResolver = getContentResolver(); 91 mQueryHandler = new QueryHandler(mContentResolver, this); 92 setContentView(R.layout.sim_list); 93 mSimList = (ListView) findViewById(R.id.messages); 94 mMessage = (TextView) findViewById(R.id.empty_message); 95 96 ActionBar actionBar = getActionBar(); 97 actionBar.setDisplayHomeAsUpEnabled(true); 98 99 init(); 100 } 101 102 @Override 103 protected void onNewIntent(Intent intent) { 104 setIntent(intent); 105 106 init(); 107 } 108 109 private void init() { 110 MessagingNotification.cancelNotification(getApplicationContext(), 111 SIM_FULL_NOTIFICATION_ID); 112 113 updateState(SHOW_BUSY); 114 startQuery(); 115 } 116 117 private class QueryHandler extends AsyncQueryHandler { 118 private final ManageSimMessages mParent; 119 120 public QueryHandler( 121 ContentResolver contentResolver, ManageSimMessages parent) { 122 super(contentResolver); 123 mParent = parent; 124 } 125 126 @Override 127 protected void onQueryComplete( 128 int token, Object cookie, Cursor cursor) { 129 mCursor = cursor; 130 if (mCursor != null) { 131 if (!mCursor.moveToFirst()) { 132 // Let user know the SIM is empty 133 updateState(SHOW_EMPTY); 134 } else if (mListAdapter == null) { 135 // Note that the MessageListAdapter doesn't support auto-requeries. If we 136 // want to respond to changes we'd need to add a line like: 137 // mListAdapter.setOnDataSetChangedListener(mDataSetChangedListener); 138 // See ComposeMessageActivity for an example. 139 mListAdapter = new MessageListAdapter( 140 mParent, mCursor, mSimList, false, null); 141 mSimList.setAdapter(mListAdapter); 142 mSimList.setOnCreateContextMenuListener(mParent); 143 updateState(SHOW_LIST); 144 } else { 145 mListAdapter.changeCursor(mCursor); 146 updateState(SHOW_LIST); 147 } 148 startManagingCursor(mCursor); 149 } else { 150 // Let user know the SIM is empty 151 updateState(SHOW_EMPTY); 152 } 153 // Show option menu when query complete. 154 invalidateOptionsMenu(); 155 } 156 } 157 158 private void startQuery() { 159 try { 160 mQueryHandler.startQuery(0, null, ICC_URI, null, null, null, null); 161 } catch (SQLiteException e) { 162 SqliteWrapper.checkSQLiteException(this, e); 163 } 164 } 165 166 private void refreshMessageList() { 167 updateState(SHOW_BUSY); 168 if (mCursor != null) { 169 stopManagingCursor(mCursor); 170 mCursor.close(); 171 } 172 startQuery(); 173 } 174 175 @Override 176 public void onCreateContextMenu( 177 ContextMenu menu, View v, 178 ContextMenu.ContextMenuInfo menuInfo) { 179 menu.add(0, MENU_COPY_TO_PHONE_MEMORY, 0, 180 R.string.sim_copy_to_phone_memory); 181 menu.add(0, MENU_DELETE_FROM_SIM, 0, R.string.sim_delete); 182 183 // TODO: Enable this once viewMessage is written. 184 // menu.add(0, MENU_VIEW, 0, R.string.sim_view); 185 } 186 187 @Override 188 public boolean onContextItemSelected(MenuItem item) { 189 AdapterView.AdapterContextMenuInfo info; 190 try { 191 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 192 } catch (ClassCastException exception) { 193 Log.e(TAG, "Bad menuInfo.", exception); 194 return false; 195 } 196 197 final Cursor cursor = (Cursor) mListAdapter.getItem(info.position); 198 199 switch (item.getItemId()) { 200 case MENU_COPY_TO_PHONE_MEMORY: 201 copyToPhoneMemory(cursor); 202 return true; 203 case MENU_DELETE_FROM_SIM: 204 confirmDeleteDialog(new OnClickListener() { 205 public void onClick(DialogInterface dialog, int which) { 206 updateState(SHOW_BUSY); 207 deleteFromSim(cursor); 208 dialog.dismiss(); 209 } 210 }, R.string.confirm_delete_SIM_message); 211 return true; 212 case MENU_VIEW: 213 viewMessage(cursor); 214 return true; 215 } 216 return super.onContextItemSelected(item); 217 } 218 219 @Override 220 public void onResume() { 221 super.onResume(); 222 registerSimChangeObserver(); 223 } 224 225 @Override 226 public void onPause() { 227 super.onPause(); 228 mContentResolver.unregisterContentObserver(simChangeObserver); 229 } 230 231 private void registerSimChangeObserver() { 232 mContentResolver.registerContentObserver( 233 ICC_URI, true, simChangeObserver); 234 } 235 236 private void copyToPhoneMemory(Cursor cursor) { 237 String address = cursor.getString( 238 cursor.getColumnIndexOrThrow("address")); 239 String body = cursor.getString(cursor.getColumnIndexOrThrow("body")); 240 Long date = cursor.getLong(cursor.getColumnIndexOrThrow("date")); 241 242 try { 243 if (isIncomingMessage(cursor)) { 244 Sms.Inbox.addMessage(mContentResolver, address, body, null, date, true /* read */); 245 } else { 246 Sms.Sent.addMessage(mContentResolver, address, body, null, date); 247 } 248 } catch (SQLiteException e) { 249 SqliteWrapper.checkSQLiteException(this, e); 250 } 251 } 252 253 private boolean isIncomingMessage(Cursor cursor) { 254 int messageStatus = cursor.getInt( 255 cursor.getColumnIndexOrThrow("status")); 256 257 return (messageStatus == SmsManager.STATUS_ON_ICC_READ) || 258 (messageStatus == SmsManager.STATUS_ON_ICC_UNREAD); 259 } 260 261 private void deleteFromSim(Cursor cursor) { 262 String messageIndexString = 263 cursor.getString(cursor.getColumnIndexOrThrow("index_on_icc")); 264 Uri simUri = ICC_URI.buildUpon().appendPath(messageIndexString).build(); 265 266 SqliteWrapper.delete(this, mContentResolver, simUri, null, null); 267 } 268 269 private void deleteAllFromSim() { 270 Cursor cursor = (Cursor) mListAdapter.getCursor(); 271 272 if (cursor != null) { 273 if (cursor.moveToFirst()) { 274 int count = cursor.getCount(); 275 276 for (int i = 0; i < count; ++i) { 277 deleteFromSim(cursor); 278 cursor.moveToNext(); 279 } 280 } 281 } 282 } 283 284 @Override 285 public boolean onPrepareOptionsMenu(Menu menu) { 286 menu.clear(); 287 288 if (mState == SHOW_LIST && (null != mCursor) && (mCursor.getCount() > 0)) { 289 menu.add(0, OPTION_MENU_DELETE_ALL, 0, R.string.menu_delete_messages).setIcon( 290 android.R.drawable.ic_menu_delete); 291 } 292 293 return true; 294 } 295 296 @Override 297 public boolean onOptionsItemSelected(MenuItem item) { 298 switch (item.getItemId()) { 299 case OPTION_MENU_DELETE_ALL: 300 confirmDeleteDialog(new OnClickListener() { 301 public void onClick(DialogInterface dialog, int which) { 302 updateState(SHOW_BUSY); 303 deleteAllFromSim(); 304 dialog.dismiss(); 305 } 306 }, R.string.confirm_delete_all_SIM_messages); 307 break; 308 309 case android.R.id.home: 310 // The user clicked on the Messaging icon in the action bar. Take them back from 311 // wherever they came from 312 finish(); 313 break; 314 } 315 316 return true; 317 } 318 319 private void confirmDeleteDialog(OnClickListener listener, int messageId) { 320 AlertDialog.Builder builder = new AlertDialog.Builder(this); 321 builder.setTitle(R.string.confirm_dialog_title); 322 builder.setIconAttribute(android.R.attr.alertDialogIcon); 323 builder.setCancelable(true); 324 builder.setPositiveButton(R.string.yes, listener); 325 builder.setNegativeButton(R.string.no, null); 326 builder.setMessage(messageId); 327 328 builder.show(); 329 } 330 331 private void updateState(int state) { 332 if (mState == state) { 333 return; 334 } 335 336 mState = state; 337 switch (state) { 338 case SHOW_LIST: 339 mSimList.setVisibility(View.VISIBLE); 340 mMessage.setVisibility(View.GONE); 341 setTitle(getString(R.string.sim_manage_messages_title)); 342 setProgressBarIndeterminateVisibility(false); 343 mSimList.requestFocus(); 344 break; 345 case SHOW_EMPTY: 346 mSimList.setVisibility(View.GONE); 347 mMessage.setVisibility(View.VISIBLE); 348 setTitle(getString(R.string.sim_manage_messages_title)); 349 setProgressBarIndeterminateVisibility(false); 350 break; 351 case SHOW_BUSY: 352 mSimList.setVisibility(View.GONE); 353 mMessage.setVisibility(View.GONE); 354 setTitle(getString(R.string.refreshing)); 355 setProgressBarIndeterminateVisibility(true); 356 break; 357 default: 358 Log.e(TAG, "Invalid State"); 359 } 360 } 361 362 private void viewMessage(Cursor cursor) { 363 // TODO: Add this. 364 } 365 } 366 367