• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             // Show option menu when query complete.
154             invalidateOptionsMenu();
155         }
156     }
157 
startQuery()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 
refreshMessageList()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
onCreateContextMenu( ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)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
onContextItemSelected(MenuItem item)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
onResume()220     public void onResume() {
221         super.onResume();
222         registerSimChangeObserver();
223     }
224 
225     @Override
onPause()226     public void onPause() {
227         super.onPause();
228         mContentResolver.unregisterContentObserver(simChangeObserver);
229     }
230 
registerSimChangeObserver()231     private void registerSimChangeObserver() {
232         mContentResolver.registerContentObserver(
233                 ICC_URI, true, simChangeObserver);
234     }
235 
copyToPhoneMemory(Cursor cursor)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 
isIncomingMessage(Cursor cursor)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 
deleteFromSim(Cursor cursor)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 
deleteAllFromSim()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
onPrepareOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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 
confirmDeleteDialog(OnClickListener listener, int messageId)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 
updateState(int state)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 
viewMessage(Cursor cursor)362     private void viewMessage(Cursor cursor) {
363         // TODO: Add this.
364     }
365 }
366 
367