Commit e241e1c3 authored by Pavel Yatsuk's avatar Pavel Yatsuk Committed by Commit Bot

[Messages] Introduce MessageDispatcher

This CL:
- Introduces MessageDispatcher and its implementation
- Makes it available through WindowAndroid, following the pattern of
  BottomSheetController
- Refactors MessageQueueManager removing it from public messages target

BUG=1123947
R=lazzzis@chromium.org

Change-Id: Ieeece06f4b2f6b31abfd9c6cfd8df5b428076f8d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2446195Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Pavel Yatsuk <pavely@chromium.org>
Cr-Commit-Position: refs/heads/master@{#815879}
parent aa6549e5
......@@ -418,6 +418,7 @@ android_library("chrome_java") {
"//components/media_router/browser/android:java",
"//components/messages/android:factory_java",
"//components/messages/android:java",
"//components/messages/android:manager_java",
"//components/minidump_uploader:minidump_uploader_java",
"//components/module_installer/android:module_installer_java",
"//components/module_installer/android:module_interface_java",
......
......@@ -81,7 +81,7 @@ import org.chromium.components.browser_ui.bottomsheet.ManagedBottomSheetControll
import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.components.feature_engagement.EventConstants;
import org.chromium.components.messages.MessageQueueManager;
import org.chromium.components.messages.ManagedMessageDispatcher;
import org.chromium.components.messages.MessagesFactory;
import org.chromium.content_public.browser.ActionModeCallbackHelper;
import org.chromium.content_public.browser.LoadUrlParams;
......@@ -158,7 +158,7 @@ public class RootUiCoordinator
private ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
private final OneshotSupplier<StartSurface> mStartSurfaceSupplier;
@Nullable
private MessageQueueManager mMessageQueueManager;
private ManagedMessageDispatcher mMessageDispatcher;
/**
* Create a new {@link RootUiCoordinator} for the given activity.
......@@ -215,11 +215,6 @@ public class RootUiCoordinator
mOverviewModeBehaviorSupplier.onAvailable(
mCallbackController.makeCancelable(this::setOverviewModeBehavior));
mStartSurfaceSupplier = startSurfaceSupplier;
if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE)) {
mMessageQueueManager =
MessagesFactory.createMessageQueueManager(mActivity.getWindowAndroid());
}
}
// TODO(pnoland, crbug.com/865801): remove this in favor of wiring it directly.
......@@ -234,9 +229,9 @@ public class RootUiCoordinator
mActivity.getLayoutManagerSupplier().removeObserver(mLayoutManagerSupplierCallback);
if (mMessageQueueManager != null) {
mMessageQueueManager.destroy();
mMessageQueueManager = null;
if (mMessageDispatcher != null) {
MessagesFactory.detachMessageDispatcher(mMessageDispatcher);
mMessageDispatcher = null;
}
if (mOverlayPanelManager != null) {
......@@ -321,6 +316,12 @@ public class RootUiCoordinator
initFindToolbarManager();
initializeToolbar();
if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE)) {
mMessageDispatcher = MessagesFactory.createMessageDispatcher(
mActivity.findViewById(R.id.message_container));
MessagesFactory.attachMessageDispatcher(
mActivity.getWindowAndroid(), mMessageDispatcher);
}
}
@Override
......
......@@ -11,7 +11,8 @@ android_library("java") {
"java/src/org/chromium/components/messages/MessageBannerView.java",
"java/src/org/chromium/components/messages/MessageBannerViewBinder.java",
"java/src/org/chromium/components/messages/MessageContainer.java",
"java/src/org/chromium/components/messages/MessageQueueManager.java",
"java/src/org/chromium/components/messages/MessageDispatcher.java",
"java/src/org/chromium/components/messages/MessageDispatcherProvider.java",
"java/src/org/chromium/components/messages/MessageStateHandler.java",
"java/src/org/chromium/components/messages/SingleActionMessage.java",
]
......@@ -38,6 +39,15 @@ android_resources("java_resources") {
]
}
# Build target for Messages manager code, that owns and initializes
# MessageDispatcher.
android_library("manager_java") {
sources = [
"java/src/org/chromium/components/messages/ManagedMessageDispatcher.java",
]
deps = [ ":java" ]
}
static_library("feature_flags") {
sources = [
"messages_feature.cc",
......@@ -79,6 +89,7 @@ android_library_factory("factory_java") {
deps = [
":java",
":manager_java",
"//ui/android:ui_full_java",
]
}
......@@ -6,12 +6,14 @@ import("//build/config/android/rules.gni")
android_library("java") {
sources = [
"java/src/org/chromium/components/messages/MessageQueueManagerImpl.java",
"java/src/org/chromium/components/messages/MessageDispatcherImpl.java",
"java/src/org/chromium/components/messages/MessageQueueManager.java",
"java/src/org/chromium/components/messages/MessagesFactory.java",
]
deps = [
"..:java",
"..:manager_java",
"//base:base_java",
"//third_party/android_deps:androidx_annotation_annotation_java",
"//ui/android:ui_full_java",
......@@ -22,7 +24,9 @@ java_library("junit") {
# Skip platform checks since Robolectric depends on requires_android targets.
bypass_platform_checks = true
testonly = true
sources = [ "java/src/org/chromium/components/messages/MessageQueueManagerImplTest.java" ]
sources = [
"java/src/org/chromium/components/messages/MessageQueueManagerTest.java",
]
deps = [
":java",
"..:java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.messages;
import org.chromium.ui.modelutil.PropertyModel;
/**
* This class implements public MessageDispatcher interface, delegating the actual work to
* MessageQueueManager.
*/
public class MessageDispatcherImpl implements ManagedMessageDispatcher {
private final MessageQueueManager mMessageQueueManager = new MessageQueueManager();
private final MessageContainer mMessageContainer;
/**
* Build a new message dispatcher
* @param messageContainer A container view for displaying message banners.
*/
public MessageDispatcherImpl(MessageContainer messageContainer) {
mMessageContainer = messageContainer;
}
@Override
public void enqueueMessage(PropertyModel messageProperties) {
MessageStateHandler messageStateHandler =
new SingleActionMessage(mMessageContainer, messageProperties);
mMessageQueueManager.enqueueMessage(messageStateHandler, messageProperties);
}
@Override
public void dismissMessage(PropertyModel messageProperties) {
mMessageQueueManager.dismissMessage(messageProperties);
}
}
......@@ -6,10 +6,6 @@ package org.chromium.components.messages;
import androidx.annotation.Nullable;
import org.chromium.base.UnownedUserData;
import org.chromium.base.UnownedUserDataKey;
import org.chromium.ui.base.WindowAndroid;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
......@@ -19,42 +15,13 @@ import java.util.Queue;
* A class managing the queue of messages. Its primary role is to decide when to show/hide current
* message and which message to show next.
*/
class MessageQueueManagerImpl implements MessageQueueManager, UnownedUserData {
private static final UnownedUserDataKey<MessageQueueManagerImpl> KEY =
new UnownedUserDataKey<>(MessageQueueManagerImpl.class);
class MessageQueueManager {
private final Queue<MessageStateHandler> mMessageQueue = new ArrayDeque<>();
private final Map<Object, MessageStateHandler> mMessageMap = new HashMap<>();
@Nullable
private MessageStateHandler mCurrentDisplayedMessage;
/**
* Get the activity's MessageQueueManager from the provided WindowAndroid.
* @param window The window to get the manager from.
* @return The activity's MessageQueueManager.
*/
public static MessageQueueManagerImpl from(WindowAndroid window) {
return KEY.retrieveDataFromHost(window.getUnownedUserDataHost());
}
public MessageQueueManagerImpl() {}
/**
* Attaches MessageQueueManager to a given window. This window will be used later to retrieve
* activity's MessageQueueManager.
* @param window The window to attach to.
*/
public void attachToWindowAndroid(WindowAndroid window) {
KEY.attachToHost(window.getUnownedUserDataHost(), this);
}
/**
* Destroys MessageQueueManager, detaching it from the WindowAndroid it was attached to.
*/
@Override
public void destroy() {
KEY.detachFromAllHosts(this);
}
public MessageQueueManager() {}
/**
* Enqueues a message. Associates the message with its key; the key is used later to dismiss the
......
......@@ -17,10 +17,10 @@ import org.mockito.Mockito;
import org.chromium.base.test.BaseRobolectricTestRunner;
/**
* Unit tests for MessageQueueManagerImpl.
* Unit tests for MessageQueueManager.
*/
@RunWith(BaseRobolectricTestRunner.class)
public class MessageQueueManagerImplTest {
public class MessageQueueManagerTest {
/**
* Tests lifecycle of a single message:
* - enqueueMessage() calls show()
......@@ -29,7 +29,7 @@ public class MessageQueueManagerImplTest {
@Test
@SmallTest
public void testEnqueueMessage() {
MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
MessageQueueManager queueManager = new MessageQueueManager();
MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
......@@ -52,7 +52,7 @@ public class MessageQueueManagerImplTest {
@Test
@SmallTest
public void testOneMessageShownAtATime() {
MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
MessageQueueManager queueManager = new MessageQueueManager();
MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
......@@ -74,7 +74,7 @@ public class MessageQueueManagerImplTest {
@Test
@SmallTest
public void testDismissBeforeShow() {
MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
MessageQueueManager queueManager = new MessageQueueManager();
MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
......@@ -98,7 +98,7 @@ public class MessageQueueManagerImplTest {
@Test(expected = IllegalStateException.class)
@SmallTest
public void testEnqueueDuplicateKey() {
MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
MessageQueueManager queueManager = new MessageQueueManager();
MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
Object key = new Object();
......@@ -113,7 +113,7 @@ public class MessageQueueManagerImplTest {
@Test
@SmallTest
public void testDismissMessageTwice() {
MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
MessageQueueManager queueManager = new MessageQueueManager();
MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
queueManager.enqueueMessage(m1, m1);
queueManager.dismissMessage(m1);
......
......@@ -9,13 +9,29 @@ import org.chromium.ui.base.WindowAndroid;
/** A factory for constructing different Messages related objects. */
public class MessagesFactory {
/**
* Creates an instance of MessageQueueManager and attaches it to WindowAndroid.
* @param windowAndroid The WindowAndroid to attach MessageQueueManager to.
* @return The constructed MessageQueueManager.
* Creates an instance of ManagedMessageDispatcher.
* @return The constructed ManagedMessageDispatcher.
*/
public static MessageQueueManager createMessageQueueManager(WindowAndroid windowAndroid) {
MessageQueueManagerImpl messageQueueManager = new MessageQueueManagerImpl();
messageQueueManager.attachToWindowAndroid(windowAndroid);
return messageQueueManager;
public static ManagedMessageDispatcher createMessageDispatcher(MessageContainer container) {
return new MessageDispatcherImpl(container);
}
/**
* Attaches MessageDispatcher as UnownedUserData to WindowAndroid making it accessible to
* components outside of chrome/android.
* @param windowAndroid The WindowAndroid to attach ManagedMessageDispatcher to.
* @param messageDispatcher The MessageDispatcher to attach.
*/
public static void attachMessageDispatcher(
WindowAndroid windowAndroid, ManagedMessageDispatcher messageDispatcher) {
MessageDispatcherProvider.attach(windowAndroid, messageDispatcher);
}
/**
* Detaches MessageDispatcher from WindowAndroid.
* @param messageDispatcher The MessageDispatcher to detach from WindowAndroid.
*/
public static void detachMessageDispatcher(ManagedMessageDispatcher messageDispatcher) {
MessageDispatcherProvider.detach(messageDispatcher);
}
}
......@@ -5,12 +5,7 @@
package org.chromium.components.messages;
/**
* The public interface for managing the queue of messages. The embedder should retain reference to
* MessageQueueManager and destroy it when the Activity gets destroyed.
* An interface for the MessageDispatcher owning object.
*/
public interface MessageQueueManager {
/**
* Destroys MessageQueueManager, detaching it from the WindowAndroid it was attached to.
*/
void destroy();
}
public interface ManagedMessageDispatcher
extends MessageDispatcher, MessageDispatcherProvider.Unowned {}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.messages;
import org.chromium.ui.modelutil.PropertyModel;
/**
* The public interface for Messages. To interact with messages, feature should obtain a reference
* to MessageDispatcher through MessageDispatcherProvider and call methods of MessageDispatcher.
*/
public interface MessageDispatcher {
/**
* Enqueues a message defined by its properties.
* @param messageProperties The PropertyModel with message's visual properties.
*/
void enqueueMessage(PropertyModel messageProperties);
/**
* Dismisses a message referenced by its PropertyModel. Hdes the message if it is currently
* displayed. Displays the next message in the queue if available.
* @param messageProperties The PropertyModel of the messageto be dismissed.
*/
void dismissMessage(PropertyModel messageProperties);
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.messages;
import org.chromium.base.UnownedUserData;
import org.chromium.base.UnownedUserDataKey;
import org.chromium.ui.base.WindowAndroid;
/**
* The class that handles association of MessageDispatcher with WindowAndroid and retrieval of the
* associated MessageDispatcher.
*/
public class MessageDispatcherProvider {
/** An interface that allows a MessageDispatcher to be associated with an unowned data host. */
interface Unowned extends MessageDispatcher, UnownedUserData {}
/** The key used to bind the MessageDispatcher to the unowned data host. */
private static final UnownedUserDataKey<Unowned> KEY = new UnownedUserDataKey<>(Unowned.class);
/**
* Retrieves the shared MessageDispatcher from the provided WindowAndroid.
* @param windowAndroid The window to retrieve MessageDispatcher from.
* @return An instance of MessageDispatcher associated with the window.
*/
public static MessageDispatcher from(WindowAndroid windowAndroid) {
return KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
}
static void attach(WindowAndroid windowAndroid, Unowned controller) {
KEY.attachToHost(windowAndroid.getUnownedUserDataHost(), controller);
}
static void detach(Unowned controller) {
KEY.detachFromAllHosts(controller);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment