1 /* 2 * Copyright (C) 2014 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.example.android.support.wearable.notifications; 18 19 import android.app.Activity; 20 import android.app.Notification; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.support.v4.app.NotificationManagerCompat; 27 import android.text.Editable; 28 import android.text.TextWatcher; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.AdapterView; 32 import android.widget.ArrayAdapter; 33 import android.widget.CheckBox; 34 import android.widget.CompoundButton; 35 import android.widget.EditText; 36 import android.widget.Spinner; 37 import android.widget.TextView; 38 39 import java.util.Arrays; 40 41 /** 42 * Main activity which posts a notification when resumed, and allows customization 43 * of that notification via controls. 44 */ 45 public class MainActivity extends Activity implements Handler.Callback { 46 private static final int MSG_POST_NOTIFICATIONS = 0; 47 private static final long POST_NOTIFICATIONS_DELAY_MS = 200; 48 49 private Handler mHandler; 50 private Spinner mPresetSpinner; 51 private EditText mTitleEditText; 52 private EditText mTextEditText; 53 private TextWatcher mTextChangedListener; 54 private Spinner mPrioritySpinner; 55 private Spinner mActionsSpinner; 56 private CheckBox mIncludeLargeIconCheckbox; 57 private CheckBox mLocalOnlyCheckbox; 58 private CheckBox mIncludeContentIntentCheckbox; 59 private CheckBox mVibrateCheckbox; 60 private BackgroundPickers mBackgroundPickers; 61 private int mPostedNotificationCount = 0; 62 63 @Override 64 protected void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 setContentView(R.layout.activity_main); 67 68 mHandler = new Handler(this); 69 mTextChangedListener = new UpdateNotificationsOnTextChangeListener(); 70 71 initPresetSpinner(); 72 initTitleEditText(); 73 initTextEditText(); 74 initPrioritySpinner(); 75 initActionsSpinner(); 76 initIncludeLargeIconCheckbox(); 77 initLocalOnlyCheckbox(); 78 initIncludeContentIntentCheckbox(); 79 initVibrateCheckbox(); 80 initBackgroundPickers(); 81 82 NotificationPreset preset = NotificationPresets.PRESETS[ 83 mPresetSpinner.getSelectedItemPosition()]; 84 updateTextEditors(preset); 85 } 86 87 @Override 88 protected void onResume() { 89 super.onResume(); 90 updateNotifications(false /* cancelExisting */); 91 } 92 93 private void initPresetSpinner() { 94 mPresetSpinner = (Spinner) findViewById(R.id.preset_spinner); 95 mPresetSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this, 96 NotificationPresets.PRESETS)); 97 mPresetSpinner.post(new Runnable() { 98 @Override 99 public void run() { 100 mPresetSpinner.setOnItemSelectedListener(new PresetSpinnerListener()); 101 } 102 }); 103 } 104 105 private void initTitleEditText() { 106 mTitleEditText = (EditText) findViewById(R.id.title_editor); 107 } 108 109 private void initTextEditText() { 110 mTextEditText = (EditText) findViewById(R.id.text_editor); 111 } 112 113 private void initPrioritySpinner() { 114 mPrioritySpinner = (Spinner) findViewById(R.id.priority_spinner); 115 mPrioritySpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this, 116 PriorityPresets.PRESETS)); 117 mPrioritySpinner.setSelection(Arrays.asList(PriorityPresets.PRESETS) 118 .indexOf(PriorityPresets.DEFAULT)); 119 mPrioritySpinner.post(new Runnable() { 120 @Override 121 public void run() { 122 mPrioritySpinner.setOnItemSelectedListener( 123 new UpdateNotificationsOnItemSelectedListener(true /* cancelExisting */)); 124 } 125 }); 126 } 127 128 private void initActionsSpinner() { 129 mActionsSpinner = (Spinner) findViewById(R.id.actions_spinner); 130 mActionsSpinner.setAdapter(new NamedPresetSpinnerArrayAdapter(this, 131 ActionsPresets.PRESETS)); 132 mActionsSpinner.post(new Runnable() { 133 @Override 134 public void run() { 135 mActionsSpinner.setOnItemSelectedListener( 136 new UpdateNotificationsOnItemSelectedListener(false /* cancelExisting */)); 137 } 138 }); 139 } 140 141 private void initIncludeLargeIconCheckbox() { 142 mIncludeLargeIconCheckbox = (CheckBox) findViewById(R.id.include_large_icon_checkbox); 143 mIncludeLargeIconCheckbox.setOnCheckedChangeListener( 144 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 145 } 146 147 private void initLocalOnlyCheckbox() { 148 mLocalOnlyCheckbox = (CheckBox) findViewById(R.id.local_only_checkbox); 149 mLocalOnlyCheckbox.setOnCheckedChangeListener( 150 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 151 } 152 153 private void initIncludeContentIntentCheckbox() { 154 mIncludeContentIntentCheckbox = (CheckBox) findViewById( 155 R.id.include_content_intent_checkbox); 156 mIncludeContentIntentCheckbox.setOnCheckedChangeListener( 157 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 158 } 159 160 private void initVibrateCheckbox() { 161 mVibrateCheckbox = (CheckBox) findViewById(R.id.vibrate_checkbox); 162 mVibrateCheckbox.setOnCheckedChangeListener( 163 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 164 } 165 166 private void initBackgroundPickers() { 167 mBackgroundPickers = new BackgroundPickers( 168 (ViewGroup) findViewById(R.id.background_pickers), 169 new BackgroundPickerListener()); 170 } 171 172 private void updateTextEditors(NotificationPreset preset) { 173 mTitleEditText.setText(getString(preset.titleResId)); 174 mTextEditText.setText(getString(preset.textResId)); 175 176 View[] presetViews = { 177 findViewById(R.id.title_label), 178 findViewById(R.id.title_editor), 179 findViewById(R.id.text_label), 180 findViewById(R.id.text_editor) 181 }; 182 183 if (preset == NotificationPresets.BASIC) { 184 for (View v : presetViews) { 185 v.setVisibility(View.VISIBLE); 186 } 187 mTitleEditText.addTextChangedListener(mTextChangedListener); 188 mTextEditText.addTextChangedListener(mTextChangedListener); 189 } else { 190 for (View v : presetViews) { 191 v.setVisibility(View.GONE); 192 } 193 mTitleEditText.removeTextChangedListener(mTextChangedListener); 194 mTextEditText.removeTextChangedListener(mTextChangedListener); 195 } 196 } 197 198 /** 199 * Begin to re-post the sample notification(s). 200 */ 201 private void updateNotifications(boolean cancelExisting) { 202 // Disable messages to skip notification deleted messages during cancel. 203 sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_DISABLE_MESSAGES) 204 .setClass(this, NotificationIntentReceiver.class)); 205 206 if (cancelExisting) { 207 // Cancel all existing notifications to trigger fresh-posting behavior: For example, 208 // switching from HIGH to LOW priority does not cause a reordering in Notification 209 // Shade. 210 NotificationManagerCompat.from(this).cancelAll(); 211 mPostedNotificationCount = 0; 212 213 // Post the updated notifications on a delay to avoid a cancel+post race condition 214 // with notification manager. 215 mHandler.removeMessages(MSG_POST_NOTIFICATIONS); 216 mHandler.sendEmptyMessageDelayed(MSG_POST_NOTIFICATIONS, POST_NOTIFICATIONS_DELAY_MS); 217 } else { 218 postNotifications(); 219 } 220 } 221 222 /** 223 * Post the sample notification(s) using current options. 224 */ 225 private void postNotifications() { 226 sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_ENABLE_MESSAGES) 227 .setClass(this, NotificationIntentReceiver.class)); 228 229 Notification[] notifications = buildNotifications(); 230 231 // Post new notifications 232 NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(this); 233 for (int i = 0; i < notifications.length; i++) { 234 notificationManagerCompat.notify(i, notifications[i]); 235 } 236 // Cancel any that are beyond the current count. 237 for (int i = notifications.length; i < mPostedNotificationCount; i++) { 238 notificationManagerCompat.cancel(i); 239 } 240 mPostedNotificationCount = notifications.length; 241 } 242 243 /** 244 * Build the sample notifications 245 */ 246 247 private Notification[] buildNotifications() { 248 NotificationPreset preset = NotificationPresets.PRESETS[ 249 mPresetSpinner.getSelectedItemPosition()]; 250 ActionsPreset actionsPreset = ActionsPresets.PRESETS[ 251 mActionsSpinner.getSelectedItemPosition()]; 252 if (preset.actionsRequired() && actionsPreset == ActionsPresets.NO_ACTIONS_PRESET) { 253 // If actions are required, but the no-actions preset was selected, change presets. 254 actionsPreset = ActionsPresets.SINGLE_ACTION_PRESET; 255 mActionsSpinner.setSelection(Arrays.asList(ActionsPresets.PRESETS).indexOf( 256 actionsPreset), true); 257 } 258 NotificationPreset.BuildOptions options = new NotificationPreset.BuildOptions( 259 mTitleEditText.getText(), 260 mTextEditText.getText(), 261 PriorityPresets.PRESETS[mPrioritySpinner.getSelectedItemPosition()], 262 actionsPreset, 263 mIncludeLargeIconCheckbox.isChecked(), 264 mLocalOnlyCheckbox.isChecked(), 265 mIncludeContentIntentCheckbox.isChecked(), 266 mVibrateCheckbox.isChecked(), 267 mBackgroundPickers.getRes()); 268 return preset.buildNotifications(this, options); 269 } 270 271 @Override 272 public boolean handleMessage(Message message) { 273 switch (message.what) { 274 case MSG_POST_NOTIFICATIONS: 275 postNotifications(); 276 return true; 277 } 278 return false; 279 } 280 281 private class PresetSpinnerListener implements AdapterView.OnItemSelectedListener { 282 @Override 283 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 284 NotificationPreset preset = NotificationPresets.PRESETS[position]; 285 mBackgroundPickers.generatePickers(preset.countBackgroundPickersRequired()); 286 updateTextEditors(preset); 287 updateNotifications(false /* cancelExisting */); 288 } 289 290 @Override 291 public void onNothingSelected(AdapterView<?> adapterView) {} 292 } 293 294 private class UpdateNotificationsOnTextChangeListener implements TextWatcher { 295 @Override 296 public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 297 298 public void onTextChanged(CharSequence s, int start, int before, int count) {} 299 300 @Override 301 public void afterTextChanged(Editable s) { 302 updateNotifications(false /* cancelExisting */); 303 } 304 } 305 306 private class UpdateNotificationsOnItemSelectedListener 307 implements AdapterView.OnItemSelectedListener { 308 private final boolean mCancelExisting; 309 310 public UpdateNotificationsOnItemSelectedListener(boolean cancelExisting) { 311 mCancelExisting = cancelExisting; 312 } 313 @Override 314 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 315 updateNotifications(mCancelExisting); 316 } 317 318 @Override 319 public void onNothingSelected(AdapterView<?> adapterView) { 320 } 321 } 322 323 private class UpdateNotificationsOnCheckedChangeListener 324 implements CompoundButton.OnCheckedChangeListener { 325 private final boolean mCancelExisting; 326 327 public UpdateNotificationsOnCheckedChangeListener(boolean cancelExisting) { 328 mCancelExisting = cancelExisting; 329 } 330 331 @Override 332 public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 333 updateNotifications(mCancelExisting); 334 } 335 } 336 337 private class BackgroundPickerListener 338 implements BackgroundPickers.OnBackgroundPickersChangedListener { 339 @Override 340 public void onBackgroundPickersChanged(BackgroundPickers pickers) { 341 updateNotifications(false /* cancelExisting */); 342 } 343 } 344 345 private class NamedPresetSpinnerArrayAdapter extends ArrayAdapter<NamedPreset> { 346 public NamedPresetSpinnerArrayAdapter(Context context, NamedPreset[] presets) { 347 super(context, R.layout.simple_spinner_item, presets); 348 } 349 350 @Override 351 public View getDropDownView(int position, View convertView, ViewGroup parent) { 352 TextView view = (TextView) super.getDropDownView(position, convertView, parent); 353 view.setText(getString(getItem(position).nameResId)); 354 return view; 355 } 356 357 @Override 358 public View getView(int position, View convertView, ViewGroup parent) { 359 TextView view = (TextView) getLayoutInflater().inflate( 360 android.R.layout.simple_spinner_item, parent, false); 361 view.setText(getString(getItem(position).nameResId)); 362 return view; 363 } 364 } 365 } 366