• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cellbroadcastreceiver;
18 
19 import android.app.ActionBar;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.FragmentManager;
23 import android.app.ListFragment;
24 import android.app.LoaderManager;
25 import android.app.NotificationManager;
26 import android.content.Context;
27 import android.content.CursorLoader;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnClickListener;
30 import android.content.Intent;
31 import android.content.Loader;
32 import android.database.Cursor;
33 import android.os.Bundle;
34 import android.provider.Telephony;
35 import android.telephony.CellBroadcastMessage;
36 import android.view.ContextMenu;
37 import android.view.ContextMenu.ContextMenuInfo;
38 import android.view.LayoutInflater;
39 import android.view.Menu;
40 import android.view.MenuInflater;
41 import android.view.MenuItem;
42 import android.view.View;
43 import android.view.View.OnCreateContextMenuListener;
44 import android.view.ViewGroup;
45 import android.widget.CursorAdapter;
46 import android.widget.ListView;
47 import android.widget.TextView;
48 
49 import java.util.ArrayList;
50 
51 /**
52  * This activity provides a list view of received cell broadcasts. Most of the work is handled
53  * in the inner CursorLoaderListFragment class.
54  */
55 public class CellBroadcastListActivity extends Activity {
56 
57     @Override
onCreate(Bundle savedInstanceState)58     protected void onCreate(Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60 
61         ActionBar actionBar = getActionBar();
62         if (actionBar != null) {
63             // android.R.id.home will be triggered in onOptionsItemSelected()
64             actionBar.setDisplayHomeAsUpEnabled(true);
65         }
66 
67         setTitle(getString(R.string.cb_list_activity_title));
68 
69         // Dismiss the notification that brought us here (if any).
70         ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE))
71                 .cancel(CellBroadcastAlertService.NOTIFICATION_ID);
72 
73         FragmentManager fm = getFragmentManager();
74 
75         // Create the list fragment and add it as our sole content.
76         if (fm.findFragmentById(android.R.id.content) == null) {
77             CursorLoaderListFragment listFragment = new CursorLoaderListFragment();
78             fm.beginTransaction().add(android.R.id.content, listFragment).commit();
79         }
80     }
81 
82     @Override
onOptionsItemSelected(MenuItem item)83     public boolean onOptionsItemSelected(MenuItem item) {
84         switch (item.getItemId()) {
85             // Respond to the action bar's Up/Home button
86             case android.R.id.home:
87                 finish();
88                 return true;
89         }
90         return super.onOptionsItemSelected(item);
91     }
92 
93     /**
94      * List fragment queries SQLite database on worker thread.
95      */
96     public static class CursorLoaderListFragment extends ListFragment
97             implements LoaderManager.LoaderCallbacks<Cursor> {
98 
99         // IDs of the main menu items.
100         private static final int MENU_DELETE_ALL           = 3;
101 
102         // IDs of the context menu items (package local, accessed from inner DeleteThreadListener).
103         static final int MENU_DELETE               = 0;
104         static final int MENU_VIEW_DETAILS         = 1;
105 
106         // This is the Adapter being used to display the list's data.
107         CursorAdapter mAdapter;
108 
109         @Override
onCreate(Bundle savedInstanceState)110         public void onCreate(Bundle savedInstanceState) {
111             super.onCreate(savedInstanceState);
112 
113             // We have a menu item to show in action bar.
114             setHasOptionsMenu(true);
115         }
116 
117         @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)118         public View onCreateView(LayoutInflater inflater, ViewGroup container,
119                 Bundle savedInstanceState) {
120             return inflater.inflate(R.layout.cell_broadcast_list_screen, container, false);
121         }
122 
123         @Override
onActivityCreated(Bundle savedInstanceState)124         public void onActivityCreated(Bundle savedInstanceState) {
125             super.onActivityCreated(savedInstanceState);
126 
127             // Set context menu for long-press.
128             ListView listView = getListView();
129             listView.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
130 
131             // Create a cursor adapter to display the loaded data.
132             mAdapter = new CellBroadcastCursorAdapter(getActivity(), null);
133             setListAdapter(mAdapter);
134 
135             // Prepare the loader.  Either re-connect with an existing one,
136             // or start a new one.
137             getLoaderManager().initLoader(0, null, this);
138         }
139 
140         @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)141         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
142             menu.add(0, MENU_DELETE_ALL, 0, R.string.menu_delete_all).setIcon(
143                     android.R.drawable.ic_menu_delete);
144         }
145 
146         @Override
onPrepareOptionsMenu(Menu menu)147         public void onPrepareOptionsMenu(Menu menu) {
148             menu.findItem(MENU_DELETE_ALL).setVisible(!mAdapter.isEmpty());
149         }
150 
151         @Override
onListItemClick(ListView l, View v, int position, long id)152         public void onListItemClick(ListView l, View v, int position, long id) {
153             CellBroadcastListItem cbli = (CellBroadcastListItem) v;
154             showDialogAndMarkRead(cbli.getMessage());
155         }
156 
157         @Override
onCreateLoader(int id, Bundle args)158         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
159             return new CursorLoader(getActivity(), CellBroadcastContentProvider.CONTENT_URI,
160                     Telephony.CellBroadcasts.QUERY_COLUMNS, null, null,
161                     Telephony.CellBroadcasts.DELIVERY_TIME + " DESC");
162         }
163 
164         @Override
onLoadFinished(Loader<Cursor> loader, Cursor data)165         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
166             // Swap the new cursor in.  (The framework will take care of closing the
167             // old cursor once we return.)
168             mAdapter.swapCursor(data);
169             getActivity().invalidateOptionsMenu();
170             updateNoAlertTextVisibility();
171         }
172 
173         @Override
onLoaderReset(Loader<Cursor> loader)174         public void onLoaderReset(Loader<Cursor> loader) {
175             // This is called when the last Cursor provided to onLoadFinished()
176             // above is about to be closed.  We need to make sure we are no
177             // longer using it.
178             mAdapter.swapCursor(null);
179         }
180 
showDialogAndMarkRead(CellBroadcastMessage cbm)181         private void showDialogAndMarkRead(CellBroadcastMessage cbm) {
182             // show emergency alerts with the warning icon, but don't play alert tone
183             Intent i = new Intent(getActivity(), CellBroadcastAlertDialog.class);
184             ArrayList<CellBroadcastMessage> messageList = new ArrayList<CellBroadcastMessage>(1);
185             messageList.add(cbm);
186             i.putParcelableArrayListExtra(CellBroadcastMessage.SMS_CB_MESSAGE_EXTRA, messageList);
187             startActivity(i);
188         }
189 
showBroadcastDetails(CellBroadcastMessage cbm)190         private void showBroadcastDetails(CellBroadcastMessage cbm) {
191             // show dialog with delivery date/time and alert details
192             CharSequence details = CellBroadcastResources.getMessageDetails(getActivity(), cbm);
193             new AlertDialog.Builder(getActivity())
194                     .setTitle(R.string.view_details_title)
195                     .setMessage(details)
196                     .setCancelable(true)
197                     .show();
198         }
199 
200         private final OnCreateContextMenuListener mOnCreateContextMenuListener =
201                 new OnCreateContextMenuListener() {
202                     @Override
203                     public void onCreateContextMenu(ContextMenu menu, View v,
204                             ContextMenuInfo menuInfo) {
205                         menu.setHeaderTitle(R.string.message_options);
206                         menu.add(0, MENU_VIEW_DETAILS, 0, R.string.menu_view_details);
207                         menu.add(0, MENU_DELETE, 0, R.string.menu_delete);
208                     }
209                 };
210 
updateNoAlertTextVisibility()211         private void updateNoAlertTextVisibility() {
212             TextView noAlertsTextView = getActivity().findViewById(R.id.empty);
213             if (noAlertsTextView != null) {
214                 noAlertsTextView.setVisibility(!hasAlertsInHistory()
215                         ? View.VISIBLE : View.INVISIBLE);
216             }
217         }
218 
219         /**
220          * @return {@code true} if the alert history database has any item
221          */
hasAlertsInHistory()222         private boolean hasAlertsInHistory() {
223             return mAdapter.getCursor().getCount() > 0;
224         }
225 
226         @Override
onContextItemSelected(MenuItem item)227         public boolean onContextItemSelected(MenuItem item) {
228             Cursor cursor = mAdapter.getCursor();
229             if (cursor != null && cursor.getPosition() >= 0) {
230                 switch (item.getItemId()) {
231                     case MENU_DELETE:
232                         confirmDeleteThread(cursor.getLong(cursor.getColumnIndexOrThrow(
233                                 Telephony.CellBroadcasts._ID)));
234                         break;
235 
236                     case MENU_VIEW_DETAILS:
237                         showBroadcastDetails(CellBroadcastMessage.createFromCursor(cursor));
238                         break;
239 
240                     default:
241                         break;
242                 }
243             }
244             return super.onContextItemSelected(item);
245         }
246 
247         @Override
onOptionsItemSelected(MenuItem item)248         public boolean onOptionsItemSelected(MenuItem item) {
249             switch(item.getItemId()) {
250                 case MENU_DELETE_ALL:
251                     confirmDeleteThread(-1);
252                     break;
253 
254                 default:
255                     return true;
256             }
257             return false;
258         }
259 
260         /**
261          * Start the process of putting up a dialog to confirm deleting a broadcast.
262          * @param rowId the row ID of the broadcast to delete, or -1 to delete all broadcasts
263          */
confirmDeleteThread(long rowId)264         public void confirmDeleteThread(long rowId) {
265             DeleteThreadListener listener = new DeleteThreadListener(rowId);
266             confirmDeleteThreadDialog(listener, (rowId == -1), getActivity());
267         }
268 
269         /**
270          * Build and show the proper delete broadcast dialog. The UI is slightly different
271          * depending on whether there are locked messages in the thread(s) and whether we're
272          * deleting a single broadcast or all broadcasts.
273          * @param listener gets called when the delete button is pressed
274          * @param deleteAll whether to show a single thread or all threads UI
275          * @param context used to load the various UI elements
276          */
confirmDeleteThreadDialog(DeleteThreadListener listener, boolean deleteAll, Context context)277         public static void confirmDeleteThreadDialog(DeleteThreadListener listener,
278                 boolean deleteAll, Context context) {
279             AlertDialog.Builder builder = new AlertDialog.Builder(context);
280             builder.setIconAttribute(android.R.attr.alertDialogIcon)
281                     .setCancelable(true)
282                     .setPositiveButton(R.string.button_delete, listener)
283                     .setNegativeButton(R.string.button_cancel, null)
284                     .setMessage(deleteAll ? R.string.confirm_delete_all_broadcasts
285                             : R.string.confirm_delete_broadcast)
286                     .show();
287         }
288 
289         public class DeleteThreadListener implements OnClickListener {
290             private final long mRowId;
291 
DeleteThreadListener(long rowId)292             public DeleteThreadListener(long rowId) {
293                 mRowId = rowId;
294             }
295 
296             @Override
onClick(DialogInterface dialog, int whichButton)297             public void onClick(DialogInterface dialog, int whichButton) {
298                 // delete from database on a background thread
299                 new CellBroadcastContentProvider.AsyncCellBroadcastTask(
300                         getActivity().getContentResolver()).execute(
301                         new CellBroadcastContentProvider.CellBroadcastOperation() {
302                             @Override
303                             public boolean execute(CellBroadcastContentProvider provider) {
304                                 if (mRowId != -1) {
305                                     return provider.deleteBroadcast(mRowId);
306                                 } else {
307                                     return provider.deleteAllBroadcasts();
308                                 }
309                             }
310                         });
311 
312                 dialog.dismiss();
313             }
314         }
315     }
316 }
317