Merge changes Id007cc64,I94afce96
* changes: Minified people row Current user and work profile availability in peoplehub
This commit is contained in:
committed by
Android (Google) Code Review
commit
5ba7d903a6
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user