diff options
22 files changed, 1501 insertions, 814 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java new file mode 100644 index 000000000000..c0a48a873780 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowProvider.java @@ -0,0 +1,74 @@ + +package com.android.systemui.plugins.statusbar; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.service.notification.StatusBarNotification; +import android.view.View; + +import java.util.ArrayList; + +import com.android.systemui.plugins.Plugin; + +public interface NotificationMenuRowProvider extends Plugin { + + public static final String ACTION = "com.android.systemui.action.PLUGIN_NOTIFICATION_MENU_ROW"; + + public static final int VERSION = 1; + + /** + * Returns a list of items to populate the menu 'behind' a notification. + */ + public ArrayList<MenuItem> getMenuItems(Context context); + + public interface OnMenuClickListener { + public void onMenuClicked(View row, int x, int y, MenuItem menu); + + public void onMenuReset(View row); + } + + public interface GutsInteractionListener { + public void onInteraction(View view); + + public void closeGuts(View view); + } + + public interface GutsContent { + public void setInteractionListener(GutsInteractionListener listener); + + public View getContentView(); + + public boolean handleCloseControls(); + } + + public interface SnoozeGutsContent extends GutsContent { + public void setSnoozeListener(SnoozeListener listener); + + public void setStatusBarNotification(StatusBarNotification sbn); + } + + public interface SnoozeListener { + public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil); + } + + public static class MenuItem { + public Drawable icon; + public String menuDescription; + public View menuView; + public GutsContent gutsContent; + + public MenuItem(Drawable i, String s, GutsContent content) { + icon = i; + menuDescription = s; + gutsContent = content; + } + + public View getGutsView() { + return gutsContent.getContentView(); + } + + public boolean onTouch(View v, int x, int y) { + return false; + } + } +} diff --git a/packages/SystemUI/res/drawable/ic_snooze.xml b/packages/SystemUI/res/drawable/ic_snooze.xml new file mode 100644 index 000000000000..b0b03a99f4a5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_snooze.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" + android:fillColor="#757575"/> + <path + android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z" + android:fillColor="#757575"/> +</vector> diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 3948dc4b8771..9d8ef83ab7e6 100644 --- a/packages/SystemUI/res/layout/notification_guts.xml +++ b/packages/SystemUI/res/layout/notification_guts.xml @@ -23,125 +23,4 @@ android:visibility="gone" android:clickable="true" android:gravity="top|start" - android:orientation="vertical" - android:paddingStart="@*android:dimen/notification_content_margin_start" - android:paddingEnd="8dp" - android:background="@color/notification_guts_bg_color" - android:theme="@*android:style/Theme.DeviceDefault.Light"> - - <!-- header --> - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="20dp" - android:paddingEnd="8dp" - android:paddingBottom="15dp" - android:id="@+id/notification_guts_header"> - <TextView - android:id="@+id/pkgname" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - style="@style/TextAppearance.NotificationGuts.Secondary" /> - <TextView - android:id="@+id/channel_name" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_below="@id/pkgname" - style="@style/TextAppearance.NotificationGuts.Header" /> - <Switch - android:id="@+id/channel_enabled_switch" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:background="@null" /> - </RelativeLayout> - <!-- Importance radio buttons --> - <LinearLayout - android:id="@+id/importance" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <RadioGroup - android:id="@+id/importance_buttons" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingEnd="@*android:dimen/notification_content_margin_end"> - <RadioButton - android:id="@+id/high_importance" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_inline_importance_height" - style="@style/TextAppearance.NotificationGuts.Radio" - android:buttonTint="@color/notification_guts_buttons" /> - <RadioButton - android:id="@+id/default_importance" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_inline_importance_height" - style="@style/TextAppearance.NotificationGuts.Radio" - android:buttonTint="@color/notification_guts_buttons" /> - <RadioButton - android:id="@+id/low_importance" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_inline_importance_height" - style="@style/TextAppearance.NotificationGuts.Radio" - android:buttonTint="@color/notification_guts_buttons" /> - <RadioButton - android:id="@+id/min_importance" - android:layout_width="wrap_content" - android:layout_height="@dimen/notification_inline_importance_height" - style="@style/TextAppearance.NotificationGuts.Radio" - android:buttonTint="@color/notification_guts_buttons" /> - </RadioGroup> - <LinearLayout - android:id="@+id/importance_buttons_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - <include layout="@layout/notification_guts_importance_text"/> - <include layout="@layout/notification_guts_importance_text"/> - <include layout="@layout/notification_guts_importance_text"/> - <include layout="@layout/notification_guts_importance_text"/> - </LinearLayout> - </LinearLayout> - <!-- Channel Disabled Text --> - <TextView - android:id="@+id/channel_disabled" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/notification_channel_disabled" - style="@style/TextAppearance.NotificationGuts.Secondary" /> - <!-- Settings and Done buttons --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="end" - android:paddingTop="16dp" - android:paddingBottom="8dp" > - - <TextView - android:id="@+id/more_settings" - android:text="@string/notification_more_settings" - android:layout_width="wrap_content" - android:layout_height="36dp" - style="@style/TextAppearance.NotificationGuts.Button" - android:background="@drawable/btn_borderless_rect" - android:gravity="center" - android:paddingEnd="8dp" - android:paddingStart="8dp" - android:focusable="true" /> - - <TextView - android:id="@+id/done" - android:text="@string/notification_done" - android:layout_width="wrap_content" - android:layout_height="36dp" - style="@style/TextAppearance.NotificationGuts.Button" - android:background="@drawable/btn_borderless_rect" - android:gravity="center" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:focusable="true"/> - </LinearLayout> -</com.android.systemui.statusbar.NotificationGuts> + android:theme="@*android:style/Theme.DeviceDefault.Light"/> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml new file mode 100644 index 000000000000..9770eccfd2c1 --- /dev/null +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2017, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.systemui.statusbar.NotificationInfo + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/notification_guts" + android:clickable="true" + android:gravity="top|start" + android:orientation="vertical" + android:paddingStart="@*android:dimen/notification_content_margin_start" + android:paddingEnd="8dp" + android:background="@color/notification_guts_bg_color" + android:theme="@*android:style/Theme.DeviceDefault.Light"> + + <!-- header --> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="20dp" + android:paddingEnd="8dp" + android:paddingBottom="15dp" + android:id="@+id/notification_guts_header"> + <TextView + android:id="@+id/pkgname" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + style="@style/TextAppearance.NotificationGuts.Secondary" /> + <TextView + android:id="@+id/channel_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentStart="true" + android:layout_below="@id/pkgname" + style="@style/TextAppearance.NotificationGuts.Header" /> + <Switch + android:id="@+id/channel_enabled_switch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:background="@null" /> + </RelativeLayout> + <!-- Importance radio buttons --> + <LinearLayout + android:id="@+id/importance" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <RadioGroup + android:id="@+id/importance_buttons" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingEnd="@*android:dimen/notification_content_margin_end"> + <RadioButton + android:id="@+id/high_importance" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_inline_importance_height" + style="@style/TextAppearance.NotificationGuts.Radio" + android:buttonTint="@color/notification_guts_buttons" /> + <RadioButton + android:id="@+id/default_importance" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_inline_importance_height" + style="@style/TextAppearance.NotificationGuts.Radio" + android:buttonTint="@color/notification_guts_buttons" /> + <RadioButton + android:id="@+id/low_importance" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_inline_importance_height" + style="@style/TextAppearance.NotificationGuts.Radio" + android:buttonTint="@color/notification_guts_buttons" /> + <RadioButton + android:id="@+id/min_importance" + android:layout_width="wrap_content" + android:layout_height="@dimen/notification_inline_importance_height" + style="@style/TextAppearance.NotificationGuts.Radio" + android:buttonTint="@color/notification_guts_buttons" /> + </RadioGroup> + <LinearLayout + android:id="@+id/importance_buttons_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <include layout="@layout/notification_guts_importance_text"/> + <include layout="@layout/notification_guts_importance_text"/> + <include layout="@layout/notification_guts_importance_text"/> + <include layout="@layout/notification_guts_importance_text"/> + </LinearLayout> + </LinearLayout> + <!-- Channel Disabled Text --> + <TextView + android:id="@+id/channel_disabled" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/notification_channel_disabled" + style="@style/TextAppearance.NotificationGuts.Secondary" /> + <!-- Settings and Done buttons --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="end" + android:paddingTop="16dp" + android:paddingBottom="8dp" > + + <TextView + android:id="@+id/more_settings" + android:text="@string/notification_more_settings" + android:layout_width="wrap_content" + android:layout_height="36dp" + style="@style/TextAppearance.NotificationGuts.Button" + android:background="@drawable/btn_borderless_rect" + android:gravity="center" + android:paddingEnd="8dp" + android:paddingStart="8dp" + android:focusable="true" /> + + <TextView + android:id="@+id/done" + android:text="@string/notification_done" + android:layout_width="wrap_content" + android:layout_height="36dp" + style="@style/TextAppearance.NotificationGuts.Button" + android:background="@drawable/btn_borderless_rect" + android:gravity="center" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:focusable="true"/> + </LinearLayout> +</com.android.systemui.statusbar.NotificationInfo> diff --git a/packages/SystemUI/res/layout/notification_settings_icon_row.xml b/packages/SystemUI/res/layout/notification_menu_row.xml index da3461b906fa..12bcf8176561 100644 --- a/packages/SystemUI/res/layout/notification_settings_icon_row.xml +++ b/packages/SystemUI/res/layout/notification_menu_row.xml @@ -14,23 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> -<com.android.systemui.statusbar.NotificationSettingsIconRow +<com.android.systemui.statusbar.NotificationMenuRow xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:visibility="invisible" - > - - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:id="@+id/gear_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="@dimen/notification_gear_padding" - android:src="@drawable/ic_settings" - android:tint="@color/notification_gear_color" - android:alpha="0" - android:background="?android:attr/selectableItemBackgroundBorderless" - /> - -</com.android.systemui.statusbar.NotificationSettingsIconRow>
\ No newline at end of file + android:visibility="invisible"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml new file mode 100644 index 000000000000..5bd64defd2d1 --- /dev/null +++ b/packages/SystemUI/res/layout/notification_snooze.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2017, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.systemui.statusbar.NotificationSnooze + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/snooze_snackbar_min_height" + android:id="@+id/notification_snooze" + android:clickable="true" + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingStart="24dp" + android:paddingEnd="24dp" + android:background="@color/snooze_snackbar_bg"> + + <TextView + android:id="@+id/snooze_option_default" + style="@style/TextAppearance.SnoozeSnackBar" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:drawableTint="@android:color/white" + android:drawableEnd="@drawable/notification_expand_more"/> + + <android.widget.Space + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_weight="1" + /> + + <TextView + android:id="@+id/undo" + style="@style/TextAppearance.SnoozeSnackBar.Button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginEnd="8dp" + android:layout_marginStart="8dp" + android:background="@drawable/btn_borderless_rect" + android:layout_gravity="end" + android:text="@string/snooze_undo" /> + + <LinearLayout + android:id="@+id/snooze_options" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:orientation="vertical"/> + +</com.android.systemui.statusbar.NotificationSnooze> diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml index e456984c5f36..d62cc184889c 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_row.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml @@ -24,10 +24,10 @@ > <ViewStub - android:layout="@layout/notification_settings_icon_row" - android:id="@+id/settings_icon_row_stub" - android:inflatedId="@+id/notification_settings_icon_row" - android:layout_width="wrap_content" + android:layout="@layout/notification_menu_row" + android:id="@+id/menu_row_stub" + android:inflatedId="@+id/notification_menu_row" + android:layout_width="match_parent" android:layout_height="match_parent" /> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 30408143e30d..1ec611a43b56 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -103,6 +103,10 @@ <color name="notification_guts_icon_tint">#8a000000</color> <color name="notification_guts_disabled_icon_tint">#4d000000</color> + <!-- Colors of the snooze menu reached via snooze icon behind a notification --> + <color name="snooze_snackbar_bg">#FF4A4A4A</color> + <color name="snooze_snackbar_text">#FFA6BAFF</color> + <color name="assist_orb_color">#ffffff</color> <color name="keyguard_user_switcher_background_gradient_color">#77000000</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 79529a7abcbc..ddcc4baf8350 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -106,11 +106,20 @@ <!-- Minimum layouted height of a notification in the statusbar--> <dimen name="min_notification_layout_height">48dp</dimen> - <!-- Width of the space containing the gear icon behind a notification --> - <dimen name="notification_gear_width">64dp</dimen> + <!-- Size of the space to place a notification menu item --> + <dimen name="notification_menu_icon_size">64dp</dimen> - <!-- The space around the gear icon displayed behind a notification --> - <dimen name="notification_gear_padding">20dp</dimen> + <!-- The space around a notification menu item --> + <dimen name="notification_menu_icon_padding">20dp</dimen> + + <!-- The minimum height for the snackbar shown after the snooze option has been chosen. --> + <dimen name="snooze_snackbar_min_height">48dp</dimen> + + <!-- The text size of options in the snooze menu. --> + <dimen name="snooze_option_text_size">14sp</dimen> + + <!-- The padding around options int the snooze menu. --> + <dimen name="snooze_option_padding">8dp</dimen> <!-- size at which Notification icons will be drawn in the status bar --> <dimen name="status_bar_icon_drawing_size">17dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 422431e7b31a..b05cc009a2fa 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1375,8 +1375,26 @@ <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> <string name="notification_done">Done</string> - <!-- Notification: Gear: Content description for the gear. [CHAR LIMIT=NONE] --> - <string name="notification_gear_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> notification controls</string> + <!-- Notification: Menu row: Content description for menu items. [CHAR LIMIT=NONE] --> + <string name="notification_menu_accessibility"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> <xliff:g id="menu_description" example="notification controls">%2$s</xliff:g></string> + + <!-- Notification: Menu row: Content description for the gear menu item. [CHAR LIMIT=NONE] --> + <string name="notification_menu_gear_description">notification controls</string> + + <!-- Notification: Menu row: Content description for the snooze icon. [CHAR LIMIT=NONE] --> + <string name="notification_menu_snooze_description">notification snooze options</string> + + <!-- Notification: Menu row: Snooze options: 15 minute option. [CHAR LIMIT=50]--> + <string name="snooze_option_15_min">15 minutes</string> + <!-- Notification: Menu row: Snooze options: 30 minute option. [CHAR LIMIT=50]--> + <string name="snooze_option_30_min">30 minutes</string> + <!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]--> + <string name="snooze_option_1_hour">1 hour</string> + <!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]--> + <string name="snooze_undo">UNDO</string> + + <!-- Notification: Menu row: Snooze: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]--> + <string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string> <!-- Title of the battery settings detail panel [CHAR LIMIT=20] --> <string name="battery_panel_title">Battery usage</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 6535a81d076b..c5a5518226d9 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -383,6 +383,20 @@ <item name="android:textColor">?android:attr/colorAccent</item> </style> + <style name="TextAppearance.SnoozeSnackBar"> + <item name="android:textSize">14sp</item> + <item name="android:fontFamily">roboto-regular</item> + <item name="android:textColor">@android:color/white</item> + </style> + + <style name="TextAppearance.SnoozeSnackBar.Button"> + <item name="android:textSize">14sp</item> + <item name="android:textAllCaps">true</item> + <item name="android:fontFamily">sans-serif-medium</item> + <item name="android:gravity">center</item> + <item name="android:textColor">@color/snooze_snackbar_text</item> + </style> + <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings"> <item name="android:colorBackground">?android:attr/colorSecondary</item> </style> diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 5c50b5ce47d9..a95713fb017e 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -32,7 +32,9 @@ import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem; import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.statusbar.NotificationMenuRow; import java.util.HashMap; @@ -85,10 +87,12 @@ public class SwipeHelper implements Gefingerpoken { private int mFalsingThreshold; private boolean mTouchAboveFalsingThreshold; private boolean mDisableHwLayers; + private Context mContext; private HashMap<View, Animator> mDismissPendingMap = new HashMap<>(); public SwipeHelper(int swipeDirection, Callback callback, Context context) { + mContext = context; mCallback = callback; mHandler = new Handler(); mSwipeDirection = swipeDirection; @@ -249,6 +253,7 @@ public class SwipeHelper implements Gefingerpoken { } } + @Override public boolean onInterceptTouchEvent(final MotionEvent ev) { final int action = ev.getAction(); @@ -280,7 +285,8 @@ public class SwipeHelper implements Gefingerpoken { mCurrView.getLocationOnScreen(mTmpPos); final int x = (int) ev.getRawX() - mTmpPos[0]; final int y = (int) ev.getRawY() - mTmpPos[1]; - mLongPressListener.onLongPress(mCurrView, x, y); + mLongPressListener.onLongPress(mCurrView, x, y, + NotificationMenuRow.getLongpressMenuItem(mContext)); } } }; @@ -379,6 +385,7 @@ public class SwipeHelper implements Gefingerpoken { animView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { + @Override public void onAnimationUpdate(ValueAnimator animation) { onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed); } @@ -401,10 +408,12 @@ public class SwipeHelper implements Gefingerpoken { anim.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; + @Override public void onAnimationCancel(Animator animation) { mCancelled = true; } + @Override public void onAnimationEnd(Animator animation) { updateSwipeProgressFromOffset(animView, canBeDismissed); mDismissPendingMap.remove(animView); @@ -435,6 +444,7 @@ public class SwipeHelper implements Gefingerpoken { public void snapChild(final View animView, final float targetLeft, float velocity) { final boolean canBeDismissed = mCallback.canChildBeDismissed(animView); AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { + @Override public void onAnimationUpdate(ValueAnimator animation) { onTranslationUpdate(animView, (float) animation.getAnimatedValue(), canBeDismissed); } @@ -447,6 +457,7 @@ public class SwipeHelper implements Gefingerpoken { int duration = SNAP_ANIM_LEN; anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { + @Override public void onAnimationEnd(Animator animator) { mSnappingChild = false; updateSwipeProgressFromOffset(animView, canBeDismissed); @@ -522,6 +533,7 @@ public class SwipeHelper implements Gefingerpoken { } } + @Override public boolean onTouchEvent(MotionEvent ev) { if (mLongPressSent) { return true; @@ -690,6 +702,6 @@ public class SwipeHelper implements Gefingerpoken { * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates * @return whether the longpress was handled */ - boolean onLongPress(View v, int x, int y); + boolean onLongPress(View v, int x, int y, MenuItem item); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 926f610d8850..f0de6961658d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -101,6 +101,10 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SwipeHelper; import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeGutsContent; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener; import com.android.systemui.recents.Recents; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.notification.VisualStabilityManager; @@ -247,6 +251,7 @@ public abstract class BaseStatusBar extends SystemUI implements // which notification is currently being longpress-examined by the user private NotificationGuts mNotificationGutsExposed; + private MenuItem mGutsMenuItem; private KeyboardShortcuts mKeyboardShortcuts; @@ -702,6 +707,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } + @Override public void start() { mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); @@ -967,7 +973,7 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row.reInflateViews(); if (exposedGuts) { mNotificationGutsExposed = entry.row.getGuts(); - bindGuts(entry.row); + bindGuts(entry.row, mGutsMenuItem); } inflateViews(entry, mStackScroller); } @@ -1032,6 +1038,7 @@ public abstract class BaseStatusBar extends SystemUI implements @Override public boolean onDismiss() { AsyncTask.execute(new Runnable() { + @Override public void run() { TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) @@ -1045,11 +1052,18 @@ public abstract class BaseStatusBar extends SystemUI implements }, false /* afterKeyguardGone */); } - private void bindGuts(final ExpandableNotificationRow row) { + protected void setNotificationSnoozed(StatusBarNotification sbn, long snoozeUntil) { + mNotificationListener.snoozeNotification(sbn.getKey(), snoozeUntil); + } + + public SnoozeListener getSnoozeListener() { + return null; + } + + private void bindGuts(final ExpandableNotificationRow row, MenuItem item) { row.inflateGuts(); + row.setGutsView(item); final StatusBarNotification sbn = row.getStatusBarNotification(); - final NotificationChannel channel = row.getEntry().channel; - PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier()); row.setTag(sbn.getPackageName()); final NotificationGuts guts = row.getGuts(); guts.setClosedListener((NotificationGuts g) -> { @@ -1059,43 +1073,53 @@ public abstract class BaseStatusBar extends SystemUI implements mNotificationGutsExposed = null; }); - final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - - final String pkg = sbn.getPackageName(); - final NotificationGuts.OnSettingsClickListener onSettingsClick = - (View v, int appUid) -> { - MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO); - guts.resetFalsingCheck(); - startAppNotificationSettingsActivity(pkg, appUid); - }; - final View.OnClickListener onDoneClick = - (View v) -> { - // If the user has security enabled, show challenge if the setting is changed. - if (guts.hasImportanceChanged() - && isLockscreenPublicMode(sbn.getUser().getIdentifier()) - && (mState == StatusBarState.KEYGUARD - || mState == StatusBarState.SHADE_LOCKED)) { - OnDismissAction dismissAction = new OnDismissAction() { - @Override - public boolean onDismiss() { - closeControls(row, guts, v); - return true; - } - }; - onLockedNotificationImportanceChange(dismissAction); - } else { - closeControls(row, guts, v); - } - }; - guts.bindNotification(pmUser, iNotificationManager, sbn, channel, - onSettingsClick, onDoneClick, mNonBlockablePkgs); + if (item.gutsContent instanceof SnoozeGutsContent) { + ((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener()); + ((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn); + } + + if (item.gutsContent instanceof NotificationInfo) { + final NotificationChannel channel = row.getEntry().channel; + PackageManager pmUser = getPackageManagerForUser(mContext, + sbn.getUser().getIdentifier()); + final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + final String pkg = sbn.getPackageName(); + NotificationInfo info = (NotificationInfo) item.gutsContent; + final NotificationInfo.OnSettingsClickListener onSettingsClick = (View v, + int appUid) -> { + MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO); + guts.resetFalsingCheck(); + startAppNotificationSettingsActivity(pkg, appUid); + }; + final View.OnClickListener onDoneClick = (View v) -> { + // If the user has security enabled, show challenge if the setting is changed. + if (info.hasImportanceChanged() + && isLockscreenPublicMode(sbn.getUser().getIdentifier()) + && (mState == StatusBarState.KEYGUARD + || mState == StatusBarState.SHADE_LOCKED)) { + OnDismissAction dismissAction = new OnDismissAction() { + @Override + public boolean onDismiss() { + saveAndCloseNotificationMenu(info, row, guts, v); + return true; + } + }; + onLockedNotificationImportanceChange(dismissAction); + } else { + saveAndCloseNotificationMenu(info, row, guts, v); + } + }; + info.bindNotification(pmUser, iNotificationManager, sbn, channel, onSettingsClick, + onDoneClick, + mNonBlockablePkgs); + } } - private void closeControls( + private void saveAndCloseNotificationMenu(NotificationInfo info, ExpandableNotificationRow row, NotificationGuts guts, View done) { guts.resetFalsingCheck(); - + info.saveImportance(); int[] rowLocation = new int[2]; int[] doneLocation = new int[2]; row.getLocationOnScreen(rowLocation); @@ -1111,7 +1135,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected SwipeHelper.LongPressListener getNotificationLongClicker() { return new SwipeHelper.LongPressListener() { @Override - public boolean onLongPress(View v, final int x, final int y) { + public boolean onLongPress(View v, final int x, final int y, + MenuItem item) { if (!(v instanceof ExpandableNotificationRow)) { return false; } @@ -1121,10 +1146,10 @@ public abstract class BaseStatusBar extends SystemUI implements } final ExpandableNotificationRow row = (ExpandableNotificationRow) v; - bindGuts(row); + bindGuts(row, item); + NotificationGuts guts = row.getGuts(); // Assume we are a status_bar_notification_row - final NotificationGuts guts = row.getGuts(); if (guts == null) { // This view has no guts. Examples are the more card or the dismiss all view return false; @@ -1142,6 +1167,7 @@ public abstract class BaseStatusBar extends SystemUI implements guts.setVisibility(View.INVISIBLE); // Post to ensure the the guts are properly laid out. guts.post(new Runnable() { + @Override public void run() { if (row.getWindowToken() == null) { Log.e(TAG, "Trying to show notification guts, but not attached to " @@ -1172,6 +1198,7 @@ public abstract class BaseStatusBar extends SystemUI implements row.closeRemoteInput(); mStackScroller.onHeightChanged(row, true /* needsAnimation */); mNotificationGutsExposed = guts; + mGutsMenuItem = item; } }); return true; @@ -1483,6 +1510,7 @@ public abstract class BaseStatusBar extends SystemUI implements } protected class H extends Handler { + @Override public void handleMessage(Message m) { switch (m.what) { case MSG_SHOW_RECENT_APPS: @@ -1754,6 +1782,7 @@ public abstract class BaseStatusBar extends SystemUI implements && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mCurrentUserId); dismissKeyguardThenExecute(new OnDismissAction() { + @Override public boolean onDismiss() { new Thread() { @Override @@ -1804,6 +1833,7 @@ public abstract class BaseStatusBar extends SystemUI implements private final class NotificationClicker implements View.OnClickListener { private final int[] mTmpInt2 = new int[2]; + @Override public void onClick(final View v) { if (!(v instanceof ExpandableNotificationRow)) { Log.e(TAG, "NotificationClicker called on a view that is not a notification row."); @@ -1847,6 +1877,7 @@ public abstract class BaseStatusBar extends SystemUI implements && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mCurrentUserId); dismissKeyguardThenExecute(new OnDismissAction() { + @Override public boolean onDismiss() { if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { // Release the HUN notification to the shade. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 013554c16f9a..f56d29d123c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -51,6 +51,7 @@ import com.android.internal.widget.CachingIconView; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem; import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.VisualStabilityManager; @@ -120,7 +121,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private int mNotificationColor; private ExpansionLogger mLogger; private String mLoggingKey; - private NotificationSettingsIconRow mSettingsIconRow; + private NotificationMenuRow mMenuRow; private NotificationGuts mGuts; private NotificationData.Entry mEntry; private StatusBarNotification mStatusBarNotification; @@ -132,7 +133,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private boolean mChildrenExpanded; private boolean mIsSummaryWithChildren; private NotificationChildrenContainer mChildrenContainer; - private ViewStub mSettingsIconRowStub; + private ViewStub mMenuRowStub; private ViewStub mGutsStub; private boolean mIsSystemChildExpanded; private boolean mIsPinned; @@ -205,6 +206,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private Runnable mOnDismissRunnable; private boolean mIsLowPriority; + @Override public boolean isGroupExpansionChanging() { if (isChildInGroup()) { return mNotificationParent.isGroupExpansionChanging(); @@ -386,8 +388,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { public void setAppName(String appName) { mAppName = appName; - if (mSettingsIconRow != null) { - mSettingsIconRow.setAppName(mAppName); + if (mMenuRow != null) { + mMenuRow.setAppName(mAppName); } } @@ -418,6 +420,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { row.setIsChildInGroup(false, null); } + @Override public boolean isChildInGroup() { return mNotificationParent != null; } @@ -455,7 +458,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override protected boolean handleSlideBack() { - if (mSettingsIconRow != null && mSettingsIconRow.isVisible()) { + if (mMenuRow != null && mMenuRow.isVisible()) { animateTranslateNotification(0 /* targetLeft */); return true; } @@ -679,6 +682,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mHeadsUpManager = headsUpManager; } + public void setGutsView(MenuItem item) { + if (mGuts != null) { + item.gutsContent.setInteractionListener(mGuts); + mGuts.setGutsContent(item.gutsContent); + } + } + public void reInflateViews() { initDimens(); if (mIsSummaryWithChildren) { @@ -695,16 +705,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mGuts.setVisibility(oldGuts.getVisibility()); addView(mGuts, index); } - if (mSettingsIconRow != null) { - View oldSettings = mSettingsIconRow; - int settingsIndex = indexOfChild(oldSettings); - removeView(oldSettings); - mSettingsIconRow = (NotificationSettingsIconRow) LayoutInflater.from(mContext).inflate( - R.layout.notification_settings_icon_row, this, false); - mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this); - mSettingsIconRow.setAppName(mAppName); - mSettingsIconRow.setVisibility(oldSettings.getVisibility()); - addView(mSettingsIconRow, settingsIndex); + if (mMenuRow != null) { + View oldMenu = mMenuRow; + int menuIndex = indexOfChild(oldMenu); + removeView(oldMenu); + mMenuRow = (NotificationMenuRow) LayoutInflater.from(mContext).inflate( + R.layout.notification_menu_row, this, false); + mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this); + mMenuRow.setAppName(mAppName); + mMenuRow.setVisibility(oldMenu.getVisibility()); + addView(mMenuRow, menuIndex); } for (NotificationContentView l : mLayouts) { @@ -1033,21 +1043,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { super.onFinishInflate(); mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic); mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded); - mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout}; for (NotificationContentView l : mLayouts) { l.setExpandClickListener(mExpandClickListener); l.setContainingNotification(this); } - - mSettingsIconRowStub = (ViewStub) findViewById(R.id.settings_icon_row_stub); - mSettingsIconRowStub.setOnInflateListener(new ViewStub.OnInflateListener() { + mMenuRowStub = (ViewStub) findViewById(R.id.menu_row_stub); + mMenuRowStub.setOnInflateListener(new ViewStub.OnInflateListener() { @Override public void onInflate(ViewStub stub, View inflated) { - mSettingsIconRow = (NotificationSettingsIconRow) inflated; - mSettingsIconRow.setNotificationRowParent(ExpandableNotificationRow.this); - mSettingsIconRow.setAppName(mAppName); + mMenuRow = (NotificationMenuRow) inflated; + mMenuRow.setNotificationRowParent(ExpandableNotificationRow.this); + mMenuRow.setAppName(mAppName); } }); mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub); @@ -1079,7 +1087,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mTranslateableViews.add(getChildAt(i)); } // Remove views that don't translate - mTranslateableViews.remove(mSettingsIconRowStub); + mTranslateableViews.remove(mMenuRowStub); mTranslateableViews.remove(mChildrenContainerStub); mTranslateableViews.remove(mGutsStub); } @@ -1094,8 +1102,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } invalidateOutline(); - if (mSettingsIconRow != null) { - mSettingsIconRow.resetState(); + if (mMenuRow != null) { + mMenuRow.resetState(true /* notify */); } } @@ -1122,8 +1130,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } invalidateOutline(); - if (mSettingsIconRow != null) { - mSettingsIconRow.updateSettingsIcons(translationX, getMeasuredWidth()); + if (mMenuRow != null) { + mMenuRow.updateMenuAlpha(translationX, getMeasuredWidth()); } } @@ -1160,8 +1168,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override public void onAnimationEnd(Animator anim) { - if (!cancelled && mSettingsIconRow != null && leftTarget == 0) { - mSettingsIconRow.resetState(); + if (!cancelled && mMenuRow != null && leftTarget == 0) { + mMenuRow.resetState(true /* notify */); mTranslateAnim = null; } } @@ -1171,17 +1179,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } public float getSpaceForGear() { - if (mSettingsIconRow != null) { - return mSettingsIconRow.getSpaceForGear(); + if (mMenuRow != null) { + return mMenuRow.getSpaceForMenu(); } return 0; } - public NotificationSettingsIconRow getSettingsRow() { - if (mSettingsIconRow == null) { - mSettingsIconRowStub.inflate(); + public NotificationMenuRow getSettingsRow() { + if (mMenuRow == null) { + mMenuRowStub.inflate(); } - return mSettingsIconRow; + return mMenuRow; } public void inflateGuts() { @@ -1404,6 +1412,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } + @Override public boolean isGroupExpanded() { return mGroupManager.isGroupExpanded(mStatusBarNotification); } @@ -1457,8 +1466,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); updateMaxHeights(); - if (mSettingsIconRow != null) { - mSettingsIconRow.updateVerticalLocation(); + if (mMenuRow != null) { + mMenuRow.updateVerticalLocation(); } updateContentShiftHeight(); } @@ -1505,6 +1514,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mSensitiveHiddenInGeneral = hideSensitive; } + @Override public void setHideSensitiveForIntrinsicHeight(boolean hideSensitive) { mHideSensitiveForIntrinsicHeight = hideSensitive; if (mIsSummaryWithChildren) { @@ -1517,6 +1527,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } + @Override public void setHideSensitive(boolean hideSensitive, boolean animated, long delay, long duration) { boolean oldShowingPublic = mShowingPublic; @@ -1581,6 +1592,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } } + @Override public boolean mustStayOnScreen() { return mIsHeadsUp; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java index 83104e685e22..f6056ddab093 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java @@ -40,6 +40,7 @@ import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.widget.CompoundButton; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RadioButton; @@ -53,6 +54,8 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent; import com.android.systemui.statusbar.stack.StackStateAnimator; import java.util.Set; @@ -60,7 +63,8 @@ import java.util.Set; /** * The guts of a notification revealed when performing a long press. */ -public class NotificationGuts extends LinearLayout { +public class NotificationGuts extends FrameLayout + implements NotificationMenuRowProvider.GutsInteractionListener { private static final String TAG = "NotificationGuts"; private static final long CLOSE_GUTS_DELAY = 8000; @@ -69,24 +73,14 @@ public class NotificationGuts extends LinearLayout { private int mClipBottomAmount; private int mActualHeight; private boolean mExposed; - private INotificationManager mINotificationManager; - private int mStartingUserImportance; - private StatusBarNotification mStatusBarNotification; - private NotificationChannel mNotificationChannel; - - private View mImportanceGroup; - private View mChannelDisabled; - private Switch mChannelEnabledSwitch; - private RadioButton mMinImportanceButton; - private RadioButton mLowImportanceButton; - private RadioButton mDefaultImportanceButton; - private RadioButton mHighImportanceButton; private Handler mHandler; private Runnable mFalsingCheck; private boolean mNeedsFalsingProtection; private OnGutsClosedListener mListener; + private GutsContent mGutsContent; + public interface OnGutsClosedListener { public void onGutsClosed(NotificationGuts guts); } @@ -103,11 +97,22 @@ public class NotificationGuts extends LinearLayout { } } }; - final TypedArray ta = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Theme, 0, 0); + final TypedArray ta = context.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.Theme, 0, 0); ta.recycle(); } + public NotificationGuts(Context context) { + this(context, null); + + } + + public void setGutsContent(GutsContent content) { + mGutsContent = content; + removeAllViews(); + addView(mGutsContent.getContentView()); + } + public void resetFalsingCheck() { mHandler.removeCallbacks(mFalsingCheck); if (mNeedsFalsingProtection && mExposed) { @@ -165,213 +170,23 @@ public class NotificationGuts extends LinearLayout { void onClick(View v, int appUid); } - void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, - final StatusBarNotification sbn, final NotificationChannel channel, - OnSettingsClickListener onSettingsClick, - OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) { - mINotificationManager = iNotificationManager; - mNotificationChannel = channel; - mStatusBarNotification = sbn; - mStartingUserImportance = channel.getImportance(); - - final String pkg = sbn.getPackageName(); - int appUid = -1; - String appname = pkg; - Drawable pkgicon = null; - try { - final ApplicationInfo info = pm.getApplicationInfo(pkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (info != null) { - appUid = info.uid; - appname = String.valueOf(pm.getApplicationLabel(info)); - pkgicon = pm.getApplicationIcon(info); - } - } catch (PackageManager.NameNotFoundException e) { - // app is gone, just show package name and generic icon - pkgicon = pm.getDefaultActivityIcon(); - } - - // If this is the placeholder channel, don't use our channel-specific text. - String appNameText; - CharSequence channelNameText; - if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { - appNameText = appname; - channelNameText = mContext.getString(R.string.notification_header_default_channel); - } else { - appNameText = mContext.getString(R.string.notification_importance_header_app, appname); - channelNameText = channel.getName(); - } - ((TextView) findViewById(R.id.pkgname)).setText(appNameText); - ((TextView) findViewById(R.id.channel_name)).setText(channelNameText); - - // Settings button. - final TextView settingsButton = (TextView) findViewById(R.id.more_settings); - if (appUid >= 0 && onSettingsClick != null) { - final int appUidF = appUid; - settingsButton.setOnClickListener( - (View view) -> { onSettingsClick.onClick(view, appUidF); }); - settingsButton.setText(R.string.notification_more_settings); - } else { - settingsButton.setVisibility(View.GONE); - } - - // Done button. - final TextView doneButton = (TextView) findViewById(R.id.done); - doneButton.setText(R.string.notification_done); - doneButton.setOnClickListener(onDoneClick); - - boolean nonBlockable = false; - try { - final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); - nonBlockable = Utils.isSystemPackage(getResources(), pm, info); - } catch (PackageManager.NameNotFoundException e) { - // unlikely. - } - if (nonBlockablePkgs != null) { - nonBlockable |= nonBlockablePkgs.contains(pkg); - } - - final View importanceButtons = findViewById(R.id.importance_buttons); - bindToggles(importanceButtons, mStartingUserImportance, nonBlockable); - - // Importance Text (hardcoded to 4 importance levels) - final ViewGroup importanceTextGroup = - (ViewGroup) findViewById(R.id.importance_buttons_text); - final int size = importanceTextGroup.getChildCount(); - for (int i = 0; i < size; i++) { - int importanceNameResId = 0; - int importanceDescResId = 0; - switch (i) { - case 0: - importanceNameResId = R.string.high_importance; - importanceDescResId = R.string.notification_importance_high; - break; - case 1: - importanceNameResId = R.string.default_importance; - importanceDescResId = R.string.notification_importance_default; - break; - case 2: - importanceNameResId = R.string.low_importance; - importanceDescResId = R.string.notification_importance_low; - break; - case 3: - importanceNameResId = R.string.min_importance; - importanceDescResId = R.string.notification_importance_min; - break; - default: - Log.e(TAG, "Too many importance groups in this layout."); - break; - } - final ViewGroup importanceChildGroup = (ViewGroup) importanceTextGroup.getChildAt(i); - ((TextView) importanceChildGroup.getChildAt(0)).setText(importanceNameResId); - ((TextView) importanceChildGroup.getChildAt(1)).setText(importanceDescResId); - } - - // Top-level importance group - mImportanceGroup = findViewById(R.id.importance); - mChannelDisabled = findViewById(R.id.channel_disabled); - updateImportanceGroup(); - } - - public boolean hasImportanceChanged() { - return mStartingUserImportance != getSelectedImportance(); - } - - private void saveImportance() { - int selectedImportance = getSelectedImportance(); - if (selectedImportance == mStartingUserImportance) { - return; - } - MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - selectedImportance - mStartingUserImportance); - mNotificationChannel.setImportance(selectedImportance); - try { - mINotificationManager.updateNotificationChannelForPackage( - mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(), - mNotificationChannel); - } catch (RemoteException e) { - // :( - } - } - - private int getSelectedImportance() { - if (!mChannelEnabledSwitch.isChecked()) { - return NotificationManager.IMPORTANCE_NONE; - } else if (mMinImportanceButton.isChecked()) { - return NotificationManager.IMPORTANCE_MIN; - } else if (mLowImportanceButton.isChecked()) { - return NotificationManager.IMPORTANCE_LOW; - } else if (mDefaultImportanceButton.isChecked()) { - return NotificationManager.IMPORTANCE_DEFAULT; - } else if (mHighImportanceButton.isChecked()) { - return NotificationManager.IMPORTANCE_HIGH; - } else { - return NotificationManager.IMPORTANCE_UNSPECIFIED; - } - } - - private void bindToggles(final View importanceButtons, final int importance, - final boolean nonBlockable) { - // Enabled Switch - mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch); - mChannelEnabledSwitch.setChecked(importance != NotificationManager.IMPORTANCE_NONE); - mChannelEnabledSwitch.setVisibility(nonBlockable ? View.INVISIBLE : View.VISIBLE); - - // Importance Buttons - mMinImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.min_importance); - mLowImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.low_importance); - mDefaultImportanceButton = - (RadioButton) importanceButtons.findViewById(R.id.default_importance); - mHighImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.high_importance); - - // Set to current importance setting - switch (importance) { - case NotificationManager.IMPORTANCE_UNSPECIFIED: - case NotificationManager.IMPORTANCE_NONE: - break; - case NotificationManager.IMPORTANCE_MIN: - mMinImportanceButton.setChecked(true); - break; - case NotificationManager.IMPORTANCE_LOW: - mLowImportanceButton.setChecked(true); - break; - case NotificationManager.IMPORTANCE_DEFAULT: - mDefaultImportanceButton.setChecked(true); - break; - case NotificationManager.IMPORTANCE_HIGH: - case NotificationManager.IMPORTANCE_MAX: - mHighImportanceButton.setChecked(true); - break; - } - - // Callback when checked. - mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - resetFalsingCheck(); - updateImportanceGroup(); - }); - ((RadioGroup) importanceButtons).setOnCheckedChangeListener( - (buttonView, isChecked) -> { resetFalsingCheck(); }); - } - - private void updateImportanceGroup() { - final boolean disabled = getSelectedImportance() == NotificationManager.IMPORTANCE_NONE; - mImportanceGroup.setVisibility(disabled ? View.GONE : View.VISIBLE); - mChannelDisabled.setVisibility(disabled ? View.VISIBLE : View.GONE); - } - public void closeControls(int x, int y, boolean saveImportance) { - if (saveImportance) { - saveImportance(); - } if (getWindowToken() == null) { if (mListener != null) { mListener.onGutsClosed(this); } return; } + if (mGutsContent == null || !mGutsContent.handleCloseControls()) { + animateClose(x, y); + } + setExposed(false, mNeedsFalsingProtection); + if (mListener != null) { + mListener.onGutsClosed(this); + } + } + + private void animateClose(int x, int y) { if (x == -1 || y == -1) { x = (getLeft() + getRight()) / 2; y = (getTop() + getHeight() / 2); @@ -391,10 +206,6 @@ public class NotificationGuts extends LinearLayout { } }); a.start(); - setExposed(false, mNeedsFalsingProtection); - if (mListener != null) { - mListener.onGutsClosed(this); - } } public void setActualHeight(int actualHeight) { @@ -439,4 +250,14 @@ public class NotificationGuts extends LinearLayout { public boolean isExposed() { return mExposed; } + + @Override + public void onInteraction(View view) { + resetFalsingCheck(); + } + + @Override + public void closeGuts(View view) { + closeControls(-1 /* x */, -1 /* y */, true /* notify */); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java new file mode 100644 index 000000000000..098984639a99 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -0,0 +1,319 @@ +package com.android.systemui.statusbar; + +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.INotificationManager; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.SeekBar; +import android.widget.Switch; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.settingslib.Utils; +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener; +import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener; +import com.android.systemui.statusbar.stack.StackStateAnimator; + +import java.util.Set; + +/** + * The guts of a notification revealed when performing a long press. + */ +public class NotificationInfo extends LinearLayout implements GutsContent { + private static final String TAG = "InfoGuts"; + + private INotificationManager mINotificationManager; + private int mStartingUserImportance; + private StatusBarNotification mStatusBarNotification; + private NotificationChannel mNotificationChannel; + + private ImageView mAutoButton; + private TextView mImportanceSummary; + private TextView mImportanceTitle; + private boolean mAuto; + + private View mImportanceGroup; + private View mChannelDisabled; + private Switch mChannelEnabledSwitch; + private RadioButton mMinImportanceButton; + private RadioButton mLowImportanceButton; + private RadioButton mDefaultImportanceButton; + private RadioButton mHighImportanceButton; + + private GutsInteractionListener mGutsInteractionListener; + + public NotificationInfo(Context context, AttributeSet attrs) { + super(context, attrs); + } + + interface OnSettingsClickListener { + void onClick(View v, int appUid); + } + + void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, + final StatusBarNotification sbn, final NotificationChannel channel, + OnSettingsClickListener onSettingsClick, + OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) { + mINotificationManager = iNotificationManager; + mNotificationChannel = channel; + mStatusBarNotification = sbn; + mStartingUserImportance = channel.getImportance(); + + final String pkg = sbn.getPackageName(); + int appUid = -1; + String appname = pkg; + Drawable pkgicon = null; + try { + final ApplicationInfo info = pm.getApplicationInfo(pkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + appUid = info.uid; + appname = String.valueOf(pm.getApplicationLabel(info)); + pkgicon = pm.getApplicationIcon(info); + } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + pkgicon = pm.getDefaultActivityIcon(); + } + + // If this is the placeholder channel, don't use our channel-specific text. + String appNameText; + CharSequence channelNameText; + if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { + appNameText = appname; + channelNameText = mContext.getString(R.string.notification_header_default_channel); + } else { + appNameText = mContext.getString(R.string.notification_importance_header_app, appname); + channelNameText = channel.getName(); + } + ((TextView) findViewById(R.id.pkgname)).setText(appNameText); + ((TextView) findViewById(R.id.channel_name)).setText(channelNameText); + + // Settings button. + final TextView settingsButton = (TextView) findViewById(R.id.more_settings); + if (appUid >= 0 && onSettingsClick != null) { + final int appUidF = appUid; + settingsButton.setOnClickListener( + (View view) -> { + onSettingsClick.onClick(view, appUidF); + }); + settingsButton.setText(R.string.notification_more_settings); + } else { + settingsButton.setVisibility(View.GONE); + } + + // Done button. + final TextView doneButton = (TextView) findViewById(R.id.done); + doneButton.setText(R.string.notification_done); + doneButton.setOnClickListener(onDoneClick); + + boolean nonBlockable = false; + try { + final PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); + nonBlockable = Utils.isSystemPackage(getResources(), pm, info); + } catch (PackageManager.NameNotFoundException e) { + // unlikely. + } + if (nonBlockablePkgs != null) { + nonBlockable |= nonBlockablePkgs.contains(pkg); + } + + final View importanceButtons = findViewById(R.id.importance_buttons); + bindToggles(importanceButtons, mStartingUserImportance, nonBlockable); + + // Importance Text (hardcoded to 4 importance levels) + final ViewGroup importanceTextGroup = (ViewGroup) findViewById( + R.id.importance_buttons_text); + final int size = importanceTextGroup.getChildCount(); + for (int i = 0; i < size; i++) { + int importanceNameResId = 0; + int importanceDescResId = 0; + switch (i) { + case 0: + importanceNameResId = R.string.high_importance; + importanceDescResId = R.string.notification_importance_high; + break; + case 1: + importanceNameResId = R.string.default_importance; + importanceDescResId = R.string.notification_importance_default; + break; + case 2: + importanceNameResId = R.string.low_importance; + importanceDescResId = R.string.notification_importance_low; + break; + case 3: + importanceNameResId = R.string.min_importance; + importanceDescResId = R.string.notification_importance_min; + break; + default: + Log.e(TAG, "Too many importance groups in this layout."); + break; + } + final ViewGroup importanceChildGroup = (ViewGroup) importanceTextGroup.getChildAt(i); + ((TextView) importanceChildGroup.getChildAt(0)).setText(importanceNameResId); + ((TextView) importanceChildGroup.getChildAt(1)).setText(importanceDescResId); + } + + // Top-level importance group + mImportanceGroup = findViewById(R.id.importance); + mChannelDisabled = findViewById(R.id.channel_disabled); + updateImportanceGroup(); + } + + public boolean hasImportanceChanged() { + return mStartingUserImportance != getSelectedImportance(); + } + + public void saveImportance() { + int selectedImportance = getSelectedImportance(); + if (selectedImportance == mStartingUserImportance) { + return; + } + MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, + selectedImportance - mStartingUserImportance); + mNotificationChannel.setImportance(selectedImportance); + try { + mINotificationManager.updateNotificationChannelForPackage( + mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(), + mNotificationChannel); + } catch (RemoteException e) { + // :( + } + } + + private int getSelectedImportance() { + if (!mChannelEnabledSwitch.isChecked()) { + return NotificationManager.IMPORTANCE_NONE; + } else if (mMinImportanceButton.isChecked()) { + return NotificationManager.IMPORTANCE_MIN; + } else if (mLowImportanceButton.isChecked()) { + return NotificationManager.IMPORTANCE_LOW; + } else if (mDefaultImportanceButton.isChecked()) { + return NotificationManager.IMPORTANCE_DEFAULT; + } else if (mHighImportanceButton.isChecked()) { + return NotificationManager.IMPORTANCE_HIGH; + } else { + return NotificationManager.IMPORTANCE_UNSPECIFIED; + } + } + + private void bindToggles(final View importanceButtons, final int importance, + final boolean nonBlockable) { + // Enabled Switch + mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch); + mChannelEnabledSwitch.setChecked(importance != NotificationManager.IMPORTANCE_NONE); + mChannelEnabledSwitch.setVisibility(nonBlockable ? View.INVISIBLE : View.VISIBLE); + + // Importance Buttons + mMinImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.min_importance); + mLowImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.low_importance); + mDefaultImportanceButton = (RadioButton) importanceButtons + .findViewById(R.id.default_importance); + mHighImportanceButton = (RadioButton) importanceButtons.findViewById(R.id.high_importance); + + // Set to current importance setting + switch (importance) { + case NotificationManager.IMPORTANCE_UNSPECIFIED: + case NotificationManager.IMPORTANCE_NONE: + break; + case NotificationManager.IMPORTANCE_MIN: + mMinImportanceButton.setChecked(true); + break; + case NotificationManager.IMPORTANCE_LOW: + mLowImportanceButton.setChecked(true); + break; + case NotificationManager.IMPORTANCE_DEFAULT: + mDefaultImportanceButton.setChecked(true); + break; + case NotificationManager.IMPORTANCE_HIGH: + case NotificationManager.IMPORTANCE_MAX: + mHighImportanceButton.setChecked(true); + break; + } + + // Callback when checked. + mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + mGutsInteractionListener.onInteraction(NotificationInfo.this); + updateImportanceGroup(); + }); + ((RadioGroup) importanceButtons).setOnCheckedChangeListener( + (buttonView, isChecked) -> { + mGutsInteractionListener.onInteraction(NotificationInfo.this); + }); + } + + private void updateImportanceGroup() { + final boolean disabled = getSelectedImportance() == NotificationManager.IMPORTANCE_NONE; + mImportanceGroup.setVisibility(disabled ? View.GONE : View.VISIBLE); + mChannelDisabled.setVisibility(disabled ? View.VISIBLE : View.GONE); + } + + public void closeControls() { + if (mGutsInteractionListener != null) { + mGutsInteractionListener.closeGuts(this); + } + } + + @Override + public void setInteractionListener(GutsInteractionListener listener) { + mGutsInteractionListener = listener; + } + + @Override + public View getContentView() { + return this; + } + + @Override + public boolean handleCloseControls() { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java new file mode 100644 index 000000000000..fad63dd4f700 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import java.util.ArrayList; + +import com.android.systemui.Interpolators; +import com.android.systemui.R; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.PluginManager; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.OnMenuClickListener; + +public class NotificationMenuRow extends FrameLayout + implements PluginListener<NotificationMenuRowProvider>, View.OnClickListener { + + private static final int ICON_ALPHA_ANIM_DURATION = 200; + + private ExpandableNotificationRow mParent; + private OnMenuClickListener mListener; + private NotificationMenuRowProvider mMenuProvider; + private ArrayList<MenuItem> mMenuItems = new ArrayList<>(); + + private ValueAnimator mFadeAnimator; + private boolean mMenuFadedIn = false; + private boolean mAnimating = false; + private boolean mOnLeft = true; + private boolean mDismissing = false; + private boolean mSnapping = false; + private boolean mIconsPlaced = false; + + private int[] mIconLocation = new int[2]; + private int[] mParentLocation = new int[2]; + + private float mHorizSpaceForIcon; + private int mVertSpaceForIcons; + + private int mIconPadding; + private int mIconTint; + + private float mAlpha = 0f; + + public NotificationMenuRow(Context context) { + this(context, null); + } + + public NotificationMenuRow(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public NotificationMenuRow(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs); + PluginManager.getInstance(getContext()).addPluginListener( + NotificationMenuRowProvider.ACTION, this, + NotificationMenuRowProvider.VERSION, false /* Allow multiple */); + mMenuItems.addAll(getDefaultNotificationMenuItems()); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + final Resources res = getResources(); + mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size); + mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height); + mIconPadding = res.getDimensionPixelSize(R.dimen.notification_menu_icon_padding); + mIconTint = res.getColor(R.color.notification_gear_color); + updateMenu(false /* notify */); + } + + public static MenuItem getLongpressMenuItem(Context context) { + Resources res = context.getResources(); + Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings); + String settingsDescription = res.getString(R.string.notification_menu_gear_description); + NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(context).inflate( + R.layout.notification_info, null, false); + MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent); + return settings; + } + + public ArrayList<MenuItem> getDefaultNotificationMenuItems() { + ArrayList<MenuItem> items = new ArrayList<MenuItem>(); + Resources res = getResources(); + + Drawable snoozeIcon = res.getDrawable(R.drawable.ic_snooze); + NotificationSnooze content = (NotificationSnooze) LayoutInflater.from(mContext) + .inflate(R.layout.notification_snooze, null, false); + String snoozeDescription = res.getString(R.string.notification_menu_snooze_description); + MenuItem snooze = new MenuItem(snoozeIcon, snoozeDescription, content); + items.add(snooze); + + Drawable settingsIcon = res.getDrawable(R.drawable.ic_settings); + String settingsDescription = res.getString(R.string.notification_menu_gear_description); + NotificationInfo settingsContent = (NotificationInfo) LayoutInflater.from(mContext).inflate( + R.layout.notification_info, null, false); + MenuItem settings = new MenuItem(settingsIcon, settingsDescription, settingsContent); + items.add(settings); + return items; + } + + private void updateMenu(boolean notify) { + removeAllViews(); + mMenuItems.clear(); + if (mMenuProvider != null) { + mMenuItems.addAll(mMenuProvider.getMenuItems(getContext())); + } + mMenuItems.addAll(getDefaultNotificationMenuItems()); + for (int i = 0; i < mMenuItems.size(); i++) { + final View v = createMenuView(mMenuItems.get(i)); + mMenuItems.get(i).menuView = v; + } + resetState(notify); + } + + private View createMenuView(MenuItem item) { + AlphaOptimizedImageView iv = new AlphaOptimizedImageView(getContext()); + addView(iv); + iv.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding); + iv.setImageDrawable(item.icon); + iv.setOnClickListener(this); + iv.setColorFilter(mIconTint); + iv.setAlpha(mAlpha); + FrameLayout.LayoutParams lp = (LayoutParams) iv.getLayoutParams(); + lp.width = (int) mHorizSpaceForIcon; + lp.height = (int) mHorizSpaceForIcon; + return iv; + } + + public void resetState(boolean notify) { + setMenuAlpha(0f); + mIconsPlaced = false; + mMenuFadedIn = false; + mAnimating = false; + mSnapping = false; + mDismissing = false; + setMenuLocation(mOnLeft ? 1 : -1 /* on left */); + if (mListener != null && notify) { + mListener.onMenuReset(mParent); + } + } + + public void setMenuClickListener(OnMenuClickListener listener) { + mListener = listener; + } + + public void setNotificationRowParent(ExpandableNotificationRow parent) { + mParent = parent; + setMenuLocation(mOnLeft ? 1 : -1); + } + + public void setAppName(String appName) { + Resources res = getResources(); + final int count = mMenuItems.size(); + for (int i = 0; i < count; i++) { + MenuItem item = mMenuItems.get(i); + String description = String.format( + res.getString(R.string.notification_menu_accessibility), + appName, item.menuDescription); + item.menuView.setContentDescription(description); + } + } + + public ExpandableNotificationRow getNotificationParent() { + return mParent; + } + + public void setMenuAlpha(float alpha) { + mAlpha = alpha; + if (alpha == 0) { + mMenuFadedIn = false; // Can fade in again once it's gone. + setVisibility(View.INVISIBLE); + } else { + setVisibility(View.VISIBLE); + } + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + getChildAt(i).setAlpha(mAlpha); + } + } + + /** + * Returns whether the menu is displayed on the left side of the view or not. + */ + public boolean isMenuOnLeft() { + return mOnLeft; + } + + /** + * Returns the horizontal space in pixels required to display the menu. + */ + public float getSpaceForMenu() { + return mHorizSpaceForIcon * getChildCount(); + } + + /** + * Indicates whether the menu is visible at 1 alpha. Does not indicate if entire view is + * visible. + */ + public boolean isVisible() { + return mAlpha > 0; + } + + public void cancelFadeAnimator() { + if (mFadeAnimator != null) { + mFadeAnimator.cancel(); + } + } + + public void updateMenuAlpha(final float transX, final float size) { + if (mAnimating || !mMenuFadedIn) { + // Don't adjust when animating, or if the menu hasn't been shown yet. + return; + } + + final float fadeThreshold = size * 0.3f; + final float absTrans = Math.abs(transX); + float desiredAlpha = 0; + + if (absTrans == 0) { + desiredAlpha = 0; + } else if (absTrans <= fadeThreshold) { + desiredAlpha = 1; + } else { + desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold)); + } + setMenuAlpha(desiredAlpha); + } + + public void fadeInMenu(final boolean fromLeft, final float transX, + final float notiThreshold) { + if (mDismissing || mAnimating) { + return; + } + if (isMenuLocationChange(transX)) { + setMenuAlpha(0f); + } + setMenuLocation((int) transX); + mFadeAnimator = ValueAnimator.ofFloat(mAlpha, 1); + mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final float absTrans = Math.abs(transX); + + boolean pastGear = (fromLeft && transX <= notiThreshold) + || (!fromLeft && absTrans <= notiThreshold); + if (pastGear && !mMenuFadedIn) { + setMenuAlpha((float) animation.getAnimatedValue()); + } + } + }); + mFadeAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mAnimating = true; + } + + @Override + public void onAnimationCancel(Animator animation) { + // TODO should animate back to 0f from current alpha + setMenuAlpha(0f); + } + + @Override + public void onAnimationEnd(Animator animation) { + mAnimating = false; + mMenuFadedIn = mAlpha == 1; + } + }); + mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN); + mFadeAnimator.setDuration(ICON_ALPHA_ANIM_DURATION); + mFadeAnimator.start(); + } + + public void updateVerticalLocation() { + if (mParent == null || mMenuItems.size() == 0) { + return; + } + int parentHeight = mParent.getCollapsedHeight(); + float translationY; + if (parentHeight < mVertSpaceForIcons) { + translationY = (parentHeight / 2) - (mHorizSpaceForIcon / 2); + } else { + translationY = (mVertSpaceForIcons - mHorizSpaceForIcon) / 2; + } + setTranslationY(translationY); + } + + @Override + public void onRtlPropertiesChanged(int layoutDirection) { + mIconsPlaced = false; + setMenuLocation(mOnLeft ? 1 : -1); + } + + public void setMenuLocation(int translation) { + boolean onLeft = translation > 0; + if ((mIconsPlaced && onLeft == mOnLeft) || mSnapping || mParent == null) { + // Do nothing + return; + } + final boolean isRtl = mParent.isLayoutRtl(); + final int count = getChildCount(); + final int width = getWidth(); + for (int i = 0; i < count; i++) { + final View v = getChildAt(i); + final float left = isRtl + ? -(width - mHorizSpaceForIcon * (i + 1)) + : i * mHorizSpaceForIcon; + final float right = isRtl + ? -i * mHorizSpaceForIcon + : width - (mHorizSpaceForIcon * (i + 1)); + v.setTranslationX(onLeft ? left : right); + } + mOnLeft = onLeft; + mIconsPlaced = true; + } + + public boolean isMenuLocationChange(float translation) { + boolean onLeft = translation > mIconPadding; + boolean onRight = translation < -mIconPadding; + if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) { + return true; + } + return false; + } + + public void setDismissing() { + mDismissing = true; + } + + public void setSnapping(boolean snapping) { + mSnapping = snapping; + } + + @Override + public void onClick(View v) { + if (mListener == null) { + // Nothing to do + return; + } + v.getLocationOnScreen(mIconLocation); + mParent.getLocationOnScreen(mParentLocation); + final int centerX = (int) (mHorizSpaceForIcon / 2); + final int centerY = (int) (v.getTranslationY() * 2 + v.getHeight()) / 2; + final int x = mIconLocation[0] - mParentLocation[0] + centerX; + final int y = mIconLocation[1] - mParentLocation[1] + centerY; + final int index = indexOfChild(v); + mListener.onMenuClicked(mParent, x, y, mMenuItems.get(index)); + } + + @Override + public void onPluginConnected(NotificationMenuRowProvider plugin, Context pluginContext) { + mMenuProvider = plugin; + updateMenu(false /* notify */); + } + + @Override + public void onPluginDisconnected(NotificationMenuRowProvider plugin) { + mMenuProvider = null; + updateMenu(false /* notify */); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java deleted file mode 100644 index 431564723992..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSettingsIconRow.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.systemui.Interpolators; -import com.android.systemui.R; - -public class NotificationSettingsIconRow extends FrameLayout implements View.OnClickListener { - - private static final int GEAR_ALPHA_ANIM_DURATION = 200; - - public interface SettingsIconRowListener { - /** - * Called when the gear behind a notification is touched. - */ - public void onGearTouched(ExpandableNotificationRow row, int x, int y); - - /** - * Called when a notification is slid back over the gear. - */ - public void onSettingsIconRowReset(ExpandableNotificationRow row); - } - - private ExpandableNotificationRow mParent; - private AlphaOptimizedImageView mGearIcon; - private float mHorizSpaceForGear; - private SettingsIconRowListener mListener; - - private ValueAnimator mFadeAnimator; - private boolean mSettingsFadedIn = false; - private boolean mAnimating = false; - private boolean mOnLeft = true; - private boolean mDismissing = false; - private boolean mSnapping = false; - private boolean mIconPlaced = false; - - private int[] mGearLocation = new int[2]; - private int[] mParentLocation = new int[2]; - private int mVertSpaceForGear; - - public NotificationSettingsIconRow(Context context) { - this(context, null); - } - - public NotificationSettingsIconRow(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public NotificationSettingsIconRow(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mGearIcon = (AlphaOptimizedImageView) findViewById(R.id.gear_icon); - mGearIcon.setOnClickListener(this); - setOnClickListener(this); - mHorizSpaceForGear = - getResources().getDimensionPixelOffset(R.dimen.notification_gear_width); - mVertSpaceForGear = getResources().getDimensionPixelOffset(R.dimen.notification_min_height); - resetState(); - } - - public void resetState() { - setGearAlpha(0f); - mIconPlaced = false; - mSettingsFadedIn = false; - mAnimating = false; - mSnapping = false; - mDismissing = false; - setIconLocation(true /* on left */); - if (mListener != null) { - mListener.onSettingsIconRowReset(mParent); - } - } - - public void setGearListener(SettingsIconRowListener listener) { - mListener = listener; - } - - public void setNotificationRowParent(ExpandableNotificationRow parent) { - mParent = parent; - setIconLocation(mOnLeft); - } - - public void setAppName(String appName) { - Resources res = getResources(); - String description = String.format(res.getString(R.string.notification_gear_accessibility), - appName); - mGearIcon.setContentDescription(description); - } - - public ExpandableNotificationRow getNotificationParent() { - return mParent; - } - - public void setGearAlpha(float alpha) { - if (alpha == 0) { - mSettingsFadedIn = false; // Can fade in again once it's gone. - setVisibility(View.INVISIBLE); - } else { - setVisibility(View.VISIBLE); - } - mGearIcon.setAlpha(alpha); - } - - /** - * Returns whether the icon is on the left side of the view or not. - */ - public boolean isIconOnLeft() { - return mOnLeft; - } - - /** - * Returns the horizontal space in pixels required to display the gear behind a notification. - */ - public float getSpaceForGear() { - return mHorizSpaceForGear; - } - - /** - * Indicates whether the gear is visible at 1 alpha. Does not indicate - * if entire view is visible. - */ - public boolean isVisible() { - return mGearIcon.getAlpha() > 0; - } - - public void cancelFadeAnimator() { - if (mFadeAnimator != null) { - mFadeAnimator.cancel(); - } - } - - public void updateSettingsIcons(final float transX, final float size) { - if (mAnimating || !mSettingsFadedIn) { - // Don't adjust when animating, or if the gear hasn't been shown yet. - return; - } - - final float fadeThreshold = size * 0.3f; - final float absTrans = Math.abs(transX); - float desiredAlpha = 0; - - if (absTrans == 0) { - desiredAlpha = 0; - } else if (absTrans <= fadeThreshold) { - desiredAlpha = 1; - } else { - desiredAlpha = 1 - ((absTrans - fadeThreshold) / (size - fadeThreshold)); - } - setGearAlpha(desiredAlpha); - } - - public void fadeInSettings(final boolean fromLeft, final float transX, - final float notiThreshold) { - if (mDismissing || mAnimating) { - return; - } - if (isIconLocationChange(transX)) { - setGearAlpha(0f); - } - setIconLocation(transX > 0 /* fromLeft */); - mFadeAnimator = ValueAnimator.ofFloat(mGearIcon.getAlpha(), 1); - mFadeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final float absTrans = Math.abs(transX); - - boolean pastGear = (fromLeft && transX <= notiThreshold) - || (!fromLeft && absTrans <= notiThreshold); - if (pastGear && !mSettingsFadedIn) { - setGearAlpha((float) animation.getAnimatedValue()); - } - } - }); - mFadeAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mAnimating = true; - } - - @Override - public void onAnimationCancel(Animator animation) { - // TODO should animate back to 0f from current alpha - mGearIcon.setAlpha(0f); - } - - @Override - public void onAnimationEnd(Animator animation) { - mAnimating = false; - mSettingsFadedIn = mGearIcon.getAlpha() == 1; - } - }); - mFadeAnimator.setInterpolator(Interpolators.ALPHA_IN); - mFadeAnimator.setDuration(GEAR_ALPHA_ANIM_DURATION); - mFadeAnimator.start(); - } - - public void updateVerticalLocation() { - if (mParent == null) { - return; - } - int parentHeight = mParent.getCollapsedHeight(); - if (parentHeight < mVertSpaceForGear) { - mGearIcon.setTranslationY((parentHeight / 2) - (mGearIcon.getHeight() / 2)); - } else { - mGearIcon.setTranslationY((mVertSpaceForGear - mGearIcon.getHeight()) / 2); - } - } - - @Override - public void onRtlPropertiesChanged(int layoutDirection) { - setIconLocation(mOnLeft); - } - - public void setIconLocation(boolean onLeft) { - if ((mIconPlaced && onLeft == mOnLeft) || mSnapping || mParent == null - || mGearIcon.getWidth() == 0) { - // Do nothing - return; - } - final boolean isRtl = mParent.isLayoutRtl(); - final float left = isRtl - ? -(mParent.getWidth() - mHorizSpaceForGear) - : 0; - final float right = isRtl - ? 0 - : mParent.getWidth() - mHorizSpaceForGear; - final float centerX = ((mHorizSpaceForGear - mGearIcon.getWidth()) / 2); - setTranslationX(onLeft ? left + centerX : right + centerX); - mOnLeft = onLeft; - mIconPlaced = true; - } - - public boolean isIconLocationChange(float translation) { - boolean onLeft = translation > mGearIcon.getPaddingStart(); - boolean onRight = translation < -mGearIcon.getPaddingStart(); - if ((mOnLeft && onRight) || (!mOnLeft && onLeft)) { - return true; - } - return false; - } - - public void setDismissing() { - mDismissing = true; - } - - public void setSnapping(boolean snapping) { - mSnapping = snapping; - } - - @Override - public void onClick(View v) { - if (v.getId() == R.id.gear_icon) { - if (mListener != null) { - mGearIcon.getLocationOnScreen(mGearLocation); - mParent.getLocationOnScreen(mParentLocation); - - final int centerX = (int) (mHorizSpaceForGear / 2); - final int centerY = - (int) (mGearIcon.getTranslationY() * 2 + mGearIcon.getHeight())/ 2; - final int x = mGearLocation[0] - mParentLocation[0] + centerX; - final int y = mGearLocation[1] - mParentLocation[1] + centerY; - mListener.onGearTouched(mParent, x, y); - } - } else { - // Do nothing when the background is touched. - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java new file mode 100644 index 000000000000..670d73efcbee --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java @@ -0,0 +1,176 @@ +package com.android.systemui.statusbar; +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.concurrent.TimeUnit; + +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.service.notification.StatusBarNotification; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RadioGroup; +import android.widget.RadioGroup.OnCheckedChangeListener; +import android.widget.TextView; +import android.widget.Toast; +import com.android.systemui.R; + +public class NotificationSnooze extends LinearLayout + implements NotificationMenuRowProvider.SnoozeGutsContent, View.OnClickListener { + + private GutsInteractionListener mGutsInteractionListener; + private SnoozeListener mSnoozeListener; + private StatusBarNotification mSbn; + + private TextView mSelectedOption; + private TextView mUndo; + private ViewGroup mSnoozeOptionView; + + private long mTimeToSnooze; + + // Default is the first option in this list + private static final int[] SNOOZE_OPTIONS = { + R.string.snooze_option_15_min, R.string.snooze_option_30_min, + R.string.snooze_option_1_hour + }; + + public NotificationSnooze(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + // Create the different options based on list + createOptionViews(); + + // Snackbar + mSelectedOption = (TextView) findViewById(R.id.snooze_option_default); + mSelectedOption.setOnClickListener(this); + mUndo = (TextView) findViewById(R.id.undo); + mUndo.setOnClickListener(this); + + // Default to first option in list + setTimeToSnooze(SNOOZE_OPTIONS[0]); + } + + private void createOptionViews() { + mSnoozeOptionView = (ViewGroup) findViewById(R.id.snooze_options); + mSnoozeOptionView.setVisibility(View.GONE); + final Resources res = getResources(); + final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size); + final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding); + for (int i = 0; i < SNOOZE_OPTIONS.length; i++) { + TextView tv = new TextView(getContext()); + tv.setTextColor(Color.WHITE); + tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); + tv.setPadding(p, p, p, p); + mSnoozeOptionView.addView(tv); + tv.setText(SNOOZE_OPTIONS[i]); + tv.setTag(SNOOZE_OPTIONS[i]); + tv.setOnClickListener(this); + } + } + + private void showSnoozeOptions(boolean show) { + mSelectedOption.setVisibility(show ? View.GONE : View.VISIBLE); + mUndo.setVisibility(show ? View.GONE : View.VISIBLE); + mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE); + } + + private void setTimeToSnooze(int optionId) { + long snoozeUntilMillis = Calendar.getInstance().getTimeInMillis(); + switch (optionId) { + case R.string.snooze_option_15_min: + snoozeUntilMillis += TimeUnit.MINUTES.toMillis(15); + break; + case R.string.snooze_option_30_min: + snoozeUntilMillis += TimeUnit.MINUTES.toMillis(30); + break; + case R.string.snooze_option_1_hour: + snoozeUntilMillis += TimeUnit.MINUTES.toMillis(60); + break; + } + mTimeToSnooze = snoozeUntilMillis; + final Resources res = getResources(); + String selectedString = String.format( + res.getString(R.string.snoozed_for_time), res.getString(optionId)); + mSelectedOption.setText(selectedString); + showSnoozeOptions(false); + } + + @Override + public void onClick(View v) { + if (mGutsInteractionListener != null) { + mGutsInteractionListener.onInteraction(this); + } + final int id = v.getId(); + final Integer tag = (Integer) v.getTag(); + if (tag != null) { + // From the option list + setTimeToSnooze(tag); + } else if (id == R.id.snooze_option_default) { + // Show more snooze options + showSnoozeOptions(true); + } else if (id == R.id.undo) { + mTimeToSnooze = -1; + mGutsInteractionListener.closeGuts(this); + } + } + + @Override + public View getContentView() { + return this; + } + + @Override + public void setStatusBarNotification(StatusBarNotification sbn) { + mSbn = sbn; + } + + @Override + public void setInteractionListener(GutsInteractionListener listener) { + mGutsInteractionListener = listener; + } + + @Override + public void setSnoozeListener(SnoozeListener listener) { + mSnoozeListener = listener; + } + + @Override + public boolean handleCloseControls() { + // When snooze is closed (i.e. there was interaction outside of the notification) + // then we commit the snooze action. + if (mSnoozeListener != null && mTimeToSnooze != -1) { + mSnoozeListener.snoozeNotification(mSbn, mTimeToSnooze); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 8dcc693a6c14..7d824772b26d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -141,6 +141,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.qs.QS; import com.android.systemui.ActivityStarter; import com.android.systemui.plugins.qs.QS.BaseStatusBarHeader; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.SnoozeListener; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; @@ -205,7 +206,7 @@ import java.util.Map; public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener, - OnHeadsUpChangedListener, VisualStabilityManager.Callback { + OnHeadsUpChangedListener, VisualStabilityManager.Callback, SnoozeListener { static final String TAG = "PhoneStatusBar"; public static final boolean DEBUG = BaseStatusBar.DEBUG; public static final boolean SPEW = false; @@ -4858,4 +4859,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + + @Override + public SnoozeListener getSnoozeListener() { + return this; + } + + @Override + public void snoozeNotification(StatusBarNotification sbn, long snoozeUntil) { + setNotificationSnoozed(sbn, snoozeUntil); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 395e8f2a1d56..b5ec398ffdd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -63,14 +63,15 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider; +import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.MenuItem; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.DismissView; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationGuts; -import com.android.systemui.statusbar.NotificationSettingsIconRow; -import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener; +import com.android.systemui.statusbar.NotificationMenuRow; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarState; @@ -94,7 +95,8 @@ import java.util.HashSet; public class NotificationStackScrollLayout extends ViewGroup implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter, ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener, - SettingsIconRowListener, ScrollContainer, VisibilityLocationProvider { + NotificationMenuRowProvider.OnMenuClickListener, ScrollContainer, + VisibilityLocationProvider { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -224,7 +226,7 @@ public class NotificationStackScrollLayout extends ViewGroup private int mMaxScrollAfterExpand; private SwipeHelper.LongPressListener mLongPressListener; - private NotificationSettingsIconRow mCurrIconRow; + private NotificationMenuRow mCurrIconRow; private View mTranslatingParentView; private View mGearExposedView; @@ -399,16 +401,20 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - public void onGearTouched(ExpandableNotificationRow row, int x, int y) { - if (mLongPressListener != null) { + public void onMenuClicked(View view, int x, int y, MenuItem item) { + if (mLongPressListener == null) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR, row.getStatusBarNotification().getPackageName()); - mLongPressListener.onLongPress(row, x, y); } + mLongPressListener.onLongPress(view, x, y, item); } @Override - public void onSettingsIconRowReset(ExpandableNotificationRow row) { + public void onMenuReset(View row) { if (mTranslatingParentView != null && row == mTranslatingParentView) { mSwipeHelper.setSnappedToGear(false); mGearExposedView = null; @@ -425,7 +431,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (DEBUG) { int y = mTopPadding; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); - y = (int) getLayoutHeight(); + y = getLayoutHeight(); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); y = getHeight() - getEmptyBottomMargin(); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -911,7 +917,7 @@ public class NotificationStackScrollLayout extends ViewGroup mDragAnimPendingChildren.remove(animView); } if (mCurrIconRow != null && targetLeft == 0) { - mCurrIconRow.resetState(); + mCurrIconRow.resetState(true /* notify */); mCurrIconRow = null; } } @@ -4121,7 +4127,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (currView instanceof ExpandableNotificationRow) { // Set the listener for the current row's gear mCurrIconRow = ((ExpandableNotificationRow) currView).getSettingsRow(); - mCurrIconRow.setGearListener(NotificationStackScrollLayout.this); + mCurrIconRow.setMenuClickListener(NotificationStackScrollLayout.this); } } @@ -4133,9 +4139,9 @@ public class NotificationStackScrollLayout extends ViewGroup mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping. // If the gear is visible and the movement is towards it it's not a location change. - boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isIconOnLeft(); + boolean onLeft = mGearSnappedTo ? mGearSnappedOnLeft : mCurrIconRow.isMenuOnLeft(); boolean locationChange = isTowardsGear(translation, onLeft) - ? false : mCurrIconRow.isIconLocationChange(translation); + ? false : mCurrIconRow.isMenuLocationChange(translation); if (locationChange) { // Don't consider it "snapped" if location has changed. setSnappedToGear(false); @@ -4146,8 +4152,8 @@ public class NotificationStackScrollLayout extends ViewGroup mCheckForDrag = null; } else { // Check scheduled, reset alpha and update location; check will fade it in - mCurrIconRow.setGearAlpha(0f); - mCurrIconRow.setIconLocation(translation > 0 /* onLeft */); + mCurrIconRow.setMenuAlpha(0f); + mCurrIconRow.setMenuLocation((int) translation); } } } @@ -4198,14 +4204,14 @@ public class NotificationStackScrollLayout extends ViewGroup return false; // Let SwipeHelper handle it. } - boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isIconOnLeft()); + boolean gestureTowardsGear = isTowardsGear(velocity, mCurrIconRow.isMenuOnLeft()); boolean gestureFastEnough = Math.abs(velocity) > getEscapeVelocity(); final double timeForGesture = ev.getEventTime() - ev.getDownTime(); final boolean showGearForSlowOnGoing = !canChildBeDismissed(animView) && timeForGesture >= SWIPE_GEAR_TIMING; if (mGearSnappedTo && mCurrIconRow.isVisible()) { - if (mGearSnappedOnLeft == mCurrIconRow.isIconOnLeft()) { + if (mGearSnappedOnLeft == mCurrIconRow.isMenuOnLeft()) { boolean coveringGear = Math.abs(getTranslation(animView)) <= getSpaceForGear(animView) * 0.6f; if (gestureTowardsGear || coveringGear) { @@ -4249,7 +4255,7 @@ public class NotificationStackScrollLayout extends ViewGroup private void snapToGear(View animView, float velocity) { final float snapBackThreshold = getSpaceForGear(animView); - final float target = mCurrIconRow.isIconOnLeft() ? snapBackThreshold + final float target = mCurrIconRow.isMenuOnLeft() ? snapBackThreshold : -snapBackThreshold; mGearExposedView = mTranslatingParentView; if (animView instanceof ExpandableNotificationRow) { @@ -4280,7 +4286,7 @@ public class NotificationStackScrollLayout extends ViewGroup final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f; final float snapBackThreshold = getSpaceForGear(animView) * multiplier; final float translation = getTranslation(animView); - return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isIconOnLeft() + return !swipedFarEnough() && mCurrIconRow.isVisible() && (mCurrIconRow.isMenuOnLeft() ? translation > snapBackThreshold : translation < -snapBackThreshold); } @@ -4349,7 +4355,7 @@ public class NotificationStackScrollLayout extends ViewGroup * Indicates the the gear has been snapped to. */ private void setSnappedToGear(boolean snapped) { - mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isIconOnLeft() : false; + mGearSnappedOnLeft = (mCurrIconRow != null) ? mCurrIconRow.isMenuOnLeft() : false; mGearSnappedTo = snapped && mCurrIconRow != null; } @@ -4389,11 +4395,11 @@ public class NotificationStackScrollLayout extends ViewGroup final float bounceBackToGearWidth = getSpaceForGear(mTranslatingParentView); final float notiThreshold = getSize(mTranslatingParentView) * 0.4f; if ((mCurrIconRow != null && (!mCurrIconRow.isVisible() - || mCurrIconRow.isIconLocationChange(translation))) + || mCurrIconRow.isMenuLocationChange(translation))) && absTransX >= bounceBackToGearWidth * 0.4 && absTransX < notiThreshold) { // Fade in the gear - mCurrIconRow.fadeInSettings(translation > 0 /* fromLeft */, translation, + mCurrIconRow.fadeInMenu(translation > 0 /* fromLeft */, translation, notiThreshold); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java index cac0806e0f15..166fcb1e8a6a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsTest.java @@ -63,7 +63,7 @@ public class NotificationGutsTest { private static final String TEST_CHANNEL = "test_channel"; private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME"; - private NotificationGuts mNotificationGuts; + private NotificationInfo mNotificationInfo; private final INotificationManager mMockINotificationManager = mock(INotificationManager.class); private final PackageManager mMockPackageManager = mock(PackageManager.class); private NotificationChannel mNotificationChannel; @@ -76,7 +76,7 @@ public class NotificationGutsTest { // Inflate the layout final LayoutInflater layoutInflater = LayoutInflater.from(InstrumentationRegistry.getTargetContext()); - mNotificationGuts = (NotificationGuts) layoutInflater.inflate(R.layout.notification_guts, + mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info, null); // PackageManager must return a packageInfo and applicationInfo. @@ -98,18 +98,18 @@ public class NotificationGutsTest { @UiThreadTest public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.pkgname); + final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); } @Test @UiThreadTest public void testBindNotification_SetsTextChannelName() throws Exception { - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.channel_name); + final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -117,12 +117,12 @@ public class NotificationGutsTest { @UiThreadTest public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, (View v, int appUid) -> { latch.countDown(); }, null, null); - final TextView settingsButton = - (TextView) mNotificationGuts.findViewById(R.id.more_settings); + final TextView settingsButton = + (TextView) mNotificationInfo.findViewById(R.id.more_settings); settingsButton.performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); @@ -132,12 +132,12 @@ public class NotificationGutsTest { @UiThreadTest public void testBindNotification_SetsOnClickListenerForDone() throws Exception { final CountDownLatch latch = new CountDownLatch(1); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, (View v) -> { latch.countDown(); }, null); - final TextView doneButton = (TextView) mNotificationGuts.findViewById(R.id.done); + final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done); doneButton.performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); @@ -146,38 +146,38 @@ public class NotificationGutsTest { @Test @UiThreadTest public void testHasImportanceChanged_DefaultsToFalse() throws Exception { - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - assertFalse(mNotificationGuts.hasImportanceChanged()); + assertFalse(mNotificationInfo.hasImportanceChanged()); } @Test @UiThreadTest public void testHasImportanceChanged_ReturnsTrueAfterButtonChecked() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); // Find the high button and check it. - RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); + RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance); highButton.setChecked(true); - assertTrue(mNotificationGuts.hasImportanceChanged()); + assertTrue(mNotificationInfo.hasImportanceChanged()); } @Test @UiThreadTest public void testImportanceButtonCheckedBasedOnInitialImportance() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); + RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance); assertTrue(highButton.isChecked()); } @Test @UiThreadTest public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); @@ -187,10 +187,10 @@ public class NotificationGutsTest { @UiThreadTest public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); + RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance); highButton.setChecked(true); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); @@ -199,10 +199,10 @@ public class NotificationGutsTest { @Test @UiThreadTest public void testCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - mNotificationGuts.closeControls(-1, -1, true); + mNotificationInfo.closeControls(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); } @@ -211,10 +211,10 @@ public class NotificationGutsTest { @UiThreadTest public void testCloseControls_DoesNotUpdateNotificationChannelIfUnspecified() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - mNotificationGuts.closeControls(-1, -1, true); + mNotificationInfo.closeControls(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); } @@ -223,12 +223,12 @@ public class NotificationGutsTest { @UiThreadTest public void testCloseControls_CallsUpdateNotificationChannelIfChanged() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); + RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance); highButton.setChecked(true); - mNotificationGuts.closeControls(-1, -1, true); + mNotificationInfo.closeControls(); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel)); assertEquals(NotificationManager.IMPORTANCE_HIGH, mNotificationChannel.getImportance()); @@ -238,12 +238,12 @@ public class NotificationGutsTest { @UiThreadTest public void testCloseControls_DoesNotUpdateNotificationChannelIfSaveFalse() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance); + RadioButton highButton = (RadioButton) mNotificationInfo.findViewById(R.id.high_importance); highButton.setChecked(true); - mNotificationGuts.closeControls(-1, -1, false); + mNotificationInfo.closeControls(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), anyInt(), any()); } @@ -252,10 +252,10 @@ public class NotificationGutsTest { @UiThreadTest public void testEnabledSwitchOnByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertTrue(enabledSwitch.isChecked()); } @@ -263,10 +263,10 @@ public class NotificationGutsTest { @UiThreadTest public void testEnabledSwitchVisibleByDefault() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); } @@ -274,11 +274,11 @@ public class NotificationGutsTest { @UiThreadTest public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); } @@ -286,13 +286,13 @@ public class NotificationGutsTest { @UiThreadTest public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); enabledSwitch.setChecked(false); - mNotificationGuts.closeControls(-1, -1, true); + mNotificationInfo.closeControls(); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel)); } @@ -301,14 +301,14 @@ public class NotificationGutsTest { @UiThreadTest public void testEnabledSwitchOverridesOtherButtons() throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager, + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, mMockStatusBarNotification, mNotificationChannel, null, null, null); - Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch); - RadioButton lowButton = (RadioButton) mNotificationGuts.findViewById(R.id.low_importance); + Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); + RadioButton lowButton = (RadioButton) mNotificationInfo.findViewById(R.id.low_importance); lowButton.setChecked(true); enabledSwitch.setChecked(false); - mNotificationGuts.closeControls(-1, -1, true); + mNotificationInfo.closeControls(); assertEquals(NotificationManager.IMPORTANCE_NONE, mNotificationChannel.getImportance()); } } |