Merge changes Id007cc64,I94afce96

* changes:
  Minified people row
  Current user and work profile availability in peoplehub
This commit is contained in:
TreeHugger Robot
2020-01-08 17:33:43 +00:00
committed by Android (Google) Code Review
13 changed files with 272 additions and 392 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

@@ -344,11 +344,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mSavedBubbleKeysPerUser = new SparseSetArray<>();
mCurrentUserId = mNotifUserManager.getCurrentUserId();
mNotifUserManager.addUserChangedListener(
newUserId -> {
saveBubbles(mCurrentUserId);
mBubbleData.dismissAll(DISMISS_USER_CHANGED);
restoreBubbles(newUserId);
mCurrentUserId = newUserId;
new NotificationLockscreenUserManager.UserChangedListener() {
@Override
public void onUserChanged(int newUserId) {
BubbleController.this.saveBubbles(mCurrentUserId);
mBubbleData.dismissAll(DISMISS_USER_CHANGED);
BubbleController.this.restoreBubbles(newUserId);
mCurrentUserId = newUserId;
}
});
mUserCreatedBubbles = new HashSet<>();

View File

@@ -49,6 +49,12 @@ public interface NotificationLockscreenUserManager {
/** Adds a listener to be notified when the current user changes. */
void addUserChangedListener(UserChangedListener listener);
/**
* Removes a listener previously registered with
* {@link #addUserChangedListener(UserChangedListener)}
*/
void removeUserChangedListener(UserChangedListener listener);
SparseArray<UserInfo> getCurrentProfiles();
void setLockscreenPublicMode(boolean isProfilePublic, int userId);
@@ -79,6 +85,7 @@ public interface NotificationLockscreenUserManager {
/** Notified when the current user changes. */
interface UserChangedListener {
void onUserChanged(int userId);
default void onUserChanged(int userId) {}
default void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {}
}
}

View File

@@ -117,51 +117,63 @@ public class NotificationLockscreenUserManagerImpl implements
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
updateCurrentProfilesCache();
Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
switch (action) {
case Intent.ACTION_USER_SWITCHED:
mCurrentUserId = intent.getIntExtra(
Intent.EXTRA_USER_HANDLE, UserHandle.USER_ALL);
updateCurrentProfilesCache();
updateLockscreenNotificationSetting();
updatePublicMode();
// The filtering needs to happen before the update call below in order to make sure
// the presenter has the updated notifications from the new user
getEntryManager().reapplyFilterAndSort("user switched");
mPresenter.onUserSwitched(mCurrentUserId);
Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
for (UserChangedListener listener : mListeners) {
listener.onUserChanged(mCurrentUserId);
}
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
updateCurrentProfilesCache();
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
// Start the overview connection to the launcher service
Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
} else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
if (intentSender != null) {
try {
mContext.startIntentSender(intentSender, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
/* ignore */
updateLockscreenNotificationSetting();
updatePublicMode();
// The filtering needs to happen before the update call below in order to
// make sure
// the presenter has the updated notifications from the new user
getEntryManager().reapplyFilterAndSort("user switched");
mPresenter.onUserSwitched(mCurrentUserId);
for (UserChangedListener listener : mListeners) {
listener.onUserChanged(mCurrentUserId);
}
}
if (notificationKey != null) {
NotificationEntry entry =
getEntryManager().getActiveNotificationUnfiltered(notificationKey);
final int count = getEntryManager().getActiveNotificationsCount();
final int rank = entry != null ? entry.getRanking().getRank() : 0;
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
rank, count, true, location);
try {
mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException exception) {
/* ignore */
break;
case Intent.ACTION_USER_ADDED:
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
updateCurrentProfilesCache();
break;
case Intent.ACTION_USER_UNLOCKED:
// Start the overview connection to the launcher service
Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
break;
case NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION:
final IntentSender intentSender = intent.getParcelableExtra(
Intent.EXTRA_INTENT);
final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
if (intentSender != null) {
try {
mContext.startIntentSender(intentSender, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
/* ignore */
}
}
}
if (notificationKey != null) {
NotificationEntry entry =
getEntryManager().getActiveNotificationUnfiltered(notificationKey);
final int count = getEntryManager().getActiveNotificationsCount();
final int rank = entry != null ? entry.getRanking().getRank() : 0;
NotificationVisibility.NotificationLocation location =
NotificationLogger.getNotificationLocation(entry);
final NotificationVisibility nv = NotificationVisibility.obtain(
notificationKey,
rank, count, true, location);
try {
mBarService.onNotificationClick(notificationKey, nv);
} catch (RemoteException exception) {
/* ignore */
}
}
break;
}
}
};
@@ -266,6 +278,8 @@ public class NotificationLockscreenUserManagerImpl implements
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_UNLOCKED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter);
IntentFilter internalFilter = new IntentFilter();
@@ -489,6 +503,11 @@ public class NotificationLockscreenUserManagerImpl implements
}
}
}
mMainHandler.post(() -> {
for (UserChangedListener listener : mListeners) {
listener.onCurrentProfilesChanged(mCurrentProfiles);
}
});
}
public boolean isAnyProfilePublicMode() {
@@ -555,6 +574,11 @@ public class NotificationLockscreenUserManagerImpl implements
mListeners.add(listener);
}
@Override
public void removeUserChangedListener(UserChangedListener listener) {
mListeners.remove(listener);
}
// public void updatePublicMode() {
// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
// // false when it should be true. Therefore, if we are not on the SHADE, don't even bother

View File

@@ -37,7 +37,8 @@ data class PersonModel(
val key: PersonKey,
val name: CharSequence,
val avatar: Drawable,
val clickIntent: PendingIntent
val clickIntent: PendingIntent,
val userId: Int
)
/** Unique identifier for a Person in PeopleHub. */

View File

@@ -17,28 +17,27 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
import android.content.Context
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
import android.os.UserHandle
import android.os.UserManager
import android.service.notification.StatusBarNotification
import android.util.TypedValue
import android.util.SparseArray
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
import com.android.systemui.plugins.NotificationPersonExtractorPlugin
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.notification.NotificationEntryListener
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.policy.ExtensionController
import java.util.ArrayDeque
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
@@ -52,8 +51,7 @@ interface NotificationPersonExtractor {
@Singleton
class NotificationPersonExtractorPluginBoundary @Inject constructor(
extensionController: ExtensionController,
private val context: Context
extensionController: ExtensionController
) : NotificationPersonExtractor {
private var plugin: NotificationPersonExtractorPlugin? = null
@@ -70,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)
plugin?.extractPerson(sbn)?.run {
PersonModel(key, name, avatar, clickIntent, sbn.user.identifier)
}
override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
@@ -84,11 +81,16 @@ class NotificationPersonExtractorPluginBoundary @Inject constructor(
@Singleton
class PeopleHubDataSourceImpl @Inject constructor(
private val notificationEntryManager: NotificationEntryManager,
private val peopleHubManager: PeopleHubManager,
private val extractor: NotificationPersonExtractor
private val extractor: NotificationPersonExtractor,
private val userManager: UserManager,
@Background private val bgExecutor: Executor,
@Main private val mainExecutor: Executor,
private val notifLockscreenUserMgr: NotificationLockscreenUserManager
) : DataSource<PeopleHubModel> {
private var userChangeSubscription: Subscription? = null
private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
private val peopleHubManagerForUser = SparseArray<PeopleHubManager>()
private val notificationEntryListener = object : NotificationEntryListener {
override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
@@ -106,31 +108,56 @@ class PeopleHubDataSourceImpl @Inject constructor(
}
private fun removeVisibleEntry(entry: NotificationEntry) {
val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
if (key?.let(peopleHubManager::removeActivePerson) == true) {
updateUi()
(extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey())?.let { key ->
val userId = entry.sbn.user.identifier
bgExecutor.execute {
val parentId = userManager.getProfileParent(userId)?.id ?: userId
mainExecutor.execute {
if (peopleHubManagerForUser[parentId]?.removeActivePerson(key) == true) {
updateUi()
}
}
}
}
}
private fun addVisibleEntry(entry: NotificationEntry) {
val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
if (personModel?.let(peopleHubManager::addActivePerson) == true) {
updateUi()
(extractor.extractPerson(entry.sbn) ?: entry.extractPerson())?.let { personModel ->
val userId = entry.sbn.user.identifier
bgExecutor.execute {
val parentId = userManager.getProfileParent(userId)?.id ?: userId
mainExecutor.execute {
val manager = peopleHubManagerForUser[parentId]
?: PeopleHubManager().also { peopleHubManagerForUser.put(parentId, it) }
if (manager.addActivePerson(personModel)) {
updateUi()
}
}
}
}
}
override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
val registerWithNotificationEntryManager = dataListeners.isEmpty()
val register = dataListeners.isEmpty()
dataListeners.add(listener)
if (registerWithNotificationEntryManager) {
if (register) {
userChangeSubscription = notifLockscreenUserMgr.registerListener(
object : NotificationLockscreenUserManager.UserChangedListener {
override fun onUserChanged(userId: Int) = updateUi()
override fun onCurrentProfilesChanged(
currentProfiles: SparseArray<UserInfo>?
) = updateUi()
})
notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
} else {
listener.onDataChanged(peopleHubManager.getPeopleHubModel())
getPeopleHubModelForCurrentUser()?.let(listener::onDataChanged)
}
return object : Subscription {
override fun unsubscribe() {
dataListeners.remove(listener)
if (dataListeners.isEmpty()) {
userChangeSubscription?.unsubscribe()
userChangeSubscription = null
notificationEntryManager
.removeNotificationEntryListener(notificationEntryListener)
}
@@ -138,16 +165,36 @@ class PeopleHubDataSourceImpl @Inject constructor(
}
}
private fun getPeopleHubModelForCurrentUser(): PeopleHubModel? {
val currentUserId = notifLockscreenUserMgr.currentUserId
val model = peopleHubManagerForUser[currentUserId]?.getPeopleHubModel()
?: return null
val currentProfiles = notifLockscreenUserMgr.currentProfiles
return model.copy(people = model.people.filter { person ->
currentProfiles[person.userId]?.isQuietModeEnabled == false
})
}
private fun updateUi() {
val model = peopleHubManager.getPeopleHubModel()
val model = getPeopleHubModelForCurrentUser() ?: return
for (listener in dataListeners) {
listener.onDataChanged(model)
}
}
}
@Singleton
class PeopleHubManager @Inject constructor() {
private fun NotificationLockscreenUserManager.registerListener(
listener: NotificationLockscreenUserManager.UserChangedListener
): Subscription {
addUserChangedListener(listener)
return object : Subscription {
override fun unsubscribe() {
removeUserChangedListener(listener)
}
}
}
class PeopleHubManager {
private val activePeople = mutableMapOf<PersonKey, PersonModel>()
private val inactivePeople = ArrayDeque<PersonModel>(MAX_STORED_INACTIVE_PEOPLE)
@@ -157,7 +204,7 @@ class PeopleHubManager @Inject constructor() {
if (inactivePeople.size >= MAX_STORED_INACTIVE_PEOPLE) {
inactivePeople.removeLast()
}
inactivePeople.push(data)
inactivePeople.add(data)
return true
}
return false
@@ -190,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)
}
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? =
@@ -272,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);

View File

@@ -98,7 +98,12 @@ class KeyguardBypassController : Dumpable {
bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
}
}, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
lockscreenUserManager.addUserChangedListener { pendingUnlockType = null }
lockscreenUserManager.addUserChangedListener(
object : NotificationLockscreenUserManager.UserChangedListener {
override fun onUserChanged(userId: Int) {
pendingUnlockType = null
}
})
}
/**

View File

@@ -178,9 +178,10 @@ private fun <T> castNull(): T = null as T
private fun fakePersonModel(
id: String,
name: CharSequence,
clickIntent: PendingIntent
clickIntent: PendingIntent,
userId: Int = 0
): PersonModel =
PersonModel(id, name, mock(Drawable::class.java), clickIntent)
PersonModel(id, name, mock(Drawable::class.java), clickIntent, userId)
private fun fakePersonViewModel(name: CharSequence): PersonViewModel =
PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass))
@@ -207,4 +208,4 @@ class FakeDataListener<T> : DataListener<T> {
override fun onDataChanged(data: T) {
lastSeen = Maybe.Just(data)
}
}
}