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 postedNotificationCount = 0; 62 63 @Override onCreate(Bundle savedInstanceState)64 protected void onCreate(Bundle savedInstanceState) { 65 super.onCreate(savedInstanceState); 66 setContentView(R.layout.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 onResume()88 protected void onResume() { 89 super.onResume(); 90 updateNotifications(false /* cancelExisting */); 91 } 92 initPresetSpinner()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 initTitleEditText()105 private void initTitleEditText() { 106 mTitleEditText = (EditText) findViewById(R.id.title_editor); 107 } 108 initTextEditText()109 private void initTextEditText() { 110 mTextEditText = (EditText) findViewById(R.id.text_editor); 111 } 112 initPrioritySpinner()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 initActionsSpinner()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 initIncludeLargeIconCheckbox()141 private void initIncludeLargeIconCheckbox() { 142 mIncludeLargeIconCheckbox = (CheckBox) findViewById(R.id.include_large_icon_checkbox); 143 mIncludeLargeIconCheckbox.setOnCheckedChangeListener( 144 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 145 } 146 initLocalOnlyCheckbox()147 private void initLocalOnlyCheckbox() { 148 mLocalOnlyCheckbox = (CheckBox) findViewById(R.id.local_only_checkbox); 149 mLocalOnlyCheckbox.setOnCheckedChangeListener( 150 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 151 } 152 initIncludeContentIntentCheckbox()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 initVibrateCheckbox()160 private void initVibrateCheckbox() { 161 mVibrateCheckbox = (CheckBox) findViewById(R.id.vibrate_checkbox); 162 mVibrateCheckbox.setOnCheckedChangeListener( 163 new UpdateNotificationsOnCheckedChangeListener(false /* cancelExisting */)); 164 } 165 initBackgroundPickers()166 private void initBackgroundPickers() { 167 mBackgroundPickers = new BackgroundPickers( 168 (ViewGroup) findViewById(R.id.background_pickers), 169 new BackgroundPickerListener()); 170 } 171 updateTextEditors(NotificationPreset preset)172 private void updateTextEditors(NotificationPreset preset) { 173 mTitleEditText.setText(getString(preset.titleResId)); 174 mTextEditText.setText(getString(preset.textResId)); 175 if (preset == NotificationPresets.BASIC) { 176 findViewById(R.id.title_edit_field).setVisibility(View.VISIBLE); 177 mTitleEditText.addTextChangedListener(mTextChangedListener); 178 findViewById(R.id.text_edit_field).setVisibility(View.VISIBLE); 179 mTextEditText.addTextChangedListener(mTextChangedListener); 180 } else { 181 findViewById(R.id.title_edit_field).setVisibility(View.GONE); 182 mTitleEditText.removeTextChangedListener(mTextChangedListener); 183 findViewById(R.id.text_edit_field).setVisibility(View.GONE); 184 mTextEditText.removeTextChangedListener(mTextChangedListener); 185 } 186 } 187 188 /** 189 * Begin to re-post the sample notification(s). 190 */ updateNotifications(boolean cancelExisting)191 private void updateNotifications(boolean cancelExisting) { 192 // Disable messages to skip notification deleted messages during cancel. 193 sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_DISABLE_MESSAGES) 194 .setClass(this, NotificationIntentReceiver.class)); 195 196 if (cancelExisting) { 197 // Cancel all existing notifications to trigger fresh-posting behavior: For example, 198 // switching from HIGH to LOW priority does not cause a reordering in Notification Shade. 199 NotificationManagerCompat.from(this).cancelAll(); 200 postedNotificationCount = 0; 201 202 // Post the updated notifications on a delay to avoid a cancel+post race condition 203 // with notification manager. 204 mHandler.removeMessages(MSG_POST_NOTIFICATIONS); 205 mHandler.sendEmptyMessageDelayed(MSG_POST_NOTIFICATIONS, POST_NOTIFICATIONS_DELAY_MS); 206 } else { 207 postNotifications(); 208 } 209 } 210 211 /** 212 * Post the sample notification(s) using current options. 213 */ postNotifications()214 private void postNotifications() { 215 sendBroadcast(new Intent(NotificationIntentReceiver.ACTION_ENABLE_MESSAGES) 216 .setClass(this, NotificationIntentReceiver.class)); 217 218 NotificationPreset preset = NotificationPresets.PRESETS[ 219 mPresetSpinner.getSelectedItemPosition()]; 220 CharSequence titlePreset = mTitleEditText.getText(); 221 CharSequence textPreset = mTextEditText.getText(); 222 PriorityPreset priorityPreset = PriorityPresets.PRESETS[ 223 mPrioritySpinner.getSelectedItemPosition()]; 224 ActionsPreset actionsPreset = ActionsPresets.PRESETS[ 225 mActionsSpinner.getSelectedItemPosition()]; 226 if (preset.actionsRequired() && actionsPreset == ActionsPresets.NO_ACTIONS_PRESET) { 227 // If actions are required, but the no-actions preset was selected, change presets. 228 actionsPreset = ActionsPresets.SINGLE_ACTION_PRESET; 229 mActionsSpinner.setSelection(Arrays.asList(ActionsPresets.PRESETS).indexOf( 230 actionsPreset), true); 231 } 232 NotificationPreset.BuildOptions options = new NotificationPreset.BuildOptions( 233 titlePreset, 234 textPreset, 235 priorityPreset, 236 actionsPreset, 237 mIncludeLargeIconCheckbox.isChecked(), 238 mLocalOnlyCheckbox.isChecked(), 239 mIncludeContentIntentCheckbox.isChecked(), 240 mVibrateCheckbox.isChecked(), 241 mBackgroundPickers.getRes()); 242 Notification[] notifications = preset.buildNotifications(this, options); 243 244 // Post new notifications 245 for (int i = 0; i < notifications.length; i++) { 246 NotificationManagerCompat.from(this).notify(i, notifications[i]); 247 } 248 // Cancel any that are beyond the current count. 249 for (int i = notifications.length; i < postedNotificationCount; i++) { 250 NotificationManagerCompat.from(this).cancel(i); 251 } 252 postedNotificationCount = notifications.length; 253 } 254 255 @Override handleMessage(Message message)256 public boolean handleMessage(Message message) { 257 switch (message.what) { 258 case MSG_POST_NOTIFICATIONS: 259 postNotifications(); 260 return true; 261 } 262 return false; 263 } 264 265 private class PresetSpinnerListener implements AdapterView.OnItemSelectedListener { 266 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)267 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 268 NotificationPreset preset = NotificationPresets.PRESETS[position]; 269 mBackgroundPickers.generatePickers(preset.countBackgroundPickersRequired()); 270 updateTextEditors(preset); 271 updateNotifications(false /* cancelExisting */); 272 } 273 274 @Override onNothingSelected(AdapterView<?> adapterView)275 public void onNothingSelected(AdapterView<?> adapterView) { 276 } 277 } 278 279 private class UpdateNotificationsOnTextChangeListener implements TextWatcher { 280 @Override beforeTextChanged(CharSequence s, int start, int count, int after)281 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 282 } 283 onTextChanged(CharSequence s, int start, int before, int count)284 public void onTextChanged(CharSequence s, int start, int before, int count) { 285 } 286 287 @Override afterTextChanged(Editable s)288 public void afterTextChanged(Editable s) { 289 updateNotifications(false /* cancelExisting */); 290 } 291 } 292 293 private class UpdateNotificationsOnItemSelectedListener 294 implements AdapterView.OnItemSelectedListener { 295 private final boolean mCancelExisting; 296 UpdateNotificationsOnItemSelectedListener(boolean cancelExisting)297 public UpdateNotificationsOnItemSelectedListener(boolean cancelExisting) { 298 mCancelExisting = cancelExisting; 299 } 300 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)301 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 302 updateNotifications(mCancelExisting); 303 } 304 305 @Override onNothingSelected(AdapterView<?> adapterView)306 public void onNothingSelected(AdapterView<?> adapterView) { 307 } 308 } 309 310 private class UpdateNotificationsOnCheckedChangeListener 311 implements CompoundButton.OnCheckedChangeListener { 312 private final boolean mCancelExisting; 313 UpdateNotificationsOnCheckedChangeListener(boolean cancelExisting)314 public UpdateNotificationsOnCheckedChangeListener(boolean cancelExisting) { 315 mCancelExisting = cancelExisting; 316 } 317 318 @Override onCheckedChanged(CompoundButton compoundButton, boolean checked)319 public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { 320 updateNotifications(mCancelExisting); 321 } 322 } 323 324 private class BackgroundPickerListener 325 implements BackgroundPickers.OnBackgroundPickersChangedListener { 326 @Override onBackgroundPickersChanged(BackgroundPickers pickers)327 public void onBackgroundPickersChanged(BackgroundPickers pickers) { 328 updateNotifications(false /* cancelExisting */); 329 } 330 } 331 332 private class NamedPresetSpinnerArrayAdapter extends ArrayAdapter<NamedPreset> { NamedPresetSpinnerArrayAdapter(Context context, NamedPreset[] presets)333 public NamedPresetSpinnerArrayAdapter(Context context, NamedPreset[] presets) { 334 super(context, R.layout.simple_spinner_item, presets); 335 } 336 337 @Override getDropDownView(int position, View convertView, ViewGroup parent)338 public View getDropDownView(int position, View convertView, ViewGroup parent) { 339 TextView view = (TextView) super.getDropDownView(position, convertView, parent); 340 view.setText(getString(getItem(position).nameResId)); 341 return view; 342 } 343 344 @Override getView(int position, View convertView, ViewGroup parent)345 public View getView(int position, View convertView, ViewGroup parent) { 346 TextView view = (TextView) getLayoutInflater().inflate( 347 android.R.layout.simple_spinner_item, parent, false); 348 view.setText(getString(getItem(position).nameResId)); 349 return view; 350 } 351 } 352 } 353