1 package 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 
18 import java.util.ArrayList;
19 import java.util.List;
20 
21 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
22 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
23 
24 import android.animation.AnimatorSet;
25 import android.animation.ObjectAnimator;
26 import android.content.Context;
27 import android.content.res.Resources;
28 import android.graphics.Typeface;
29 import android.service.notification.SnoozeCriterion;
30 import android.service.notification.StatusBarNotification;
31 import android.text.SpannableString;
32 import android.text.style.StyleSpan;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 import android.view.LayoutInflater;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.widget.ImageView;
39 import android.widget.LinearLayout;
40 import android.widget.TextView;
41 
42 import com.android.systemui.Interpolators;
43 import com.android.systemui.R;
44 
45 public class NotificationSnooze extends LinearLayout
46         implements NotificationGuts.GutsContent, View.OnClickListener {
47 
48     private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
49     private NotificationGuts mGutsContainer;
50     private NotificationSwipeActionHelper mSnoozeListener;
51     private StatusBarNotification mSbn;
52 
53     private TextView mSelectedOptionText;
54     private TextView mUndoButton;
55     private ImageView mExpandButton;
56     private View mDivider;
57     private ViewGroup mSnoozeOptionContainer;
58     private List<SnoozeOption> mSnoozeOptions;
59     private int mCollapsedHeight;
60     private SnoozeOption mDefaultOption;
61     private SnoozeOption mSelectedOption;
62     private boolean mSnoozing;
63     private boolean mExpanded;
64     private AnimatorSet mExpandAnimation;
65 
NotificationSnooze(Context context, AttributeSet attrs)66     public NotificationSnooze(Context context, AttributeSet attrs) {
67         super(context, attrs);
68     }
69 
70     @Override
onFinishInflate()71     protected void onFinishInflate() {
72         super.onFinishInflate();
73         mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
74         findViewById(R.id.notification_snooze).setOnClickListener(this);
75         mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
76         mUndoButton = (TextView) findViewById(R.id.undo);
77         mUndoButton.setOnClickListener(this);
78         mExpandButton = (ImageView) findViewById(R.id.expand_button);
79         mDivider = findViewById(R.id.divider);
80         mDivider.setAlpha(0f);
81         mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
82         mSnoozeOptionContainer.setAlpha(0f);
83 
84         // Create the different options based on list
85         mSnoozeOptions = getDefaultSnoozeOptions();
86         createOptionViews();
87 
88         // Default to first option in list
89         setSelected(mDefaultOption);
90     }
91 
setSnoozeOptions(final List<SnoozeCriterion> snoozeList)92     public void setSnoozeOptions(final List<SnoozeCriterion> snoozeList) {
93         if (snoozeList == null) {
94             return;
95         }
96         mSnoozeOptions.clear();
97         mSnoozeOptions = getDefaultSnoozeOptions();
98         final int count = Math.min(MAX_ASSISTANT_SUGGESTIONS, snoozeList.size());
99         for (int i = 0; i < count; i++) {
100             SnoozeCriterion sc = snoozeList.get(i);
101             mSnoozeOptions.add(new SnoozeOption(sc, 0, sc.getExplanation(), sc.getConfirmation()));
102         }
103         createOptionViews();
104     }
105 
isExpanded()106     public boolean isExpanded() {
107         return mExpanded;
108     }
109 
setSnoozeListener(NotificationSwipeActionHelper listener)110     public void setSnoozeListener(NotificationSwipeActionHelper listener) {
111         mSnoozeListener = listener;
112     }
113 
setStatusBarNotification(StatusBarNotification sbn)114     public void setStatusBarNotification(StatusBarNotification sbn) {
115         mSbn = sbn;
116     }
117 
getDefaultSnoozeOptions()118     private ArrayList<SnoozeOption> getDefaultSnoozeOptions() {
119         ArrayList<SnoozeOption> options = new ArrayList<>();
120         options.add(createOption(R.string.snooze_option_15_min, 15));
121         options.add(createOption(R.string.snooze_option_30_min, 30));
122         mDefaultOption = createOption(R.string.snooze_option_1_hour, 60);
123         options.add(mDefaultOption);
124         options.add(createOption(R.string.snooze_option_2_hour, 60 * 2));
125         return options;
126     }
127 
createOption(int descriptionResId, int minutes)128     private SnoozeOption createOption(int descriptionResId, int minutes) {
129         Resources res = getResources();
130         final String description = res.getString(descriptionResId);
131         String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
132         SpannableString string = new SpannableString(resultText);
133         string.setSpan(new StyleSpan(Typeface.BOLD),
134                 resultText.length() - description.length(), resultText.length(), 0 /* flags */);
135         return new SnoozeOption(null, minutes, res.getString(descriptionResId), string);
136     }
137 
createOptionViews()138     private void createOptionViews() {
139         mSnoozeOptionContainer.removeAllViews();
140         LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
141                 Context.LAYOUT_INFLATER_SERVICE);
142         for (int i = 0; i < mSnoozeOptions.size(); i++) {
143             SnoozeOption option = mSnoozeOptions.get(i);
144             TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
145                     mSnoozeOptionContainer, false);
146             mSnoozeOptionContainer.addView(tv);
147             tv.setText(option.description);
148             tv.setTag(option);
149             tv.setOnClickListener(this);
150         }
151     }
152 
hideSelectedOption()153     private void hideSelectedOption() {
154         final int childCount = mSnoozeOptionContainer.getChildCount();
155         for (int i = 0; i < childCount; i++) {
156             final View child = mSnoozeOptionContainer.getChildAt(i);
157             child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
158         }
159     }
160 
showSnoozeOptions(boolean show)161     private void showSnoozeOptions(boolean show) {
162         int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
163                 : com.android.internal.R.drawable.ic_expand_notification;
164         mExpandButton.setImageResource(drawableId);
165         if (mExpanded != show) {
166             mExpanded = show;
167             animateSnoozeOptions(show);
168             if (mGutsContainer != null) {
169                 mGutsContainer.onHeightChanged();
170             }
171         }
172     }
173 
animateSnoozeOptions(boolean show)174     private void animateSnoozeOptions(boolean show) {
175         if (mExpandAnimation != null) {
176             mExpandAnimation.cancel();
177         }
178         ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
179                 mDivider.getAlpha(), show ? 1f : 0f);
180         ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
181                 mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
182         mExpandAnimation = new AnimatorSet();
183         mExpandAnimation.playTogether(dividerAnim, optionAnim);
184         mExpandAnimation.setDuration(150);
185         mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
186         mExpandAnimation.start();
187     }
188 
setSelected(SnoozeOption option)189     private void setSelected(SnoozeOption option) {
190         mSelectedOption = option;
191         mSelectedOptionText.setText(option.confirmation);
192         showSnoozeOptions(false);
193         hideSelectedOption();
194     }
195 
196     @Override
onClick(View v)197     public void onClick(View v) {
198         if (mGutsContainer != null) {
199             mGutsContainer.resetFalsingCheck();
200         }
201         final int id = v.getId();
202         final SnoozeOption tag = (SnoozeOption) v.getTag();
203         if (tag != null) {
204             setSelected(tag);
205         } else if (id == R.id.notification_snooze) {
206             // Toggle snooze options
207             showSnoozeOptions(!mExpanded);
208         } else {
209             // Undo snooze was selected
210             mSelectedOption = null;
211             int[] parentLoc = new int[2];
212             int[] targetLoc = new int[2];
213             mGutsContainer.getLocationOnScreen(parentLoc);
214             v.getLocationOnScreen(targetLoc);
215             final int centerX = v.getWidth() / 2;
216             final int centerY = v.getHeight() / 2;
217             final int x = targetLoc[0] - parentLoc[0] + centerX;
218             final int y = targetLoc[1] - parentLoc[1] + centerY;
219             showSnoozeOptions(false);
220             mGutsContainer.closeControls(x, y, false /* save */, false /* force */);
221         }
222     }
223 
224     @Override
getActualHeight()225     public int getActualHeight() {
226         return mExpanded ? getHeight() : mCollapsedHeight;
227     }
228 
229     @Override
willBeRemoved()230     public boolean willBeRemoved() {
231         return mSnoozing;
232     }
233 
234     @Override
getContentView()235     public View getContentView() {
236         // Reset the view before use
237         setSelected(mDefaultOption);
238         return this;
239     }
240 
241     @Override
setGutsParent(NotificationGuts guts)242     public void setGutsParent(NotificationGuts guts) {
243         mGutsContainer = guts;
244     }
245 
246     @Override
handleCloseControls(boolean save, boolean force)247     public boolean handleCloseControls(boolean save, boolean force) {
248         if (mExpanded && !force) {
249             // Collapse expanded state on outside touch
250             showSnoozeOptions(false);
251             return true;
252         } else if (mSnoozeListener != null && mSelectedOption != null) {
253             // Snooze option selected so commit it
254             mSnoozing = true;
255             mSnoozeListener.snooze(mSbn, mSelectedOption);
256             return true;
257         } else {
258             // The view should actually be closed
259             setSelected(mSnoozeOptions.get(0));
260             return false; // Return false here so that guts handles closing the view
261         }
262     }
263 
264     @Override
isLeavebehind()265     public boolean isLeavebehind() {
266         return true;
267     }
268 }
269