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