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