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.Activity; 20 import android.app.AlertDialog; 21 import android.app.FragmentManager; 22 import android.app.ListFragment; 23 import android.app.LoaderManager; 24 import android.app.NotificationManager; 25 import android.content.Context; 26 import android.content.CursorLoader; 27 import android.content.DialogInterface; 28 import android.content.DialogInterface.OnClickListener; 29 import android.content.Intent; 30 import android.content.Loader; 31 import android.database.Cursor; 32 import android.os.Bundle; 33 import android.os.UserManager; 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 48 import java.util.ArrayList; 49 50 /** 51 * This activity provides a list view of received cell broadcasts. Most of the work is handled 52 * in the inner CursorLoaderListFragment class. 53 */ 54 public class CellBroadcastListActivity extends Activity { 55 56 @Override 57 protected void onCreate(Bundle savedInstanceState) { 58 super.onCreate(savedInstanceState); 59 60 setTitle(getString(R.string.cb_list_activity_title)); 61 62 // Dismiss the notification that brought us here (if any). 63 ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)) 64 .cancel(CellBroadcastAlertService.NOTIFICATION_ID); 65 66 FragmentManager fm = getFragmentManager(); 67 68 // Create the list fragment and add it as our sole content. 69 if (fm.findFragmentById(android.R.id.content) == null) { 70 CursorLoaderListFragment listFragment = new CursorLoaderListFragment(); 71 fm.beginTransaction().add(android.R.id.content, listFragment).commit(); 72 } 73 } 74 75 /** 76 * List fragment queries SQLite database on worker thread. 77 */ 78 public static class CursorLoaderListFragment extends ListFragment 79 implements LoaderManager.LoaderCallbacks<Cursor> { 80 81 // IDs of the main menu items. 82 private static final int MENU_DELETE_ALL = 3; 83 private static final int MENU_PREFERENCES = 4; 84 85 // IDs of the context menu items (package local, accessed from inner DeleteThreadListener). 86 static final int MENU_DELETE = 0; 87 static final int MENU_VIEW_DETAILS = 1; 88 89 // This is the Adapter being used to display the list's data. 90 CursorAdapter mAdapter; 91 92 @Override 93 public void onCreate(Bundle savedInstanceState) { 94 super.onCreate(savedInstanceState); 95 96 // We have a menu item to show in action bar. 97 setHasOptionsMenu(true); 98 } 99 100 @Override 101 public View onCreateView(LayoutInflater inflater, ViewGroup container, 102 Bundle savedInstanceState) { 103 return inflater.inflate(R.layout.cell_broadcast_list_screen, container, false); 104 } 105 106 @Override 107 public void onActivityCreated(Bundle savedInstanceState) { 108 super.onActivityCreated(savedInstanceState); 109 110 // Set context menu for long-press. 111 ListView listView = getListView(); 112 listView.setOnCreateContextMenuListener(mOnCreateContextMenuListener); 113 114 // Create a cursor adapter to display the loaded data. 115 mAdapter = new CellBroadcastCursorAdapter(getActivity(), null); 116 setListAdapter(mAdapter); 117 118 // Prepare the loader. Either re-connect with an existing one, 119 // or start a new one. 120 getLoaderManager().initLoader(0, null, this); 121 } 122 123 @Override 124 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 125 menu.add(0, MENU_DELETE_ALL, 0, R.string.menu_delete_all).setIcon( 126 android.R.drawable.ic_menu_delete); 127 if (UserManager.get(getActivity()).isAdminUser()) { 128 menu.add(0, MENU_PREFERENCES, 0, R.string.menu_preferences).setIcon( 129 android.R.drawable.ic_menu_preferences); 130 } 131 } 132 133 @Override 134 public void onPrepareOptionsMenu(Menu menu) { 135 menu.findItem(MENU_DELETE_ALL).setVisible(!mAdapter.isEmpty()); 136 } 137 138 @Override 139 public void onListItemClick(ListView l, View v, int position, long id) { 140 CellBroadcastListItem cbli = (CellBroadcastListItem) v; 141 showDialogAndMarkRead(cbli.getMessage()); 142 } 143 144 @Override 145 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 146 return new CursorLoader(getActivity(), CellBroadcastContentProvider.CONTENT_URI, 147 Telephony.CellBroadcasts.QUERY_COLUMNS, null, null, 148 Telephony.CellBroadcasts.DELIVERY_TIME + " DESC"); 149 } 150 151 @Override 152 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 153 // Swap the new cursor in. (The framework will take care of closing the 154 // old cursor once we return.) 155 mAdapter.swapCursor(data); 156 getActivity().invalidateOptionsMenu(); 157 } 158 159 @Override 160 public void onLoaderReset(Loader<Cursor> loader) { 161 // This is called when the last Cursor provided to onLoadFinished() 162 // above is about to be closed. We need to make sure we are no 163 // longer using it. 164 mAdapter.swapCursor(null); 165 } 166 167 private void showDialogAndMarkRead(CellBroadcastMessage cbm) { 168 // show emergency alerts with the warning icon, but don't play alert tone 169 Intent i = new Intent(getActivity(), CellBroadcastAlertDialog.class); 170 ArrayList<CellBroadcastMessage> messageList = new ArrayList<CellBroadcastMessage>(1); 171 messageList.add(cbm); 172 i.putParcelableArrayListExtra(CellBroadcastMessage.SMS_CB_MESSAGE_EXTRA, messageList); 173 startActivity(i); 174 } 175 176 private void showBroadcastDetails(CellBroadcastMessage cbm) { 177 // show dialog with delivery date/time and alert details 178 CharSequence details = CellBroadcastResources.getMessageDetails(getActivity(), cbm); 179 new AlertDialog.Builder(getActivity()) 180 .setTitle(R.string.view_details_title) 181 .setMessage(details) 182 .setCancelable(true) 183 .show(); 184 } 185 186 private final OnCreateContextMenuListener mOnCreateContextMenuListener = 187 new OnCreateContextMenuListener() { 188 @Override 189 public void onCreateContextMenu(ContextMenu menu, View v, 190 ContextMenuInfo menuInfo) { 191 menu.setHeaderTitle(R.string.message_options); 192 menu.add(0, MENU_VIEW_DETAILS, 0, R.string.menu_view_details); 193 menu.add(0, MENU_DELETE, 0, R.string.menu_delete); 194 } 195 }; 196 197 @Override 198 public boolean onContextItemSelected(MenuItem item) { 199 Cursor cursor = mAdapter.getCursor(); 200 if (cursor != null && cursor.getPosition() >= 0) { 201 switch (item.getItemId()) { 202 case MENU_DELETE: 203 confirmDeleteThread(cursor.getLong(cursor.getColumnIndexOrThrow( 204 Telephony.CellBroadcasts._ID))); 205 break; 206 207 case MENU_VIEW_DETAILS: 208 showBroadcastDetails(CellBroadcastMessage.createFromCursor(cursor)); 209 break; 210 211 default: 212 break; 213 } 214 } 215 return super.onContextItemSelected(item); 216 } 217 218 @Override 219 public boolean onOptionsItemSelected(MenuItem item) { 220 switch(item.getItemId()) { 221 case MENU_DELETE_ALL: 222 confirmDeleteThread(-1); 223 break; 224 225 case MENU_PREFERENCES: 226 Intent intent = new Intent(getActivity(), CellBroadcastSettings.class); 227 startActivity(intent); 228 break; 229 230 default: 231 return true; 232 } 233 return false; 234 } 235 236 /** 237 * Start the process of putting up a dialog to confirm deleting a broadcast. 238 * @param rowId the row ID of the broadcast to delete, or -1 to delete all broadcasts 239 */ 240 public void confirmDeleteThread(long rowId) { 241 DeleteThreadListener listener = new DeleteThreadListener(rowId); 242 confirmDeleteThreadDialog(listener, (rowId == -1), getActivity()); 243 } 244 245 /** 246 * Build and show the proper delete broadcast dialog. The UI is slightly different 247 * depending on whether there are locked messages in the thread(s) and whether we're 248 * deleting a single broadcast or all broadcasts. 249 * @param listener gets called when the delete button is pressed 250 * @param deleteAll whether to show a single thread or all threads UI 251 * @param context used to load the various UI elements 252 */ 253 public static void confirmDeleteThreadDialog(DeleteThreadListener listener, 254 boolean deleteAll, Context context) { 255 AlertDialog.Builder builder = new AlertDialog.Builder(context); 256 builder.setIconAttribute(android.R.attr.alertDialogIcon) 257 .setCancelable(true) 258 .setPositiveButton(R.string.button_delete, listener) 259 .setNegativeButton(R.string.button_cancel, null) 260 .setMessage(deleteAll ? R.string.confirm_delete_all_broadcasts 261 : R.string.confirm_delete_broadcast) 262 .show(); 263 } 264 265 public class DeleteThreadListener implements OnClickListener { 266 private final long mRowId; 267 268 public DeleteThreadListener(long rowId) { 269 mRowId = rowId; 270 } 271 272 @Override 273 public void onClick(DialogInterface dialog, int whichButton) { 274 // delete from database on a background thread 275 new CellBroadcastContentProvider.AsyncCellBroadcastTask( 276 getActivity().getContentResolver()).execute( 277 new CellBroadcastContentProvider.CellBroadcastOperation() { 278 @Override 279 public boolean execute(CellBroadcastContentProvider provider) { 280 if (mRowId != -1) { 281 return provider.deleteBroadcast(mRowId); 282 } else { 283 return provider.deleteAllBroadcasts(); 284 } 285 } 286 }); 287 288 dialog.dismiss(); 289 } 290 } 291 } 292 } 293