Increased the collapsed size of messaging notifications

Messaging notifications now get an increased boost in size,
since those are usually important to the user.

Test: existing tests pass
Bug: 34469375
Change-Id: Idfc2d2403b04c4c2d17b821e3ccbbbd48d31654d
This commit is contained in:
Selim Cinek
2017-01-25 15:28:28 -08:00
parent 9363c69542
commit 7d1009b3bd
7 changed files with 222 additions and 84 deletions

View File

@@ -48,6 +48,7 @@ import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.BidiFormatter;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -3854,10 +3855,24 @@ public class Notification implements Parcelable
* 3. Standard template view
*/
public RemoteViews createContentView() {
return createContentView(false /* increasedheight */ );
}
/**
* Construct a RemoteViews for the smaller content view.
*
* @param increasedHeight true if this layout be created with an increased height. Some
* styles may support showing more then just that basic 1U size
* and the system may decide to render important notifications
* slightly bigger even when collapsed.
*
* @hide
*/
public RemoteViews createContentView(boolean increasedHeight) {
if (mN.contentView != null && (mStyle == null || !mStyle.displayCustomViewInline())) {
return mN.contentView;
} else if (mStyle != null) {
final RemoteViews styleView = mStyle.makeContentView();
final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
if (styleView != null) {
return styleView;
}
@@ -4486,6 +4501,19 @@ public class Notification implements Parcelable
return (flags & ongoingFlags) != 0;
}
/**
* @return the style class of this notification
* @hide
*/
public Class<? extends Notification.Style> getNotificationStyle() {
String templateClass = extras.getString(Notification.EXTRA_TEMPLATE);
if (!TextUtils.isEmpty(templateClass)) {
return Notification.getNotificationStyleClass(templateClass);
}
return null;
}
/**
* @return true if this notification is colorized. This also factors in wheather the
* notification is ongoing.
@@ -4607,11 +4635,13 @@ public class Notification implements Parcelable
}
/**
* Construct a Style-specific RemoteViews for the final 1U notification layout.
* Construct a Style-specific RemoteViews for the collapsed notification layout.
* The default implementation has nothing additional to add.
*
* @param increasedHeight true if this layout be created with an increased height.
* @hide
*/
public RemoteViews makeContentView() {
public RemoteViews makeContentView(boolean increasedHeight) {
return null;
}
@@ -4956,6 +4986,23 @@ public class Notification implements Parcelable
mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
}
/**
* @param increasedHeight true if this layout be created with an increased height.
*
* @hide
*/
@Override
public RemoteViews makeContentView(boolean increasedHeight) {
if (increasedHeight) {
ArrayList<Action> actions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
RemoteViews remoteViews = makeBigContentView();
mBuilder.mActions = actions;
return remoteViews;
}
return super.makeContentView(increasedHeight);
}
/**
* @hide
*/
@@ -5220,17 +5267,25 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public RemoteViews makeContentView() {
Message m = findLatestIncomingMessage();
CharSequence title = mConversationTitle != null
? mConversationTitle
: (m == null) ? null : m.mSender;
CharSequence text = (m == null)
? null
: mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
public RemoteViews makeContentView(boolean increasedHeight) {
if (!increasedHeight) {
Message m = findLatestIncomingMessage();
CharSequence title = mConversationTitle != null
? mConversationTitle
: (m == null) ? null : m.mSender;
CharSequence text = (m == null)
? null
: mConversationTitle != null ? makeMessageLine(m, mBuilder) : m.mText;
return mBuilder.applyStandardTemplateWithActions(mBuilder.getBaseLayoutResource(),
mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
return mBuilder.applyStandardTemplate(mBuilder.getBaseLayoutResource(),
mBuilder.mParams.reset().hasProgress(false).title(title).text(text));
} else {
ArrayList<Action> actions = mBuilder.mActions;
mBuilder.mActions = new ArrayList<>();
RemoteViews remoteViews = makeBigContentView();
mBuilder.mActions = actions;
return remoteViews;
}
}
private Message findLatestIncomingMessage() {
@@ -5795,7 +5850,7 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public RemoteViews makeContentView() {
public RemoteViews makeContentView(boolean increasedHeight) {
return makeMediaContentView();
}
@@ -5971,7 +6026,7 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public RemoteViews makeContentView() {
public RemoteViews makeContentView(boolean increasedHeight) {
return makeStandardTemplateWithCustomContent(mBuilder.mN.contentView);
}
@@ -6085,8 +6140,8 @@ public class Notification implements Parcelable
* @hide
*/
@Override
public RemoteViews makeContentView() {
RemoteViews remoteViews = super.makeContentView();
public RemoteViews makeContentView(boolean increasedHeight) {
RemoteViews remoteViews = super.makeContentView(false /* increasedHeight */);
return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
mBuilder.mN.contentView);
}
@@ -6108,7 +6163,7 @@ public class Notification implements Parcelable
return buildIntoRemoteView(remoteViews, R.id.notification_main_column,
customRemoteView);
} else if (customRemoteView != mBuilder.mN.contentView){
remoteViews = super.makeContentView();
remoteViews = super.makeContentView(false /* increasedHeight */);
return buildIntoRemoteView(remoteViews, R.id.notification_content_container,
customRemoteView);
} else {

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.internal.util;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.ArrayMap;
import java.util.Objects;
/**
* A util to look up messaging related functions for notifications. This is used for both the
* ranking and the actual layout.
*/
public class NotificationMessagingUtil {
private static final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
private final Context mContext;
private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
public NotificationMessagingUtil(Context context) {
mContext = context;
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING), false, mSmsContentObserver);
}
@SuppressWarnings("deprecation")
private boolean isDefaultMessagingApp(StatusBarNotification sbn) {
final int userId = sbn.getUserId();
if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false;
if (mDefaultSmsApp.get(userId) == null) {
cacheDefaultSmsApp(userId);
}
return Objects.equals(mDefaultSmsApp.get(userId), sbn.getPackageName());
}
private void cacheDefaultSmsApp(int userId) {
mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
}
private final ContentObserver mSmsContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) {
cacheDefaultSmsApp(userId);
}
}
};
public boolean isImportantMessaging(StatusBarNotification sbn, int importance) {
if (importance < NotificationManager.IMPORTANCE_LOW) {
return false;
}
Class<? extends Notification.Style> style = sbn.getNotification().getNotificationStyle();
if (Notification.MessagingStyle.class.equals(style)) {
return true;
}
if (Notification.CATEGORY_MESSAGE.equals(sbn.getNotification().category)
&& isDefaultMessagingApp(sbn)) {
return true;
}
return false;
}
}

View File

@@ -69,6 +69,9 @@
<!-- Height of a small notification in the status bar-->
<dimen name="notification_min_height">92dp</dimen>
<!-- Height of a small notification in the status bar if it is a large (like messaging)-->
<dimen name="notification_min_height_large">132dp</dimen>
<!-- Height of a small notification in the status bar which was used before android N -->
<dimen name="notification_min_height_legacy">64dp</dimen>

View File

@@ -77,6 +77,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private int mMaxHeadsUpHeightLegacy;
private int mMaxHeadsUpHeight;
private int mNotificationMinHeight;
private int mNotificationMinHeightLarge;
private int mNotificationMaxHeight;
private int mNotificationAmbientHeight;
private int mIncreasedPaddingBetweenElements;
@@ -207,6 +208,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private Runnable mOnDismissRunnable;
private boolean mIsLowPriority;
private boolean mIsColorized;
private boolean mUseIncreasedCollapsedHeight;
@Override
public boolean isGroupExpansionChanging() {
@@ -341,8 +343,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
boolean customView = layout.getContractedChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
mNotificationMinHeightLegacy : mNotificationMinHeight;
int minHeight;
if (customView && beforeN && !mIsSummaryWithChildren) {
minHeight = mNotificationMinHeightLegacy;
} else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) {
minHeight = mNotificationMinHeightLarge;
} else {
minHeight = mNotificationMinHeight;
}
boolean headsUpCustom = layout.getHeadsUpChild() != null &&
layout.getHeadsUpChild().getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
@@ -979,6 +987,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
}
public void setUseIncreasedCollapsedHeight(boolean use) {
mUseIncreasedCollapsedHeight = use;
}
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -992,6 +1004,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private void initDimens() {
mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
mNotificationMinHeightLarge = getFontScaledHeight(
R.dimen.notification_min_height_large);
mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
mMaxHeadsUpHeightLegacy = getFontScaledHeight(

View File

@@ -122,13 +122,13 @@ public class NotificationData {
}
public boolean cacheContentViews(Context ctx, Notification updatedNotification,
boolean isLowPriority) {
boolean isLowPriority, boolean useIncreasedCollapsedView) {
boolean applyInPlace = false;
if (updatedNotification != null) {
final Notification.Builder updatedNotificationBuilder
= Notification.Builder.recoverBuilder(ctx, updatedNotification);
final RemoteViews newContentView = createContentView(updatedNotificationBuilder,
isLowPriority);
isLowPriority, useIncreasedCollapsedView);
final RemoteViews newBigContentView = createBigContentView(
updatedNotificationBuilder, isLowPriority);
final RemoteViews newHeadsUpContentView =
@@ -158,7 +158,8 @@ public class NotificationData {
final Notification.Builder builder
= Notification.Builder.recoverBuilder(ctx, notification.getNotification());
cachedContentView = createContentView(builder, isLowPriority);
cachedContentView = createContentView(builder, isLowPriority,
useIncreasedCollapsedView);
cachedBigContentView = createBigContentView(builder, isLowPriority);
cachedHeadsUpContentView = builder.createHeadsUpContentView();
cachedPublicContentView = builder.makePublicContentView();
@@ -184,11 +185,11 @@ public class NotificationData {
}
private RemoteViews createContentView(Notification.Builder builder,
boolean isAmbient) {
if (isAmbient) {
boolean isLowPriority, boolean useLarge) {
if (isLowPriority) {
return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
}
return builder.createContentView();
return builder.createContentView(useLarge);
}
// Returns true if the RemoteViews are the same.

View File

@@ -1,3 +1,5 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
@@ -112,6 +114,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -701,6 +704,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
};
private NotificationMessagingUtil mMessagingUtil;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private UserSwitcherController mUserSwitcherController;
private NetworkController mNetworkController;
@@ -763,6 +767,7 @@ public class StatusBar extends SystemUI implements DemoMode,
Context.DEVICE_POLICY_SERVICE);
mNotificationData = new NotificationData(this);
mMessagingUtil = new NotificationMessagingUtil(mContext);
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -6318,8 +6323,10 @@ public class StatusBar extends SystemUI implements DemoMode,
final StatusBarNotification sbn = entry.notification;
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
mNotificationData.getImportance(sbn.getKey()));
try {
entry.cacheContentViews(mContext, null, isLowPriority);
entry.cacheContentViews(mContext, null, isLowPriority, useIncreasedCollapsedHeight);
} catch (RuntimeException e) {
Log.e(TAG, "Unable to get notification remote views", e);
return false;
@@ -6489,6 +6496,7 @@ public class StatusBar extends SystemUI implements DemoMode,
row.setUserExpanded(userExpanded);
}
row.setUserLocked(userLocked);
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.onNotificationUpdated(entry);
return true;
}
@@ -6978,10 +6986,13 @@ public class StatusBar extends SystemUI implements DemoMode,
Notification n = notification.getNotification();
mNotificationData.updateRanking(ranking);
boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(notification,
mNotificationData.getImportance(notification.getKey()));
entry.row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
boolean applyInPlace;
try {
applyInPlace = entry.cacheContentViews(mContext, notification.getNotification(),
mNotificationData.isAmbient(key));
mNotificationData.isAmbient(key), useIncreasedCollapsedHeight);
} catch (RuntimeException e) {
Log.e(TAG, "Unable to get notification remote views", e);
applyInPlace = false;

View File

@@ -31,6 +31,8 @@ import android.telecom.TelecomManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import com.android.internal.util.NotificationMessagingUtil;
import java.util.Comparator;
import java.util.Objects;
@@ -40,18 +42,15 @@ import java.util.Objects;
public class NotificationComparator
implements Comparator<NotificationRecord> {
private final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
private final Context mContext;
private final NotificationMessagingUtil mMessagingUtil;
private String mDefaultPhoneApp;
private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
public NotificationComparator(Context context) {
mContext = context;
mContext.registerReceiver(mPhoneAppBroadcastReceiver,
new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING), false, mSmsContentObserver);
mMessagingUtil = new NotificationMessagingUtil(mContext);
}
@Override
@@ -73,9 +72,15 @@ public class NotificationComparator
return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
}
boolean leftMessaging = isImportantMessaging(left);
boolean rightMessaging = isImportantMessaging(right);
if (leftMessaging != rightMessaging) {
return -1 * Boolean.compare(leftMessaging, rightMessaging);
}
// Next: sufficiently import person to person communication
boolean leftPeople = isImportantMessaging(left);
boolean rightPeople = isImportantMessaging(right);
boolean leftPeople = isImportantPeople(left);
boolean rightPeople = isImportantPeople(right);
if (leftPeople && rightPeople){
// by contact proximity, close to far. if same proximity, check further fields.
@@ -128,50 +133,31 @@ public class NotificationComparator
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
// TODO: add whitelist
return isCall(record) || isMediaNotification(record);
}
protected boolean isImportantMessaging(NotificationRecord record) {
protected boolean isImportantPeople(NotificationRecord record) {
if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
return false;
}
Class<? extends Notification.Style> style = getNotificationStyle(record);
if (Notification.MessagingStyle.class.equals(style)) {
return true;
}
if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
return true;
}
if (record.getNotification().category == Notification.CATEGORY_MESSAGE
&& isDefaultMessagingApp(record)) {
return true;
}
return false;
}
protected boolean isImportantMessaging(NotificationRecord record) {
return mMessagingUtil.isImportantMessaging(record.sbn, record.getImportance());
}
private boolean isOngoing(NotificationRecord record) {
final int ongoingFlags =
Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_ONGOING_EVENT;
return (record.getNotification().flags & ongoingFlags) != 0;
}
private Class<? extends Notification.Style> getNotificationStyle(NotificationRecord record) {
String templateClass =
record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE);
if (!TextUtils.isEmpty(templateClass)) {
return Notification.getNotificationStyleClass(templateClass);
}
return null;
}
private boolean isMediaNotification(NotificationRecord record) {
return record.getNotification().extras.getParcelable(
Notification.EXTRA_MEDIA_SESSION) != null;
@@ -191,18 +177,6 @@ public class NotificationComparator
return Objects.equals(pkg, mDefaultPhoneApp);
}
@SuppressWarnings("deprecation")
private boolean isDefaultMessagingApp(NotificationRecord record) {
final int userId = record.getUserId();
if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false;
if (mDefaultSmsApp.get(userId) == null) {
mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
}
return Objects.equals(mDefaultSmsApp.get(userId), record.sbn.getPackageName());
}
private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -210,17 +184,4 @@ public class NotificationComparator
intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
}
};
private final ContentObserver mSmsContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) {
mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
}
}
};
}