Commit 9adcf511 authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[MFill Android] Create view binder for modern accessory sheet

This CL adds and connects the new view binder which creates chips and
footer commands based on the AccessorySheetTab data.

In order to make the "ripple effect" work for chips, the drawable draws
a large rounded rectangle in the background color over the animated
image which effectively cuts and rounds the corners.

Bug: 853772, 911084
Change-Id: If2740994aa752e11233c90eca062b529b8ea36ce
Reviewed-on: https://chromium-review.googlesource.com/c/1367650
Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#619959}
parent 799e2b3d
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_vertical|start"
android:fillViewport="true"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
android:orientation="vertical">
<LinearLayout
android:gravity="center_vertical|start"
android:fillViewport="true"
android:layout_height="@dimen/keyboard_accessory_suggestion_height"
android:paddingTop="@dimen/keyboard_accessory_sheet_padding"
android:paddingBottom="@dimen/keyboard_accessory_sheet_padding"
android:layout_width="match_parent"
android:orientation="horizontal">
<org.chromium.ui.widget.ChipView
android:id="@+id/suggestion_text"
android:gravity="center_vertical|start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/InputChip" />
<Space
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_width="0dp" />
<ImageView
android:id="@+id/favicon"
android:layout_width="@dimen/keyboard_accessory_suggestion_icon_size"
android:layout_height="@dimen/keyboard_accessory_suggestion_icon_size"
android:importantForAccessibility="no"
android:layout_gravity="center"/>
</LinearLayout>
<org.chromium.ui.widget.ChipView
android:id="@+id/password_text"
android:gravity="center_vertical|start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
style="@style/InputChip" />
<View style="@style/HorizontalDivider"
android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding" />
</org.chromium.chrome.browser.autofill.keyboard_accessory.PasswordAccessoryInfoView>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_vertical|start"
android:fillViewport="true"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="0dp"
android:orientation="vertical">
<View style="@style/HorizontalDivider"
android:layout_marginTop="0dp"
android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding" />
<org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/tab_title"
android:paddingStart="@dimen/keyboard_accessory_suggestion_padding"
android:paddingEnd="@dimen/keyboard_accessory_suggestion_padding"
android:paddingTop="@dimen/keyboard_accessory_suggestion_offset"
android:paddingBottom="@dimen/keyboard_accessory_suggestion_offset"
android:gravity="center_vertical|start"
android:textAppearance="@style/TextAppearance.BlackHint1"
android:minHeight="@dimen/keyboard_accessory_height"
app:leading="@dimen/text_size_large_leading"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<View style="@style/HorizontalDivider"
android:id="@+id/title_divider"
android:layout_marginTop="@dimen/keyboard_accessory_sheet_padding"
android:layout_marginBottom="@dimen/keyboard_accessory_sheet_padding"
android:layout_marginStart="@dimen/keyboard_accessory_suggestion_padding"
android:layout_marginEnd="@dimen/keyboard_accessory_suggestion_padding" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:fillViewport="true"
android:minHeight="48dp"
android:gravity="center_vertical|start"
android:textAppearance="@style/TextAppearance.BlackTitle1"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="?android:attr/selectableItemBackground"/>
...@@ -10,7 +10,10 @@ import android.support.v7.widget.RecyclerView; ...@@ -10,7 +10,10 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece; import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
import org.chromium.chrome.browser.modelutil.ListModel; import org.chromium.chrome.browser.modelutil.ListModel;
...@@ -35,6 +38,56 @@ class AccessorySheetTabViewBinder { ...@@ -35,6 +38,56 @@ class AccessorySheetTabViewBinder {
void bind(T t, V view) {} void bind(T t, V view) {}
} }
/**
* Creates an {@link ElementViewHolder} for the given |viewType|.
* @param parent A {@link android.view.ViewParent} to attach this view to.
* @param viewType A {@link AccessorySheetDataPiece.Type} describing the view to be created.
* @return A {@link ElementViewHolder}.
*/
static ElementViewHolder create(ViewGroup parent, @AccessorySheetDataPiece.Type int viewType) {
switch (viewType) {
case AccessorySheetDataPiece.Type.TITLE:
return new TitleViewHolder(parent);
case AccessorySheetDataPiece.Type.FOOTER_COMMAND:
return new FooterCommandViewHolder(parent);
}
assert false : "Unhandled type of data piece: " + viewType;
return null;
}
/**
* Holds a Title consisting of a top divider, a text view and a bottom divider.
*/
static class TitleViewHolder extends ElementViewHolder<String, LinearLayout> {
TitleViewHolder(ViewGroup parent) {
super(parent, R.layout.keyboard_accessory_sheet_tab_title);
}
@Override
protected void bind(String displayText, LinearLayout view) {
TextView titleView = view.findViewById(R.id.tab_title);
titleView.setText(displayText);
titleView.setContentDescription(displayText);
}
}
/**
* Holds a clickable {@link TextView} that represents a footer command.
*/
static class FooterCommandViewHolder
extends ElementViewHolder<KeyboardAccessoryData.FooterCommand, TextView> {
FooterCommandViewHolder(ViewGroup parent) {
super(parent, R.layout.password_accessory_sheet_option);
}
@Override
protected void bind(KeyboardAccessoryData.FooterCommand footerCommand, TextView view) {
view.setText(footerCommand.getDisplayText());
view.setContentDescription(footerCommand.getDisplayText());
view.setOnClickListener(v -> footerCommand.execute());
}
}
static void initializeView( static void initializeView(
RecyclerView view, @Nullable RecyclerView.OnScrollListener scrollListener) { RecyclerView view, @Nullable RecyclerView.OnScrollListener scrollListener) {
view.setLayoutManager( view.setLayoutManager(
......
// 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.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v7.content.res.AppCompatResources;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.chromium.chrome.R;
import org.chromium.ui.widget.ChipView;
/**
* This view represents a section of user credentials in the password tab of the keyboard accessory.
*/
class PasswordAccessoryInfoView extends LinearLayout {
private ImageView mIcon;
private ChipView mUsername;
private ChipView mPassword;
/**
* Constructor for inflating from XML.
*/
public PasswordAccessoryInfoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mIcon = findViewById(R.id.favicon);
mUsername = findViewById(R.id.suggestion_text);
mPassword = findViewById(R.id.password_text);
}
void setIconForBitmap(@Nullable Bitmap favicon) {
Drawable icon;
if (favicon == null) {
icon = AppCompatResources.getDrawable(getContext(), R.drawable.ic_globe_36dp);
} else {
icon = new BitmapDrawable(getContext().getResources(), favicon);
}
final int kIconSize = getContext().getResources().getDimensionPixelSize(
R.dimen.keyboard_accessory_suggestion_icon_size);
icon.setBounds(0, 0, kIconSize, kIconSize);
mIcon.setImageDrawable(icon);
}
ChipView getUsername() {
return mUsername;
}
ChipView getPassword() {
return mPassword;
}
}
\ No newline at end of file
...@@ -13,6 +13,7 @@ import android.view.ViewGroup; ...@@ -13,6 +13,7 @@ import android.view.ViewGroup;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece; import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.AccessorySheetData;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider; import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
...@@ -72,7 +73,11 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina ...@@ -72,7 +73,11 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina
@Override @Override
public void onTabCreated(ViewGroup view) { public void onTabCreated(ViewGroup view) {
super.onTabCreated(view); super.onTabCreated(view);
PasswordAccessorySheetViewBinder.initializeView((RecyclerView) view, mModel); if (ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_KEYBOARD_ACCESSORY)) {
PasswordAccessorySheetModernViewBinder.initializeView((RecyclerView) view, mModel);
} else {
PasswordAccessorySheetViewBinder.initializeView((RecyclerView) view, mModel);
}
} }
@Override @Override
...@@ -91,8 +96,8 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina ...@@ -91,8 +96,8 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina
/** /**
* Creates an adapter to an {@link PasswordAccessorySheetViewBinder} that is wired * Creates an adapter to an {@link PasswordAccessorySheetViewBinder} that is wired
* up to a model change processor listening to the {@link ListModel <AccessorySheetDataPiece>}. * up to a model change processor listening to the {@link AccessorySheetTabModel}.
* @param model the {@link ListModel <Item>} the adapter gets its data from. * @param model the {@link AccessorySheetTabModel} the adapter gets its data from.
* @return Returns a fully initialized and wired adapter to a PasswordAccessorySheetViewBinder. * @return Returns a fully initialized and wired adapter to a PasswordAccessorySheetViewBinder.
*/ */
static RecyclerViewAdapter<AccessorySheetTabViewBinder.ElementViewHolder, Void> createAdapter( static RecyclerViewAdapter<AccessorySheetTabViewBinder.ElementViewHolder, Void> createAdapter(
...@@ -103,6 +108,20 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina ...@@ -103,6 +108,20 @@ public class PasswordAccessorySheetCoordinator extends AccessorySheetTabCoordina
PasswordAccessorySheetViewBinder::create); PasswordAccessorySheetViewBinder::create);
} }
/**
* Creates an adapter to an {@link PasswordAccessorySheetModernViewBinder} that is wired up to
* the model change processor which listens to the {@link AccessorySheetTabModel}.
* @param model the {@link AccessorySheetTabModel} the adapter gets its data from.
* @return Returns an {@link PasswordAccessorySheetModernViewBinder} wired to a MCP.
*/
static RecyclerViewAdapter<AccessorySheetTabViewBinder.ElementViewHolder, Void>
createModernAdapter(ListModel<AccessorySheetDataPiece> model) {
return new RecyclerViewAdapter<>(
new SimpleRecyclerViewMcp<>(model, AccessorySheetDataPiece::getType,
AccessorySheetTabViewBinder.ElementViewHolder::bind),
PasswordAccessorySheetModernViewBinder::create);
}
@VisibleForTesting @VisibleForTesting
AccessorySheetTabModel getSheetDataPiecesForTesting() { AccessorySheetTabModel getSheetDataPiecesForTesting() {
return mModel; return mModel;
......
// 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.v7.widget.RecyclerView;
import android.text.method.PasswordTransformationMethod;
import android.view.ViewGroup;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabViewBinder.ElementViewHolder;
import org.chromium.chrome.browser.modelutil.ListModel;
import org.chromium.ui.widget.ChipView;
/**
* This stateless class provides methods to bind a {@link ListModel<AccessorySheetDataPiece>}
* to the {@link RecyclerView} used as view of a tab for the accessory sheet component.
*/
class PasswordAccessorySheetModernViewBinder {
static ElementViewHolder create(ViewGroup parent, @AccessorySheetDataPiece.Type int viewType) {
switch (viewType) {
case AccessorySheetDataPiece.Type.PASSWORD_INFO:
return new PasswordInfoViewHolder(parent);
case AccessorySheetDataPiece.Type.TITLE: // Intentional fallthrough.
case AccessorySheetDataPiece.Type.FOOTER_COMMAND: // Intentional fallthrough.
return AccessorySheetTabViewBinder.create(parent, viewType);
}
assert false : "Unhandled type of data piece: " + viewType;
return null;
}
/**
* Holds a TextView that represents a list entry.
*/
static class PasswordInfoViewHolder
extends ElementViewHolder<KeyboardAccessoryData.UserInfo, PasswordAccessoryInfoView> {
PasswordInfoViewHolder(ViewGroup parent) {
super(parent, R.layout.keyboard_accessory_sheet_tab_password_info);
}
@Override
protected void bind(KeyboardAccessoryData.UserInfo info, PasswordAccessoryInfoView view) {
bindChipView(view.getUsername(), info.getFields().get(0));
bindChipView(view.getPassword(), info.getFields().get(1));
view.setIconForBitmap(null); // Set the default icon, then try to get a better one.
if (info.getFaviconProvider() != null) {
info.getFaviconProvider().fetchFavicon(
itemView.getContext().getResources().getDimensionPixelSize(
R.dimen.keyboard_accessory_suggestion_icon_size),
view::setIconForBitmap);
}
}
void bindChipView(ChipView chip, KeyboardAccessoryData.UserInfo.Field field) {
chip.getInnerTextView().setTransformationMethod(
field.isObfuscated() ? new PasswordTransformationMethod() : null);
chip.getInnerTextView().setText(field.getDisplayText());
chip.getInnerTextView().setContentDescription(field.getA11yDescription());
chip.setOnClickListener(!field.isSelectable() ? null : src -> field.triggerSelection());
chip.setClickable(field.isSelectable());
chip.setEnabled(field.isSelectable());
}
}
static void initializeView(RecyclerView view, AccessorySheetTabModel model) {
view.setAdapter(PasswordAccessorySheetCoordinator.createModernAdapter(model));
}
}
\ No newline at end of file
...@@ -127,9 +127,11 @@ chrome_java_sources = [ ...@@ -127,9 +127,11 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingBridge.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryInfoView.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java", "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AbstractAutofillAssistantUiController.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AbstractAutofillAssistantUiController.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java", "java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java",
...@@ -1910,6 +1912,7 @@ chrome_test_java_sources = [ ...@@ -1910,6 +1912,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java", "javatests/src/org/chromium/chrome/browser/autofill/AutofillTestHelper.java",
"javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java", "javatests/src/org/chromium/chrome/browser/autofill/PersonalDataManagerTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/FakeKeyboard.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/FakeKeyboard.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryViewTest.java",
...@@ -1918,6 +1921,7 @@ chrome_test_java_sources = [ ...@@ -1918,6 +1921,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingUiCaptureTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingUiCaptureTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java", "javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetModernViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java", "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java",
"javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java", "javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java",
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java", "javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java",
......
// 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 static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import android.support.annotation.LayoutRes;
import android.support.test.filters.MediumTest;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece.Type;
import org.chromium.chrome.browser.modelutil.RecyclerViewAdapter;
import org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcp;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.ui.DeferredViewStubInflationProvider;
import org.chromium.ui.widget.TextViewWithLeading;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
/**
* View tests for the password accessory sheet.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class AccessorySheetTabViewTest {
private final AccessorySheetTabModel mModel = new AccessorySheetTabModel();
private AtomicReference<RecyclerView> mView = new AtomicReference<>();
@Rule
public ChromeActivityTestRule<ChromeTabbedActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeTabbedActivity.class);
/**
* This helper method inflates the accessory sheet and loads the given layout as minimalistic
* Tab. The passed callback then allows access to the inflated layout.
* @param layout The layout to be inflated.
* @param listener Is called with the inflated layout when the Accessory Sheet initializes it.
*/
private void openLayoutInAccessorySheet(
@LayoutRes int layout, KeyboardAccessoryData.Tab.Listener listener) {
ThreadUtils.runOnUiThreadBlocking(() -> {
AccessorySheetCoordinator accessorySheet =
new AccessorySheetCoordinator(new DeferredViewStubInflationProvider<>(
mActivityTestRule.getActivity().findViewById(
R.id.keyboard_accessory_sheet_stub)));
accessorySheet.addTab(new KeyboardAccessoryData.Tab(
null, null, layout, AccessoryTabType.ALL, listener));
accessorySheet.setHeight(
mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
R.dimen.keyboard_accessory_sheet_height));
accessorySheet.show();
});
}
@Before
public void setUp() throws InterruptedException {
mActivityTestRule.startMainActivityOnBlankPage();
openLayoutInAccessorySheet(
R.layout.password_accessory_sheet, new KeyboardAccessoryData.Tab.Listener() {
@Override
public void onTabCreated(ViewGroup view) {
mView.set((RecyclerView) view);
AccessorySheetTabViewBinder.initializeView(mView.get(), null);
((RecyclerView) view)
.setAdapter(new RecyclerViewAdapter<>(
new SimpleRecyclerViewMcp<>(mModel,
AccessorySheetDataPiece::getType,
AccessorySheetTabViewBinder
.ElementViewHolder::bind),
AccessorySheetTabViewBinder::create));
}
@Override
public void onTabShown() {}
});
CriteriaHelper.pollUiThread(Criteria.equals(true, () -> mView.get() != null));
}
@After
public void tearDown() {
mView.set(null);
}
@Test
@MediumTest
public void testAddingATitleToTheModelRendersIt() {
assertThat(mView.get().getChildCount(), is(0));
ThreadUtils.runOnUiThreadBlocking(
() -> { mModel.add(new AccessorySheetDataPiece("Passwords", Type.TITLE)); });
CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
assertThat(layout.getChildCount(), is(3));
assertThat(layout.getChildAt(0), instanceOf(View.class)); // The top divider.
assertThat(layout.getChildAt(1), instanceOf(TextViewWithLeading.class));
assertThat(layout.getChildAt(2), instanceOf(View.class)); // Divider to commands.
assertThat(((TextView) layout.getChildAt(1)).getText(), is("Passwords"));
}
@Test
@MediumTest
public void testAddingFooterCommandToTheModelRendersButton() throws ExecutionException {
final AtomicReference<Boolean> clicked = new AtomicReference<>(false);
assertThat(mView.get().getChildCount(), is(0));
ManualFillingTestHelper.createTestCredentials();
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.add(new AccessorySheetDataPiece(
new KeyboardAccessoryData.FooterCommand(
"Manage passwords", item -> clicked.set(true)),
Type.FOOTER_COMMAND));
});
CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
assertThat(mView.get().getChildAt(0), instanceOf(TextView.class));
TextView btn = (TextView) mView.get().getChildAt(0);
assertThat(btn.getText(), is("Manage passwords"));
ThreadUtils.runOnUiThreadBlocking(btn::performClick);
assertThat(clicked.get(), is(true));
}
}
\ 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 static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import android.support.test.filters.MediumTest;
import android.support.v7.widget.RecyclerView;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.ChromeTabbedActivity;
import org.chromium.chrome.browser.autofill.keyboard_accessory.AccessorySheetTabModel.AccessorySheetDataPiece;
import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.UserInfo;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.ui.DeferredViewStubInflationProvider;
import org.chromium.ui.widget.ChipView;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;
/**
* View tests for the password accessory sheet.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class PasswordAccessorySheetModernViewTest {
private final AccessorySheetTabModel mModel = new AccessorySheetTabModel();
private AtomicReference<RecyclerView> mView = new AtomicReference<>();
@Rule
public ChromeActivityTestRule<ChromeTabbedActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeTabbedActivity.class);
@Before
public void setUp() throws InterruptedException {
mActivityTestRule.startMainActivityOnBlankPage();
ThreadUtils.runOnUiThreadBlocking(() -> {
AccessorySheetCoordinator accessorySheet =
new AccessorySheetCoordinator(new DeferredViewStubInflationProvider<>(
mActivityTestRule.getActivity().findViewById(
R.id.keyboard_accessory_sheet_stub)));
accessorySheet.addTab(
new KeyboardAccessoryData.Tab(null, null, R.layout.password_accessory_sheet,
AccessoryTabType.ALL, new KeyboardAccessoryData.Tab.Listener() {
@Override
public void onTabCreated(ViewGroup view) {
mView.set((RecyclerView) view);
AccessorySheetTabViewBinder.initializeView(mView.get(), null);
PasswordAccessorySheetModernViewBinder.initializeView(
mView.get(), mModel);
}
@Override
public void onTabShown() {}
}));
accessorySheet.setHeight(
mActivityTestRule.getActivity().getResources().getDimensionPixelSize(
R.dimen.keyboard_accessory_sheet_height));
accessorySheet.show();
});
CriteriaHelper.pollUiThread(Criteria.equals(true, () -> mView.get() != null));
}
@After
public void tearDown() {
mView.set(null);
}
@Test
@MediumTest
public void testAddingCaptionsToTheModelRendersThem() {
assertThat(mView.get().getChildCount(), is(0));
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.add(
new AccessorySheetDataPiece("Passwords", AccessorySheetDataPiece.Type.TITLE));
});
CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
View title = mView.get().findViewById(R.id.tab_title);
assertThat(title, is(not(nullValue())));
assertThat(title, instanceOf(TextView.class));
assertThat(((TextView) title).getText(), is("Passwords"));
}
@Test
@MediumTest
public void testAddingUserInfoToTheModelRendersClickableActions() throws ExecutionException {
final AtomicReference<Boolean> clicked = new AtomicReference<>(false);
assertThat(mView.get().getChildCount(), is(0));
UserInfo testInfo = new UserInfo(null);
testInfo.addField(new UserInfo.Field(
"Name Suggestion", "Name Suggestion", false, item -> clicked.set(true)));
testInfo.addField(new UserInfo.Field(
"Password Suggestion", "Password Suggestion", true, item -> clicked.set(true)));
ThreadUtils.runOnUiThreadBlocking(() -> {
mModel.add(new AccessorySheetDataPiece(
testInfo, AccessorySheetDataPiece.Type.PASSWORD_INFO));
});
CriteriaHelper.pollUiThread(Criteria.equals(1, () -> mView.get().getChildCount()));
assertThat(getNameSuggestion().getInnerTextView().getText(), is("Name Suggestion"));
assertThat(getPasswordSuggestion().getInnerTextView().getText(), is("Password Suggestion"));
assertThat(getPasswordSuggestion().getInnerTextView().getTransformationMethod(),
instanceOf(PasswordTransformationMethod.class));
ThreadUtils.runOnUiThreadBlocking(getNameSuggestion()::performClick);
assertThat(clicked.get(), is(true));
clicked.set(false);
ThreadUtils.runOnUiThreadBlocking(getPasswordSuggestion()::performClick);
assertThat(clicked.get(), is(true));
}
private ChipView getNameSuggestion() {
assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
View view = layout.findViewById(R.id.suggestion_text);
assertThat(view, is(not(nullValue())));
assertThat(view, instanceOf(ChipView.class));
return (ChipView) view;
}
private ChipView getPasswordSuggestion() {
assertThat(mView.get().getChildAt(0), instanceOf(LinearLayout.class));
LinearLayout layout = (LinearLayout) mView.get().getChildAt(0);
View view = layout.findViewById(R.id.password_text);
assertThat(view, is(not(nullValue())));
assertThat(view, instanceOf(ChipView.class));
return (ChipView) view;
}
}
\ No newline at end of file
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