Minified people row

Bug: 146664602
Test: manual, atest
Change-Id: Id007cc64a6d37dea51bbdd5d70d7eac4df7556ac
This commit is contained in:
Steve Elliott
2019-12-19 12:40:29 -05:00
parent b47f1c7d83
commit d991c048bc
7 changed files with 108 additions and 326 deletions

View File

@@ -18,7 +18,10 @@
<com.android.systemui.statusbar.notification.stack.PeopleHubView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="105dp">
android:layout_height="@dimen/notification_section_header_height"
android:focusable="true"
android:clickable="true"
>
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
@@ -34,199 +37,56 @@
android:id="@+id/people_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:gravity="center"
android:orientation="horizontal">
<View
android:layout_width="8dp"
android:layout_height="match_parent"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="@dimen/notification_section_header_padding_left"
android:gravity="start"
android:textAlignment="gravity"
android:text="@string/notification_section_header_conversations"
android:textSize="12sp"
android:textColor="@color/notification_section_header_label_color"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
/>
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:id="@+id/person_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
/>
</LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:id="@+id/person_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
/>
</LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:id="@+id/person_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
/>
</LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:id="@+id/person_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
/>
</LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
/>
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
<ImageView
android:id="@+id/person_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:scaleType="fitCenter"
/>
<TextView
android:id="@+id/person_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:ellipsize="end"
android:maxLines="2"
android:textAlignment="center"
/>
</LinearLayout>
<View
android:layout_width="8dp"
android:layout_height="match_parent"
android:layout_weight="1"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:padding="8dp"
android:scaleType="fitCenter"
/>
</LinearLayout>
@@ -236,4 +96,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.systemui.statusbar.notification.stack.PeopleHubView>
</com.android.systemui.statusbar.notification.stack.PeopleHubView>

View File

@@ -1163,6 +1163,9 @@
<!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
<string name="notification_section_header_gentle">Silent notifications</string>
<!-- Section title for conversational notifications. [CHAR LIMIT=40] -->
<string name="notification_section_header_conversations">Conversations</string>
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>

View File

@@ -17,24 +17,16 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
import android.content.Context
import android.content.pm.UserInfo
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.UserHandle
import android.os.UserManager
import android.service.notification.StatusBarNotification
import android.util.SparseArray
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.MessagingGroup
import com.android.launcher3.icons.BaseIconFactory
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -59,8 +51,7 @@ interface NotificationPersonExtractor {
@Singleton
class NotificationPersonExtractorPluginBoundary @Inject constructor(
extensionController: ExtensionController,
private val context: Context
extensionController: ExtensionController
) : NotificationPersonExtractor {
private var plugin: NotificationPersonExtractorPlugin? = null
@@ -77,9 +68,8 @@ class NotificationPersonExtractorPluginBoundary @Inject constructor(
}
override fun extractPerson(sbn: StatusBarNotification) =
plugin?.extractPerson(sbn)?.let { data ->
val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
PersonModel(data.key, data.name, badged, data.clickIntent, sbn.user.identifier)
plugin?.extractPerson(sbn)?.run {
PersonModel(key, name, avatar, clickIntent, sbn.user.identifier)
}
override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
@@ -214,7 +204,7 @@ class PeopleHubManager {
if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
inactivePeople.removeLast()
}
inactivePeople.push(data)
inactivePeople.add(data)
return true
}
return false
@@ -247,63 +237,7 @@ private fun NotificationEntry.extractPerson(): PersonModel? {
?: extras.getString(Notification.EXTRA_TITLE)
?: return null
val drawable = extractAvatarFromRow(this) ?: return null
val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
return PersonModel(key, name, badgedAvatar, clickIntent, sbn.user.identifier)
}
private fun addBadgeToDrawable(
drawable: Drawable,
context: Context,
packageName: String,
user: UserHandle
): Drawable {
val pm = context.packageManager
val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
return object : Drawable() {
override fun draw(canvas: Canvas) {
val iconBounds = getBounds()
val factory = object : BaseIconFactory(
context,
0 /* unused */,
iconBounds.width(),
true) {}
val badge = factory.createBadgedIconBitmap(
appInfo.loadIcon(pm),
user,
true,
appInfo.isInstantApp,
null)
val badgeDrawable = BitmapDrawable(context.resources, badge.icon)
.apply {
alpha = drawable.alpha
colorFilter = drawable.colorFilter
val badgeWidth = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
15f,
context.resources.displayMetrics
).toInt()
setBounds(
iconBounds.left + (iconBounds.width() - badgeWidth),
iconBounds.top + (iconBounds.height() - badgeWidth),
iconBounds.right,
iconBounds.bottom)
}
drawable.bounds = iconBounds
drawable.draw(canvas)
badgeDrawable.draw(canvas)
}
override fun setAlpha(alpha: Int) {
drawable.alpha = alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
drawable.colorFilter = colorFilter
}
@PixelFormat.Opacity
override fun getOpacity(): Int = PixelFormat.OPAQUE
}
return PersonModel(key, name, drawable, clickIntent, sbn.user.identifier)
}
fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
@@ -329,4 +263,4 @@ private fun NotificationEntry.extractPersonKey(): PersonKey? =
if (isMessagingNotification()) key else null
private fun NotificationEntry.isMessagingNotification() =
sbn.notification.notificationStyle == Notification.MessagingStyle::class.java
sbn.notification.notificationStyle == Notification.MessagingStyle::class.java

View File

@@ -187,18 +187,18 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
@Override
public boolean beginsSection(@NonNull View view, @Nullable View previous) {
boolean begin = false;
if (view instanceof ExpandableNotificationRow) {
if (previous instanceof ExpandableNotificationRow) {
if (view instanceof ActivatableNotificationView) {
if (previous instanceof ActivatableNotificationView) {
// If we're drawing the first non-person notification, break out a section
ExpandableNotificationRow curr = (ExpandableNotificationRow) view;
ExpandableNotificationRow prev = (ExpandableNotificationRow) previous;
ActivatableNotificationView curr = (ActivatableNotificationView) view;
ActivatableNotificationView prev = (ActivatableNotificationView) previous;
begin = curr.getEntry().getBucket() != prev.getEntry().getBucket();
begin = getBucket(curr) != getBucket(prev);
}
}
if (!begin) {
begin = view == mGentleHeader || previous == mPeopleHubView;
begin = view == mGentleHeader || view == mPeopleHubView;
}
return begin;
@@ -230,29 +230,42 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
return;
}
int lastPersonIndex = -1;
int firstGentleNotifIndex = -1;
boolean peopleNotificationsPresent = false;
int firstNonHeadsUpIndex = -1;
int firstGentleIndex = -1;
int notifCount = 0;
final int n = mParent.getChildCount();
for (int i = 0; i < n; i++) {
View child = mParent.getChildAt(i);
if (child instanceof ExpandableNotificationRow
&& child.getVisibility() != View.GONE) {
if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) {
notifCount++;
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
if (firstNonHeadsUpIndex == -1 && !row.isHeadsUp()) {
firstNonHeadsUpIndex = i;
}
if (row.getEntry().getBucket() == BUCKET_PEOPLE) {
lastPersonIndex = i;
peopleNotificationsPresent = true;
}
if (row.getEntry().getBucket() == BUCKET_SILENT) {
firstGentleNotifIndex = i;
firstGentleIndex = i;
break;
}
}
}
// make room for peopleHub
firstGentleNotifIndex += adjustPeopleHubVisibilityAndPosition(lastPersonIndex);
if (firstNonHeadsUpIndex == -1) {
firstNonHeadsUpIndex = firstGentleIndex != -1 ? firstGentleIndex : notifCount;
}
adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
// make room for peopleHub
int offset = adjustPeopleHubVisibilityAndPosition(
firstNonHeadsUpIndex, peopleNotificationsPresent);
if (firstGentleIndex != -1) {
firstGentleIndex += offset;
}
adjustGentleHeaderVisibilityAndPosition(firstGentleIndex);
mGentleHeader.setAreThereDismissableGentleNotifs(
mParent.hasActiveClearableNotifications(ROWS_GENTLE));
@@ -294,13 +307,15 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
}
private int adjustPeopleHubVisibilityAndPosition(int lastPersonIndex) {
final boolean showPeopleHeader = mPeopleHubVisible
&& mNumberOfSections > 2
&& mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
private int adjustPeopleHubVisibilityAndPosition(
int targetIndex, boolean peopleNotificationsPresent) {
final boolean showPeopleHeader = mNumberOfSections > 2
&& mStatusBarStateController.getState() != StatusBarState.KEYGUARD
&& (peopleNotificationsPresent || mPeopleHubVisible);
final int currentHubIndex = mParent.indexOfChild(mPeopleHubView);
final boolean currentlyVisible = currentHubIndex >= 0;
int targetIndex = lastPersonIndex + 1;
mPeopleHubView.setCanSwipe(showPeopleHeader && !peopleNotificationsPresent);
if (!showPeopleHeader) {
if (currentlyVisible) {

View File

@@ -31,6 +31,7 @@ import com.android.systemui.SwipeHelper;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
@@ -298,8 +299,8 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
@Override
public Animator getViewTranslationAnimator(View v, float target,
ValueAnimator.AnimatorUpdateListener listener) {
if (v instanceof SwipeableView) {
return ((SwipeableView) v).getTranslateViewAnimator(target, listener);
if (v instanceof ExpandableNotificationRow) {
return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
} else {
return superGetViewTranslationAnimator(v, target, listener);
}

View File

@@ -16,38 +16,22 @@
package com.android.systemui.statusbar.notification.stack
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.FloatProperty
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin
import com.android.systemui.statusbar.notification.people.DataListener
import com.android.systemui.statusbar.notification.people.PersonViewModel
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView
private val TRANSLATE_CONTENT = object : FloatProperty<PeopleHubView>("translate") {
override fun setValue(view: PeopleHubView, value: Float) {
view.translation = value
}
override fun get(view: PeopleHubView) = view.translation
}
class PeopleHubView(context: Context, attrs: AttributeSet) :
ActivatableNotificationView(context, attrs), SwipeableView {
private lateinit var contents: ViewGroup
private lateinit var personControllers: List<PersonDataListenerImpl>
private var translateAnim: ObjectAnimator? = null
val personViewAdapters: Sequence<DataListener<PersonViewModel?>>
get() = personControllers.asSequence()
@@ -56,9 +40,10 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
super.onFinishInflate()
contents = requireViewById(R.id.people_list)
personControllers = (0 until contents.childCount)
.reversed()
.asSequence()
.mapNotNull { idx ->
(contents.getChildAt(idx) as? LinearLayout)?.let(::PersonDataListenerImpl)
(contents.getChildAt(idx) as? ImageView)?.let(::PersonDataListenerImpl)
}
.toList()
}
@@ -69,41 +54,32 @@ class PeopleHubView(context: Context, attrs: AttributeSet) :
override fun createMenu(): NotificationMenuRowPlugin? = null
override fun getTranslateViewAnimator(
leftTarget: Float,
listener: ValueAnimator.AnimatorUpdateListener?
): Animator =
ObjectAnimator
.ofFloat(this, TRANSLATE_CONTENT, leftTarget)
.apply {
listener?.let { addUpdateListener(listener) }
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(anim: Animator) {
translateAnim = null
}
})
}
.also {
translateAnim?.cancel()
translateAnim = it
}
override fun resetTranslation() {
translateAnim?.cancel()
translationX = 0f
}
private inner class PersonDataListenerImpl(val viewGroup: ViewGroup) :
override fun setTranslation(translation: Float) {
if (canSwipe) {
super.setTranslation(translation)
}
}
var canSwipe: Boolean = true
set(value) {
if (field != value) {
if (field) {
resetTranslation()
}
field = value
}
}
private inner class PersonDataListenerImpl(val avatarView: ImageView) :
DataListener<PersonViewModel?> {
val nameView = viewGroup.requireViewById<TextView>(R.id.person_name)
val avatarView = viewGroup.requireViewById<ImageView>(R.id.person_icon)
override fun onDataChanged(data: PersonViewModel?) {
viewGroup.visibility = data?.let { View.VISIBLE } ?: View.INVISIBLE
nameView.text = data?.name
avatarView.setImageDrawable(data?.icon)
viewGroup.setOnClickListener { data?.onClick?.invoke() }
avatarView.setOnClickListener { data?.onClick?.invoke() }
}
}
}

View File

@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
import android.animation.Animator;
import android.animation.ValueAnimator;
import androidx.annotation.Nullable;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -32,10 +29,6 @@ public interface SwipeableView {
/** Optionally creates a menu for this view. */
@Nullable NotificationMenuRowPlugin createMenu();
/** Animator for translating the view, simulating a swipe. */
Animator getTranslateViewAnimator(
float leftTarget, ValueAnimator.AnimatorUpdateListener listener);
/** Sets the translation amount for an in-progress swipe. */
void setTranslation(float translate);