Home | History | Annotate | Download | only in ui
      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
    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
    122     protected void onNewIntent(Intent intent) {
    123         setIntent(intent);
    124 
    125         init();
    126     }
    127 
    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 
    139         public QueryHandler(
    140                 ContentResolver contentResolver, ManageSimMessages parent) {
    141             super(contentResolver);
    142             mParent = parent;
    143         }
    144 
    145         @Override
    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 
    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 
    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
    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
    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
    239     public void onResume() {
    240         super.onResume();
    241         registerSimChangeObserver();
    242     }
    243 
    244     @Override
    245     public void onPause() {
    246         super.onPause();
    247         unregisterReceiver(mBroadcastReceiver);
    248         mContentResolver.unregisterContentObserver(simChangeObserver);
    249     }
    250 
    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 
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    385     private void viewMessage(Cursor cursor) {
    386         // TODO: Add this.
    387     }
    388 }
    389 
    390