Commit 77624f00 authored by Bernhard Bauer's avatar Bernhard Bauer Committed by Commit Bot

Add support for read-only properties in PropertyModel

Read-only properties can be initialized with a Builder.

Migrate KeyboardAccessoryModel and AccessorySheetModel to be based on
PropertyModel.

Change-Id: I22f461f904ccefe1ecdde12d7ead62bacb0acf29
Reviewed-on: https://chromium-review.googlesource.com/1179885
Commit-Queue: Bernhard Bauer <bauerb@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarFriedrich Horschig [CEST] <fhorschig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589881}
parent d38023cc
......@@ -4,6 +4,12 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.ACTIVE_TAB_INDEX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.HEIGHT;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.NO_ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.VISIBLE;
import android.support.annotation.Nullable;
import android.support.annotation.Px;
import android.support.v4.view.PagerAdapter;
......@@ -13,6 +19,7 @@ import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.ui.ViewProvider;
/**
......@@ -30,12 +37,16 @@ public class AccessorySheetCoordinator {
* @param viewProvider A provider for the accessory layout.
*/
public AccessorySheetCoordinator(ViewProvider<ViewPager> viewProvider) {
AccessorySheetModel model = new AccessorySheetModel();
PropertyModel model = new PropertyModel.Builder(TABS, ACTIVE_TAB_INDEX, VISIBLE, HEIGHT)
.with(TABS, new ListModel<>())
.with(ACTIVE_TAB_INDEX, NO_ACTIVE_TAB)
.with(VISIBLE, false)
.build();
new LazyConstructionPropertyMcp<>(model, AccessorySheetModel.PropertyKey.VISIBLE,
AccessorySheetModel::isVisible, viewProvider, AccessorySheetViewBinder::bind);
LazyConstructionPropertyMcp.create(
model, VISIBLE, viewProvider, AccessorySheetViewBinder::bind);
KeyboardAccessoryMetricsRecorder.registerMetricsObserver(model);
KeyboardAccessoryMetricsRecorder.registerAccessorySheetModelMetricsObserver(model);
mMediator = new AccessorySheetMediator(model);
}
......
......@@ -4,94 +4,100 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetModel.NO_ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.ACTIVE_TAB_INDEX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.NO_ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.VISIBLE;
import android.support.annotation.Nullable;
import android.support.annotation.Px;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
/**
* Contains the controller logic of the AccessorySheet component.
* It communicates with data providers and native backends to update a {@link AccessorySheetModel}.
* It communicates with data providers and native backends to update a model based on {@link
* AccessorySheetProperties}.
*/
class AccessorySheetMediator
implements PropertyObservable.PropertyObserver<AccessorySheetModel.PropertyKey> {
private final AccessorySheetModel mModel;
class AccessorySheetMediator implements PropertyObservable.PropertyObserver<PropertyKey> {
private final PropertyModel mModel;
AccessorySheetMediator(AccessorySheetModel model) {
AccessorySheetMediator(PropertyModel model) {
mModel = model;
mModel.addObserver(this);
}
@Nullable
KeyboardAccessoryData.Tab getTab() {
if (mModel.getActiveTabIndex() == NO_ACTIVE_TAB) return null;
return mModel.getTabList().get(mModel.getActiveTabIndex());
if (mModel.get(ACTIVE_TAB_INDEX) == NO_ACTIVE_TAB) return null;
return mModel.get(TABS).get(mModel.get(ACTIVE_TAB_INDEX));
}
@VisibleForTesting
AccessorySheetModel getModelForTesting() {
PropertyModel getModelForTesting() {
return mModel;
}
void show() {
mModel.setVisible(true);
mModel.set(VISIBLE, true);
}
void setHeight(int height) {
mModel.setHeight(height);
mModel.set(AccessorySheetProperties.HEIGHT, height);
}
public @Px int getHeight() {
return mModel.getHeight();
return mModel.get(AccessorySheetProperties.HEIGHT);
}
void hide() {
mModel.setVisible(false);
mModel.set(VISIBLE, false);
}
boolean isShown() {
return mModel.isVisible();
return mModel.get(VISIBLE);
}
void addTab(KeyboardAccessoryData.Tab tab) {
mModel.getTabList().add(tab);
if (mModel.getActiveTabIndex() == NO_ACTIVE_TAB) {
mModel.setActiveTabIndex(mModel.getTabList().size() - 1);
mModel.get(TABS).add(tab);
if (mModel.get(ACTIVE_TAB_INDEX) == NO_ACTIVE_TAB) {
mModel.set(ACTIVE_TAB_INDEX, mModel.get(TABS).size() - 1);
}
}
void removeTab(KeyboardAccessoryData.Tab tab) {
assert mModel.getActiveTabIndex() != NO_ACTIVE_TAB;
mModel.setActiveTabIndex(getNextActiveTab(tab));
mModel.getTabList().remove(tab);
if (mModel.getActiveTabIndex() == NO_ACTIVE_TAB) hide();
assert mModel.get(ACTIVE_TAB_INDEX) != NO_ACTIVE_TAB;
mModel.set(ACTIVE_TAB_INDEX, getNextActiveTab(tab));
mModel.get(TABS).remove(tab);
if (mModel.get(ACTIVE_TAB_INDEX) == NO_ACTIVE_TAB) hide();
}
void setTabs(KeyboardAccessoryData.Tab[] tabs) {
mModel.getTabList().set(tabs);
mModel.setActiveTabIndex(mModel.getTabList().size() - 1);
mModel.get(TABS).set(tabs);
mModel.set(ACTIVE_TAB_INDEX, mModel.get(TABS).size() - 1);
}
void setActiveTab(int position) {
assert position < mModel.getTabList().size()
assert position < mModel.get(TABS).size()
|| position >= 0 : position + " is not a valid tab index!";
mModel.setActiveTabIndex(position);
mModel.set(ACTIVE_TAB_INDEX, position);
}
/**
* Returns the position of a tab which needs to become the active tab. If the tab to be deleted
* is the active tab, return the item on its left. If it was the first item in the list, return
* the new first item. If no items remain, return {@link AccessorySheetModel#NO_ACTIVE_TAB}.
* the new first item. If no items remain, return {@link
* AccessorySheetProperties#NO_ACTIVE_TAB}.
* @param tabToBeDeleted The tab to be removed from the list.
* @return The position of the tab which should become active.
*/
private int getNextActiveTab(KeyboardAccessoryData.Tab tabToBeDeleted) {
int activeTab = mModel.getActiveTabIndex();
int activeTab = mModel.get(ACTIVE_TAB_INDEX);
for (int i = 0; i <= activeTab; i++) {
KeyboardAccessoryData.Tab tabLeftToActiveTab = mModel.getTabList().get(i);
KeyboardAccessoryData.Tab tabLeftToActiveTab = mModel.get(TABS).get(i);
// If we delete the active tab or a tab left to it, the new active tab moves left.
if (tabLeftToActiveTab == tabToBeDeleted) {
--activeTab;
......@@ -100,21 +106,19 @@ class AccessorySheetMediator
}
if (activeTab >= 0) return activeTab; // The new active tab is valid.
// If there are items left, take the first one.
int itemCountAfterDeletion = mModel.getTabList().size() - 1;
int itemCountAfterDeletion = mModel.get(TABS).size() - 1;
return itemCountAfterDeletion > 0 ? 0 : NO_ACTIVE_TAB;
}
@Override
public void onPropertyChanged(PropertyObservable<AccessorySheetModel.PropertyKey> source,
@Nullable AccessorySheetModel.PropertyKey propertyKey) {
if (propertyKey == AccessorySheetModel.PropertyKey.VISIBLE) {
if (mModel.isVisible() && getTab() != null && getTab().getListener() != null) {
public void onPropertyChanged(PropertyObservable<PropertyKey> source, PropertyKey propertyKey) {
if (propertyKey == VISIBLE) {
if (mModel.get(VISIBLE) && getTab() != null && getTab().getListener() != null) {
getTab().getListener().onTabShown();
}
return;
}
if (propertyKey == AccessorySheetModel.PropertyKey.ACTIVE_TAB_INDEX
|| propertyKey == AccessorySheetModel.PropertyKey.HEIGHT) {
if (propertyKey == ACTIVE_TAB_INDEX || propertyKey == AccessorySheetProperties.HEIGHT) {
return;
}
assert false : "Every property update needs to be handled explicitly!";
......
// Copyright 2018 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.chrome.browser.autofill.keyboard_accessory;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* This model holds all view state of the accessory sheet.
* It is updated by the {@link AccessorySheetMediator} and emits notification on which observers
* like the view binder react.
*/
class AccessorySheetModel extends PropertyObservable<AccessorySheetModel.PropertyKey> {
public static class PropertyKey {
public static final List<PropertyKey> ALL_PROPERTIES = new ArrayList<>();
private PropertyKey() {
ALL_PROPERTIES.add(this);
}
public static final PropertyKey TAB_LIST = new PropertyKey();
public static final PropertyKey ACTIVE_TAB_INDEX = new PropertyKey();
public static final PropertyKey VISIBLE = new PropertyKey();
public static final PropertyKey HEIGHT = new PropertyKey();
}
public static final int NO_ACTIVE_TAB = -1;
private int mActiveTabIndex = NO_ACTIVE_TAB;
private boolean mVisible;
private int mHeight;
private final ListModel<Tab> mTabList = new ListModel<>();
@Override
public Collection<PropertyKey> getAllSetProperties() {
return PropertyKey.ALL_PROPERTIES;
}
ListModel<Tab> getTabList() {
return mTabList;
}
void setVisible(boolean visible) {
if (mVisible == visible) return; // Nothing to do here: same value.
mVisible = visible;
notifyPropertyChanged(PropertyKey.VISIBLE);
}
boolean isVisible() {
return mVisible;
}
void setHeight(int height) {
if (height == mHeight) return; // Same value, nothing to do here.
mHeight = height;
notifyPropertyChanged(PropertyKey.HEIGHT);
}
int getHeight() {
return mHeight;
}
int getActiveTabIndex() {
return mActiveTabIndex;
}
void setActiveTabIndex(int activeTabPosition) {
if (mActiveTabIndex == activeTabPosition) return;
assert((activeTabPosition >= 0 && activeTabPosition < mTabList.size())
|| activeTabPosition == NO_ACTIVE_TAB)
: "Tried to set invalid index '" + activeTabPosition + "' as active tab!";
mActiveTabIndex = activeTabPosition;
notifyPropertyChanged(PropertyKey.ACTIVE_TAB_INDEX);
}
}
\ No newline at end of file
// Copyright 2018 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.chrome.browser.autofill.keyboard_accessory;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
/**
* This model holds all view state of the accessory sheet.
* It is updated by the {@link AccessorySheetMediator} and emits notification on which observers
* like the view binder react.
*/
class AccessorySheetProperties {
public static final ReadableObjectPropertyKey<ListModel<Tab>> TABS =
new ReadableObjectPropertyKey<>();
public static final WritableIntPropertyKey ACTIVE_TAB_INDEX = new WritableIntPropertyKey();
public static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
public static final WritableIntPropertyKey HEIGHT = new WritableIntPropertyKey();
public static final int NO_ACTIVE_TAB = -1;
private AccessorySheetProperties() {}
}
\ No newline at end of file
......@@ -4,36 +4,41 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.ACTIVE_TAB_INDEX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.HEIGHT;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.NO_ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.VISIBLE;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetModel.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
/**
* Observes {@link AccessorySheetModel} changes (like a newly available tab) and triggers the
* Observes {@link AccessorySheetProperties} changes (like a newly available tab) and triggers the
* {@link AccessorySheetViewBinder} which will modify the view accordingly.
*/
class AccessorySheetViewBinder {
public static void bind(
AccessorySheetModel model, ViewPager viewPager, PropertyKey propertyKey) {
if (propertyKey == PropertyKey.TAB_LIST) {
public static void bind(PropertyModel model, ViewPager viewPager, PropertyKey propertyKey) {
if (propertyKey == TABS) {
viewPager.setAdapter(
AccessorySheetCoordinator.createTabViewAdapter(model.getTabList(), viewPager));
} else if (propertyKey == PropertyKey.VISIBLE) {
AccessorySheetCoordinator.createTabViewAdapter(model.get(TABS), viewPager));
} else if (propertyKey == VISIBLE) {
viewPager.bringToFront(); // Ensure toolbars and other containers are overlaid.
viewPager.setVisibility(model.isVisible() ? View.VISIBLE : View.GONE);
if (model.isVisible()
&& model.getActiveTabIndex() != AccessorySheetModel.NO_ACTIVE_TAB) {
announceOpenedTab(viewPager, model.getTabList().get(model.getActiveTabIndex()));
viewPager.setVisibility(model.get(VISIBLE) ? View.VISIBLE : View.GONE);
if (model.get(VISIBLE) && model.get(ACTIVE_TAB_INDEX) != NO_ACTIVE_TAB) {
announceOpenedTab(viewPager, model.get(TABS).get(model.get(ACTIVE_TAB_INDEX)));
}
} else if (propertyKey == PropertyKey.HEIGHT) {
} else if (propertyKey == HEIGHT) {
ViewGroup.LayoutParams p = viewPager.getLayoutParams();
p.height = model.getHeight();
p.height = model.get(HEIGHT);
viewPager.setLayoutParams(p);
} else if (propertyKey == PropertyKey.ACTIVE_TAB_INDEX) {
if (model.getActiveTabIndex() != AccessorySheetModel.NO_ACTIVE_TAB) {
viewPager.setCurrentItem(model.getActiveTabIndex());
} else if (propertyKey == ACTIVE_TAB_INDEX) {
if (model.get(ACTIVE_TAB_INDEX) != NO_ACTIVE_TAB) {
viewPager.setCurrentItem(model.get(ACTIVE_TAB_INDEX));
}
} else {
assert false : "Every possible property update needs to be handled!";
......
......@@ -4,6 +4,13 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIONS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BOTTOM_OFFSET_PX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TAB_SELECTION_CALLBACKS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.VISIBLE;
import android.support.annotation.Px;
import org.chromium.base.VisibleForTesting;
......@@ -12,6 +19,7 @@ import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessory
import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
import org.chromium.ui.ViewProvider;
......@@ -60,18 +68,25 @@ public class KeyboardAccessoryCoordinator {
*/
public KeyboardAccessoryCoordinator(VisibilityDelegate visibilityDelegate,
ViewProvider<KeyboardAccessoryView> viewProvider) {
KeyboardAccessoryModel model = new KeyboardAccessoryModel();
PropertyModel model = new PropertyModel
.Builder(ACTIONS, TABS, VISIBLE, BOTTOM_OFFSET_PX, ACTIVE_TAB,
TAB_SELECTION_CALLBACKS)
.with(TABS, new ListModel<>())
.with(ACTIONS, new ListModel<>())
.with(ACTIVE_TAB, null)
.with(VISIBLE, false)
.build();
mMediator = new KeyboardAccessoryMediator(model, visibilityDelegate);
viewProvider.whenLoaded(view -> view.setTabSelectionAdapter(mMediator));
new LazyConstructionPropertyMcp<>(model, KeyboardAccessoryModel.PropertyKey.VISIBLE,
KeyboardAccessoryModel::isVisible, viewProvider, KeyboardAccessoryViewBinder::bind);
KeyboardAccessoryMetricsRecorder.registerMetricsObserver(model);
LazyConstructionPropertyMcp.create(
model, VISIBLE, viewProvider, KeyboardAccessoryViewBinder::bind);
KeyboardAccessoryMetricsRecorder.registerKeyboardAccessoryModelMetricsObserver(model);
}
/**
* Creates an adapter to an {@link ActionViewHolder} that is wired
* up to the model change processor which listens to the given {@link KeyboardAccessoryModel}.
* up to the model change processor which listens to the given action list.
* @param actions The list of actions shown represented by the adapter.
* @return Returns a fully initialized and wired adapter to an ActionViewHolder.
*/
......@@ -85,16 +100,16 @@ public class KeyboardAccessoryCoordinator {
/**
* Creates the {@link TabViewBinder} that is linked to the {@link ListModelChangeProcessor} that
* connects the given {@link KeyboardAccessoryView} to the given {@link KeyboardAccessoryModel}.
* @param model the {@link KeyboardAccessoryModel} whose data is used by the TabViewBinder.
* connects the given {@link KeyboardAccessoryView} to the given action list.
* @param model the {@link KeyboardAccessoryProperties} whose data is used by the TabViewBinder.
* @param inflatedView the {@link KeyboardAccessoryView} to which the TabViewBinder binds data.
* @return Returns a fully initialized and wired {@link TabViewBinder}.
*/
static TabViewBinder createTabViewBinder(
KeyboardAccessoryModel model, KeyboardAccessoryView inflatedView) {
PropertyModel model, KeyboardAccessoryView inflatedView) {
TabViewBinder tabViewBinder = new TabViewBinder();
model.addTabListObserver(
new ListModelChangeProcessor<>(model.getTabList(), inflatedView, tabViewBinder));
model.get(TABS).addObserver(
new ListModelChangeProcessor<>(model.get(TABS), inflatedView, tabViewBinder));
return tabViewBinder;
}
......
......@@ -5,6 +5,12 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTrigger.MANUAL_CLOSE;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIONS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BOTTOM_OFFSET_PX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TAB_SELECTION_CALLBACKS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.VISIBLE;
import android.support.annotation.Nullable;
import android.support.annotation.Px;
......@@ -14,6 +20,8 @@ import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryCoordinator.VisibilityDelegate;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
import org.chromium.chrome.browser.modelutil.ListObservable;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
import java.util.ArrayList;
......@@ -22,31 +30,30 @@ import java.util.List;
/**
* This is the second part of the controller of the keyboard accessory component.
* It is responsible to update the {@link KeyboardAccessoryModel} based on Backend calls and notify
* the Backend if the {@link KeyboardAccessoryModel} changes.
* From the backend, it receives all actions that the accessory can perform (most prominently
* generating passwords) and lets the {@link KeyboardAccessoryModel} know of these actions and which
* callback to trigger when selecting them.
* It is responsible for updating the model based on backend calls and notify the backend if the
* model changes. From the backend, it receives all actions that the accessory can perform (most
* prominently generating passwords) and lets the model know of these actions and which callback to
* trigger when selecting them.
*/
class KeyboardAccessoryMediator
implements ListObservable.ListObserver<Void>,
PropertyObservable.PropertyObserver<KeyboardAccessoryModel.PropertyKey>,
PropertyObservable.PropertyObserver<PropertyKey>,
KeyboardAccessoryData.Observer<KeyboardAccessoryData.Action>,
TabLayout.OnTabSelectedListener {
private final KeyboardAccessoryModel mModel;
private final PropertyModel mModel;
private final VisibilityDelegate mVisibilityDelegate;
private boolean mShowIfNotEmpty;
KeyboardAccessoryMediator(KeyboardAccessoryModel model, VisibilityDelegate visibilityDelegate) {
KeyboardAccessoryMediator(PropertyModel model, VisibilityDelegate visibilityDelegate) {
mModel = model;
mVisibilityDelegate = visibilityDelegate;
// Add mediator as observer so it can use model changes as signal for accessory visibility.
mModel.addObserver(this);
mModel.getTabList().addObserver(this);
mModel.getActionList().addObserver(this);
mModel.setTabSelectionCallbacks(this);
mModel.get(TABS).addObserver(this);
mModel.get(ACTIONS).addObserver(this);
mModel.set(TAB_SELECTION_CALLBACKS, this);
}
@Override
......@@ -55,14 +62,14 @@ class KeyboardAccessoryMediator
// If there is a new list, retain all actions that are of a different type than the provided
// actions.
List<Action> retainedActions = new ArrayList<>();
for (Action a : mModel.getActionList()) {
for (Action a : mModel.get(ACTIONS)) {
if (a.getActionType() == typeId) continue;
retainedActions.add(a);
}
// Always append autofill suggestions to the very end.
int insertPos = typeId == AccessoryAction.AUTOFILL_SUGGESTION ? retainedActions.size() : 0;
retainedActions.addAll(insertPos, Arrays.asList(actions));
mModel.setActions(retainedActions.toArray(new Action[retainedActions.size()]));
mModel.get(ACTIONS).set(retainedActions);
}
void requestShowing() {
......@@ -76,15 +83,15 @@ class KeyboardAccessoryMediator
}
void addTab(KeyboardAccessoryData.Tab tab) {
mModel.addTab(tab);
mModel.get(TABS).add(tab);
}
void removeTab(KeyboardAccessoryData.Tab tab) {
mModel.removeTab(tab);
mModel.get(TABS).remove(tab);
}
void setTabs(KeyboardAccessoryData.Tab[] tabs) {
mModel.getTabList().set(tabs);
mModel.get(TABS).set(tabs);
}
void dismiss() {
......@@ -93,50 +100,50 @@ class KeyboardAccessoryMediator
}
void closeActiveTab() {
mModel.setActiveTab(null);
mModel.set(ACTIVE_TAB, null);
}
@VisibleForTesting
KeyboardAccessoryModel getModelForTesting() {
PropertyModel getModelForTesting() {
return mModel;
}
@Override
public void onItemRangeInserted(ListObservable source, int index, int count) {
assert source == mModel.getActionList() || source == mModel.getTabList();
assert source == mModel.get(ACTIONS) || source == mModel.get(TABS);
updateVisibility();
}
@Override
public void onItemRangeRemoved(ListObservable source, int index, int count) {
assert source == mModel.getActionList() || source == mModel.getTabList();
assert source == mModel.get(ACTIONS) || source == mModel.get(TABS);
updateVisibility();
}
@Override
public void onItemRangeChanged(
ListObservable source, int index, int count, @Nullable Void payload) {
assert source == mModel.getActionList() || source == mModel.getTabList();
assert source == mModel.get(ACTIONS) || source == mModel.get(TABS);
assert payload == null;
updateVisibility();
}
@Override
public void onPropertyChanged(PropertyObservable<KeyboardAccessoryModel.PropertyKey> source,
@Nullable KeyboardAccessoryModel.PropertyKey propertyKey) {
public void onPropertyChanged(
PropertyObservable<PropertyKey> source, @Nullable PropertyKey propertyKey) {
// Update the visibility only if we haven't set it just now.
if (propertyKey == KeyboardAccessoryModel.PropertyKey.VISIBLE) {
if (propertyKey == VISIBLE) {
// When the accessory just (dis)appeared, there should be no active tab.
closeActiveTab();
mVisibilityDelegate.onBottomControlSpaceChanged();
if (!mModel.isVisible()) {
if (!mModel.get(VISIBLE)) {
// TODO(fhorschig|ioanap): Maybe the generation bridge should take care of that.
onItemsAvailable(AccessoryAction.GENERATE_PASSWORD_AUTOMATIC, new Action[0]);
}
return;
}
if (propertyKey == KeyboardAccessoryModel.PropertyKey.ACTIVE_TAB) {
Integer activeTab = mModel.activeTab();
if (propertyKey == ACTIVE_TAB) {
Integer activeTab = mModel.get(ACTIVE_TAB);
if (activeTab == null) {
mVisibilityDelegate.onCloseAccessorySheet();
updateVisibility();
......@@ -145,8 +152,7 @@ class KeyboardAccessoryMediator
mVisibilityDelegate.onChangeAccessorySheet(activeTab);
return;
}
if (propertyKey == KeyboardAccessoryModel.PropertyKey.BOTTOM_OFFSET
|| propertyKey == KeyboardAccessoryModel.PropertyKey.TAB_SELECTION_CALLBACKS) {
if (propertyKey == BOTTOM_OFFSET_PX || propertyKey == TAB_SELECTION_CALLBACKS) {
return;
}
assert false : "Every property update needs to be handled explicitly!";
......@@ -154,7 +160,7 @@ class KeyboardAccessoryMediator
@Override
public void onTabSelected(TabLayout.Tab tab) {
mModel.setActiveTab(tab.getPosition());
mModel.set(ACTIVE_TAB, tab.getPosition());
}
@Override
......@@ -162,37 +168,37 @@ class KeyboardAccessoryMediator
@Override
public void onTabReselected(TabLayout.Tab tab) {
if (mModel.activeTab() == null) {
mModel.setActiveTab(tab.getPosition());
if (mModel.get(ACTIVE_TAB) == null) {
mModel.set(ACTIVE_TAB, tab.getPosition());
} else {
KeyboardAccessoryMetricsRecorder.recordSheetTrigger(
mModel.getTabList().get(mModel.activeTab()).getRecordingType(), MANUAL_CLOSE);
mModel.get(TABS).get(mModel.get(ACTIVE_TAB)).getRecordingType(), MANUAL_CLOSE);
mVisibilityDelegate.onOpenKeyboard(); // This will close the active tab gently.
}
}
boolean hasContents() {
return mModel.getActionList().size() > 0 || mModel.getTabList().size() > 0;
return mModel.get(ACTIONS).size() > 0 || mModel.get(TABS).size() > 0;
}
private boolean shouldShowAccessory() {
if (!mShowIfNotEmpty && mModel.activeTab() == null) return false;
if (!mShowIfNotEmpty && mModel.get(ACTIVE_TAB) == null) return false;
return hasContents();
}
private void updateVisibility() {
mModel.setVisible(shouldShowAccessory());
mModel.set(VISIBLE, shouldShowAccessory());
}
public void setBottomOffset(@Px int bottomOffset) {
mModel.setBottomOffset(bottomOffset);
mModel.set(BOTTOM_OFFSET_PX, bottomOffset);
}
public boolean isShown() {
return mModel.isVisible();
return mModel.get(VISIBLE);
}
public boolean hasActiveTab() {
return mModel.isVisible() && mModel.activeTab() != null;
return mModel.get(VISIBLE) && mModel.get(ACTIVE_TAB) != null;
}
}
// Copyright 2018 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.chrome.browser.autofill.keyboard_accessory;
import android.support.annotation.Nullable;
import android.support.annotation.Px;
import android.support.design.widget.TabLayout;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.ListObservable;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* As model of the keyboard accessory component, this class holds the data relevant to the visual
* state of the accessory.
* This includes the visibility of the accessory in general, any available tabs and actions.
* Whenever the state changes, it notifies its listeners - like the
* {@link KeyboardAccessoryMediator} or the ModelChangeProcessor.
*/
class KeyboardAccessoryModel extends PropertyObservable<KeyboardAccessoryModel.PropertyKey> {
/** Keys uniquely identifying model properties. */
static class PropertyKey {
// This list contains all properties and is only necessary because the view needs to
// iterate over all properties when it's created to ensure it is in sync with this model.
static final List<PropertyKey> ALL_PROPERTIES = new ArrayList<>();
static final PropertyKey ACTIONS = new PropertyKey();
static final PropertyKey TABS = new PropertyKey();
static final PropertyKey VISIBLE = new PropertyKey();
static final PropertyKey BOTTOM_OFFSET = new PropertyKey();
static final PropertyKey ACTIVE_TAB = new PropertyKey();
static final PropertyKey TAB_SELECTION_CALLBACKS = new PropertyKey();
private PropertyKey() {
ALL_PROPERTIES.add(this);
}
}
private ListModel<KeyboardAccessoryData.Action> mActionListObservable;
private ListModel<KeyboardAccessoryData.Tab> mTabListObservable;
private boolean mVisible;
private @Px int mBottomOffset;
private @Nullable Integer mActiveTab;
private TabLayout.OnTabSelectedListener mTabSelectionCallbacks;
KeyboardAccessoryModel() {
mActionListObservable = new ListModel<>();
mTabListObservable = new ListModel<>();
}
@Override
public Collection<PropertyKey> getAllSetProperties() {
return PropertyKey.ALL_PROPERTIES;
}
void addActionListObserver(ListObservable.ListObserver<Void> observer) {
mActionListObservable.addObserver(observer);
}
void setActions(KeyboardAccessoryData.Action[] actions) {
mActionListObservable.set(actions);
}
ListModel<KeyboardAccessoryData.Action> getActionList() {
return mActionListObservable;
}
void addTabListObserver(ListObservable.ListObserver<Void> observer) {
mTabListObservable.addObserver(observer);
}
void addTab(KeyboardAccessoryData.Tab tab) {
mTabListObservable.add(tab);
}
void removeTab(KeyboardAccessoryData.Tab tab) {
mTabListObservable.remove(tab);
}
ListModel<KeyboardAccessoryData.Tab> getTabList() {
return mTabListObservable;
}
void setVisible(boolean visible) {
if (mVisible == visible) return; // Nothing to do here: same value.
mVisible = visible;
notifyPropertyChanged(PropertyKey.VISIBLE);
}
boolean isVisible() {
return mVisible;
}
void setBottomOffset(@Px int bottomOffset) {
if (mBottomOffset == bottomOffset) return; // Nothing to do here: same value.
mBottomOffset = bottomOffset;
notifyPropertyChanged(PropertyKey.BOTTOM_OFFSET);
}
@Px
int bottomOffset() {
return mBottomOffset;
}
@SuppressWarnings("ReferenceEquality") // No action if both are null or exact same object.
void setActiveTab(@Nullable Integer activeTab) {
if (activeTab == mActiveTab) return;
if (null != mActiveTab && mActiveTab.equals(activeTab)) return;
mActiveTab = activeTab;
notifyPropertyChanged(PropertyKey.ACTIVE_TAB);
}
@Nullable
Integer activeTab() {
return mActiveTab;
}
TabLayout.OnTabSelectedListener getTabSelectionCallbacks() {
return mTabSelectionCallbacks;
}
void setTabSelectionCallbacks(TabLayout.OnTabSelectedListener tabSelectionCallbacks) {
if (tabSelectionCallbacks == mTabSelectionCallbacks) return; // Nothing to do: same object.
mTabSelectionCallbacks = tabSelectionCallbacks;
notifyPropertyChanged(PropertyKey.TAB_SELECTION_CALLBACKS);
}
}
// Copyright 2018 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.chrome.browser.autofill.keyboard_accessory;
import android.support.design.widget.TabLayout;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
/**
* As model of the keyboard accessory component, this class holds the data relevant to the visual
* state of the accessory.
* This includes the visibility of the accessory in general, any available tabs and actions.
* Whenever the state changes, it notifies its listeners - like the
* {@link KeyboardAccessoryMediator} or the ModelChangeProcessor.
*/
class KeyboardAccessoryProperties {
static final ReadableObjectPropertyKey<ListModel<KeyboardAccessoryData.Action>> ACTIONS =
new ReadableObjectPropertyKey<>();
static final ReadableObjectPropertyKey<ListModel<KeyboardAccessoryData.Tab>> TABS =
new ReadableObjectPropertyKey<>();
static final WritableBooleanPropertyKey VISIBLE = new WritableBooleanPropertyKey();
static final WritableIntPropertyKey BOTTOM_OFFSET_PX = new WritableIntPropertyKey();
static final /* @Nullable */ WritableObjectPropertyKey<Integer> ACTIVE_TAB =
new WritableObjectPropertyKey<>();
static final WritableObjectPropertyKey<TabLayout.OnTabSelectedListener>
TAB_SELECTION_CALLBACKS = new WritableObjectPropertyKey<>();
private KeyboardAccessoryProperties() {}
}
......@@ -4,6 +4,14 @@
package org.chromium.chrome.browser.autofill.keyboard_accessory;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIONS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BOTTOM_OFFSET_PX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TAB_SELECTION_CALLBACKS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.VISIBLE;
import android.support.design.widget.TabLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
......@@ -13,13 +21,14 @@ import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryModel.PropertyKey;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.ListModelChangeProcessor;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
/**
* Observes {@link KeyboardAccessoryModel} changes (like a newly available tab) and triggers the
* {@link KeyboardAccessoryViewBinder} which will modify the view accordingly.
* Observes {@link KeyboardAccessoryProperties} changes (like a newly available tab) and triggers
* the {@link KeyboardAccessoryViewBinder} which will modify the view accordingly.
*/
class KeyboardAccessoryViewBinder {
static class ActionViewHolder extends RecyclerView.ViewHolder {
......@@ -89,39 +98,40 @@ class KeyboardAccessoryViewBinder {
}
}
public static void bind(KeyboardAccessoryModel model, KeyboardAccessoryView view,
PropertyKey propertyKey) {
if (propertyKey == PropertyKey.ACTIONS) {
public static void bind(
PropertyModel model, KeyboardAccessoryView view, PropertyKey propertyKey) {
if (propertyKey == ACTIONS) {
view.setActionsAdapter(
KeyboardAccessoryCoordinator.createActionsAdapter(model.getActionList()));
} else if (propertyKey == PropertyKey.TABS) {
KeyboardAccessoryCoordinator.createActionsAdapter(model.get(ACTIONS)));
} else if (propertyKey == TABS) {
KeyboardAccessoryCoordinator.createTabViewBinder(model, view)
.updateAllTabs(view, model.getTabList());
} else if (propertyKey == PropertyKey.VISIBLE) {
view.setActiveTabColor(model.activeTab());
.updateAllTabs(view, model.get(TABS));
} else if (propertyKey == VISIBLE) {
view.setActiveTabColor(model.get(ACTIVE_TAB));
setActiveTabHint(model, view);
view.setVisible(model.isVisible());
} else if (propertyKey == PropertyKey.ACTIVE_TAB) {
view.setActiveTabColor(model.activeTab());
view.setVisible(model.get(VISIBLE));
} else if (propertyKey == ACTIVE_TAB) {
view.setActiveTabColor(model.get(ACTIVE_TAB));
setActiveTabHint(model, view);
} else if (propertyKey == PropertyKey.BOTTOM_OFFSET) {
view.setBottomOffset(model.bottomOffset());
} else if (propertyKey == PropertyKey.TAB_SELECTION_CALLBACKS) {
} else if (propertyKey == BOTTOM_OFFSET_PX) {
view.setBottomOffset(model.get(BOTTOM_OFFSET_PX));
} else if (propertyKey == TAB_SELECTION_CALLBACKS) {
// Don't add null as listener. It's a valid state but an invalid argument.
if (model.getTabSelectionCallbacks() == null) return;
view.setTabSelectionAdapter(model.getTabSelectionCallbacks());
TabLayout.OnTabSelectedListener listener = model.get(TAB_SELECTION_CALLBACKS);
if (listener == null) return;
view.setTabSelectionAdapter(listener);
} else {
assert false : "Every possible property update needs to be handled!";
}
}
private static void setActiveTabHint(KeyboardAccessoryModel model, KeyboardAccessoryView view) {
private static void setActiveTabHint(PropertyModel model, KeyboardAccessoryView view) {
int activeTab = -1;
if (model.activeTab() != null) {
activeTab = model.activeTab();
if (model.get(ACTIVE_TAB) != null) {
activeTab = model.get(ACTIVE_TAB);
}
for (int i = 0; i < model.getTabList().size(); ++i) {
Tab tab = model.getTabList().get(i);
for (int i = 0; i < model.get(TABS).size(); ++i) {
Tab tab = model.get(TABS).get(i);
if (activeTab == i) {
view.setTabDescription(i, R.string.keyboard_accessory_sheet_hide);
} else {
......
......@@ -7,7 +7,7 @@ package org.chromium.chrome.browser.download.home.empty;
import android.support.annotation.IntDef;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
......@@ -23,13 +23,13 @@ interface EmptyProperties {
}
/** The current state of the empty view. */
public static final IntPropertyKey STATE = new IntPropertyKey();
public static final WritableIntPropertyKey STATE = new WritableIntPropertyKey();
/** The current text resource to use for the empty view. */
public static final IntPropertyKey EMPTY_TEXT_RES_ID = new IntPropertyKey();
public static final WritableIntPropertyKey EMPTY_TEXT_RES_ID = new WritableIntPropertyKey();
/** The current icon resource to use for the empty view. */
public static final IntPropertyKey EMPTY_ICON_RES_ID = new IntPropertyKey();
public static final WritableIntPropertyKey EMPTY_ICON_RES_ID = new WritableIntPropertyKey();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {STATE, EMPTY_TEXT_RES_ID, EMPTY_ICON_RES_ID};
......
......@@ -8,24 +8,25 @@ import android.view.View;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.BooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
/** The properties needed to render the download home filter view. */
public interface FilterProperties {
/** The {@link View} to show in the content area. */
public static final ObjectPropertyKey<View> CONTENT_VIEW = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<View> CONTENT_VIEW =
new WritableObjectPropertyKey<>();
/** Which {@code TabType} should be selected. */
public static final IntPropertyKey SELECTED_TAB = new IntPropertyKey();
public static final WritableIntPropertyKey SELECTED_TAB = new WritableIntPropertyKey();
/** The callback listener for {@code TabType} selection changes. */
public static final ObjectPropertyKey<Callback</* @TabType */ Integer>> CHANGE_LISTENER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<Callback</* @TabType */ Integer>>
CHANGE_LISTENER = new WritableObjectPropertyKey<>();
/** Whether or not to show the tabs or just show the content. */
public static final BooleanPropertyKey SHOW_TABS = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey SHOW_TABS = new WritableBooleanPropertyKey();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {CONTENT_VIEW, SELECTED_TAB, CHANGE_LISTENER, SHOW_TABS};
......
......@@ -6,8 +6,8 @@ package org.chromium.chrome.browser.download.home.list;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.BooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.components.offline_items_collection.OfflineItemVisuals;
import org.chromium.components.offline_items_collection.VisualsCallback;
......@@ -32,34 +32,41 @@ public interface ListProperties {
}
/** Whether or not item animations should be enabled. */
BooleanPropertyKey ENABLE_ITEM_ANIMATIONS = new BooleanPropertyKey();
WritableBooleanPropertyKey ENABLE_ITEM_ANIMATIONS = new WritableBooleanPropertyKey();
/** The callback for when a UI action should open a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_OPEN = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_OPEN =
new WritableObjectPropertyKey<>();
/** The callback for when a UI action should pause a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_PAUSE = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_PAUSE =
new WritableObjectPropertyKey<>();
/** The callback for when a UI action should resume a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_RESUME = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_RESUME =
new WritableObjectPropertyKey<>();
/** The callback for when a UI action should cancel a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_CANCEL = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_CANCEL =
new WritableObjectPropertyKey<>();
/** The callback for when a UI action should share a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_SHARE = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_SHARE =
new WritableObjectPropertyKey<>();
/** The callback for when a UI action should remove a {@link OfflineItem}. */
ObjectPropertyKey<Callback<OfflineItem>> CALLBACK_REMOVE = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<OfflineItem>> CALLBACK_REMOVE =
new WritableObjectPropertyKey<>();
/** The provider to retrieve expensive assets for a {@link OfflineItem}. */
ObjectPropertyKey<VisualsProvider> PROVIDER_VISUALS = new ObjectPropertyKey<>();
WritableObjectPropertyKey<VisualsProvider> PROVIDER_VISUALS = new WritableObjectPropertyKey<>();
/** The callback to trigger when a UI action selects or deselects a {@link ListItem}. */
ObjectPropertyKey<Callback<ListItem>> CALLBACK_SELECTION = new ObjectPropertyKey<>();
WritableObjectPropertyKey<Callback<ListItem>> CALLBACK_SELECTION =
new WritableObjectPropertyKey<>();
/** Whether or not selection mode is currently active. */
BooleanPropertyKey SELECTION_MODE_ACTIVE = new BooleanPropertyKey();
WritableBooleanPropertyKey SELECTION_MODE_ACTIVE = new WritableBooleanPropertyKey();
PropertyKey[] ALL_KEYS = new PropertyKey[] {ENABLE_ITEM_ANIMATIONS, CALLBACK_OPEN,
CALLBACK_PAUSE, CALLBACK_RESUME, CALLBACK_CANCEL, CALLBACK_SHARE, CALLBACK_REMOVE,
......
......@@ -12,7 +12,7 @@ import android.widget.TextView;
import org.chromium.chrome.browser.download.home.filter.OfflineItemFilterSource;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.download.R;
......@@ -48,7 +48,8 @@ public class StorageCoordinator {
/** The properties needed to render the download home storage summary view. */
private static class StorageProperties {
/** The storage summary text to show in the content area. */
public static final ObjectPropertyKey<String> STORAGE_INFO_TEXT = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> STORAGE_INFO_TEXT =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {STORAGE_INFO_TEXT};
}
......
......@@ -50,16 +50,21 @@ public class LazyConstructionPropertyMcp<M extends PropertyObservable<P>, V exte
mPendingProperties.addAll(mModel.getAllSetProperties());
mViewProvider.whenLoaded(this ::onViewCreated);
mViewProvider.whenLoaded(this::onViewCreated);
// The model should start out hidden.
assert !mVisibilityPredicate.isVisible(mModel);
// The visibility property should be set initially, to avoid spurious property change
// notifications that would cause the view to be inflated prematurely.
assert mPendingProperties.contains(mVisibilityProperty);
mModel.addObserver(this);
}
public static <M extends PropertyModel, V extends View>
LazyConstructionPropertyMcp<M, V, PropertyKey> create(M model,
PropertyModel.BooleanPropertyKey visibilityProperty,
PropertyModel.WritableBooleanPropertyKey visibilityProperty,
ViewProvider<V> viewFactory,
PropertyModelChangeProcessor.ViewBinder<M, V, PropertyKey> viewBinder) {
return new LazyConstructionPropertyMcp<>(model, visibilityProperty,
......
......@@ -18,23 +18,40 @@ import java.util.Map;
* Generic property model that aims to provide an extensible and efficient model for ease of use.
*/
public class PropertyModel extends PropertyObservable<PropertyKey> {
/** The key type for boolean model properties. */
public final static class BooleanPropertyKey implements PropertyKey {}
/** The key type for read-ony boolean model properties. */
public static class ReadableBooleanPropertyKey implements PropertyKey {}
/** The key type for float model properties. */
public static class FloatPropertyKey implements PropertyKey {}
/** The key type for mutable boolean model properties. */
public final static class WritableBooleanPropertyKey extends ReadableBooleanPropertyKey {}
/** The key type for int model properties. */
public static class IntPropertyKey implements PropertyKey {}
/** The key type for read-only float model properties. */
public static class ReadableFloatPropertyKey implements PropertyKey {}
/** The key type for mutable float model properties. */
public final static class WritableFloatPropertyKey extends ReadableFloatPropertyKey {}
/** The key type for read-only int model properties. */
public static class ReadableIntPropertyKey implements PropertyKey {}
/** The key type for mutable int model properties. */
public final static class WritableIntPropertyKey extends ReadableIntPropertyKey {}
/**
* The key type for Object model properties.
* The key type for read-only Object model properties.
*
* @param <T> The type of the Object being tracked by the key.
*/
public static class ObjectPropertyKey<T> implements PropertyKey {}
public static class ReadableObjectPropertyKey<T> implements PropertyKey {}
private final Map<PropertyKey, ValueContainer> mData = new HashMap<>();
/**
* The key type for mutable Object model properties.
*
* @param <T> The type of the Object being tracked by the key.
*/
public final static class WritableObjectPropertyKey<T>
extends ReadableObjectPropertyKey<T> implements PropertyKey {}
private final Map<PropertyKey, ValueContainer> mData;
/**
* Constructs a model for the given list of keys.
......@@ -42,10 +59,11 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
* @param keys The key types supported by this model.
*/
public PropertyModel(PropertyKey... keys) {
for (PropertyKey key : keys) {
if (mData.containsKey(key)) throw new IllegalArgumentException("Duplicate key: " + key);
mData.put(key, null);
}
this(buildData(keys));
}
private PropertyModel(Map<PropertyKey, ValueContainer> startingValues) {
mData = startingValues;
}
@RemovableInRelease
......@@ -58,7 +76,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Get the current value from the float based key.
*/
public float get(FloatPropertyKey key) {
public float get(ReadableFloatPropertyKey key) {
validateKey(key);
FloatContainer container = (FloatContainer) mData.get(key);
return container == null ? 0f : container.value;
......@@ -67,7 +85,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Set the value for the float based key.
*/
public void set(FloatPropertyKey key, float value) {
public void set(WritableFloatPropertyKey key, float value) {
validateKey(key);
FloatContainer container = (FloatContainer) mData.get(key);
if (container == null) {
......@@ -76,6 +94,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
} else if (container.value == value) {
return;
}
container.value = value;
notifyPropertyChanged(key);
}
......@@ -83,7 +102,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Get the current value from the int based key.
*/
public int get(IntPropertyKey key) {
public int get(ReadableIntPropertyKey key) {
validateKey(key);
IntContainer container = (IntContainer) mData.get(key);
return container == null ? 0 : container.value;
......@@ -92,7 +111,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Set the value for the int based key.
*/
public void set(IntPropertyKey key, int value) {
public void set(WritableIntPropertyKey key, int value) {
validateKey(key);
IntContainer container = (IntContainer) mData.get(key);
if (container == null) {
......@@ -101,6 +120,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
} else if (container.value == value) {
return;
}
container.value = value;
notifyPropertyChanged(key);
}
......@@ -108,7 +128,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Get the current value from the boolean based key.
*/
public boolean get(BooleanPropertyKey key) {
public boolean get(ReadableBooleanPropertyKey key) {
validateKey(key);
BooleanContainer container = (BooleanContainer) mData.get(key);
return container == null ? false : container.value;
......@@ -117,7 +137,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
/**
* Set the value for the boolean based key.
*/
public void set(BooleanPropertyKey key, boolean value) {
public void set(WritableBooleanPropertyKey key, boolean value) {
validateKey(key);
BooleanContainer container = (BooleanContainer) mData.get(key);
if (container == null) {
......@@ -126,6 +146,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
} else if (container.value == value) {
return;
}
container.value = value;
notifyPropertyChanged(key);
}
......@@ -134,7 +155,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
* Get the current value from the object based key.
*/
@SuppressWarnings("unchecked")
public <T> T get(ObjectPropertyKey<T> key) {
public <T> T get(ReadableObjectPropertyKey<T> key) {
validateKey(key);
ObjectContainer<T> container = (ObjectContainer<T>) mData.get(key);
return container == null ? null : container.value;
......@@ -144,7 +165,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
* Set the value for the Object based key.
*/
@SuppressWarnings("unchecked")
public <T> void set(ObjectPropertyKey<T> key, T value) {
public <T> void set(WritableObjectPropertyKey<T> key, T value) {
validateKey(key);
ObjectContainer<T> container = (ObjectContainer<T>) mData.get(key);
if (container == null) {
......@@ -153,6 +174,7 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
} else if (ObjectsCompat.equals(container.value, value)) {
return;
}
container.value = value;
notifyPropertyChanged(key);
}
......@@ -166,6 +188,75 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
return properties;
}
/**
* Allows constructing a new {@link PropertyModel} with read-only properties.
*/
public static class Builder {
private final Map<PropertyKey, ValueContainer> mData;
public Builder(PropertyKey... keys) {
this(buildData(keys));
}
private Builder(Map<PropertyKey, ValueContainer> values) {
mData = values;
}
@RemovableInRelease
private void validateKey(PropertyKey key) {
if (!mData.containsKey(key)) {
throw new IllegalArgumentException("Invalid key passed in: " + key);
}
}
public Builder with(ReadableFloatPropertyKey key, float value) {
validateKey(key);
FloatContainer container = new FloatContainer();
container.value = value;
mData.put(key, container);
return this;
}
public Builder with(ReadableIntPropertyKey key, int value) {
validateKey(key);
IntContainer container = new IntContainer();
container.value = value;
mData.put(key, container);
return this;
}
public Builder with(ReadableBooleanPropertyKey key, boolean value) {
validateKey(key);
BooleanContainer container = new BooleanContainer();
container.value = value;
mData.put(key, container);
return this;
}
public <T> Builder with(ReadableObjectPropertyKey<T> key, T value) {
validateKey(key);
ObjectContainer<T> container = new ObjectContainer<>();
container.value = value;
mData.put(key, container);
return this;
}
public PropertyModel build() {
return new PropertyModel(mData);
}
}
private static Map<PropertyKey, ValueContainer> buildData(PropertyKey[] keys) {
Map<PropertyKey, ValueContainer> data = new HashMap<>();
for (PropertyKey key : keys) {
if (data.containsKey(key)) {
throw new IllegalArgumentException("Duplicate key: " + key);
}
data.put(key, null);
}
return data;
}
private interface ValueContainer {}
private static class FloatContainer implements ValueContainer { public float value; }
private static class IntContainer implements ValueContainer { public int value; }
......
......@@ -9,8 +9,8 @@ import android.view.ActionMode;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.WindowDelegate;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.BooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.chrome.browser.omnibox.UrlBar.ScrollType;
import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarDelegate;
import org.chromium.chrome.browser.omnibox.UrlBar.UrlBarTextContextMenuDelegate;
......@@ -77,43 +77,46 @@ class UrlBarProperties {
}
/** The callback for contextual action modes (cut, copy, etc...). */
public static final ObjectPropertyKey<ActionMode.Callback> ACTION_MODE_CALLBACK =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ActionMode.Callback> ACTION_MODE_CALLBACK =
new WritableObjectPropertyKey<>();
/** Whether focus should be allowed on the view. */
public static final BooleanPropertyKey ALLOW_FOCUS = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey ALLOW_FOCUS = new WritableBooleanPropertyKey();
/** Specified the autocomplete text to be shown to the user. */
public static final ObjectPropertyKey<AutocompleteText> AUTOCOMPLETE_TEXT =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<AutocompleteText> AUTOCOMPLETE_TEXT =
new WritableObjectPropertyKey<>();
/** The main delegate that provides additional capabilities to the UrlBar. */
public static final ObjectPropertyKey<UrlBarDelegate> DELEGATE = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<UrlBarDelegate> DELEGATE =
new WritableObjectPropertyKey<>();
/** The callback to be notified on focus changes. */
public static final ObjectPropertyKey<Callback<Boolean>> FOCUS_CHANGE_CALLBACK =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<Callback<Boolean>> FOCUS_CHANGE_CALLBACK =
new WritableObjectPropertyKey<>();
/** Whether the cursor should be shown in the view. */
public static final BooleanPropertyKey SHOW_CURSOR = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey SHOW_CURSOR = new WritableBooleanPropertyKey();
/** Delegate that provides additional functionality to the textual context actions. */
public static final ObjectPropertyKey<UrlBarTextContextMenuDelegate>
TEXT_CONTEXT_MENU_DELEGATE = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<UrlBarTextContextMenuDelegate>
TEXT_CONTEXT_MENU_DELEGATE = new WritableObjectPropertyKey<>();
/** The primary text state for what is shown in the view. */
public static final ObjectPropertyKey<UrlBarTextState> TEXT_STATE = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<UrlBarTextState> TEXT_STATE =
new WritableObjectPropertyKey<>();
/** The listener to be notified of URL direction changes. */
public static final ObjectPropertyKey<UrlDirectionListener> URL_DIRECTION_LISTENER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<UrlDirectionListener> URL_DIRECTION_LISTENER =
new WritableObjectPropertyKey<>();
/** Specifies whether dark text colors should be used in the view. */
public static final BooleanPropertyKey USE_DARK_TEXT_COLORS = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey USE_DARK_TEXT_COLORS =
new WritableBooleanPropertyKey();
/** The delegate that provides Window capabilities to the view. */
public static final ObjectPropertyKey<WindowDelegate> WINDOW_DELEGATE =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<WindowDelegate> WINDOW_DELEGATE =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {ACTION_MODE_CALLBACK, ALLOW_FOCUS, AUTOCOMPLETE_TEXT, DELEGATE,
......
......@@ -13,15 +13,16 @@ import org.chromium.chrome.browser.modelutil.PropertyModel;
class PasswordGenerationDialogModel extends PropertyModel {
/** The generated password to be displayed in the dialog. */
public static final ObjectPropertyKey<String> GENERATED_PASSWORD = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> GENERATED_PASSWORD =
new WritableObjectPropertyKey<>();
/** Explanation text for how the generated password is saved. */
public static final ObjectPropertyKey<String>
SAVE_EXPLANATION_TEXT = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> SAVE_EXPLANATION_TEXT =
new WritableObjectPropertyKey<>();
/** Callback invoked when the password is accepted or rejected by the user. */
public static final ObjectPropertyKey<Callback<Boolean>> PASSWORD_ACTION_CALLBACK =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<Callback<Boolean>> PASSWORD_ACTION_CALLBACK =
new WritableObjectPropertyKey<>();
/** Default constructor */
public PasswordGenerationDialogModel() {
......
......@@ -16,39 +16,42 @@ import org.chromium.ui.resources.ResourceManager;
*/
public class BottomToolbarModel extends PropertyModel {
/** The Y offset of the view in px. */
public static final IntPropertyKey Y_OFFSET = new IntPropertyKey();
public static final WritableIntPropertyKey Y_OFFSET = new WritableIntPropertyKey();
/** Whether the Android view version of the toolbar is visible. */
public static final BooleanPropertyKey ANDROID_VIEW_VISIBLE = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey ANDROID_VIEW_VISIBLE =
new WritableBooleanPropertyKey();
/** Whether the composited version of the toolbar is visible. */
public static final BooleanPropertyKey COMPOSITED_VIEW_VISIBLE = new BooleanPropertyKey();
public static final WritableBooleanPropertyKey COMPOSITED_VIEW_VISIBLE =
new WritableBooleanPropertyKey();
/** A {@link LayoutManager} to attach overlays to. */
public static final ObjectPropertyKey<LayoutManager> LAYOUT_MANAGER = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<LayoutManager> LAYOUT_MANAGER =
new WritableObjectPropertyKey<>();
/** The browser's {@link ToolbarSwipeLayout}. */
public static final ObjectPropertyKey<ToolbarSwipeLayout> TOOLBAR_SWIPE_LAYOUT =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ToolbarSwipeLayout> TOOLBAR_SWIPE_LAYOUT =
new WritableObjectPropertyKey<>();
/** A {@link ResourceManager} for loading textures into the compositor. */
public static final ObjectPropertyKey<ResourceManager> RESOURCE_MANAGER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ResourceManager> RESOURCE_MANAGER =
new WritableObjectPropertyKey<>();
/** A handler for swipe events on the toolbar. */
public static final ObjectPropertyKey<EdgeSwipeHandler> TOOLBAR_SWIPE_HANDLER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<EdgeSwipeHandler> TOOLBAR_SWIPE_HANDLER =
new WritableObjectPropertyKey<>();
/** Data used to show the first button. */
public static final ObjectPropertyKey<ToolbarButtonData> FIRST_BUTTON_DATA =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ToolbarButtonData> FIRST_BUTTON_DATA =
new WritableObjectPropertyKey<>();
/** Data used to show the second button. */
public static final ObjectPropertyKey<ToolbarButtonData> SECOND_BUTTON_DATA =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ToolbarButtonData> SECOND_BUTTON_DATA =
new WritableObjectPropertyKey<>();
/** Primary color of bottom toolbar. */
public static final IntPropertyKey PRIMARY_COLOR = new IntPropertyKey();
public static final WritableIntPropertyKey PRIMARY_COLOR = new WritableIntPropertyKey();
/** Default constructor. */
public BottomToolbarModel() {
......
......@@ -9,26 +9,27 @@ import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
/**
* The properties needed to render the tab switcher button.
*/
public interface TabSwitcherButtonProperties {
/** The current number of tabs. */
public static final IntPropertyKey NUMBER_OF_TABS = new IntPropertyKey();
public static final WritableIntPropertyKey NUMBER_OF_TABS = new WritableIntPropertyKey();
/** The click listener for the tab switcher button. */
public static final ObjectPropertyKey<OnClickListener> ON_CLICK_LISTENER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<OnClickListener> ON_CLICK_LISTENER =
new WritableObjectPropertyKey<>();
/** The long click listener for the tab switcher button. */
public static final ObjectPropertyKey<OnLongClickListener> ON_LONG_CLICK_LISTENER =
new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<OnLongClickListener> ON_LONG_CLICK_LISTENER =
new WritableObjectPropertyKey<>();
/** The button tint. */
public static final ObjectPropertyKey<ColorStateList> TINT = new ObjectPropertyKey<>();
public static final WritableObjectPropertyKey<ColorStateList> TINT =
new WritableObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER, TINT};
......
......@@ -99,13 +99,13 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessoryPagerAdapter.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetMediator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetModel.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetProperties.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryModel.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryProperties.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryView.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java",
......
......@@ -18,6 +18,11 @@ import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.ACTIVE_TAB_INDEX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.HEIGHT;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.NO_ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.VISIBLE;
import static org.chromium.chrome.test.util.ViewUtils.waitForView;
import android.support.test.filters.MediumTest;
......@@ -40,6 +45,8 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.ui.DeferredViewStubInflationProvider;
......@@ -55,7 +62,7 @@ import java.util.concurrent.ExecutionException;
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class AccessorySheetViewTest {
private AccessorySheetModel mModel;
private PropertyModel mModel;
private BlockingQueue<ViewPager> mViewPager;
@Rule
......@@ -68,13 +75,18 @@ public class AccessorySheetViewTest {
ThreadUtils.runOnUiThreadBlocking(() -> {
ViewStub viewStub = mActivityTestRule.getActivity().findViewById(
R.id.keyboard_accessory_sheet_stub);
mModel = new AccessorySheetModel();
mModel.setHeight(mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
org.chromium.chrome.R.dimen.keyboard_accessory_sheet_height));
int height = mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
R.dimen.keyboard_accessory_sheet_height);
mModel = new PropertyModel.Builder(TABS, ACTIVE_TAB_INDEX, VISIBLE, HEIGHT)
.with(HEIGHT, height)
.with(TABS, new ListModel<>())
.with(ACTIVE_TAB_INDEX, NO_ACTIVE_TAB)
.with(VISIBLE, false)
.build();
ViewProvider<ViewPager> provider = new DeferredViewStubInflationProvider<>(viewStub);
mViewPager = new ArrayBlockingQueue<>(1);
new LazyConstructionPropertyMcp<>(mModel, AccessorySheetModel.PropertyKey.VISIBLE,
AccessorySheetModel::isVisible, provider, AccessorySheetViewBinder::bind);
LazyConstructionPropertyMcp.create(
mModel, VISIBLE, provider, AccessorySheetViewBinder::bind);
provider.whenLoaded(mViewPager::add);
});
}
......@@ -87,12 +99,12 @@ public class AccessorySheetViewTest {
assertNull(mViewPager.poll());
// After setting the visibility to true, the view should exist and be visible.
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.setVisible(true); });
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.set(VISIBLE, true); });
ViewPager viewPager = mViewPager.take();
assertEquals(viewPager.getVisibility(), View.VISIBLE);
// After hiding the view, the view should still exist but be invisible.
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.setVisible(false); });
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.set(VISIBLE, false); });
assertNotEquals(viewPager.getVisibility(), View.VISIBLE);
}
......@@ -100,7 +112,7 @@ public class AccessorySheetViewTest {
@MediumTest
public void testAddingTabToModelRendersTabsView() throws InterruptedException {
final String kSampleAction = "Some Action";
mModel.getTabList().add(new Tab(null, null, R.layout.empty_accessory_sheet,
mModel.get(TABS).add(new Tab(null, null, R.layout.empty_accessory_sheet,
AccessoryTabType.PASSWORDS, new Tab.Listener() {
@Override
public void onTabCreated(ViewGroup view) {
......@@ -115,12 +127,12 @@ public class AccessorySheetViewTest {
@Override
public void onTabShown() {}
}));
mModel.setActiveTabIndex(0);
mModel.set(ACTIVE_TAB_INDEX, 0);
// Shouldn't cause the view to be inflated.
assertNull(mViewPager.poll());
// Setting visibility should cause the Tab to be rendered.
ThreadUtils.runOnUiThreadBlocking(() -> mModel.setVisible(true));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true));
assertNotNull(mViewPager.take());
onView(withText(kSampleAction)).check(matches(isDisplayed()));
......@@ -131,14 +143,14 @@ public class AccessorySheetViewTest {
public void testSettingActiveTabIndexChangesTab() {
final String kFirstTab = "First Tab";
final String kSecondTab = "Second Tab";
mModel.getTabList().add(createTestTabWithTextView(kFirstTab));
mModel.getTabList().add(createTestTabWithTextView(kSecondTab));
mModel.setActiveTabIndex(0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.setVisible(true)); // Render view.
mModel.get(TABS).add(createTestTabWithTextView(kFirstTab));
mModel.get(TABS).add(createTestTabWithTextView(kSecondTab));
mModel.set(ACTIVE_TAB_INDEX, 0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true)); // Render view.
onView(withText(kFirstTab)).check(matches(isDisplayed()));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.setActiveTabIndex(1));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.set(ACTIVE_TAB_INDEX, 1));
onView(isRoot()).check((r, e) -> waitForView((ViewGroup) r, withText(kSecondTab)));
}
......@@ -148,15 +160,14 @@ public class AccessorySheetViewTest {
public void testRemovingTabDeletesItsView() {
final String kFirstTab = "First Tab";
final String kSecondTab = "Second Tab";
mModel.getTabList().add(createTestTabWithTextView(kFirstTab));
mModel.getTabList().add(createTestTabWithTextView(kSecondTab));
mModel.setActiveTabIndex(0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.setVisible(true)); // Render view.
mModel.get(TABS).add(createTestTabWithTextView(kFirstTab));
mModel.get(TABS).add(createTestTabWithTextView(kSecondTab));
mModel.set(ACTIVE_TAB_INDEX, 0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true)); // Render view.
onView(withText(kFirstTab)).check(matches(isDisplayed()));
ThreadUtils.runOnUiThreadBlocking(
() -> mModel.getTabList().remove(mModel.getTabList().get(0)));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.get(TABS).remove(mModel.get(TABS).get(0)));
onView(withText(kFirstTab)).check(doesNotExist());
}
......@@ -165,21 +176,21 @@ public class AccessorySheetViewTest {
@MediumTest
public void testReplaceLastTab() {
final String kFirstTab = "First Tab";
mModel.getTabList().add(createTestTabWithTextView(kFirstTab));
mModel.setActiveTabIndex(0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.setVisible(true)); // Render view.
mModel.get(TABS).add(createTestTabWithTextView(kFirstTab));
mModel.set(ACTIVE_TAB_INDEX, 0);
ThreadUtils.runOnUiThreadBlocking(() -> mModel.set(VISIBLE, true)); // Render view.
// Remove the last tab.
onView(withText(kFirstTab)).check(matches(isDisplayed()));
ThreadUtils.runOnUiThreadBlocking(
() -> { mModel.getTabList().remove(mModel.getTabList().get(0)); });
() -> { mModel.get(TABS).remove(mModel.get(TABS).get(0)); });
onView(withText(kFirstTab)).check(doesNotExist());
// Add a new first tab.
final String kSecondTab = "Second Tab";
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.getTabList().add(createTestTabWithTextView(kSecondTab));
mModel.setActiveTabIndex(0);
mModel.get(TABS).add(createTestTabWithTextView(kSecondTab));
mModel.set(ACTIVE_TAB_INDEX, 0);
});
onView(isRoot()).check((r, e) -> waitForView((ViewGroup) r, withText(kSecondTab)));
}
......
......@@ -23,6 +23,12 @@ import static org.junit.Assert.assertTrue;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessoryAction.AUTOFILL_SUGGESTION;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessoryAction.GENERATE_PASSWORD_AUTOMATIC;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIONS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.ACTIVE_TAB;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.BOTTOM_OFFSET_PX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.TAB_SELECTION_CALLBACKS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryProperties.VISIBLE;
import static org.chromium.chrome.test.util.ViewUtils.VIEW_GONE;
import static org.chromium.chrome.test.util.ViewUtils.VIEW_INVISIBLE;
import static org.chromium.chrome.test.util.ViewUtils.VIEW_NULL;
......@@ -46,6 +52,8 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.modelutil.LazyConstructionPropertyMcp;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.ui.DeferredViewStubInflationProvider;
......@@ -63,7 +71,7 @@ import java.util.concurrent.atomic.AtomicReference;
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class KeyboardAccessoryViewTest {
private KeyboardAccessoryModel mModel;
private PropertyModel mModel;
private BlockingQueue<KeyboardAccessoryView> mKeyboardAccessoryView;
@Rule
......@@ -95,16 +103,21 @@ public class KeyboardAccessoryViewTest {
public void setUp() throws InterruptedException {
mActivityTestRule.startMainActivityOnBlankPage();
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel = new KeyboardAccessoryModel();
mModel = new PropertyModel
.Builder(ACTIONS, TABS, VISIBLE, BOTTOM_OFFSET_PX, ACTIVE_TAB,
TAB_SELECTION_CALLBACKS)
.with(TABS, new ListModel<>())
.with(ACTIONS, new ListModel<>())
.with(VISIBLE, false)
.build();
ViewStub viewStub =
mActivityTestRule.getActivity().findViewById(R.id.keyboard_accessory_stub);
mKeyboardAccessoryView = new ArrayBlockingQueue<>(1);
ViewProvider<KeyboardAccessoryView> provider =
new DeferredViewStubInflationProvider<>(viewStub);
new LazyConstructionPropertyMcp<>(mModel,
KeyboardAccessoryModel.PropertyKey.VISIBLE, KeyboardAccessoryModel::isVisible,
provider, KeyboardAccessoryViewBinder::bind);
LazyConstructionPropertyMcp.create(
mModel, VISIBLE, provider, KeyboardAccessoryViewBinder::bind);
provider.whenLoaded(mKeyboardAccessoryView::add);
});
}
......@@ -117,12 +130,12 @@ public class KeyboardAccessoryViewTest {
assertNull(mKeyboardAccessoryView.poll());
// After setting the visibility to true, the view should exist and be visible.
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.setVisible(true); });
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.set(VISIBLE, true); });
KeyboardAccessoryView view = mKeyboardAccessoryView.take();
assertEquals(view.getVisibility(), View.VISIBLE);
// After hiding the view, the view should still exist but be invisible.
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.setVisible(false); });
ThreadUtils.runOnUiThreadBlocking(() -> { mModel.set(VISIBLE, false); });
assertNotEquals(view.getVisibility(), View.VISIBLE);
}
......@@ -134,8 +147,8 @@ public class KeyboardAccessoryViewTest {
"Test Button", GENERATE_PASSWORD_AUTOMATIC, action -> buttonClicked.set(true));
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.setVisible(true);
mModel.getActionList().add(testAction);
mModel.set(VISIBLE, true);
mModel.get(ACTIONS).add(testAction);
});
onView(isRoot()).check((root, e) -> waitForView((ViewGroup) root, withText("Test Button")));
......@@ -148,8 +161,8 @@ public class KeyboardAccessoryViewTest {
@MediumTest
public void testCanAddSingleButtons() {
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.setVisible(true);
mModel.getActionList().set(new KeyboardAccessoryData.Action[] {
mModel.set(VISIBLE, true);
mModel.get(ACTIONS).set(new KeyboardAccessoryData.Action[] {
new KeyboardAccessoryData.Action(
"First", GENERATE_PASSWORD_AUTOMATIC, action -> {}),
new KeyboardAccessoryData.Action("Second", AUTOFILL_SUGGESTION, action -> {})});
......@@ -161,7 +174,7 @@ public class KeyboardAccessoryViewTest {
ThreadUtils.runOnUiThreadBlocking(
()
-> mModel.getActionList().add(new KeyboardAccessoryData.Action(
-> mModel.get(ACTIONS).add(new KeyboardAccessoryData.Action(
"Third", GENERATE_PASSWORD_AUTOMATIC, action -> {})));
onView(isRoot()).check((root, e) -> waitForView((ViewGroup) root, withText("Third")));
......@@ -174,8 +187,8 @@ public class KeyboardAccessoryViewTest {
@MediumTest
public void testCanRemoveSingleButtons() {
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.setVisible(true);
mModel.getActionList().set(new KeyboardAccessoryData.Action[] {
mModel.set(VISIBLE, true);
mModel.get(ACTIONS).set(new KeyboardAccessoryData.Action[] {
new KeyboardAccessoryData.Action(
"First", GENERATE_PASSWORD_AUTOMATIC, action -> {}),
new KeyboardAccessoryData.Action(
......@@ -190,7 +203,7 @@ public class KeyboardAccessoryViewTest {
onView(withText("Third")).check(matches(isDisplayed()));
ThreadUtils.runOnUiThreadBlocking(
() -> mModel.getActionList().remove(mModel.getActionList().get(1)));
() -> mModel.get(ACTIONS).remove(mModel.get(ACTIONS).get(1)));
onView(isRoot()).check((root, e)
-> waitForView((ViewGroup) root, withText("Second"),
......@@ -204,8 +217,8 @@ public class KeyboardAccessoryViewTest {
@MediumTest
public void testRemovesTabs() {
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.setVisible(true);
mModel.getTabList().set(new KeyboardAccessoryData.Tab[] {createTestTab("FirstTab"),
mModel.set(VISIBLE, true);
mModel.get(TABS).set(new KeyboardAccessoryData.Tab[] {createTestTab("FirstTab"),
createTestTab("SecondTab"), createTestTab("ThirdTab")});
});
......@@ -215,8 +228,7 @@ public class KeyboardAccessoryViewTest {
onView(isTabWithDescription("SecondTab")).check(matches(isDisplayed()));
onView(isTabWithDescription("ThirdTab")).check(matches(isDisplayed()));
ThreadUtils.runOnUiThreadBlocking(
() -> mModel.getTabList().remove(mModel.getTabList().get(1)));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.get(TABS).remove(mModel.get(TABS).get(1)));
onView(isRoot()).check(
(root, e)
......@@ -231,8 +243,8 @@ public class KeyboardAccessoryViewTest {
@MediumTest
public void testAddsTabs() {
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.setVisible(true);
mModel.getTabList().set(new KeyboardAccessoryData.Tab[] {
mModel.set(VISIBLE, true);
mModel.get(TABS).set(new KeyboardAccessoryData.Tab[] {
createTestTab("FirstTab"), createTestTab("SecondTab")});
});
......@@ -242,7 +254,7 @@ public class KeyboardAccessoryViewTest {
onView(isTabWithDescription("SecondTab")).check(matches(isDisplayed()));
onView(isTabWithDescription("ThirdTab")).check(doesNotExist());
ThreadUtils.runOnUiThreadBlocking(() -> mModel.getTabList().add(createTestTab("ThirdTab")));
ThreadUtils.runOnUiThreadBlocking(() -> mModel.get(TABS).add(createTestTab("ThirdTab")));
onView(isRoot()).check(
(root, e) -> waitForView((ViewGroup) root, isTabWithDescription("ThirdTab")));
......
......@@ -12,6 +12,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.ACTIVE_TAB_INDEX;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.HEIGHT;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.TABS;
import static org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetProperties.VISIBLE;
import android.support.v4.view.ViewPager;
import android.view.ViewGroup;
......@@ -26,9 +31,10 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.test.ShadowRecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.asynctask.CustomShadowAsyncTask;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetModel.PropertyKey;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Tab;
import org.chromium.chrome.browser.modelutil.ListObservable;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyObservable;
import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
......@@ -52,7 +58,7 @@ public class AccessorySheetControllerTest {
private AccessorySheetCoordinator mCoordinator;
private AccessorySheetMediator mMediator;
private AccessorySheetModel mModel;
private PropertyModel mModel;
@Before
public void setUp() {
......@@ -78,18 +84,19 @@ public class AccessorySheetControllerTest {
// Calling show on the mediator should make model propagate that it's visible.
mMediator.show();
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.VISIBLE);
assertThat(mModel.isVisible(), is(true));
verify(mMockPropertyObserver).onPropertyChanged(mModel, VISIBLE);
assertThat(mModel.get(VISIBLE), is(true));
// Calling show again does nothing.
mMediator.show();
verify(mMockPropertyObserver) // Still the same call and no new one added.
.onPropertyChanged(mModel, PropertyKey.VISIBLE);
.onPropertyChanged(mModel, VISIBLE);
// Calling hide on the mediator should make model propagate that it's invisible.
mMediator.hide();
verify(mMockPropertyObserver, times(2)).onPropertyChanged(mModel, PropertyKey.VISIBLE);
assertThat(mModel.isVisible(), is(false));
verify(mMockPropertyObserver, times(2)).onPropertyChanged(mModel, VISIBLE);
assertThat(mModel.get(VISIBLE), is(false));
}
@Test
......@@ -98,28 +105,29 @@ public class AccessorySheetControllerTest {
// Setting height triggers the observer and changes the model.
mCoordinator.setHeight(123);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.HEIGHT);
assertThat(mModel.getHeight(), is(123));
verify(mMockPropertyObserver).onPropertyChanged(mModel, HEIGHT);
assertThat(mModel.get(HEIGHT), is(123));
// Setting the same height doesn't trigger anything.
mCoordinator.setHeight(123);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.HEIGHT); // No 2nd call.
verify(mMockPropertyObserver).onPropertyChanged(mModel, HEIGHT); // No 2nd call.
// Setting a different height triggers again.
mCoordinator.setHeight(234);
verify(mMockPropertyObserver, times(2)).onPropertyChanged(mModel, PropertyKey.HEIGHT);
assertThat(mModel.getHeight(), is(234));
verify(mMockPropertyObserver, times(2)).onPropertyChanged(mModel, HEIGHT);
assertThat(mModel.get(HEIGHT), is(234));
}
@Test
public void testModelNotifiesChangesForNewSheet() {
mModel.addObserver(mMockPropertyObserver);
mModel.getTabList().addObserver(mTabListObserver);
mModel.get(TABS).addObserver(mTabListObserver);
assertThat(mModel.getTabList().size(), is(0));
assertThat(mModel.get(TABS).size(), is(0));
mCoordinator.addTab(mTabs[0]);
verify(mTabListObserver).onItemRangeInserted(mModel.getTabList(), 0, 1);
assertThat(mModel.getTabList().size(), is(1));
verify(mTabListObserver).onItemRangeInserted(mModel.get(TABS), 0, 1);
assertThat(mModel.get(TABS).size(), is(1));
}
@Test
......@@ -127,21 +135,21 @@ public class AccessorySheetControllerTest {
mModel.addObserver(mMockPropertyObserver);
// Initially, there is no active Tab.
assertThat(mModel.getTabList().size(), is(0));
assertThat(mModel.get(TABS).size(), is(0));
assertThat(mCoordinator.getTab(), is(nullValue()));
// The first tab becomes the active Tab.
mCoordinator.addTab(mTabs[0]);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.ACTIVE_TAB_INDEX);
assertThat(mModel.getTabList().size(), is(1));
assertThat(mModel.getActiveTabIndex(), is(0));
verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB_INDEX);
assertThat(mModel.get(TABS).size(), is(1));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(0));
assertThat(mCoordinator.getTab(), is(mTabs[0]));
// A second tab is added but doesn't become automatically active.
mCoordinator.addTab(mTabs[1]);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.ACTIVE_TAB_INDEX);
assertThat(mModel.getTabList().size(), is(2));
assertThat(mModel.getActiveTabIndex(), is(0));
verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB_INDEX);
assertThat(mModel.get(TABS).size(), is(2));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(0));
}
@Test
......@@ -150,13 +158,13 @@ public class AccessorySheetControllerTest {
mCoordinator.addTab(mTabs[1]);
mCoordinator.addTab(mTabs[2]);
mCoordinator.addTab(mTabs[3]);
assertThat(mModel.getTabList().size(), is(4));
assertThat(mModel.getActiveTabIndex(), is(0));
assertThat(mModel.get(TABS).size(), is(4));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(0));
mCoordinator.removeTab(mTabs[0]);
assertThat(mModel.getTabList().size(), is(3));
assertThat(mModel.getActiveTabIndex(), is(0));
assertThat(mModel.get(TABS).size(), is(3));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(0));
}
@Test
......@@ -164,8 +172,8 @@ public class AccessorySheetControllerTest {
mCoordinator.addTab(mTabs[0]);
mCoordinator.removeTab(mTabs[0]);
assertThat(mModel.getTabList().size(), is(0));
assertThat(mModel.getActiveTabIndex(), is(AccessorySheetModel.NO_ACTIVE_TAB));
assertThat(mModel.get(TABS).size(), is(0));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(AccessorySheetProperties.NO_ACTIVE_TAB));
}
@Test
......@@ -174,14 +182,14 @@ public class AccessorySheetControllerTest {
mCoordinator.addTab(mTabs[1]);
mCoordinator.addTab(mTabs[2]);
mCoordinator.addTab(mTabs[3]);
mModel.setActiveTabIndex(2);
mModel.set(ACTIVE_TAB_INDEX, 2);
mModel.addObserver(mMockPropertyObserver);
mCoordinator.removeTab(mTabs[2]);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.ACTIVE_TAB_INDEX);
assertThat(mModel.getTabList().size(), is(3));
assertThat(mModel.getActiveTabIndex(), is(1));
verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB_INDEX);
assertThat(mModel.get(TABS).size(), is(3));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(1));
}
@Test
......@@ -190,14 +198,14 @@ public class AccessorySheetControllerTest {
mCoordinator.addTab(mTabs[1]);
mCoordinator.addTab(mTabs[2]);
mCoordinator.addTab(mTabs[3]);
mModel.setActiveTabIndex(2);
mModel.set(ACTIVE_TAB_INDEX, 2);
mModel.addObserver(mMockPropertyObserver);
mCoordinator.removeTab(mTabs[1]);
verify(mMockPropertyObserver).onPropertyChanged(mModel, PropertyKey.ACTIVE_TAB_INDEX);
assertThat(mModel.getTabList().size(), is(3));
assertThat(mModel.getActiveTabIndex(), is(1));
verify(mMockPropertyObserver).onPropertyChanged(mModel, ACTIVE_TAB_INDEX);
assertThat(mModel.get(TABS).size(), is(3));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(1));
}
@Test
......@@ -206,12 +214,12 @@ public class AccessorySheetControllerTest {
mCoordinator.addTab(mTabs[1]);
mCoordinator.addTab(mTabs[2]);
mCoordinator.addTab(mTabs[3]);
mModel.setActiveTabIndex(2);
mModel.set(ACTIVE_TAB_INDEX, 2);
mCoordinator.removeTab(mTabs[3]);
assertThat(mModel.getTabList().size(), is(3));
assertThat(mModel.getActiveTabIndex(), is(2));
assertThat(mModel.get(TABS).size(), is(3));
assertThat(mModel.get(ACTIVE_TAB_INDEX), is(2));
}
@Test
......
......@@ -27,9 +27,9 @@ import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.modelutil.PropertyModel.BooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor.ViewBinder;
import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
......@@ -39,9 +39,10 @@ import org.chromium.chrome.test.util.browser.modelutil.FakeViewProvider;
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class LazyConstructionPropertyMcpTest {
private static final BooleanPropertyKey VISIBILITY = new BooleanPropertyKey();
private static final ObjectPropertyKey<String> STRING_PROPERTY = new ObjectPropertyKey<>();
private static final IntPropertyKey INT_PROPERTY = new IntPropertyKey();
private static final WritableBooleanPropertyKey VISIBILITY = new WritableBooleanPropertyKey();
private static final WritableObjectPropertyKey<String> STRING_PROPERTY =
new WritableObjectPropertyKey<>();
private static final WritableIntPropertyKey INT_PROPERTY = new WritableIntPropertyKey();
private static final PropertyKey[] ALL_PROPERTIES =
new PropertyKey[] {VISIBILITY, STRING_PROPERTY, INT_PROPERTY};
private PropertyModel mModel;
......
......@@ -17,10 +17,10 @@ import org.mockito.Mockito;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.modelutil.PropertyModel.BooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.FloatPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.IntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableFloatPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableIntPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyObservable.PropertyObserver;
import java.util.ArrayList;
......@@ -36,21 +36,24 @@ public class PropertyModelTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
public static BooleanPropertyKey BOOLEAN_PROPERTY_A = new BooleanPropertyKey();
public static BooleanPropertyKey BOOLEAN_PROPERTY_B = new BooleanPropertyKey();
public static BooleanPropertyKey BOOLEAN_PROPERTY_C = new BooleanPropertyKey();
public static WritableBooleanPropertyKey BOOLEAN_PROPERTY_A = new WritableBooleanPropertyKey();
public static WritableBooleanPropertyKey BOOLEAN_PROPERTY_B = new WritableBooleanPropertyKey();
public static WritableBooleanPropertyKey BOOLEAN_PROPERTY_C = new WritableBooleanPropertyKey();
public static FloatPropertyKey FLOAT_PROPERTY_A = new FloatPropertyKey();
public static FloatPropertyKey FLOAT_PROPERTY_B = new FloatPropertyKey();
public static FloatPropertyKey FLOAT_PROPERTY_C = new FloatPropertyKey();
public static WritableFloatPropertyKey FLOAT_PROPERTY_A = new WritableFloatPropertyKey();
public static WritableFloatPropertyKey FLOAT_PROPERTY_B = new WritableFloatPropertyKey();
public static WritableFloatPropertyKey FLOAT_PROPERTY_C = new WritableFloatPropertyKey();
public static IntPropertyKey INT_PROPERTY_A = new IntPropertyKey();
public static IntPropertyKey INT_PROPERTY_B = new IntPropertyKey();
public static IntPropertyKey INT_PROPERTY_C = new IntPropertyKey();
public static WritableIntPropertyKey INT_PROPERTY_A = new WritableIntPropertyKey();
public static WritableIntPropertyKey INT_PROPERTY_B = new WritableIntPropertyKey();
public static WritableIntPropertyKey INT_PROPERTY_C = new WritableIntPropertyKey();
public static ObjectPropertyKey<Object> OBJECT_PROPERTY_A = new ObjectPropertyKey<>();
public static ObjectPropertyKey<String> OBJECT_PROPERTY_B = new ObjectPropertyKey<>();
public static ObjectPropertyKey<List<Integer>> OBJECT_PROPERTY_C = new ObjectPropertyKey<>();
public static WritableObjectPropertyKey<Object> OBJECT_PROPERTY_A =
new WritableObjectPropertyKey<>();
public static WritableObjectPropertyKey<String> OBJECT_PROPERTY_B =
new WritableObjectPropertyKey<>();
public static WritableObjectPropertyKey<List<Integer>> OBJECT_PROPERTY_C =
new WritableObjectPropertyKey<>();
@Test
public void getAllSetProperties() {
......@@ -73,7 +76,8 @@ public class PropertyModelTest {
verifyBooleanUpdate(model, BOOLEAN_PROPERTY_B, false);
}
private void verifyBooleanUpdate(PropertyModel model, BooleanPropertyKey key, boolean value) {
private void verifyBooleanUpdate(
PropertyModel model, WritableBooleanPropertyKey key, boolean value) {
@SuppressWarnings("unchecked")
PropertyObserver<PropertyKey> observer = Mockito.mock(PropertyObserver.class);
model.addObserver(observer);
......@@ -102,7 +106,7 @@ public class PropertyModelTest {
verifyFloatUpdate(model, FLOAT_PROPERTY_A, Float.MAX_VALUE);
}
private void verifyFloatUpdate(PropertyModel model, FloatPropertyKey key, float value) {
private void verifyFloatUpdate(PropertyModel model, WritableFloatPropertyKey key, float value) {
@SuppressWarnings("unchecked")
PropertyObserver<PropertyKey> observer = Mockito.mock(PropertyObserver.class);
model.addObserver(observer);
......@@ -127,7 +131,7 @@ public class PropertyModelTest {
verifyIntUpdate(model, INT_PROPERTY_A, Integer.MIN_VALUE);
}
private void verifyIntUpdate(PropertyModel model, IntPropertyKey key, int value) {
private void verifyIntUpdate(PropertyModel model, WritableIntPropertyKey key, int value) {
@SuppressWarnings("unchecked")
PropertyObserver<PropertyKey> observer = Mockito.mock(PropertyObserver.class);
model.addObserver(observer);
......@@ -163,7 +167,8 @@ public class PropertyModelTest {
verifyObjectUpdate(model, OBJECT_PROPERTY_C, list);
}
private <T> void verifyObjectUpdate(PropertyModel model, ObjectPropertyKey<T> key, T value) {
private <T> void verifyObjectUpdate(
PropertyModel model, WritableObjectPropertyKey<T> key, T value) {
@SuppressWarnings("unchecked")
PropertyObserver<PropertyKey> observer = Mockito.mock(PropertyObserver.class);
model.addObserver(observer);
......
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