Merge "Change Notification Inline Settings for Channels"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f47277805a
@@ -16,150 +16,109 @@
|
||||
-->
|
||||
|
||||
<com.android.systemui.statusbar.NotificationGuts
|
||||
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: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">
|
||||
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: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 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<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:paddingTop="15dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:id="@+id/notification_guts_header"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="start">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:src="@android:drawable/arrow_down_float" />
|
||||
android:layout_alignParentStart="true"
|
||||
style="@style/TextAppearance.NotificationGuts.Secondary" />
|
||||
<TextView
|
||||
android:id="@+id/pkgname"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.NotificationGuts.Header" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/debug_info"
|
||||
android:layout_weight="0"
|
||||
style="@style/TextAppearance.NotificationGuts.Header"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
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 -->
|
||||
<RadioGroup
|
||||
<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:paddingStart="3dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp" >
|
||||
<RadioButton
|
||||
android:id="@+id/silent_importance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:paddingStart="22dp"
|
||||
android:text="@string/show_silently"
|
||||
style="@style/TextAppearance.NotificationGuts.Radio"
|
||||
android:buttonTint="@color/notification_guts_buttons" />
|
||||
<RadioButton
|
||||
android:id="@+id/block_importance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:paddingStart="22dp"
|
||||
android:text="@string/block"
|
||||
style="@style/TextAppearance.NotificationGuts.Radio"
|
||||
android:buttonTint="@color/notification_guts_buttons" />
|
||||
<RadioButton
|
||||
android:id="@+id/reset_importance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:paddingStart="22dp"
|
||||
style="@style/TextAppearance.NotificationGuts.Radio"
|
||||
android:buttonTint="@color/notification_guts_buttons" />
|
||||
</RadioGroup>
|
||||
<!-- Importance slider -->
|
||||
<LinearLayout
|
||||
android:id="@+id/importance_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:visibility="gone">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
style="@style/TextAppearance.NotificationGuts.Primary"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:paddingBottom="2dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignStart="@android:id/title"
|
||||
android:textAlignment="viewStart"
|
||||
style="@style/TextAppearance.NotificationGuts.Secondary"
|
||||
android:maxLines="3"
|
||||
android:minLines="2"
|
||||
android:paddingBottom="4dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/auto_importance"
|
||||
android:src="@drawable/notification_auto_importance"
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar"
|
||||
android:layout_marginStart="56dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:focusable="true"
|
||||
android:background="#00ffffff"
|
||||
android:progressBackgroundTint="@color/notification_guts_secondary_slider_color"
|
||||
android:thumbTint="?android:attr/colorAccent"
|
||||
android:progressTint="?android:attr/colorAccent"
|
||||
style="@android:style/Widget.Material.SeekBar.Discrete"
|
||||
android:tickMarkTint="@android:color/black" />
|
||||
|
||||
</FrameLayout>
|
||||
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>
|
||||
<!-- buttons -->
|
||||
<!-- 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" >
|
||||
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"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/notification_inline_importance_height"
|
||||
android:paddingTop="4dp"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:textAlignment="viewStart"
|
||||
android:singleLine="true"
|
||||
android:text="@string/high_importance"
|
||||
android:textAppearance="@style/TextAppearance.NotificationGuts.Primary" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:textAlignment="viewStart"
|
||||
android:singleLine="true"
|
||||
android:text="@string/notification_importance_high"
|
||||
android:textAppearance="@style/TextAppearance.NotificationGuts.Secondary" />
|
||||
</LinearLayout>
|
||||
@@ -302,6 +302,9 @@
|
||||
<!-- The height of the divider between the individual notifications when the notification wants it to be increased. This is currently the case for notification groups -->
|
||||
<dimen name="notification_divider_height_increased">6dp</dimen>
|
||||
|
||||
<!-- The height of an importance selection in the inline notification controls -->
|
||||
<dimen name="notification_inline_importance_height">55dp</dimen>
|
||||
|
||||
<!-- The minimum amount of top overscroll to go to the quick settings. -->
|
||||
<dimen name="min_top_overscroll_to_qs">36dp</dimen>
|
||||
|
||||
|
||||
@@ -1334,41 +1334,38 @@
|
||||
\n- Block all notifications from the app
|
||||
</string>
|
||||
|
||||
<!-- Notification importance title, user unspecified status-->
|
||||
<string name="user_unspecified_importance">Importance: Automatic</string>
|
||||
<!-- Notification importance title, blocked status-->
|
||||
<string name="blocked_importance">Importance: Level 0</string>
|
||||
<!-- Notification Inline Controls: Header for apps that are not yet using notification channels. -->
|
||||
<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>
|
||||
|
||||
<!-- Notification Inline Controls: Header text for describing from which app this notification
|
||||
originates. The line below this in the layout will display the channel name.
|
||||
Note for localization: For languages in which the two separate lines cannot be a continuous
|
||||
sentence, translate this as a separate statement: "[Calendar] notifications" -->
|
||||
<string name="notification_importance_header_app"><xliff:g id="app" example="Calendar">%s</xliff:g> notifications for</string>
|
||||
|
||||
<!-- Notification importance title, min status-->
|
||||
<string name="min_importance">Importance: Level 1</string>
|
||||
<string name="min_importance">Low</string>
|
||||
<!-- Notification importance title, low status-->
|
||||
<string name="low_importance">Importance: Level 2</string>
|
||||
<string name="low_importance">Medium</string>
|
||||
<!-- Notification importance title, normal status-->
|
||||
<string name="default_importance">Importance: Level 3</string>
|
||||
<string name="default_importance">High</string>
|
||||
<!-- Notification importance title, high status-->
|
||||
<string name="high_importance">Importance: Level 4</string>
|
||||
<!-- Notification importance title, max status-->
|
||||
<string name="max_importance">Importance: Level 5</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
|
||||
<string name="notification_importance_user_unspecified">App determines importance for each notification.</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: blocked importance level description -->
|
||||
<string name="notification_importance_blocked">Never show notifications from this app.</string>
|
||||
<string name="high_importance">Urgent</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: min importance level description -->
|
||||
<string name="notification_importance_min">No full screen interruption, peeking, sound, or vibration. Hide from lock screen and status bar.</string>
|
||||
<string name="notification_importance_min">No sound or visual interruption</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: low importance level description -->
|
||||
<string name="notification_importance_low">No full screen interruption, peeking, sound, or vibration.</string>
|
||||
<string name="notification_importance_low">Show silently</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: normal importance level description -->
|
||||
<string name="notification_importance_default">No full screen interruption or peeking.</string>
|
||||
<string name="notification_importance_default">Make sound</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: high importance level description -->
|
||||
<string name="notification_importance_high">Always peek. No full screen interruption.</string>
|
||||
|
||||
<!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
|
||||
<string name="notification_importance_max">Always peek, and allow full screen interruption.</string>
|
||||
<string name="notification_importance_high">Make sound and pop on screen</string>
|
||||
|
||||
<!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
|
||||
<string name="notification_more_settings">More settings</string>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
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.
|
||||
@@ -359,12 +359,14 @@
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.NotificationGuts.Header">
|
||||
<item name="android:alpha">.38</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:textSize">20sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.NotificationGuts.Secondary">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
<item name="android:alpha">.38</item>
|
||||
<item name="android:textSize">12sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.NotificationGuts.Primary">
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.StackId;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@@ -1058,74 +1059,43 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
row.setTag(sbn.getPackageName());
|
||||
final NotificationGuts guts = row.getGuts();
|
||||
guts.setClosedListener(this);
|
||||
|
||||
final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||
|
||||
final String pkg = sbn.getPackageName();
|
||||
String appname = pkg;
|
||||
Drawable pkgicon = null;
|
||||
int appUid = -1;
|
||||
try {
|
||||
final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
|
||||
PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
| PackageManager.MATCH_DISABLED_COMPONENTS);
|
||||
if (info != null) {
|
||||
appname = String.valueOf(pmUser.getApplicationLabel(info));
|
||||
pkgicon = pmUser.getApplicationIcon(info);
|
||||
appUid = info.uid;
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// app is gone, just show package name and generic icon
|
||||
pkgicon = pmUser.getDefaultActivityIcon();
|
||||
}
|
||||
|
||||
((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
|
||||
((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
|
||||
|
||||
final TextView settingsButton = (TextView) guts.findViewById(R.id.more_settings);
|
||||
if (appUid >= 0) {
|
||||
final int appUidF = appUid;
|
||||
settingsButton.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
final NotificationGuts.OnSettingsClickListener onSettingsClick =
|
||||
(View v, int appUid) -> {
|
||||
MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
|
||||
guts.resetFalsingCheck();
|
||||
startAppNotificationSettingsActivity(pkg, appUidF);
|
||||
}
|
||||
});
|
||||
settingsButton.setText(R.string.notification_more_settings);
|
||||
} else {
|
||||
settingsButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
guts.bindImportance(pmUser, sbn, mNonBlockablePkgs,
|
||||
mNotificationData.getImportance(sbn.getKey()));
|
||||
|
||||
final TextView doneButton = (TextView) guts.findViewById(R.id.done);
|
||||
doneButton.setText(R.string.notification_done);
|
||||
doneButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(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() {
|
||||
saveImportanceCloseControls(sbn, row, guts, v);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
onLockedNotificationImportanceChange(dismissAction);
|
||||
} else {
|
||||
saveImportanceCloseControls(sbn, row, guts, v);
|
||||
}
|
||||
}
|
||||
});
|
||||
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, onSettingsClick, onDoneClick,
|
||||
mNonBlockablePkgs);
|
||||
}
|
||||
|
||||
private void saveImportanceCloseControls(StatusBarNotification sbn,
|
||||
private void closeControls(
|
||||
ExpandableNotificationRow row, NotificationGuts guts, View done) {
|
||||
guts.resetFalsingCheck();
|
||||
guts.saveImportance(sbn);
|
||||
|
||||
int[] rowLocation = new int[2];
|
||||
int[] doneLocation = new int[2];
|
||||
@@ -1222,7 +1192,7 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
|
||||
public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
|
||||
if (mNotificationGutsExposed != null) {
|
||||
mNotificationGutsExposed.closeControls(x, y, true /* notify */);
|
||||
mNotificationGutsExposed.closeControls(x, y, true /* save */);
|
||||
}
|
||||
if (resetGear) {
|
||||
mStackScroller.resetExposedGearView(animate, true /* force */);
|
||||
@@ -1722,13 +1692,6 @@ public abstract class BaseStatusBar extends SystemUI implements
|
||||
}
|
||||
entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
|
||||
|
||||
if (MULTIUSER_DEBUG) {
|
||||
TextView debug = (TextView) row.findViewById(R.id.debug_info);
|
||||
if (debug != null) {
|
||||
debug.setVisibility(View.VISIBLE);
|
||||
debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
|
||||
}
|
||||
}
|
||||
entry.row = row;
|
||||
entry.row.setOnActivatedListener(this);
|
||||
entry.row.setExpandable(bigContentViewLocal != null);
|
||||
|
||||
@@ -1338,7 +1338,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
if (isUserLocked()) {
|
||||
return getActualHeight();
|
||||
}
|
||||
if (mGuts != null && mGuts.areGutsExposed()) {
|
||||
if (mGuts != null && mGuts.isExposed()) {
|
||||
return mGuts.getHeight();
|
||||
} else if ((isChildInGroup() && !isGroupExpanded())) {
|
||||
return mPrivateLayout.getMinHeight();
|
||||
@@ -1589,7 +1589,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
}
|
||||
|
||||
public boolean areGutsExposed() {
|
||||
return (mGuts != null && mGuts.areGutsExposed());
|
||||
return (mGuts != null && mGuts.isExposed());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1634,7 +1634,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
@Override
|
||||
public void setActualHeight(int height, boolean notifyListeners) {
|
||||
super.setActualHeight(height, notifyListeners);
|
||||
if (mGuts != null && mGuts.areGutsExposed()) {
|
||||
if (mGuts != null && mGuts.isExposed()) {
|
||||
mGuts.setActualHeight(height);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ package com.android.systemui.statusbar;
|
||||
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;
|
||||
@@ -33,13 +35,17 @@ 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.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;
|
||||
@@ -48,16 +54,14 @@ import com.android.settingslib.Utils;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The guts of a notification revealed when performing a long press.
|
||||
*/
|
||||
public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
|
||||
public static final String SHOW_SLIDER = "show_importance_slider";
|
||||
|
||||
public class NotificationGuts extends LinearLayout {
|
||||
private static final String TAG = "NotificationGuts";
|
||||
private static final long CLOSE_GUTS_DELAY = 8000;
|
||||
|
||||
private Drawable mBackground;
|
||||
@@ -67,22 +71,20 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
|
||||
private boolean mExposed;
|
||||
private INotificationManager mINotificationManager;
|
||||
private int mStartingUserImportance;
|
||||
private int mNotificationImportance;
|
||||
private boolean mShowSlider;
|
||||
private StatusBarNotification mStatusBarNotification;
|
||||
|
||||
private SeekBar mSeekBar;
|
||||
private ImageView mAutoButton;
|
||||
private ColorStateList mActiveSliderTint;
|
||||
private ColorStateList mInactiveSliderTint;
|
||||
private float mActiveSliderAlpha = 1.0f;
|
||||
private float mInactiveSliderAlpha;
|
||||
private TextView mImportanceSummary;
|
||||
private TextView mImportanceTitle;
|
||||
private boolean mAuto;
|
||||
|
||||
private RadioButton mBlock;
|
||||
private RadioButton mSilent;
|
||||
private RadioButton mReset;
|
||||
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;
|
||||
@@ -101,29 +103,15 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
|
||||
@Override
|
||||
public void run() {
|
||||
if (mNeedsFalsingProtection && mExposed) {
|
||||
closeControls(-1 /* x */, -1 /* y */, true /* notify */);
|
||||
closeControls(-1 /* x */, -1 /* y */, false /* save */);
|
||||
}
|
||||
}
|
||||
};
|
||||
final TypedArray ta =
|
||||
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Theme, 0, 0);
|
||||
mInactiveSliderAlpha =
|
||||
ta.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
TunerService.get(mContext).removeTunable(this);
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
public void resetFalsingCheck() {
|
||||
mHandler.removeCallbacks(mFalsingCheck);
|
||||
if (mNeedsFalsingProtection && mExposed) {
|
||||
@@ -177,203 +165,213 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
|
||||
}
|
||||
}
|
||||
|
||||
void bindImportance(final PackageManager pm, final StatusBarNotification sbn,
|
||||
final Set<String> nonBlockablePkgs, final int importance) {
|
||||
mINotificationManager = INotificationManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||
mStartingUserImportance = NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
mNotificationImportance = importance;
|
||||
interface OnSettingsClickListener {
|
||||
void onClick(View v, int appUid);
|
||||
}
|
||||
|
||||
void bindNotification(final PackageManager pm, final INotificationManager iNotificationManager,
|
||||
final StatusBarNotification sbn, OnSettingsClickListener onSettingsClick,
|
||||
OnClickListener onDoneClick, final Set<String> nonBlockablePkgs) {
|
||||
mINotificationManager = iNotificationManager;
|
||||
mStatusBarNotification = sbn;
|
||||
final NotificationChannel channel = sbn.getNotificationChannel();
|
||||
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(sbn.getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
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(sbn.getPackageName());
|
||||
nonBlockable |= nonBlockablePkgs.contains(pkg);
|
||||
}
|
||||
|
||||
final View importanceSlider = findViewById(R.id.importance_slider);
|
||||
final View importanceButtons = findViewById(R.id.importance_buttons);
|
||||
if (mShowSlider) {
|
||||
bindSlider(importanceSlider, nonBlockable);
|
||||
importanceSlider.setVisibility(View.VISIBLE);
|
||||
importanceButtons.setVisibility(View.GONE);
|
||||
} else {
|
||||
bindToggles(importanceButtons, mStartingUserImportance, nonBlockable);
|
||||
importanceButtons.setVisibility(View.VISIBLE);
|
||||
importanceSlider.setVisibility(View.GONE);
|
||||
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();
|
||||
}
|
||||
|
||||
void saveImportance(final StatusBarNotification sbn) {
|
||||
int progress = getSelectedImportance();
|
||||
private void saveImportance() {
|
||||
int selectedImportance = getSelectedImportance();
|
||||
if (selectedImportance == mStartingUserImportance) {
|
||||
return;
|
||||
}
|
||||
final NotificationChannel channel = mStatusBarNotification.getNotificationChannel();
|
||||
MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
|
||||
progress - mStartingUserImportance);
|
||||
selectedImportance - mStartingUserImportance);
|
||||
channel.setImportance(selectedImportance);
|
||||
try {
|
||||
mINotificationManager.updateNotificationChannelForPackage(
|
||||
mStatusBarNotification.getPackageName(), mStatusBarNotification.getUid(),
|
||||
channel);
|
||||
} catch (RemoteException e) {
|
||||
// :(
|
||||
}
|
||||
}
|
||||
|
||||
private int getSelectedImportance() {
|
||||
if (mSeekBar!= null && mSeekBar.isShown()) {
|
||||
if (mSeekBar.isEnabled()) {
|
||||
return mSeekBar.getProgress();
|
||||
} else {
|
||||
return NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
}
|
||||
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 {
|
||||
if (mBlock.isChecked()) {
|
||||
return NotificationManager.IMPORTANCE_NONE;
|
||||
} else if (mSilent.isChecked()) {
|
||||
return NotificationManager.IMPORTANCE_LOW;
|
||||
} else {
|
||||
return NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
}
|
||||
return NotificationManager.IMPORTANCE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void bindToggles(final View importanceButtons, final int importance,
|
||||
final boolean nonBlockable) {
|
||||
((RadioGroup) importanceButtons).setOnCheckedChangeListener(
|
||||
new RadioGroup.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(RadioGroup group, int checkedId) {
|
||||
resetFalsingCheck();
|
||||
}
|
||||
});
|
||||
mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
|
||||
mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
|
||||
mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
|
||||
if (nonBlockable) {
|
||||
mBlock.setVisibility(View.GONE);
|
||||
mReset.setText(mContext.getString(R.string.do_not_silence));
|
||||
} else {
|
||||
mReset.setText(mContext.getString(R.string.do_not_silence_block));
|
||||
}
|
||||
mBlock.setText(mContext.getString(R.string.block));
|
||||
mSilent.setText(mContext.getString(R.string.show_silently));
|
||||
if (importance == NotificationManager.IMPORTANCE_LOW) {
|
||||
mSilent.setChecked(true);
|
||||
} else {
|
||||
mReset.setChecked(true);
|
||||
}
|
||||
}
|
||||
// Enabled Switch
|
||||
mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
|
||||
mChannelEnabledSwitch.setChecked(importance != NotificationManager.IMPORTANCE_NONE);
|
||||
mChannelEnabledSwitch.setVisibility(nonBlockable ? View.INVISIBLE : View.VISIBLE);
|
||||
|
||||
private void bindSlider(final View importanceSlider, final boolean nonBlockable) {
|
||||
mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
|
||||
mInactiveSliderTint = loadColorStateList(R.color.notification_guts_disabled_slider_color);
|
||||
// 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);
|
||||
|
||||
mImportanceSummary = ((TextView) importanceSlider.findViewById(R.id.summary));
|
||||
mImportanceTitle = ((TextView) importanceSlider.findViewById(R.id.title));
|
||||
mSeekBar = (SeekBar) importanceSlider.findViewById(R.id.seekbar);
|
||||
|
||||
final int minProgress = nonBlockable ?
|
||||
NotificationManager.IMPORTANCE_MIN
|
||||
: NotificationManager.IMPORTANCE_NONE;
|
||||
mSeekBar.setMax(NotificationManager.IMPORTANCE_HIGH);
|
||||
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
resetFalsingCheck();
|
||||
if (progress < minProgress) {
|
||||
seekBar.setProgress(minProgress);
|
||||
progress = minProgress;
|
||||
}
|
||||
updateTitleAndSummary(progress);
|
||||
if (fromUser) {
|
||||
MetricsLogger.action(mContext, MetricsEvent.ACTION_MODIFY_IMPORTANCE_SLIDER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
resetFalsingCheck();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
mSeekBar.setProgress(mNotificationImportance);
|
||||
|
||||
mAutoButton = (ImageView) importanceSlider.findViewById(R.id.auto_importance);
|
||||
mAutoButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mAuto = !mAuto;
|
||||
applyAuto();
|
||||
}
|
||||
});
|
||||
mAuto = mStartingUserImportance == NotificationManager.IMPORTANCE_UNSPECIFIED;
|
||||
applyAuto();
|
||||
}
|
||||
|
||||
private void applyAuto() {
|
||||
mSeekBar.setEnabled(!mAuto);
|
||||
|
||||
final ColorStateList starTint = mAuto ? mActiveSliderTint : mInactiveSliderTint;
|
||||
final float alpha = mAuto ? mInactiveSliderAlpha : mActiveSliderAlpha;
|
||||
Drawable icon = mAutoButton.getDrawable().mutate();
|
||||
icon.setTintList(starTint);
|
||||
mAutoButton.setImageDrawable(icon);
|
||||
mSeekBar.setAlpha(alpha);
|
||||
|
||||
if (mAuto) {
|
||||
mSeekBar.setProgress(mNotificationImportance);
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_user_unspecified));
|
||||
mImportanceTitle.setText(mContext.getString(
|
||||
R.string.user_unspecified_importance));
|
||||
} else {
|
||||
updateTitleAndSummary(mSeekBar.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitleAndSummary(int progress) {
|
||||
switch (progress) {
|
||||
// Set to current importance setting
|
||||
switch (importance) {
|
||||
case NotificationManager.IMPORTANCE_NONE:
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_blocked));
|
||||
mImportanceTitle.setText(mContext.getString(R.string.blocked_importance));
|
||||
break;
|
||||
case NotificationManager.IMPORTANCE_MIN:
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_min));
|
||||
mImportanceTitle.setText(mContext.getString(R.string.min_importance));
|
||||
case NotificationManager.IMPORTANCE_UNSPECIFIED:
|
||||
mMinImportanceButton.setChecked(true);
|
||||
break;
|
||||
case NotificationManager.IMPORTANCE_LOW:
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_low));
|
||||
mImportanceTitle.setText(mContext.getString(R.string.low_importance));
|
||||
mLowImportanceButton.setChecked(true);
|
||||
break;
|
||||
case NotificationManager.IMPORTANCE_DEFAULT:
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_default));
|
||||
mImportanceTitle.setText(mContext.getString(R.string.default_importance));
|
||||
mDefaultImportanceButton.setChecked(true);
|
||||
break;
|
||||
case NotificationManager.IMPORTANCE_HIGH:
|
||||
case NotificationManager.IMPORTANCE_MAX:
|
||||
mImportanceSummary.setText(mContext.getString(
|
||||
R.string.notification_importance_high));
|
||||
mImportanceTitle.setText(mContext.getString(R.string.high_importance));
|
||||
mHighImportanceButton.setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Callback when checked.
|
||||
mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
resetFalsingCheck();
|
||||
updateImportanceGroup();
|
||||
});
|
||||
((RadioGroup) importanceButtons).setOnCheckedChangeListener(
|
||||
(buttonView, isChecked) -> { resetFalsingCheck(); });
|
||||
}
|
||||
|
||||
private ColorStateList loadColorStateList(int colorResId) {
|
||||
return ColorStateList.valueOf(mContext.getColor(colorResId));
|
||||
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 notify) {
|
||||
public void closeControls(int x, int y, boolean saveImportance) {
|
||||
if (saveImportance) {
|
||||
saveImportance();
|
||||
}
|
||||
if (getWindowToken() == null) {
|
||||
if (notify && mListener != null) {
|
||||
if (mListener != null) {
|
||||
mListener.onGutsClosed(this);
|
||||
}
|
||||
return;
|
||||
@@ -398,7 +396,7 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
|
||||
});
|
||||
a.start();
|
||||
setExposed(false, mNeedsFalsingProtection);
|
||||
if (notify && mListener != null) {
|
||||
if (mListener != null) {
|
||||
mListener.onGutsClosed(this);
|
||||
}
|
||||
}
|
||||
@@ -442,14 +440,7 @@ public class NotificationGuts extends LinearLayout implements TunerService.Tunab
|
||||
}
|
||||
}
|
||||
|
||||
public boolean areGutsExposed() {
|
||||
public boolean isExposed() {
|
||||
return mExposed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTuningChanged(String key, String newValue) {
|
||||
if (SHOW_SLIDER.equals(key)) {
|
||||
mShowSlider = newValue != null && Integer.parseInt(newValue) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,15 +138,14 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int ownMaxHeight = mMaxNotificationHeight;
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
boolean hasFixedHeight = heightMode == MeasureSpec.EXACTLY;
|
||||
boolean isHeightLimited = heightMode == MeasureSpec.AT_MOST;
|
||||
int size = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int newHeightSpec = heightMeasureSpec;
|
||||
if (hasFixedHeight || isHeightLimited) {
|
||||
ownMaxHeight = Math.min(ownMaxHeight, size);
|
||||
newHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
|
||||
}
|
||||
int newHeightSpec = MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.AT_MOST);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
if (mOverflowNumber != null) {
|
||||
mOverflowNumber.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
|
||||
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* 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 static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.annotation.UiThreadTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import com.android.systemui.R;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class NotificationGutsTest {
|
||||
private static final String TEST_PACKAGE_NAME = "test_package";
|
||||
private static final String TEST_CHANNEL = "test_channel";
|
||||
private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
|
||||
|
||||
private NotificationGuts mNotificationGuts;
|
||||
private final INotificationManager mMockINotificationManager = mock(INotificationManager.class);
|
||||
private final PackageManager mMockPackageManager = mock(PackageManager.class);
|
||||
private NotificationChannel mNotificationChannel;
|
||||
private final StatusBarNotification mMockStatusBarNotification =
|
||||
mock(StatusBarNotification.class);
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setUp() throws Exception {
|
||||
// Inflate the layout
|
||||
final LayoutInflater layoutInflater =
|
||||
LayoutInflater.from(InstrumentationRegistry.getTargetContext());
|
||||
mNotificationGuts = (NotificationGuts) layoutInflater.inflate(R.layout.notification_guts,
|
||||
null);
|
||||
|
||||
// PackageManager must return a packageInfo and applicationInfo.
|
||||
final PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = TEST_PACKAGE_NAME;
|
||||
when(mMockPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
|
||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
applicationInfo.uid = 1; // non-zero
|
||||
when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
|
||||
applicationInfo);
|
||||
|
||||
// mMockStatusBarNotification with a test channel.
|
||||
mNotificationChannel = new NotificationChannel(
|
||||
TEST_CHANNEL, TEST_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
|
||||
when(mMockStatusBarNotification.getNotificationChannel()).thenReturn(mNotificationChannel);
|
||||
when(mMockStatusBarNotification.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testBindNotification_SetsTextApplicationName() throws Exception {
|
||||
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.pkgname);
|
||||
assertTrue(textView.getText().toString().contains("App Name"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testBindNotification_SetsTextChannelName() throws Exception {
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
final TextView textView = (TextView) mNotificationGuts.findViewById(R.id.channel_name);
|
||||
assertEquals(TEST_CHANNEL_NAME, textView.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, (View v, int appUid) -> { latch.countDown(); },
|
||||
null, null);
|
||||
|
||||
final TextView settingsButton =
|
||||
(TextView) mNotificationGuts.findViewById(R.id.more_settings);
|
||||
settingsButton.performClick();
|
||||
// Verify that listener was triggered.
|
||||
assertEquals(0, latch.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null,
|
||||
(View v) -> { latch.countDown(); },
|
||||
null);
|
||||
|
||||
final TextView doneButton = (TextView) mNotificationGuts.findViewById(R.id.done);
|
||||
doneButton.performClick();
|
||||
// Verify that listener was triggered.
|
||||
assertEquals(0, latch.getCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testHasImportanceChanged_DefaultsToFalse() throws Exception {
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
assertFalse(mNotificationGuts.hasImportanceChanged());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testHasImportanceChanged_ReturnsTrueAfterButtonChecked() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
// Find the high button and check it.
|
||||
RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
|
||||
highButton.setChecked(true);
|
||||
assertTrue(mNotificationGuts.hasImportanceChanged());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testImportanceButtonCheckedBasedOnInitialImportance() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_HIGH);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
|
||||
assertTrue(highButton.isChecked());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
|
||||
anyString(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
|
||||
highButton.setChecked(true);
|
||||
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
|
||||
anyString(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCloseControls_DoesNotUpdateNotificationChannelIfUnchanged() throws Exception {
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
mNotificationGuts.closeControls(-1, -1, true);
|
||||
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
|
||||
anyString(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCloseControls_CallsUpdateNotificationChannelIfChanged() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
|
||||
highButton.setChecked(true);
|
||||
mNotificationGuts.closeControls(-1, -1, true);
|
||||
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
|
||||
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
|
||||
assertEquals(NotificationManager.IMPORTANCE_HIGH, mNotificationChannel.getImportance());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testCloseControls_DoesNotUpdateNotificationChannelIfSaveFalse() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
RadioButton highButton = (RadioButton) mNotificationGuts.findViewById(R.id.high_importance);
|
||||
highButton.setChecked(true);
|
||||
mNotificationGuts.closeControls(-1, -1, false);
|
||||
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
|
||||
anyString(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testEnabledSwitchOnByDefault() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
|
||||
assertTrue(enabledSwitch.isChecked());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testEnabledSwitchVisibleByDefault() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
|
||||
assertEquals(View.VISIBLE, enabledSwitch.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
|
||||
|
||||
Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
|
||||
assertEquals(View.INVISIBLE, enabledSwitch.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, Collections.singleton(TEST_PACKAGE_NAME));
|
||||
|
||||
Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
|
||||
enabledSwitch.setChecked(false);
|
||||
mNotificationGuts.closeControls(-1, -1, true);
|
||||
verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
|
||||
eq(TEST_PACKAGE_NAME), anyInt(), eq(mNotificationChannel));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testEnabledSwitchOverridesOtherButtons() throws Exception {
|
||||
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
|
||||
mNotificationGuts.bindNotification(mMockPackageManager, mMockINotificationManager,
|
||||
mMockStatusBarNotification, null, null, null);
|
||||
|
||||
Switch enabledSwitch = (Switch) mNotificationGuts.findViewById(R.id.channel_enabled_switch);
|
||||
RadioButton lowButton = (RadioButton) mNotificationGuts.findViewById(R.id.low_importance);
|
||||
lowButton.setChecked(true);
|
||||
enabledSwitch.setChecked(false);
|
||||
mNotificationGuts.closeControls(-1, -1, true);
|
||||
assertEquals(NotificationManager.IMPORTANCE_NONE, mNotificationChannel.getImportance());
|
||||
}
|
||||
}
|
||||
@@ -1547,8 +1547,9 @@ public class NotificationManagerService extends SystemService {
|
||||
@Override
|
||||
public void updateNotificationChannelForPackage(String pkg, int uid,
|
||||
NotificationChannel channel) {
|
||||
checkCallerIsSystem();
|
||||
enforceSystemOrSystemUI("Caller not system or systemui");
|
||||
if (!channel.isAllowed()) {
|
||||
// cancel
|
||||
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channel.getId(), 0, 0, true,
|
||||
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
|
||||
null);
|
||||
|
||||
Reference in New Issue
Block a user