Merge changes from topic 'nmr-plugin'
* changes: Add snooze to notification menu row Add snooze listener so that a plugin can snooze a notification Change NotificationGuts to contain a view Rename notification settings icon row / gear to notification menu row Create + implement plugin for NotificationSettingsIconRow
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
packages/SystemUI/res/drawable/ic_snooze.xml
Normal file
12
packages/SystemUI/res/drawable/ic_snooze.xml
Normal file
@@ -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>
|
||||
@@ -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"/>
|
||||
|
||||
146
packages/SystemUI/res/layout/notification_info.xml
Normal file
146
packages/SystemUI/res/layout/notification_info.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
android:visibility="invisible"/>
|
||||
65
packages/SystemUI/res/layout/notification_snooze.xml
Normal file
65
packages/SystemUI/res/layout/notification_snooze.xml
Normal file
@@ -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>
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
if (item.gutsContent instanceof SnoozeGutsContent) {
|
||||
((SnoozeGutsContent) item.gutsContent).setSnoozeListener(getSnoozeListener());
|
||||
((SnoozeGutsContent) item.gutsContent).setStatusBarNotification(sbn);
|
||||
}
|
||||
|
||||
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 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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user