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 onCreate(Bundle icicle)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 onNewIntent(Intent intent)103 protected void onNewIntent(Intent intent) { 104 setIntent(intent); 105 106 init(); 107 } 108 init()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 QueryHandler( ContentResolver contentResolver, ManageSimMessages parent)120 public QueryHandler( 121 ContentResolver contentResolver, ManageSimMessages parent) { 122 super(contentResolver); 123 mParent = parent; 124 } 125 126 @Override onQueryComplete( int token, Object cookie, Cursor cursor)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 } 154 } 155 startQuery()156 private void startQuery() { 157 try { 158 mQueryHandler.startQuery(0, null, ICC_URI, null, null, null, null); 159 } catch (SQLiteException e) { 160 SqliteWrapper.checkSQLiteException(this, e); 161 } 162 } 163 refreshMessageList()164 private void refreshMessageList() { 165 updateState(SHOW_BUSY); 166 if (mCursor != null) { 167 stopManagingCursor(mCursor); 168 mCursor.close(); 169 } 170 startQuery(); 171 } 172 173 @Override onCreateContextMenu( ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)174 public void onCreateContextMenu( 175 ContextMenu menu, View v, 176 ContextMenu.ContextMenuInfo menuInfo) { 177 menu.add(0, MENU_COPY_TO_PHONE_MEMORY, 0, 178 R.string.sim_copy_to_phone_memory); 179 menu.add(0, MENU_DELETE_FROM_SIM, 0, R.string.sim_delete); 180 181 // TODO: Enable this once viewMessage is written. 182 // menu.add(0, MENU_VIEW, 0, R.string.sim_view); 183 } 184 185 @Override onContextItemSelected(MenuItem item)186 public boolean onContextItemSelected(MenuItem item) { 187 AdapterView.AdapterContextMenuInfo info; 188 try { 189 info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 190 } catch (ClassCastException exception) { 191 Log.e(TAG, "Bad menuInfo.", exception); 192 return false; 193 } 194 195 final Cursor cursor = (Cursor) mListAdapter.getItem(info.position); 196 197 switch (item.getItemId()) { 198 case MENU_COPY_TO_PHONE_MEMORY: 199 copyToPhoneMemory(cursor); 200 return true; 201 case MENU_DELETE_FROM_SIM: 202 confirmDeleteDialog(new OnClickListener() { 203 public void onClick(DialogInterface dialog, int which) { 204 updateState(SHOW_BUSY); 205 deleteFromSim(cursor); 206 dialog.dismiss(); 207 } 208 }, R.string.confirm_delete_SIM_message); 209 return true; 210 case MENU_VIEW: 211 viewMessage(cursor); 212 return true; 213 } 214 return super.onContextItemSelected(item); 215 } 216 217 @Override onResume()218 public void onResume() { 219 super.onResume(); 220 registerSimChangeObserver(); 221 } 222 223 @Override onPause()224 public void onPause() { 225 super.onPause(); 226 mContentResolver.unregisterContentObserver(simChangeObserver); 227 } 228 registerSimChangeObserver()229 private void registerSimChangeObserver() { 230 mContentResolver.registerContentObserver( 231 ICC_URI, true, simChangeObserver); 232 } 233 copyToPhoneMemory(Cursor cursor)234 private void copyToPhoneMemory(Cursor cursor) { 235 String address = cursor.getString( 236 cursor.getColumnIndexOrThrow("address")); 237 String body = cursor.getString(cursor.getColumnIndexOrThrow("body")); 238 Long date = cursor.getLong(cursor.getColumnIndexOrThrow("date")); 239 240 try { 241 if (isIncomingMessage(cursor)) { 242 Sms.Inbox.addMessage(mContentResolver, address, body, null, date, true /* read */); 243 } else { 244 Sms.Sent.addMessage(mContentResolver, address, body, null, date); 245 } 246 } catch (SQLiteException e) { 247 SqliteWrapper.checkSQLiteException(this, e); 248 } 249 } 250 isIncomingMessage(Cursor cursor)251 private boolean isIncomingMessage(Cursor cursor) { 252 int messageStatus = cursor.getInt( 253 cursor.getColumnIndexOrThrow("status")); 254 255 return (messageStatus == SmsManager.STATUS_ON_ICC_READ) || 256 (messageStatus == SmsManager.STATUS_ON_ICC_UNREAD); 257 } 258 deleteFromSim(Cursor cursor)259 private void deleteFromSim(Cursor cursor) { 260 String messageIndexString = 261 cursor.getString(cursor.getColumnIndexOrThrow("index_on_icc")); 262 Uri simUri = ICC_URI.buildUpon().appendPath(messageIndexString).build(); 263 264 SqliteWrapper.delete(this, mContentResolver, simUri, null, null); 265 } 266 deleteAllFromSim()267 private void deleteAllFromSim() { 268 Cursor cursor = (Cursor) mListAdapter.getCursor(); 269 270 if (cursor != null) { 271 if (cursor.moveToFirst()) { 272 int count = cursor.getCount(); 273 274 for (int i = 0; i < count; ++i) { 275 deleteFromSim(cursor); 276 cursor.moveToNext(); 277 } 278 } 279 } 280 } 281 282 @Override onPrepareOptionsMenu(Menu menu)283 public boolean onPrepareOptionsMenu(Menu menu) { 284 menu.clear(); 285 286 if (mState == SHOW_LIST && (null != mCursor) && (mCursor.getCount() > 0)) { 287 menu.add(0, OPTION_MENU_DELETE_ALL, 0, R.string.menu_delete_messages).setIcon( 288 android.R.drawable.ic_menu_delete); 289 } 290 291 return true; 292 } 293 294 @Override onOptionsItemSelected(MenuItem item)295 public boolean onOptionsItemSelected(MenuItem item) { 296 switch (item.getItemId()) { 297 case OPTION_MENU_DELETE_ALL: 298 confirmDeleteDialog(new OnClickListener() { 299 public void onClick(DialogInterface dialog, int which) { 300 updateState(SHOW_BUSY); 301 deleteAllFromSim(); 302 dialog.dismiss(); 303 } 304 }, R.string.confirm_delete_all_SIM_messages); 305 break; 306 307 case android.R.id.home: 308 // The user clicked on the Messaging icon in the action bar. Take them back from 309 // wherever they came from 310 finish(); 311 break; 312 } 313 314 return true; 315 } 316 confirmDeleteDialog(OnClickListener listener, int messageId)317 private void confirmDeleteDialog(OnClickListener listener, int messageId) { 318 AlertDialog.Builder builder = new AlertDialog.Builder(this); 319 builder.setTitle(R.string.confirm_dialog_title); 320 builder.setIconAttribute(android.R.attr.alertDialogIcon); 321 builder.setCancelable(true); 322 builder.setPositiveButton(R.string.yes, listener); 323 builder.setNegativeButton(R.string.no, null); 324 builder.setMessage(messageId); 325 326 builder.show(); 327 } 328 updateState(int state)329 private void updateState(int state) { 330 if (mState == state) { 331 return; 332 } 333 334 mState = state; 335 switch (state) { 336 case SHOW_LIST: 337 mSimList.setVisibility(View.VISIBLE); 338 mMessage.setVisibility(View.GONE); 339 setTitle(getString(R.string.sim_manage_messages_title)); 340 setProgressBarIndeterminateVisibility(false); 341 mSimList.requestFocus(); 342 break; 343 case SHOW_EMPTY: 344 mSimList.setVisibility(View.GONE); 345 mMessage.setVisibility(View.VISIBLE); 346 setTitle(getString(R.string.sim_manage_messages_title)); 347 setProgressBarIndeterminateVisibility(false); 348 break; 349 case SHOW_BUSY: 350 mSimList.setVisibility(View.GONE); 351 mMessage.setVisibility(View.GONE); 352 setTitle(getString(R.string.refreshing)); 353 setProgressBarIndeterminateVisibility(true); 354 break; 355 default: 356 Log.e(TAG, "Invalid State"); 357 } 358 } 359 viewMessage(Cursor cursor)360 private void viewMessage(Cursor cursor) { 361 // TODO: Add this. 362 } 363 } 364 365