diff options
author | Julia Reynolds <juliacr@google.com> | 2018-01-03 12:27:24 -0500 |
---|---|---|
committer | Julia Reynolds <juliacr@google.com> | 2018-01-10 12:56:57 -0500 |
commit | 437cdb146498cbfabd6ed0c2466fc5349b0f1d65 (patch) | |
tree | 9bd4b70839591b1413550c86891355e4db3a3749 | |
parent | 41eae630b38e49fd031d5ee14a06a9bb62a77d49 (diff) |
Inline noti blocking is now in the form of a question
And has an 'undo' flow that matches snoozing.
Test: runtest systemui
Bug: 63927402
Change-Id: Idc17b8d950bc3da7ec9fe035dc8a65146d12c456
9 files changed, 537 insertions, 790 deletions
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a659b370560f..14d4c02a8d39 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -193,10 +193,10 @@ <dimen name="notification_header_background_height">49.5dp</dimen> <!-- The top padding for the notification header --> - <dimen name="notification_header_padding_top">14dp</dimen> + <dimen name="notification_header_padding_top">16dp</dimen> <!-- The bottom padding for the notification header --> - <dimen name="notification_header_padding_bottom">13dp</dimen> + <dimen name="notification_header_padding_bottom">16dp</dimen> <!-- The margin at the bottom of the notification header. --> <dimen name="notification_header_margin_bottom">5dp</dimen> diff --git a/packages/SystemUI/res/drawable/ic_info.xml b/packages/SystemUI/res/drawable/ic_info.xml new file mode 100644 index 000000000000..1eb4004ac778 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_info.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2018 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 + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M22,34h4L26,22h-4v12zM24,4C12.95,4 4,12.95 4,24s8.95,20 20,20 20,-8.95 20,-20S35.05,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16 -7.18,16 -16,16zM22,18h4v-4h-4v4z"/> +</vector> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 2e35d8673635..6b5bd1260acc 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -23,23 +23,22 @@ android:clickable="true" android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" + android:paddingEnd="@*android:dimen/notification_content_margin_end" android:background="@color/notification_guts_bg_color" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> - <LinearLayout + <RelativeLayout android:layout_width="match_parent" - android:layout_height="@*android:dimen/notification_header_height" + android:layout_height="@dimen/notification_guts_header_height" android:clipChildren="false" - android:paddingTop="@*android:dimen/notification_header_padding_top" - android:paddingBottom="@*android:dimen/notification_header_padding_bottom" - android:gravity="center_vertical" - android:orientation="horizontal" > + android:clipToPadding="false" + android:layout_marginTop="@*android:dimen/notification_header_padding_top" > <ImageView android:id="@+id/pkgicon" - android:layout_width="@*android:dimen/notification_header_icon_size" - android:layout_height="@*android:dimen/notification_header_icon_size" - android:layout_marginEnd="3dp"/> + android:layout_width="@dimen/notification_guts_header_height" + android:layout_height="@dimen/notification_guts_header_height" + android:layout_marginEnd="3dp" /> <TextView android:id="@+id/pkgname" android:layout_width="wrap_content" @@ -47,7 +46,9 @@ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info" android:layout_marginStart="3dp" android:layout_marginEnd="2dp" - android:singleLine="true"/> + android:singleLine="true" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkgicon" /> <TextView android:id="@+id/pkg_group_divider" android:layout_width="wrap_content" @@ -55,7 +56,9 @@ android:textAppearance="@*android:style/TextAppearance.Material.Notification.Info" android:layout_marginStart="2dp" android:layout_marginEnd="2dp" - android:text="@*android:string/notification_header_divider_symbol"/> + android:text="@*android:string/notification_header_divider_symbol" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkgname" /> <TextView android:id="@+id/group_name" android:layout_width="wrap_content" @@ -64,82 +67,103 @@ android:layout_marginStart="2dp" android:layout_marginEnd="2dp" android:ellipsize="end" - android:maxLines="1"/> - </LinearLayout> + android:maxLines="1" + android:layout_centerVertical="true" + android:layout_toEndOf="@id/pkg_group_divider" /> + <ImageButton + android:id="@+id/info" + android:src="@drawable/ic_info" + android:tint="?android:attr/colorAccent" + android:layout_width="@dimen/notification_guts_header_height" + android:layout_height="@dimen/notification_guts_header_height" + android:contentDescription="@string/notification_more_settings" + android:background="@drawable/ripple_drawable" + android:layout_alignParentEnd="true" /> + </RelativeLayout> - <!-- Channel Info Block --> <LinearLayout + android:id="@+id/prompt" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="20dp" - android:layout_marginEnd="@*android:dimen/notification_content_margin_end" + android:layout_marginBottom="@dimen/notification_guts_button_spacing" + android:layout_marginTop="@*android:dimen/notification_header_padding_top" android:orientation="vertical"> - <!-- Channel Text --> + + <!-- Channel Info Block --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="vertical"> <!-- Channel Name --> <TextView android:id="@+id/channel_name" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginBottom="6dp" style="@style/TextAppearance.NotificationInfo.Primary" /> - <!-- Ban Channel Switch --> - <Switch - android:id="@+id/channel_enabled_switch" + <!-- Question prompt --> + <TextView + android:id="@+id/block_prompt" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="end|center_vertical" - android:contentDescription="@string/notification_channel_switch_accessibility" - android:background="@null" /> + style="@style/TextAppearance.NotificationInfo.Secondary" /> </LinearLayout> - <!-- Secondary Text - only one shows at a time --> - <TextView - android:id="@+id/channel_disabled" - android:layout_width="wrap_content" + + <!-- Settings and Done buttons --> + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/notification_channel_disabled" - style="@style/TextAppearance.NotificationInfo.Secondary.Warning" /> + android:orientation="horizontal" + android:layout_marginTop="@dimen/notification_guts_button_spacing" + android:gravity="end" > + + <!-- Optional link to app. Only appears if the channel is not disabled and the app + asked for it --> + <TextView + android:id="@+id/app_settings" + android:text="@string/notification_app_settings" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:ellipsize="end" + android:maxLines="1" + style="@style/TextAppearance.NotificationInfo.Button"/> + <TextView + android:id="@+id/block" + android:text="@string/inline_stop_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@style/TextAppearance.NotificationInfo.Button"/> + <TextView + android:id="@+id/keep" + android:text="@string/inline_keep_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginEnd="-8dp" + style="@style/TextAppearance.NotificationInfo.Button"/> + </LinearLayout> + </LinearLayout> + <RelativeLayout + android:id="@+id/confirmation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/notification_guts_button_spacing" + android:layout_marginTop="@*android:dimen/notification_header_padding_top" + android:visibility="gone" + android:orientation="horizontal" > <TextView - android:id="@+id/num_channels_desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/notification_channel_disabled" - style="@style/TextAppearance.NotificationInfo.Secondary" /> - <!-- Optional link to app. Only appears if the channel is not disabled --> + style="@style/TextAppearance.NotificationInfo.Secondary.Warning"/> <TextView - android:id="@+id/app_settings" + android:id="@+id/undo" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:visibility="gone" - android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationInfo.Secondary.Link"/> - </LinearLayout> - - <!-- Settings and Done buttons --> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="48dp" - android:orientation="horizontal" - android:gravity="end" - android:layout_marginEnd="@*android:dimen/notification_content_margin_end" - android:layout_marginBottom="8dp" > - <TextView - android:id="@+id/more_settings" - android:text="@string/notification_more_settings" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginEnd="8dp" + android:text="@string/inline_undo" + android:layout_alignParentEnd="true" + android:layout_marginEnd="-8dp" style="@style/TextAppearance.NotificationInfo.Button"/> - <TextView - android:id="@+id/done" - android:text="@string/notification_done" - android:layout_width="wrap_content" - android:layout_height="match_parent" - style="@style/TextAppearance.NotificationInfo.Button"/> - </LinearLayout> + </RelativeLayout> </com.android.systemui.statusbar.NotificationInfo> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 60e9ebf4ba5d..8f2068111cd8 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -148,6 +148,12 @@ <!-- The space around a notification menu item --> <dimen name="notification_menu_icon_padding">20dp</dimen> + <!-- The veritical space around the buttons in the inline settings --> + <dimen name="notification_guts_button_spacing">20dp</dimen> + + <!-- The height of the header in inline settings --> + <dimen name="notification_guts_header_height">24dp</dimen> + <!-- The minimum height for the snackbar shown after the snooze option has been chosen. --> <dimen name="snooze_snackbar_min_height">56dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 98537a102e1a..a19917d91667 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1445,38 +1445,22 @@ <string name="notification_header_default_channel">Notifications</string> <!-- Notification Inline Controls: Shown when a channel's notifications are currently blocked --> - <string name="notification_channel_disabled">You won\'t get these notifications anymore</string> + <string name="notification_channel_disabled">You won\'t see these notifications anymore</string> - <!-- Notification: Control panel: Label that shows how many channels are included in this bundle - of notifications. Replaces the channel name and only appears when there is more than one channel. --> - <string name="notification_num_channels"> <xliff:g id="number">%d</xliff:g> notification categories</string> + <!-- Notification Inline controls: continue receiving notifications prompt, channel level --> + <string name="inline_keep_showing">Keep showing these notifications?</string> - <!-- Notification: Control panel: Label that shows when an app has not upgraded to use channels. - Hints that the user's only option is to block all of the app's notifications. --> - <string name="notification_default_channel_desc">This app doesn\'t have notification categories</string> + <!-- Notification inline controls: block notifications button --> + <string name="inline_stop_button">Stop notifications</string> - <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> - <string name="notification_unblockable_desc">Notifications from this app can\'t be turned off</string> + <!-- Notification inline controls: keep getting notifications button --> + <string name="inline_keep_button">Keep showing</string> - <!-- Notification: Control panel: Label that shows how many channels this application has - defined, describing the current notification channel as "1 out of n notification categories from this app". --> - <plurals name="notification_num_channels_desc"> - <item quantity="one">1 out of <xliff:g id="number">%s</xliff:g> notification category from this app</item> - <item quantity="other">1 out of <xliff:g id="number">%s</xliff:g> notification categories from this app</item> - </plurals> + <!-- Notification Inline controls: continue receiving notifications prompt, app level --> + <string name="inline_keep_showing_app">Keep showing notifications from this app?</string> - <!-- Notification: Control panel: For bundles of notifications, this label that lists the - channels used by the contained notifications. The first two channels are listed by name, - followed by "and N others" - Example: "Friend requests, Friend confirmations" - Example: "Friend requests, Friend confirmations, and 1 other" - Example: "Friend requests, Friend confirmations, and 2 others" - --> - <string name="notification_channels_list_desc_2"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g></string> - <plurals name="notification_channels_list_desc_2_and_others"> - <item quantity="one"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> other</item> - <item quantity="other"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> others</item> - </plurals> + <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> + <string name="notification_unblockable_desc">These notifications can\'t be turned off</string> <!-- Notification: Control panel: Accessibility description for expanded inline controls view, used to control settings about notifications related to the current notification. --> @@ -1488,16 +1472,15 @@ or disable notifications from this channel --> <string name="notification_channel_switch_accessibility">Allow notifications from this channel</string> <!-- Notification: Control panel: Label for button that launches notification settings. Used - when this app has defined more than a single channel for notifications. --> - <string name="notification_all_categories">All Categories</string> - <!-- Notification: Control panel: Label for button that launches notification settings. Used when this app has only defined a single channel for notifications. --> <string name="notification_more_settings">More settings</string> <!-- Notification: Control panel: Label for a link that launches notification settings in the app that sent the notification. --> - <string name="notification_app_settings">Customize: <xliff:g id="sub_category" example="Work chats">%1$s</xliff:g></string> + <string name="notification_app_settings">Customize</string> <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> <string name="notification_done">Done</string> + <!-- Notification: inline controls: undo block button --> + <string name="inline_undo">Undo</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> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 87ad6f6bc948..441c18431894 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -229,10 +229,9 @@ public class NotificationGutsManager implements Dumpable { } } try { - info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels), - row.getEntry().channel.getImportance(), sbn, onSettingsClick, - onAppSettingsClick, onDoneClick, mCheckSaveListener, - mNonBlockablePkgs); + info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel, + channels.size(), sbn, mCheckSaveListener, onSettingsClick, + onAppSettingsClick, mNonBlockablePkgs); } catch (RemoteException e) { Log.e(TAG, e.toString()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 8d1bb5fe940d..6279fdc46599 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -18,6 +18,10 @@ package com.android.systemui.statusbar; import static android.app.NotificationManager.IMPORTANCE_NONE; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; @@ -38,12 +42,12 @@ import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.LinearLayout; -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 java.lang.IllegalArgumentException; @@ -57,25 +61,37 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private static final String TAG = "InfoGuts"; private INotificationManager mINotificationManager; + private PackageManager mPm; + private String mPkg; private String mAppName; private int mAppUid; - private List<NotificationChannel> mNotificationChannels; + private int mNumNotificationChannels; private NotificationChannel mSingleNotificationChannel; + private int mStartingUserImportance; + private int mChosenImportance; private boolean mIsSingleDefaultChannel; + private boolean mNonblockable; private StatusBarNotification mSbn; - private int mStartingUserImportance; + private AnimatorSet mExpandAnimation; - private TextView mNumChannelsView; - private View mChannelDisabledView; - private TextView mSettingsLinkView; - private Switch mChannelEnabledSwitch; private CheckSaveListener mCheckSaveListener; + private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; - private PackageManager mPm; - private NotificationGuts mGutsContainer; + private OnClickListener mOnKeepShowing = v -> { + closeControls(v); + }; + + private OnClickListener mOnStopNotifications = v -> { + swapContent(false); + }; + + private OnClickListener mOnUndo = v -> { + swapContent(true); + }; + public NotificationInfo(Context context, AttributeSet attrs) { super(context, attrs); } @@ -98,141 +114,93 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G public void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager, final String pkg, - final List<NotificationChannel> notificationChannels, - int startingUserImportance, + final NotificationChannel notificationChannel, + final int numChannels, final StatusBarNotification sbn, - OnSettingsClickListener onSettingsClick, - OnAppSettingsClickListener onAppSettingsClick, - OnClickListener onDoneClick, - CheckSaveListener checkSaveListener, + final CheckSaveListener checkSaveListener, + final OnSettingsClickListener onSettingsClick, + final OnAppSettingsClickListener onAppSettingsClick, final Set<String> nonBlockablePkgs) throws RemoteException { mINotificationManager = iNotificationManager; mPkg = pkg; - mNotificationChannels = notificationChannels; - mCheckSaveListener = checkSaveListener; + mNumNotificationChannels = numChannels; mSbn = sbn; mPm = pm; mAppSettingsClickListener = onAppSettingsClick; - mStartingUserImportance = startingUserImportance; mAppName = mPkg; - Drawable pkgicon = null; - CharSequence channelNameText = ""; - ApplicationInfo info = null; - try { - info = pm.getApplicationInfo(mPkg, - PackageManager.MATCH_UNINSTALLED_PACKAGES - | PackageManager.MATCH_DISABLED_COMPONENTS - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_DIRECT_BOOT_AWARE); - if (info != null) { - mAppUid = sbn.getUid(); - mAppName = 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(); - } - ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); + mCheckSaveListener = checkSaveListener; + mOnSettingsClickListener = onSettingsClick; + mSingleNotificationChannel = notificationChannel; + mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); - int numTotalChannels = iNotificationManager.getNumNotificationChannelsForPackage( + int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); - if (mNotificationChannels.isEmpty()) { + if (mNumNotificationChannels == 0) { throw new IllegalArgumentException("bindNotification requires at least one channel"); } else { - if (mNotificationChannels.size() == 1) { - mSingleNotificationChannel = mNotificationChannels.get(0); - // Special behavior for the Default channel if no other channels have been defined. - mIsSingleDefaultChannel = - (mSingleNotificationChannel.getId() - .equals(NotificationChannel.DEFAULT_CHANNEL_ID) && - numTotalChannels <= 1); - } else { - mSingleNotificationChannel = null; - mIsSingleDefaultChannel = false; - } + // Special behavior for the Default channel if no other channels have been defined. + mIsSingleDefaultChannel = mNumNotificationChannels == 1 + && mSingleNotificationChannel.getId() + .equals(NotificationChannel.DEFAULT_CHANNEL_ID) + && numTotalChannels <= 1; } - boolean nonBlockable = false; try { final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) { - final int numChannels = mNotificationChannels.size(); - for (int i = 0; i < numChannels; i++) { - final NotificationChannel notificationChannel = mNotificationChannels.get(i); - // If any of the system channels is not blockable, the bundle is nonblockable - if (!notificationChannel.isBlockableSystem()) { - nonBlockable = true; - break; - } + if (mSingleNotificationChannel != null + && !mSingleNotificationChannel.isBlockableSystem()) { + mNonblockable = true; } } } catch (PackageManager.NameNotFoundException e) { // unlikely. } if (nonBlockablePkgs != null) { - nonBlockable |= nonBlockablePkgs.contains(pkg); + mNonblockable |= nonBlockablePkgs.contains(pkg); } + mNonblockable |= (mNumNotificationChannels > 1); - String channelsDescText; - mNumChannelsView = findViewById(R.id.num_channels_desc); - if (nonBlockable) { - channelsDescText = mContext.getString(R.string.notification_unblockable_desc); - } else if (mIsSingleDefaultChannel) { - channelsDescText = mContext.getString(R.string.notification_default_channel_desc); - } else { - switch (mNotificationChannels.size()) { - case 1: - channelsDescText = String.format(mContext.getResources().getQuantityString( - R.plurals.notification_num_channels_desc, numTotalChannels), - numTotalChannels); - break; - case 2: - channelsDescText = mContext.getString( - R.string.notification_channels_list_desc_2, - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName()); - break; - default: - final int numOthers = mNotificationChannels.size() - 2; - channelsDescText = String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_channels_list_desc_2_and_others, - numOthers), - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName(), - numOthers); + bindHeader(); + bindPrompt(); + bindButtons(); + } + + private void bindHeader() throws RemoteException { + // Package name + Drawable pkgicon = null; + ApplicationInfo info; + try { + info = mPm.getApplicationInfo(mPkg, + PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE); + if (info != null) { + mAppUid = mSbn.getUid(); + mAppName = String.valueOf(mPm.getApplicationLabel(info)); + pkgicon = mPm.getApplicationIcon(info); } + } catch (PackageManager.NameNotFoundException e) { + // app is gone, just show package name and generic icon + pkgicon = mPm.getDefaultActivityIcon(); } - mNumChannelsView.setText(channelsDescText); - - if (mSingleNotificationChannel == null) { - // Multiple channels don't use a channel name for the title. - channelNameText = mContext.getString(R.string.notification_num_channels, - mNotificationChannels.size()); - } else if (mIsSingleDefaultChannel || nonBlockable) { - // If this is the default channel or the app is unblockable, - // don't use our channel-specific text. - channelNameText = mContext.getString(R.string.notification_header_default_channel); - } else { - channelNameText = mSingleNotificationChannel.getName(); - } + ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon); ((TextView) findViewById(R.id.pkgname)).setText(mAppName); - ((TextView) findViewById(R.id.channel_name)).setText(channelNameText); // Set group information if this channel has an associated group. CharSequence groupName = null; if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { final NotificationChannelGroup notificationChannelGroup = - iNotificationManager.getNotificationChannelGroupForPackage( - mSingleNotificationChannel.getGroup(), pkg, mAppUid); + mINotificationManager.getNotificationChannelGroupForPackage( + mSingleNotificationChannel.getGroup(), mPkg, mAppUid); if (notificationChannelGroup != null) { groupName = notificationChannelGroup.getName(); } } - TextView groupNameView = ((TextView) findViewById(R.id.group_name)); - TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider)); + TextView groupNameView = findViewById(R.id.group_name); + TextView groupDividerView = findViewById(R.id.pkg_group_divider); if (groupName != null) { groupNameView.setText(groupName); groupNameView.setVisibility(View.VISIBLE); @@ -242,53 +210,55 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G groupDividerView.setVisibility(View.GONE); } - bindButtons(nonBlockable); - - // Top-level importance group - mChannelDisabledView = findViewById(R.id.channel_disabled); - updateSecondaryText(); - // Settings button. - final TextView settingsButton = (TextView) findViewById(R.id.more_settings); - if (mAppUid >= 0 && onSettingsClick != null) { + final View settingsButton = findViewById(R.id.info); + if (mAppUid >= 0 && mOnSettingsClickListener != null) { settingsButton.setVisibility(View.VISIBLE); final int appUidF = mAppUid; settingsButton.setOnClickListener( (View view) -> { - onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF); + mOnSettingsClickListener.onClick(view, + mNumNotificationChannels > 1 ? null : mSingleNotificationChannel, + appUidF); }); - if (numTotalChannels <= 1 || nonBlockable) { - settingsButton.setText(R.string.notification_more_settings); - } else { - settingsButton.setText(R.string.notification_all_categories); - } } else { settingsButton.setVisibility(View.GONE); } + } - // Done button. - final TextView doneButton = (TextView) findViewById(R.id.done); - doneButton.setText(R.string.notification_done); - doneButton.setOnClickListener(onDoneClick); + private void bindPrompt() { + final TextView channelName = findViewById(R.id.channel_name); + final TextView blockPrompt = findViewById(R.id.block_prompt); + if (mNonblockable) { + if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + channelName.setVisibility(View.GONE); + } else { + channelName.setText(mSingleNotificationChannel.getName()); + } - // Optional settings link - updateAppSettingsLink(); + blockPrompt.setText(R.string.notification_unblockable_desc); + } else { + if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) { + channelName.setVisibility(View.GONE); + blockPrompt.setText(R.string.inline_keep_showing_app); + } else { + channelName.setText(mSingleNotificationChannel.getName()); + blockPrompt.setText(R.string.inline_keep_showing); + } + } } private boolean hasImportanceChanged() { - return mSingleNotificationChannel != null && - mChannelEnabledSwitch != null && - mStartingUserImportance != getSelectedImportance(); + return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance; } private void saveImportance() { - if (!hasImportanceChanged()) { + if (mNonblockable || !hasImportanceChanged()) { return; } - final int selectedImportance = getSelectedImportance(); MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, - selectedImportance - mStartingUserImportance); - mSingleNotificationChannel.setImportance(selectedImportance); + mChosenImportance - mStartingUserImportance); + mSingleNotificationChannel.setImportance(mChosenImportance); mSingleNotificationChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); try { mINotificationManager.updateNotificationChannelForPackage( @@ -298,30 +268,78 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private int getSelectedImportance() { - if (!mChannelEnabledSwitch.isChecked()) { - return IMPORTANCE_NONE; + private void bindButtons() { + View block = findViewById(R.id.block); + block.setOnClickListener(mOnStopNotifications); + TextView keep = findViewById(R.id.keep); + keep.setOnClickListener(mOnKeepShowing); + findViewById(R.id.undo).setOnClickListener(mOnUndo); + + if (mNonblockable) { + keep.setText(R.string.notification_done); + block.setVisibility(GONE); + } + + // app settings link + TextView settingsLinkView = findViewById(R.id.app_settings); + Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, + mSbn.getId(), mSbn.getTag()); + if (settingsIntent != null + && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { + settingsLinkView.setVisibility(View.VISIBLE); + settingsLinkView.setText(mContext.getString(R.string.notification_app_settings, + mSbn.getNotification().getSettingsText())); + settingsLinkView.setOnClickListener((View view) -> { + mAppSettingsClickListener.onClick(view, settingsIntent); + }); } else { - return mStartingUserImportance; + settingsLinkView.setVisibility(View.GONE); } } - private void bindButtons(final boolean nonBlockable) { - // Enabled Switch - mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch); - mChannelEnabledSwitch.setChecked( - mStartingUserImportance != IMPORTANCE_NONE); - final boolean visible = !nonBlockable && mSingleNotificationChannel != null; - mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - - // Callback when checked. - mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (mGutsContainer != null) { - mGutsContainer.resetFalsingCheck(); + private void swapContent(boolean showPrompt) { + if (mExpandAnimation != null) { + mExpandAnimation.cancel(); + } + + if (showPrompt) { + mChosenImportance = mStartingUserImportance; + } else { + mChosenImportance = IMPORTANCE_NONE; + } + + View prompt = findViewById(R.id.prompt); + View confirmation = findViewById(R.id.confirmation); + ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA, + prompt.getAlpha(), showPrompt ? 1f : 0f); + promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT); + ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA, + confirmation.getAlpha(), showPrompt ? 0f : 1f); + confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN); + + prompt.setVisibility(showPrompt ? VISIBLE : GONE); + confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + + mExpandAnimation = new AnimatorSet(); + mExpandAnimation.playTogether(promptAnim, confirmAnim); + mExpandAnimation.setDuration(150); + mExpandAnimation.addListener(new AnimatorListenerAdapter() { + boolean cancelled = false; + + @Override + public void onAnimationCancel(Animator animation) { + cancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!cancelled) { + prompt.setVisibility(showPrompt ? VISIBLE : GONE); + confirmation.setVisibility(showPrompt ? GONE : VISIBLE); + } } - updateSecondaryText(); - updateAppSettingsLink(); }); + mExpandAnimation.start(); } @Override @@ -339,35 +357,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } } - private void updateSecondaryText() { - final boolean disabled = mSingleNotificationChannel != null && - getSelectedImportance() == IMPORTANCE_NONE; - if (disabled) { - mChannelDisabledView.setVisibility(View.VISIBLE); - mNumChannelsView.setVisibility(View.GONE); - } else { - mChannelDisabledView.setVisibility(View.GONE); - mNumChannelsView.setVisibility(mIsSingleDefaultChannel ? View.INVISIBLE : View.VISIBLE); - } - } - - private void updateAppSettingsLink() { - mSettingsLinkView = findViewById(R.id.app_settings); - Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, - mSbn.getId(), mSbn.getTag()); - if (settingsIntent != null && getSelectedImportance() != IMPORTANCE_NONE - && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { - mSettingsLinkView.setVisibility(View.VISIBLE); - mSettingsLinkView.setText(mContext.getString(R.string.notification_app_settings, - mSbn.getNotification().getSettingsText())); - mSettingsLinkView.setOnClickListener((View view) -> { - mAppSettingsClickListener.onClick(view, settingsIntent); - }); - } else { - mSettingsLinkView.setVisibility(View.GONE); - } - } - private Intent getAppSettingsIntent(PackageManager pm, String packageName, NotificationChannel channel, int id, String tag) { Intent intent = new Intent(Intent.ACTION_MAIN) @@ -390,6 +379,18 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G return intent; } + private void closeControls(View v) { + int[] parentLoc = new int[2]; + int[] targetLoc = new int[2]; + mGutsContainer.getLocationOnScreen(parentLoc); + v.getLocationOnScreen(targetLoc); + final int centerX = v.getWidth() / 2; + final int centerY = v.getHeight() / 2; + final int x = targetLoc[0] - parentLoc[0] + centerX; + final int y = targetLoc[1] - parentLoc[1] + centerY; + mGutsContainer.closeControls(x, y, false /* save */, false /* force */); + } + @Override public void setGutsParent(NotificationGuts guts) { mGutsContainer = guts; @@ -397,7 +398,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G @Override public boolean willBeRemoved() { - return mChannelEnabledSwitch != null && !mChannelEnabledSwitch.isChecked(); + return hasImportanceChanged(); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index 672193835ac3..d77bf69ad365 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -16,11 +16,13 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.any; @@ -50,11 +52,11 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.PollingCheck; import android.testing.UiThreadTest; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; -import android.widget.Switch; import android.widget.TextView; import com.android.systemui.R; @@ -66,7 +68,6 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -117,50 +118,31 @@ public class NotificationInfoTest extends SysuiTestCase { // Some test channels. mNotificationChannel = new NotificationChannel( - TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW); + TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); mDefaultNotificationChannel = new NotificationChannel( NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, - NotificationManager.IMPORTANCE_LOW); + IMPORTANCE_LOW); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, new Notification(), UserHandle.CURRENT, null, 0); } - private CharSequence getStringById(int resId) { - return mContext.getString(resId); + // TODO: if tests are taking too long replace this with something that makes the animation + // finish instantly. + private void waitForUndoButton() { + PollingCheck.waitFor(1000, + () -> VISIBLE == mNotificationInfo.findViewById(R.id.confirmation).getVisibility()); } - - private CharSequence getNumChannelsDescString(int numChannels) { - return String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_num_channels_desc, numChannels), - numChannels); - } - - private CharSequence getChannelsListDescString(NotificationChannel... channels) { - if (channels.length == 2) { - return mContext.getString(R.string.notification_channels_list_desc_2, - channels[0].getName(), channels[1].getName()); - } else { - final int numOthers = channels.length - 2; - return String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_channels_list_desc_2_and_others, numOthers), - channels[0].getName(), channels[1].getName(), numOthers); - } - } - - private CharSequence getNumChannelsString(int numChannels) { - return mContext.getString(R.string.notification_num_channels, numChannels); + private void waitForStopButton() { + PollingCheck.waitFor(1000, + () -> VISIBLE == mNotificationInfo.findViewById(R.id.prompt).getVisibility()); } @Test public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.pkgname); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); } @@ -170,24 +152,19 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final ImageView iconView = (ImageView) mNotificationInfo.findViewById(R.id.pkgicon); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); - assertEquals(View.GONE, groupNameView.getVisibility()); - final TextView groupDividerView = - (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider); - assertEquals(View.GONE, groupDividerView.getVisibility()); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); + assertEquals(GONE, groupNameView.getVisibility()); + final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); + assertEquals(GONE, groupDividerView.getVisibility()); } @Test @@ -199,75 +176,50 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); - final TextView groupDividerView = - (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider); + final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); assertEquals(View.VISIBLE, groupDividerView.getVisibility()); } @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @Test public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mContext.getString(R.string.notification_header_default_channel), - textView.getText()); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(GONE, textView.getVisibility()); } @Test - public void testBindNotification_UnblockablePackageDoesNotUseChannelName() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mContext.getString(R.string.notification_header_default_channel), - textView.getText()); - } - - @Test - public void testBindNotification_DefaultChannelUsesNameWhenMoreThanOneChannelExists() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); + public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView textView = (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(mDefaultNotificationChannel.getName(), textView.getText()); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); + final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(VISIBLE, textView.getVisibility()); } @Test public void testBindNotification_SetsOnClickListenerForSettings() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, null, null, null); + }, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); @@ -276,24 +228,20 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @Test - public void testBindNotification_SettingsButtonReappersAfterSecondBind() throws Exception { + public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> {}, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, + (View v, NotificationChannel c, int appUid) -> { + }, null, null); + final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -301,279 +249,63 @@ public class NotificationInfoTest extends SysuiTestCase { public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, null, null, null); - - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - settingsButton.performClick(); - // Verify that listener was triggered. - assertEquals(0, latch.getCount()); - } - - @Test - public void testBindNotification_SettingsTextWithOneChannel() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText()); - } - - @Test - public void testBindNotification_SettingsTextWithMultipleChannels() throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, null); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_all_categories), settingsButton.getText()); - } - - @Test - public void testBindNotification_SettingsTextWithMultipleChannelsForUnblockableApp() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, - (View v, NotificationChannel c, int appUid) -> { - }, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView settingsButton = - (TextView) mNotificationInfo.findViewById(R.id.more_settings); - assertEquals(getStringById(R.string.notification_more_settings), settingsButton.getText()); - } - - @Test - public void testBindNotification_SetsOnClickListenerForDone() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, - null, (View v) -> { - latch.countDown(); - }, - null, null); + }, null, null); - final TextView doneButton = (TextView) mNotificationInfo.findViewById(R.id.done); - doneButton.performClick(); + mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. assertEquals(0, latch.getCount()); } @Test - public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(View.INVISIBLE, numChannelsView.getVisibility()); - } - - @Test - public void testBindNotification_NumChannelsTextDisplaysWhenMoreThanOneChannelExists() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.VISIBLE); - assertEquals(getNumChannelsDescString(2), numChannelsView.getText()); - } - - @Test - public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel() - throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.VISIBLE); - assertEquals(getNumChannelsDescString(1), numChannelsView.getText()); - } - - @Test - public void testBindNotification_NumChannelsTextScalesWithNumberOfChannels() - throws Exception { - when(mMockINotificationManager.getNumNotificationChannelsForPackage( - eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(2); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(getNumChannelsDescString(2), numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenTwoInBundle() - throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenThreeInBundle() - throws Exception { - NotificationChannel thirdChannel = new NotificationChannel( - "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals( - getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel, - thirdChannel), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_NumChannelsTextListsChannelsWhenFourInBundle() - throws Exception { - NotificationChannel thirdChannel = new NotificationChannel( - "third_channel", "third_channel", NotificationManager.IMPORTANCE_LOW); - NotificationChannel fourthChannel = new NotificationChannel( - "fourth_channel", "fourth_channel", NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, - Arrays.asList(mNotificationChannel, mDefaultNotificationChannel, thirdChannel, - fourthChannel), mNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals( - getChannelsListDescString(mNotificationChannel, mDefaultNotificationChannel, - thirdChannel, fourthChannel), - numChannelsView.getText()); - } - - @Test @UiThreadTest - public void testBindNotification_ChannelNameChangesWhenBundleFromDifferentChannels() + public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); final TextView channelNameView = - (TextView) mNotificationInfo.findViewById(R.id.channel_name); - assertEquals(getNumChannelsString(2), channelNameView.getText()); + mNotificationInfo.findViewById(R.id.channel_name); + assertEquals(GONE, channelNameView.getVisibility()); } @Test @UiThreadTest - public void testEnabledSwitchInvisibleIfBundleFromDifferentChannels() throws Exception { + public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(channelDisabledView.getVisibility(), View.GONE); - } - - @Test - public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(channelDisabledView.getVisibility(), View.VISIBLE); - // Replaces the numChannelsView - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(numChannelsView.getVisibility(), View.GONE); + TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + final TextView blockView = mNotificationInfo.findViewById(R.id.block); + assertEquals(GONE, blockView.getVisibility()); } @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - final TextView numChannelsView = - (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertEquals(View.VISIBLE, numChannelsView.getVisibility()); + final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); + assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), - numChannelsView.getText()); - } - - @Test - @UiThreadTest - public void testBindNotification_ChannelDisabledTextShowsForDefaultChannel() - throws Exception { - mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), - mDefaultNotificationChannel.getImportance(), mSbn, null, null, - null, null, null); - final TextView channelDisabledView = - (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertEquals(View.VISIBLE, channelDisabledView.getVisibility()); + view.getText()); } @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @Test public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @@ -582,9 +314,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -596,9 +326,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -606,134 +334,65 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testEnabledSwitchOnByDefault() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertTrue(enabledSwitch.isChecked()); - } - - @Test - public void testEnabledButtonOffWhenAlreadyBanned() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertFalse(enabledSwitch.isChecked()); - } - - @Test - public void testEnabledSwitchVisibleByDefault() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); - } - - @Test - public void testEnabledSwitchInvisibleIfNonBlockableSystemChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(false); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), any()); } @Test - public void testEnabledSwitchVisibleIfBlockableSystemChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(true); + public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_SYSTEM_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, null); - - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.VISIBLE, enabledSwitch.getVisibility()); - } + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); - @Test - public void testEnabledSwitchInvisibleIfMultiChannelSummary() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationChannel.setBlockableSystem(true); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + mNotificationInfo.handleCloseControls(true, false); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - assertEquals(View.INVISIBLE, enabledSwitch.getVisibility()); + ArgumentCaptor<NotificationChannel> updated = + ArgumentCaptor.forClass(NotificationChannel.class); + verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + anyString(), eq(TEST_UID), updated.capture()); + assertTrue((updated.getValue().getUserLockedFields() + & NotificationChannel.USER_LOCKED_IMPORTANCE) != 0); } @Test - public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + public void testBlockUndoDoesNotCallUpdateNotificationChannel() throws Exception { + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - mNotificationInfo.handleCloseControls(true, false); - verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), any()); - } + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); - @Test - public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); + mNotificationInfo.findViewById(R.id.undo).performClick(); + waitForStopButton(); mNotificationInfo.handleCloseControls(true, false); ArgumentCaptor<NotificationChannel> updated = ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( + verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & NotificationChannel.USER_LOCKED_IMPORTANCE) != 0); + assertEquals(IMPORTANCE_LOW, mNotificationChannel.getImportance()); } @Test public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, + Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(false, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -741,16 +400,14 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsDoesNotUpdateIfCheckSaveListenerIsNoOp() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, - Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, Collections.singleton(TEST_PACKAGE_NAME)); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -758,17 +415,15 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception { - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), mSbn, null, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, - Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, null); - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); + mNotificationInfo.findViewById(R.id.block).performClick(); + waitForUndoButton(); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel)); @@ -785,22 +440,19 @@ public class NotificationInfoTest extends SysuiTestCase { List<ResolveInfo> ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null, null, null); + }, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); - assertTrue(settingsLink.getText().toString().length() > settingsText.length()); - assertTrue(settingsLink.getText().toString().contains(settingsText)); settingsLink.performClick(); assertEquals(0, latch.getCount()); } @@ -816,17 +468,17 @@ public class NotificationInfoTest extends SysuiTestCase { List<ResolveInfo> ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel, mDefaultNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, (View v, Intent intent) -> { + TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, + (View v, Intent intent) -> { latch.countDown(); - }, null, null, null); + }, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -837,18 +489,16 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNoSettingsLink_noHandlingActivity() throws Exception { final String settingsText = "work chats"; when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setSettingsText(settingsText).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.GONE, settingsLink.getVisibility()); + assertEquals(GONE, settingsLink.getVisibility()); } @Test @@ -860,52 +510,17 @@ public class NotificationInfoTest extends SysuiTestCase { List<ResolveInfo> ris = new ArrayList<>(); ris.add(ri); when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); + mNotificationChannel.setImportance(IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build(); StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.GONE, settingsLink.getVisibility()); + assertEquals(GONE, settingsLink.getVisibility()); } - @Test - public void testNoSettingsLink_afterBlockingChannel() throws Exception { - final String settingsText = "work chats"; - final ResolveInfo ri = new ResolveInfo(); - ri.activityInfo = new ActivityInfo(); - ri.activityInfo.packageName = TEST_PACKAGE_NAME; - ri.activityInfo.name = "something"; - List<ResolveInfo> ris = new ArrayList<>(); - ris.add(ri); - when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); - mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW); - Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) - .setSettingsText(settingsText).build(); - StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, - 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); - - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel), - mNotificationChannel.getImportance(), sbn, null, null, null, - null, null); - final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - - // Block channel - Switch enabledSwitch = (Switch) mNotificationInfo.findViewById(R.id.channel_enabled_switch); - enabledSwitch.setChecked(false); - - assertEquals(View.GONE, settingsLink.getVisibility()); - - //unblock - enabledSwitch.setChecked(true); - assertEquals(View.VISIBLE, settingsLink.getVisibility()); - } @Test public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception { diff --git a/tests/testables/src/android/testing/PollingCheck.java b/tests/testables/src/android/testing/PollingCheck.java new file mode 100644 index 000000000000..5a31450f048f --- /dev/null +++ b/tests/testables/src/android/testing/PollingCheck.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 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 android.testing; + +import junit.framework.Assert; +import java.util.concurrent.Callable; + +public abstract class PollingCheck { + private static final long TIME_SLICE = 50; + private long mTimeout = 3000; + + public static interface PollingCheckCondition { + boolean canProceed(); + } + + public PollingCheck() { + } + + public PollingCheck(long timeout) { + mTimeout = timeout; + } + + protected abstract boolean check(); + + public void run() { + if (check()) { + return; + } + + long timeout = mTimeout; + while (timeout > 0) { + try { + Thread.sleep(TIME_SLICE); + } catch (InterruptedException e) { + Assert.fail("unexpected InterruptedException"); + } + + if (check()) { + return; + } + + timeout -= TIME_SLICE; + } + + Assert.fail("unexpected timeout"); + } + + public static void check(CharSequence message, long timeout, Callable<Boolean> condition) + throws Exception { + while (timeout > 0) { + if (condition.call()) { + return; + } + + Thread.sleep(TIME_SLICE); + timeout -= TIME_SLICE; + } + + Assert.fail(message.toString()); + } + + public static void waitFor(final PollingCheckCondition condition) { + new PollingCheck() { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } + + public static void waitFor(long timeout, final PollingCheckCondition condition) { + new PollingCheck(timeout) { + @Override + protected boolean check() { + return condition.canProceed(); + } + }.run(); + } +} + |