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