Commit 2e4facbd authored by Matthew Jones's avatar Matthew Jones Committed by Commit Bot

Add ActivityTabTabObserver

This patch introduces the ActivityTabTabObserver, a TabObserver that
only watches the activity tab. When the activity tab changes, the
observer automatically moves from the previous tab to the current
one. Use of this API is as simple as extending the new class.

The ActivityTabTabObserver itself extends the EmptyTabObserver and
adds a convenience method for notifications about the observed tab
changing (onObservingDifferentTab).

Bug: 871279
Change-Id: I5f3358953f5fdc5f9c4f9acd790af0b4d3d2d58a
Reviewed-on: https://chromium-review.googlesource.com/c/1347620
Commit-Queue: Matthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611377}
parent b96620c8
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser;
import android.support.annotation.CallSuper;
import org.chromium.base.ObserverList;
import org.chromium.base.ObserverList.RewindableIterator;
import org.chromium.chrome.browser.compositor.layouts.Layout;
......@@ -11,6 +13,7 @@ import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
import org.chromium.chrome.browser.compositor.layouts.StaticLayout;
import org.chromium.chrome.browser.compositor.layouts.phone.SimpleAnimationLayout;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
......@@ -50,6 +53,64 @@ public class ActivityTabProvider {
public abstract void onActivityTabChanged(Tab tab);
}
/**
* A utility class for observing the activity tab via {@link TabObserver}. When the activity
* tab changes, the observer is switched to that tab.
*/
public static class ActivityTabTabObserver extends EmptyTabObserver {
/** A handle to the activity tab provider. */
private final ActivityTabProvider mTabProvider;
/** An observer to watch for a changing activity tab and move this tab observer. */
private final ActivityTabObserver mActivityTabObserver;
/** The current activity tab. */
private Tab mTab;
/**
* Create a new {@link TabObserver} that only observes the activity tab.
* @param tabProvider An {@link ActivityTabProvider} to get the activity tab.
*/
public ActivityTabTabObserver(ActivityTabProvider tabProvider) {
mTabProvider = tabProvider;
mActivityTabObserver = (tab, hint) -> {
updateObservedTab(tab);
onObservingDifferentTab(tab);
};
mTabProvider.addObserver(mActivityTabObserver);
updateObservedTab(mTabProvider.getActivityTab());
}
/**
* Update the tab being observed.
* @param newTab The new tab to observe.
*/
private void updateObservedTab(Tab newTab) {
if (mTab != null) mTab.removeObserver(ActivityTabTabObserver.this);
mTab = newTab;
if (mTab != null) mTab.addObserver(ActivityTabTabObserver.this);
}
/**
* A notification that the observer has switched to observing a different tab. This will not
* be called for the initial tab being attached to after creation.
* @param tab The tab that the observer is now observing. This can be null.
*/
protected void onObservingDifferentTab(Tab tab) {}
/**
* Clean up any state held by this observer.
*/
@CallSuper
public void destroy() {
if (mTab != null) {
mTab.removeObserver(this);
mTab = null;
}
mTabProvider.removeObserver(mActivityTabObserver);
}
}
/** The list of observers to send events to. */
private final ObserverList<ActivityTabObserver> mObservers = new ObserverList<>();
......@@ -166,6 +227,16 @@ public class ActivityTabProvider {
}
}
/**
* Add an observer but do not immediately trigger the event. This should only be used in
* extremely specific cases where the observer would trigger an event from the constructor of
* the implementing class (see {@link ActivityTabTabObserver}).
* @param observer The observer to be added.
*/
private void addObserver(ActivityTabObserver observer) {
mObservers.addObserver(observer);
}
/**
* @param observer The {@link ActivityTabObserver} to add to the activity. This will trigger the
* {@link ActivityTabObserver#onActivityTabChanged(Tab, boolean)} event to be
......
......@@ -21,6 +21,7 @@ import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabTabObserver;
import org.chromium.chrome.browser.compositor.layouts.Layout;
import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver;
import org.chromium.chrome.browser.tab.Tab;
......@@ -39,6 +40,27 @@ import java.util.concurrent.TimeoutException;
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class ActivityTabProviderTest {
/** A test observer that provides access to the tab being observed. */
private static class TestActivityTabTabObserver extends ActivityTabTabObserver {
/** Callback helper for notification that the observer is watching a different tab. */
private CallbackHelper mObserverMoveHelper;
/** The tab currently being observed. */
private Tab mObservedTab;
public TestActivityTabTabObserver(ActivityTabProvider provider) {
super(provider);
mObserverMoveHelper = new CallbackHelper();
mObservedTab = provider.getActivityTab();
}
@Override
public void onObservingDifferentTab(Tab tab) {
mObservedTab = tab;
mObserverMoveHelper.notifyCalled();
}
}
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
......@@ -205,6 +227,28 @@ public class ActivityTabProviderTest {
assertEquals("The activity tab should not have changed.", activityTabBefore, mActivityTab);
}
/** Test that the {@link ActivityTabTabObserver} switches between tabs as the tab changes. */
@Test
@SmallTest
@Feature({"ActivityTabObserver"})
public void testActivityTabTabObserver() throws InterruptedException, TimeoutException {
Tab startingTab = getModelSelectedTab();
TestActivityTabTabObserver tabObserver = new TestActivityTabTabObserver(mProvider);
assertEquals("The observer should be attached to the starting tab.", startingTab,
tabObserver.mObservedTab);
ChromeTabUtils.fullyLoadUrlInNewTab(InstrumentationRegistry.getInstrumentation(), mActivity,
ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL, false);
assertNotEquals("The tab should have changed.", startingTab, getModelSelectedTab());
assertEquals("The observer should be attached to the new tab.", getModelSelectedTab(),
tabObserver.mObservedTab);
tabObserver.destroy();
}
/**
* Enter or exit the tab switcher with animations and wait for the scene to change.
* @param inSwitcher Whether to enter or exit the tab switcher.
......
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