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