Home | History | Annotate | Download | only in notifications
      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