0

Fetch user avatars for collaboration messages.

Bug: 374806190
Change-Id: Ib45785ca7208a7b295de0f953d644faabb674ee5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6020330
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Siddhartha S <ssid@chromium.org>
Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org>
Commit-Queue: Siddhartha S <ssid@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1383287}
This commit is contained in:
Sky Malice
2024-11-14 22:44:20 +00:00
committed by Chromium LUCI CQ
parent 4e72956d07
commit f6b02b90d9
6 changed files with 123 additions and 34 deletions
chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing
components
collaboration
public
BUILD.gn
android
java
src
org
chromium
components
data_sharing
public
android
java
src
org
chromium
components

@ -6,6 +6,8 @@ package org.chromium.chrome.browser.data_sharing;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
@ -14,6 +16,7 @@ import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.chromium.base.Callback;
import org.chromium.base.CallbackUtils;
import org.chromium.base.Token;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory;
@ -27,6 +30,10 @@ import org.chromium.components.collaboration.messaging.InstantNotificationLevel;
import org.chromium.components.collaboration.messaging.MessageUtils;
import org.chromium.components.collaboration.messaging.MessagingBackendService;
import org.chromium.components.collaboration.messaging.MessagingBackendService.InstantMessageDelegate;
import org.chromium.components.data_sharing.DataSharingUIDelegate;
import org.chromium.components.data_sharing.GroupMember;
import org.chromium.components.data_sharing.configs.DataSharingAvatarBitmapConfig;
import org.chromium.components.data_sharing.configs.DataSharingAvatarBitmapConfig.DataSharingAvatarCallback;
import org.chromium.components.messages.MessageBannerProperties;
import org.chromium.components.messages.MessageDispatcher;
import org.chromium.components.messages.MessageDispatcherProvider;
@ -34,6 +41,7 @@ import org.chromium.components.messages.MessageIdentifier;
import org.chromium.components.messages.PrimaryActionClickBehavior;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.util.ColorUtils;
import java.util.ArrayList;
import java.util.List;
@ -63,6 +71,7 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
}
private final List<AttachedWindowInfo> mAttachList = new ArrayList<>();
private final DataSharingUIDelegate mDataSharingUiDelegate;
/**
* @param profile The current profile to get dependencies with.
@ -72,6 +81,7 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
MessagingBackendService messagingBackendService =
MessagingBackendServiceFactory.getForProfile(profile);
messagingBackendService.setInstantMessageDelegate(this);
mDataSharingUiDelegate = DataSharingServiceFactory.getForProfile(profile).getUiDelegate();
}
/**
@ -191,9 +201,21 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
return null;
}
private Drawable iconFromMessage(Context context) {
// TODO(https://crbug.com/369163940): Fetch this, potentially async.
return ContextCompat.getDrawable(context, R.drawable.ic_features_24dp);
private void fetchAvatarIconFromMessage(
Context context, GroupMember groupMember, Callback<Drawable> onDrawable) {
DataSharingAvatarCallback onBitmap =
(Bitmap bitmap) -> onDrawable.onResult(new BitmapDrawable(bitmap));
int sizeInPixels =
context.getResources().getDimensionPixelSize(R.dimen.message_description_icon_size);
DataSharingAvatarBitmapConfig config =
new DataSharingAvatarBitmapConfig.Builder()
.setContext(context)
.setGroupMember(groupMember)
.setIsDarkMode(ColorUtils.inNightMode(context))
.setAvatarSizeInPixels(sizeInPixels)
.setDataSharingAvatarCallback(onBitmap)
.build();
mDataSharingUiDelegate.getAvatarBitmap(config);
}
private void showTabRemoved(
@ -207,16 +229,23 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
context.getString(
R.string.data_sharing_browser_message_removed_tab, givenName, tabTitle);
String buttonText = context.getString(R.string.data_sharing_browser_message_reopen);
Drawable icon = iconFromMessage(context);
GroupMember groupMember = MessageUtils.extractMember(message);
// TODO(https://crbug.com/369163940): Once the message has the url, we can restore.
showGenericMessage(
messageDispatcher,
MessageIdentifier.TAB_REMOVED_THROUGH_COLLABORATION,
title,
buttonText,
icon,
() -> {},
onSuccess);
Runnable action = CallbackUtils.emptyRunnable();
fetchAvatarIconFromMessage(
context,
groupMember,
(icon) -> {
showGenericMessage(
messageDispatcher,
MessageIdentifier.TAB_REMOVED_THROUGH_COLLABORATION,
title,
buttonText,
icon,
action,
onSuccess);
});
}
private void showTabChange(
@ -230,16 +259,23 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
context.getString(
R.string.data_sharing_browser_message_changed_tab, givenName, tabTitle);
String buttonText = context.getString(R.string.data_sharing_browser_message_reopen);
Drawable icon = iconFromMessage(context);
GroupMember groupMember = MessageUtils.extractMember(message);
// TODO(https://crbug.com/369163940): Once the message has the url, we can restore.
showGenericMessage(
messageDispatcher,
MessageIdentifier.TAB_NAVIGATED_THROUGH_COLLABORATION,
title,
buttonText,
icon,
() -> {},
onSuccess);
Runnable action = CallbackUtils.emptyRunnable();
fetchAvatarIconFromMessage(
context,
groupMember,
(icon) -> {
showGenericMessage(
messageDispatcher,
MessageIdentifier.TAB_NAVIGATED_THROUGH_COLLABORATION,
title,
buttonText,
icon,
action,
onSuccess);
});
}
private void showCollaborationMemberAdded(
@ -258,7 +294,7 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
givenName,
tabGroupTitle);
String buttonText = activity.getString(R.string.data_sharing_browser_message_manage);
Drawable icon = iconFromMessage(activity);
GroupMember groupMember = MessageUtils.extractMember(message);
Runnable openManageSharingRunnable =
() -> {
// TODO(crbug.com/379148260): Use shared #isCollaborationIdValid.
@ -266,14 +302,20 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
dataSharingTabManager.showManageSharing(activity, collaborationId);
}
};
showGenericMessage(
messageDispatcher,
MessageIdentifier.COLLABORATION_MEMBER_ADDED,
title,
buttonText,
icon,
openManageSharingRunnable,
onSuccess);
fetchAvatarIconFromMessage(
activity,
groupMember,
(icon) -> {
showGenericMessage(
messageDispatcher,
MessageIdentifier.COLLABORATION_MEMBER_ADDED,
title,
buttonText,
icon,
openManageSharingRunnable,
onSuccess);
});
}
private void showCollaborationRemoved(
@ -294,7 +336,7 @@ public class InstantMessageDelegateImpl implements InstantMessageDelegate {
title,
buttonText,
icon,
() -> {},
CallbackUtils.emptyRunnable(),
onSuccess);
}

@ -25,6 +25,7 @@ import static org.chromium.components.messages.MessageBannerProperties.TITLE;
import static org.chromium.components.messages.PrimaryActionClickBehavior.DISMISS_IMMEDIATELY;
import android.app.Activity;
import android.graphics.Bitmap;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
@ -54,6 +55,9 @@ import org.chromium.components.collaboration.messaging.MessageAttribution;
import org.chromium.components.collaboration.messaging.MessagingBackendService;
import org.chromium.components.collaboration.messaging.TabGroupMessageMetadata;
import org.chromium.components.collaboration.messaging.TabMessageMetadata;
import org.chromium.components.data_sharing.DataSharingService;
import org.chromium.components.data_sharing.DataSharingUIDelegate;
import org.chromium.components.data_sharing.configs.DataSharingAvatarBitmapConfig;
import org.chromium.components.messages.ManagedMessageDispatcher;
import org.chromium.components.messages.MessageIdentifier;
import org.chromium.components.messages.MessagesFactory;
@ -81,12 +85,15 @@ public class InstantMessageDelegateImplUnitTest {
@Mock private Profile mProfile;
@Mock private MessagingBackendService mMessagingBackendService;
@Mock private DataSharingService mDataSharingService;
@Mock private DataSharingUIDelegate mDataSharingUiDelegate;
@Mock private ManagedMessageDispatcher mManagedMessageDispatcher;
@Mock private WindowAndroid mWindowAndroid;
@Mock private TabGroupModelFilter mTabGroupModelFilter;
@Mock private Callback<Boolean> mSuccessCallback;
@Mock private DataSharingNotificationManager mDataSharingNotificationManager;
@Mock private DataSharingTabManager mDataSharingTabManager;
@Mock private Bitmap mAvatarBitmap;
@Captor private ArgumentCaptor<PropertyModel> mPropertyModelCaptor;
@ -102,6 +109,13 @@ public class InstantMessageDelegateImplUnitTest {
private void onActivity(Activity activity) {
MessagingBackendServiceFactory.setForTesting(mMessagingBackendService);
DataSharingServiceFactory.setForTesting(mDataSharingService);
when(mDataSharingService.getUiDelegate()).thenReturn(mDataSharingUiDelegate);
MockitoHelper.doCallback(
(DataSharingAvatarBitmapConfig config) ->
config.getDataSharingAvatarCallback().onAvatarLoaded(mAvatarBitmap))
.when(mDataSharingUiDelegate)
.getAvatarBitmap(any());
when(mWindowAndroid.getUnownedUserDataHost()).thenReturn(mUnownedUserDataHost);
MessagesFactory.attachMessageDispatcher(mWindowAndroid, mManagedMessageDispatcher);

@ -143,6 +143,7 @@ if (is_android) {
":java",
"//base:base_java",
"//base:base_junit_test_support",
"//components/data_sharing:test_support_java",
"//components/data_sharing/public:public_java",
"//components/saved_tab_groups/public:java",
"//third_party/android_deps:robolectric_all_java",

@ -7,6 +7,7 @@ package org.chromium.components.collaboration.messaging;
import androidx.annotation.Nullable;
import org.chromium.base.Token;
import org.chromium.components.data_sharing.GroupMember;
/** Provides functions to safely read fields out of messages, performing null checks. */
public class MessageUtils {
@ -74,4 +75,15 @@ public class MessageUtils {
? null
: message.attribution.collaborationId;
}
/** Returns a GroupMember associated with the message, prioritizing affected over triggering. */
public static GroupMember extractMember(@Nullable InstantMessage message) {
if (message == null || message.attribution == null) {
return null;
} else if (message.attribution.affectedUser != null) {
return message.attribution.affectedUser;
} else {
return message.attribution.triggeringUser;
}
}
}

@ -6,6 +6,9 @@ package org.chromium.components.collaboration.messaging;
import static org.junit.Assert.assertEquals;
import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER1;
import static org.chromium.components.data_sharing.SharedGroupTestHelper.GROUP_MEMBER2;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -125,4 +128,21 @@ public class MessageUtilsUnitTest {
assertEquals("", MessageUtils.extractTabGroupTitle(null));
}
@Test
public void testExtractMember() {
assertEquals(null, MessageUtils.extractMember(null));
InstantMessage message = new InstantMessage();
assertEquals(null, MessageUtils.extractMember(message));
message.attribution = new MessageAttribution();
assertEquals(null, MessageUtils.extractMember(message));
message.attribution.triggeringUser = GROUP_MEMBER1;
assertEquals(GROUP_MEMBER1, MessageUtils.extractMember(message));
message.attribution.affectedUser = GROUP_MEMBER2;
assertEquals(GROUP_MEMBER2, MessageUtils.extractMember(message));
}
}

@ -19,15 +19,15 @@ public final class DataSharingAvatarBitmapConfig {
private final DataSharingAvatarCallback mDataSharingAvatarCallback;
/** Interface used to pass the result of avatar loading. */
@FunctionalInterface
public interface DataSharingAvatarCallback {
/**
* Called when the avatar bitmap is ready.
*
* @param bitmap The loaded avatar bitmap. If might return null, if group member is
* invalid.
* @param bitmap The loaded avatar bitmap. If might return null, if group member is invalid.
*/
default void onAvatarLoaded(Bitmap bitmap) {}
void onAvatarLoaded(Bitmap bitmap);
}
private DataSharingAvatarBitmapConfig(Builder builder) {