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