blob: c45ca54024db9ac1f1b7fb5826a33f9d540db5f9 [file] [log] [blame]
Mady Mellor04d7a0f2017-01-25 13:16:03 -08001package com.android.systemui.statusbar;
2/*
3 * Copyright (C) 2017 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License
16 */
17
18import java.util.ArrayList;
Mady Mellor754d8222017-01-25 15:29:39 -080019import java.util.List;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080020
Mady Mellor95d743c2017-01-10 12:05:27 -080021import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
22import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080023
Mady Mellor920fd892017-06-06 09:35:03 -070024import android.animation.Animator;
25import android.animation.AnimatorListenerAdapter;
Mady Mellore09fb702017-03-30 13:23:29 -070026import android.animation.AnimatorSet;
27import android.animation.ObjectAnimator;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080028import android.content.Context;
29import android.content.res.Resources;
Mady Mellore09fb702017-03-30 13:23:29 -070030import android.graphics.Typeface;
Mady Mellor920fd892017-06-06 09:35:03 -070031import android.os.Bundle;
Mady Mellor754d8222017-01-25 15:29:39 -080032import android.service.notification.SnoozeCriterion;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080033import android.service.notification.StatusBarNotification;
Mady Mellore09fb702017-03-30 13:23:29 -070034import android.text.SpannableString;
35import android.text.style.StyleSpan;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080036import android.util.AttributeSet;
Mady Mellor754d8222017-01-25 15:29:39 -080037import android.util.Log;
Mady Mellore09fb702017-03-30 13:23:29 -070038import android.view.LayoutInflater;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080039import android.view.View;
40import android.view.ViewGroup;
Mady Mellor920fd892017-06-06 09:35:03 -070041import android.view.accessibility.AccessibilityEvent;
42import android.view.accessibility.AccessibilityNodeInfo;
43import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
Mady Mellore09fb702017-03-30 13:23:29 -070044import android.widget.ImageView;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080045import android.widget.LinearLayout;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080046import android.widget.TextView;
Mady Mellore09fb702017-03-30 13:23:29 -070047
48import com.android.systemui.Interpolators;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080049import com.android.systemui.R;
50
51public class NotificationSnooze extends LinearLayout
Mady Mellor95d743c2017-01-10 12:05:27 -080052 implements NotificationGuts.GutsContent, View.OnClickListener {
Mady Mellor04d7a0f2017-01-25 13:16:03 -080053
Mady Mellor920fd892017-06-06 09:35:03 -070054 /**
55 * If this changes more number increases, more assistant action resId's should be defined for
56 * accessibility purposes, see {@link #setSnoozeOptions(List)}
57 */
Mady Mellore09fb702017-03-30 13:23:29 -070058 private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
Mady Mellor95d743c2017-01-10 12:05:27 -080059 private NotificationGuts mGutsContainer;
60 private NotificationSwipeActionHelper mSnoozeListener;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080061 private StatusBarNotification mSbn;
62
Mady Mellor754d8222017-01-25 15:29:39 -080063 private TextView mSelectedOptionText;
64 private TextView mUndoButton;
Mady Mellore09fb702017-03-30 13:23:29 -070065 private ImageView mExpandButton;
66 private View mDivider;
67 private ViewGroup mSnoozeOptionContainer;
Mady Mellor754d8222017-01-25 15:29:39 -080068 private List<SnoozeOption> mSnoozeOptions;
Mady Mellore09fb702017-03-30 13:23:29 -070069 private int mCollapsedHeight;
Mady Mellor651fbde2017-05-18 14:02:51 -070070 private SnoozeOption mDefaultOption;
Mady Mellor754d8222017-01-25 15:29:39 -080071 private SnoozeOption mSelectedOption;
Mady Mellore09fb702017-03-30 13:23:29 -070072 private boolean mSnoozing;
73 private boolean mExpanded;
74 private AnimatorSet mExpandAnimation;
Mady Mellor04d7a0f2017-01-25 13:16:03 -080075
76 public NotificationSnooze(Context context, AttributeSet attrs) {
77 super(context, attrs);
78 }
79
80 @Override
81 protected void onFinishInflate() {
82 super.onFinishInflate();
Mady Mellore09fb702017-03-30 13:23:29 -070083 mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
84 findViewById(R.id.notification_snooze).setOnClickListener(this);
85 mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
86 mUndoButton = (TextView) findViewById(R.id.undo);
87 mUndoButton.setOnClickListener(this);
88 mExpandButton = (ImageView) findViewById(R.id.expand_button);
89 mDivider = findViewById(R.id.divider);
90 mDivider.setAlpha(0f);
91 mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
Mady Mellor920fd892017-06-06 09:35:03 -070092 mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
Mady Mellore09fb702017-03-30 13:23:29 -070093 mSnoozeOptionContainer.setAlpha(0f);
94
Mady Mellor04d7a0f2017-01-25 13:16:03 -080095 // Create the different options based on list
Mady Mellor754d8222017-01-25 15:29:39 -080096 mSnoozeOptions = getDefaultSnoozeOptions();
Mady Mellor04d7a0f2017-01-25 13:16:03 -080097 createOptionViews();
98
Mady Mellor651fbde2017-05-18 14:02:51 -070099 setSelected(mDefaultOption);
Mady Mellor754d8222017-01-25 15:29:39 -0800100 }
101
Mady Mellor920fd892017-06-06 09:35:03 -0700102 @Override
103 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
104 super.onInitializeAccessibilityEvent(event);
105 if (mGutsContainer != null && mGutsContainer.isExposed()) {
106 if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
107 event.getText().add(mSelectedOptionText.getText());
108 }
109 }
110 }
111
112 @Override
113 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
114 super.onInitializeAccessibilityNodeInfo(info);
115 info.addAction(new AccessibilityAction(R.id.action_snooze_undo,
116 getResources().getString(R.string.snooze_undo)));
117 int count = mSnoozeOptions.size();
118 for (int i = 0; i < count; i++) {
119 AccessibilityAction action = mSnoozeOptions.get(i).getAccessibilityAction();
120 if (action != null) {
121 info.addAction(action);
122 }
123 }
124 }
125
126 @Override
127 public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
128 if (super.performAccessibilityActionInternal(action, arguments)) {
129 return true;
130 }
131 if (action == R.id.action_snooze_undo) {
132 undoSnooze(mUndoButton);
133 return true;
134 }
135 for (int i = 0; i < mSnoozeOptions.size(); i++) {
136 SnoozeOption so = mSnoozeOptions.get(i);
137 if (so.getAccessibilityAction() != null
138 && so.getAccessibilityAction().getId() == action) {
139 setSelected(so);
140 return true;
141 }
142 }
143 return false;
144 }
145
Mady Mellor754d8222017-01-25 15:29:39 -0800146 public void setSnoozeOptions(final List<SnoozeCriterion> snoozeList) {
147 if (snoozeList == null) {
148 return;
149 }
150 mSnoozeOptions.clear();
151 mSnoozeOptions = getDefaultSnoozeOptions();
152 final int count = Math.min(MAX_ASSISTANT_SUGGESTIONS, snoozeList.size());
153 for (int i = 0; i < count; i++) {
154 SnoozeCriterion sc = snoozeList.get(i);
Mady Mellor920fd892017-06-06 09:35:03 -0700155 AccessibilityAction action = new AccessibilityAction(
156 R.id.action_snooze_assistant_suggestion_1, sc.getExplanation());
157 mSnoozeOptions.add(new NotificationSnoozeOption(sc, 0, sc.getExplanation(),
158 sc.getConfirmation(), action));
Mady Mellor754d8222017-01-25 15:29:39 -0800159 }
160 createOptionViews();
161 }
162
Mady Mellorc2dbe492017-03-30 13:22:03 -0700163 public boolean isExpanded() {
164 return mExpanded;
165 }
166
167 public void setSnoozeListener(NotificationSwipeActionHelper listener) {
168 mSnoozeListener = listener;
169 }
170
171 public void setStatusBarNotification(StatusBarNotification sbn) {
172 mSbn = sbn;
173 }
174
Mady Mellor754d8222017-01-25 15:29:39 -0800175 private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
176 ArrayList<SnoozeOption> options = new ArrayList<>();
Mady Mellor920fd892017-06-06 09:35:03 -0700177
178 options.add(createOption(15 /* minutes */, R.id.action_snooze_15_min));
179 options.add(createOption(30 /* minutes */, R.id.action_snooze_30_min));
180 mDefaultOption = createOption(60 /* minutes */, R.id.action_snooze_1_hour);
Mady Mellor651fbde2017-05-18 14:02:51 -0700181 options.add(mDefaultOption);
Mady Mellor920fd892017-06-06 09:35:03 -0700182 options.add(createOption(60 * 2 /* minutes */, R.id.action_snooze_2_hours));
Mady Mellor754d8222017-01-25 15:29:39 -0800183 return options;
184 }
185
Mady Mellor920fd892017-06-06 09:35:03 -0700186 private SnoozeOption createOption(int minutes, int accessibilityActionId) {
Mady Mellor754d8222017-01-25 15:29:39 -0800187 Resources res = getResources();
Mady Mellor6143b902017-05-10 17:16:28 -0700188 boolean showInHours = minutes >= 60;
189 int pluralResId = showInHours
190 ? R.plurals.snoozeHourOptions
191 : R.plurals.snoozeMinuteOptions;
192 int count = showInHours ? (minutes / 60) : minutes;
193 String description = res.getQuantityString(pluralResId, count, count);
Mady Mellore09fb702017-03-30 13:23:29 -0700194 String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
195 SpannableString string = new SpannableString(resultText);
196 string.setSpan(new StyleSpan(Typeface.BOLD),
197 resultText.length() - description.length(), resultText.length(), 0 /* flags */);
Mady Mellor920fd892017-06-06 09:35:03 -0700198 AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description);
199 return new NotificationSnoozeOption(null, minutes, description, string,
200 action);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800201 }
202
203 private void createOptionViews() {
Mady Mellore09fb702017-03-30 13:23:29 -0700204 mSnoozeOptionContainer.removeAllViews();
205 LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
206 Context.LAYOUT_INFLATER_SERVICE);
Mady Mellor754d8222017-01-25 15:29:39 -0800207 for (int i = 0; i < mSnoozeOptions.size(); i++) {
208 SnoozeOption option = mSnoozeOptions.get(i);
Mady Mellore09fb702017-03-30 13:23:29 -0700209 TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
210 mSnoozeOptionContainer, false);
211 mSnoozeOptionContainer.addView(tv);
Mady Mellor920fd892017-06-06 09:35:03 -0700212 tv.setText(option.getDescription());
Mady Mellor754d8222017-01-25 15:29:39 -0800213 tv.setTag(option);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800214 tv.setOnClickListener(this);
215 }
Mady Mellore09fb702017-03-30 13:23:29 -0700216 }
Mady Mellor754d8222017-01-25 15:29:39 -0800217
Mady Mellore09fb702017-03-30 13:23:29 -0700218 private void hideSelectedOption() {
219 final int childCount = mSnoozeOptionContainer.getChildCount();
220 for (int i = 0; i < childCount; i++) {
221 final View child = mSnoozeOptionContainer.getChildAt(i);
222 child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
223 }
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800224 }
225
226 private void showSnoozeOptions(boolean show) {
Mady Mellore09fb702017-03-30 13:23:29 -0700227 int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
228 : com.android.internal.R.drawable.ic_expand_notification;
229 mExpandButton.setImageResource(drawableId);
Selim Cineke8ea7f62017-04-28 19:22:17 -0700230 if (mExpanded != show) {
231 mExpanded = show;
232 animateSnoozeOptions(show);
233 if (mGutsContainer != null) {
234 mGutsContainer.onHeightChanged();
235 }
Mady Mellore09fb702017-03-30 13:23:29 -0700236 }
237 }
238
239 private void animateSnoozeOptions(boolean show) {
240 if (mExpandAnimation != null) {
241 mExpandAnimation.cancel();
242 }
243 ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
244 mDivider.getAlpha(), show ? 1f : 0f);
245 ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
246 mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
Mady Mellor920fd892017-06-06 09:35:03 -0700247 mSnoozeOptionContainer.setVisibility(View.VISIBLE);
Mady Mellore09fb702017-03-30 13:23:29 -0700248 mExpandAnimation = new AnimatorSet();
249 mExpandAnimation.playTogether(dividerAnim, optionAnim);
250 mExpandAnimation.setDuration(150);
251 mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
Mady Mellor920fd892017-06-06 09:35:03 -0700252 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
253 boolean cancelled = false;
254
255 @Override
256 public void onAnimationCancel(Animator animation) {
257 cancelled = true;
258 }
259
260 @Override
261 public void onAnimationEnd(Animator animation) {
262 if (!show && !cancelled) {
263 mSnoozeOptionContainer.setVisibility(View.INVISIBLE);
264 mSnoozeOptionContainer.setAlpha(0f);
265 }
266 }
267 });
Mady Mellore09fb702017-03-30 13:23:29 -0700268 mExpandAnimation.start();
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800269 }
270
Mady Mellor754d8222017-01-25 15:29:39 -0800271 private void setSelected(SnoozeOption option) {
272 mSelectedOption = option;
Mady Mellor920fd892017-06-06 09:35:03 -0700273 mSelectedOptionText.setText(option.getConfirmation());
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800274 showSnoozeOptions(false);
Mady Mellore09fb702017-03-30 13:23:29 -0700275 hideSelectedOption();
Mady Mellor920fd892017-06-06 09:35:03 -0700276 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800277 }
278
279 @Override
280 public void onClick(View v) {
Mady Mellor95d743c2017-01-10 12:05:27 -0800281 if (mGutsContainer != null) {
282 mGutsContainer.resetFalsingCheck();
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800283 }
284 final int id = v.getId();
Mady Mellor754d8222017-01-25 15:29:39 -0800285 final SnoozeOption tag = (SnoozeOption) v.getTag();
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800286 if (tag != null) {
Mady Mellor754d8222017-01-25 15:29:39 -0800287 setSelected(tag);
Mady Mellore09fb702017-03-30 13:23:29 -0700288 } else if (id == R.id.notification_snooze) {
289 // Toggle snooze options
290 showSnoozeOptions(!mExpanded);
Mady Mellor754d8222017-01-25 15:29:39 -0800291 } else {
Mady Mellore09fb702017-03-30 13:23:29 -0700292 // Undo snooze was selected
Mady Mellor920fd892017-06-06 09:35:03 -0700293 undoSnooze(v);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800294 }
295 }
296
Mady Mellor920fd892017-06-06 09:35:03 -0700297 private void undoSnooze(View v) {
298 mSelectedOption = null;
299 int[] parentLoc = new int[2];
300 int[] targetLoc = new int[2];
301 mGutsContainer.getLocationOnScreen(parentLoc);
302 v.getLocationOnScreen(targetLoc);
303 final int centerX = v.getWidth() / 2;
304 final int centerY = v.getHeight() / 2;
305 final int x = targetLoc[0] - parentLoc[0] + centerX;
306 final int y = targetLoc[1] - parentLoc[1] + centerY;
307 showSnoozeOptions(false);
308 mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
309 }
310
Mady Mellore09fb702017-03-30 13:23:29 -0700311 @Override
312 public int getActualHeight() {
313 return mExpanded ? getHeight() : mCollapsedHeight;
Mady Mellor754d8222017-01-25 15:29:39 -0800314 }
315
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800316 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800317 public boolean willBeRemoved() {
318 return mSnoozing;
319 }
320
321 @Override
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800322 public View getContentView() {
Mady Mellore09fb702017-03-30 13:23:29 -0700323 // Reset the view before use
Mady Mellor651fbde2017-05-18 14:02:51 -0700324 setSelected(mDefaultOption);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800325 return this;
326 }
327
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800328 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800329 public void setGutsParent(NotificationGuts guts) {
330 mGutsContainer = guts;
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800331 }
332
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800333 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700334 public boolean handleCloseControls(boolean save, boolean force) {
335 if (mExpanded && !force) {
336 // Collapse expanded state on outside touch
337 showSnoozeOptions(false);
338 return true;
339 } else if (mSnoozeListener != null && mSelectedOption != null) {
340 // Snooze option selected so commit it
Mady Mellor434180c2017-02-13 11:29:42 -0800341 mSnoozing = true;
Mady Mellor95d743c2017-01-10 12:05:27 -0800342 mSnoozeListener.snooze(mSbn, mSelectedOption);
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800343 return true;
Mady Mellorc2dbe492017-03-30 13:22:03 -0700344 } else {
345 // The view should actually be closed
346 setSelected(mSnoozeOptions.get(0));
347 return false; // Return false here so that guts handles closing the view
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800348 }
Mady Mellorc2dbe492017-03-30 13:22:03 -0700349 }
350
351 @Override
352 public boolean isLeavebehind() {
353 return true;
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800354 }
Mady Mellor920fd892017-06-06 09:35:03 -0700355
356 public class NotificationSnoozeOption implements SnoozeOption {
357 private SnoozeCriterion mCriterion;
358 private int mMinutesToSnoozeFor;
359 private CharSequence mDescription;
360 private CharSequence mConfirmation;
361 private AccessibilityAction mAction;
362
363 public NotificationSnoozeOption(SnoozeCriterion sc, int minToSnoozeFor,
364 CharSequence description,
365 CharSequence confirmation, AccessibilityAction action) {
366 mCriterion = sc;
367 mMinutesToSnoozeFor = minToSnoozeFor;
368 mDescription = description;
369 mConfirmation = confirmation;
370 mAction = action;
371 }
372
373 @Override
374 public SnoozeCriterion getSnoozeCriterion() {
375 return mCriterion;
376 }
377
378 @Override
379 public CharSequence getDescription() {
380 return mDescription;
381 }
382
383 @Override
384 public CharSequence getConfirmation() {
385 return mConfirmation;
386 }
387
388 @Override
389 public int getMinutesToSnoozeFor() {
390 return mMinutesToSnoozeFor;
391 }
392
393 @Override
394 public AccessibilityAction getAccessibilityAction() {
395 return mAction;
396 }
397
398 }
Mady Mellor04d7a0f2017-01-25 13:16:03 -0800399}