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