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