Commit b58d8d43 authored by Clemens Arbesser's avatar Clemens Arbesser Committed by Commit Bot

[Autofill Assistant] New controls to allow specifying a date/time range.

This change makes it possible for users to select a date/time range. The UI ensures that the range is non-empty and valid, i.e., that the end of the range is after the start of the range. For now, this uses the stock android DateTimePicker in a popup dialog.

Adjusting the start of the range to after the end of the range will automatically clamp the end of the range to the new start value, and vice-versa. A short demo video is in the bug linked below.

Bug: b/139121989
Change-Id: I0c99db66ea1350e81a3aba1246602f7aab8fce4c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1803437
Commit-Queue: Clemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarMathias Carlen <mcarlen@chromium.org>
Reviewed-by: default avatarJinsuk Kim <jinsukkim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698480}
parent 03a160d3
...@@ -130,6 +130,9 @@ android_library("java") { ...@@ -130,6 +130,9 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataNativeDelegate.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantPaymentMethodSection.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantPaymentMethodSection.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantCollectUserDataModel.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateChoiceOptions.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateSection.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantDateTime.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantShippingAddressSection.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantShippingAddressSection.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantTermsSection.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java", "java/src/org/chromium/chrome/browser/autofill_assistant/user_data/AssistantVerticalExpander.java",
......
...@@ -2,4 +2,5 @@ include_rules = [ ...@@ -2,4 +2,5 @@ include_rules = [
"+chrome/lib/image_fetcher", "+chrome/lib/image_fetcher",
"+chrome/browser/ui/android/widget", "+chrome/browser/ui/android/widget",
"+content/public/android/java/src/org/chromium/content_public/browser", "+content/public/android/java/src/org/chromium/content_public/browser",
"+content/public/android/java/src/org/chromium/content/browser/picker",
] ]
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 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"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/datetime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"/>
</LinearLayout>
\ No newline at end of file
...@@ -16,6 +16,8 @@ public class AssistantTagsForTesting { ...@@ -16,6 +16,8 @@ public class AssistantTagsForTesting {
public static final String COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG = "contact"; public static final String COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG = "contact";
public static final String COLLECT_USER_DATA_PAYMENT_METHOD_SECTION_TAG = "payment"; public static final String COLLECT_USER_DATA_PAYMENT_METHOD_SECTION_TAG = "payment";
public static final String COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG = "shipping"; public static final String COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG = "shipping";
public static final String COLLECT_USER_DATA_DATE_RANGE_START_TAG = "date_start";
public static final String COLLECT_USER_DATA_DATE_RANGE_END_TAG = "date_end";
public static final String COLLECT_USER_DATA_TERMS_REQUIRE_REVIEW = "require_review"; public static final String COLLECT_USER_DATA_TERMS_REQUIRE_REVIEW = "require_review";
public static final String VERTICAL_EXPANDER_CHEVRON = "chevron"; public static final String VERTICAL_EXPANDER_CHEVRON = "chevron";
public static final String COLLECT_USER_DATA_CHOICE_LIST = "choicelist"; public static final String COLLECT_USER_DATA_CHOICE_LIST = "choicelist";
......
...@@ -5,15 +5,20 @@ ...@@ -5,15 +5,20 @@
package org.chromium.chrome.browser.autofill_assistant.user_data; package org.chromium.chrome.browser.autofill_assistant.user_data;
import android.app.Activity; import android.app.Activity;
import android.os.Build;
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.LinearLayout;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.autofill_assistant.R; import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting; import org.chromium.chrome.browser.autofill_assistant.AssistantTagsForTesting;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor; import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import java.text.DateFormat;
import java.util.Locale;
// TODO(crbug.com/806868): Use mCarouselCoordinator to show chips. // TODO(crbug.com/806868): Use mCarouselCoordinator to show chips.
/** /**
...@@ -28,6 +33,21 @@ public class AssistantCollectUserDataCoordinator { ...@@ -28,6 +33,21 @@ public class AssistantCollectUserDataCoordinator {
public AssistantCollectUserDataCoordinator( public AssistantCollectUserDataCoordinator(
Activity activity, AssistantCollectUserDataModel model) { Activity activity, AssistantCollectUserDataModel model) {
this(activity, model,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? activity.getResources().getConfiguration().getLocales().get(0)
: activity.getResources().getConfiguration().locale);
}
private AssistantCollectUserDataCoordinator(
Activity activity, AssistantCollectUserDataModel model, Locale locale) {
this(activity, model, locale,
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale));
}
@VisibleForTesting
public AssistantCollectUserDataCoordinator(Activity activity,
AssistantCollectUserDataModel model, Locale locale, DateFormat dateFormat) {
mActivity = activity; mActivity = activity;
mModel = model; mModel = model;
int sectionToSectionPadding = activity.getResources().getDimensionPixelSize( int sectionToSectionPadding = activity.getResources().getDimensionPixelSize(
...@@ -58,6 +78,14 @@ public class AssistantCollectUserDataCoordinator { ...@@ -58,6 +78,14 @@ public class AssistantCollectUserDataCoordinator {
AssistantContactDetailsSection contactDetailsSection = AssistantContactDetailsSection contactDetailsSection =
new AssistantContactDetailsSection(mActivity, paymentRequestExpanderAccordion); new AssistantContactDetailsSection(mActivity, paymentRequestExpanderAccordion);
createSeparator(paymentRequestExpanderAccordion); createSeparator(paymentRequestExpanderAccordion);
AssistantDateSection dateRangeStartSection = new AssistantDateSection(
mActivity, paymentRequestExpanderAccordion, locale, dateFormat);
createSeparator(paymentRequestExpanderAccordion);
AssistantDateSection dateRangeEndSection = new AssistantDateSection(
mActivity, paymentRequestExpanderAccordion, locale, dateFormat);
createSeparator(paymentRequestExpanderAccordion);
AssistantPaymentMethodSection paymentMethodSection = AssistantPaymentMethodSection paymentMethodSection =
new AssistantPaymentMethodSection(mActivity, paymentRequestExpanderAccordion); new AssistantPaymentMethodSection(mActivity, paymentRequestExpanderAccordion);
createSeparator(paymentRequestExpanderAccordion); createSeparator(paymentRequestExpanderAccordion);
...@@ -73,6 +101,10 @@ public class AssistantCollectUserDataCoordinator { ...@@ -73,6 +101,10 @@ public class AssistantCollectUserDataCoordinator {
paymentRequestExpanderAccordion.setTag( paymentRequestExpanderAccordion.setTag(
AssistantTagsForTesting.COLLECT_USER_DATA_ACCORDION_TAG); AssistantTagsForTesting.COLLECT_USER_DATA_ACCORDION_TAG);
loginSection.getView().setTag(AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG); loginSection.getView().setTag(AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG);
dateRangeStartSection.getView().setTag(
AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_START_TAG);
dateRangeEndSection.getView().setTag(
AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_END_TAG);
contactDetailsSection.getView().setTag( contactDetailsSection.getView().setTag(
AssistantTagsForTesting.COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG); AssistantTagsForTesting.COLLECT_USER_DATA_CONTACT_DETAILS_SECTION_TAG);
paymentMethodSection.getView().setTag( paymentMethodSection.getView().setTag(
...@@ -83,8 +115,9 @@ public class AssistantCollectUserDataCoordinator { ...@@ -83,8 +115,9 @@ public class AssistantCollectUserDataCoordinator {
// Bind view and mediator through the model. // Bind view and mediator through the model.
mViewHolder = new AssistantCollectUserDataBinder.ViewHolder(mPaymentRequestUI, mViewHolder = new AssistantCollectUserDataBinder.ViewHolder(mPaymentRequestUI,
paymentRequestExpanderAccordion, sectionToSectionPadding, loginSection, paymentRequestExpanderAccordion, sectionToSectionPadding, loginSection,
contactDetailsSection, paymentMethodSection, shippingAddressSection, termsSection, contactDetailsSection, dateRangeStartSection, dateRangeEndSection,
termsAsCheckboxSection, DIVIDER_TAG, activity); paymentMethodSection, shippingAddressSection, termsSection, termsAsCheckboxSection,
DIVIDER_TAG, activity);
AssistantCollectUserDataBinder binder = new AssistantCollectUserDataBinder(); AssistantCollectUserDataBinder binder = new AssistantCollectUserDataBinder();
PropertyModelChangeProcessor.create(model, mViewHolder, binder); PropertyModelChangeProcessor.create(model, mViewHolder, binder);
......
...@@ -34,4 +34,11 @@ public interface AssistantCollectUserDataDelegate { ...@@ -34,4 +34,11 @@ public interface AssistantCollectUserDataDelegate {
/** The currently selected login choice has changed. */ /** The currently selected login choice has changed. */
void onLoginChoiceChanged(@Nullable AssistantLoginChoice loginChoice); void onLoginChoiceChanged(@Nullable AssistantLoginChoice loginChoice);
/** The start of the date/time range has changed. */
void onDateTimeRangeStartChanged(
int year, int month, int day, int hour, int minute, int second);
/** The end of the date/time range has changed. */
void onDateTimeRangeEndChanged(int year, int month, int day, int hour, int minute, int second);
} }
...@@ -104,6 +104,21 @@ public class AssistantCollectUserDataModel extends PropertyModel { ...@@ -104,6 +104,21 @@ public class AssistantCollectUserDataModel extends PropertyModel {
public static final WritableObjectPropertyKey<String> BILLING_POSTAL_CODE_MISSING_TEXT = public static final WritableObjectPropertyKey<String> BILLING_POSTAL_CODE_MISSING_TEXT =
new WritableObjectPropertyKey<>(); new WritableObjectPropertyKey<>();
public static final WritableBooleanPropertyKey REQUEST_DATE_RANGE =
new WritableBooleanPropertyKey();
public static final WritableObjectPropertyKey<AssistantDateChoiceOptions> DATE_RANGE_START =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> DATE_RANGE_START_LABEL =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<AssistantDateChoiceOptions> DATE_RANGE_END =
new WritableObjectPropertyKey<>();
public static final WritableObjectPropertyKey<String> DATE_RANGE_END_LABEL =
new WritableObjectPropertyKey<>();
public AssistantCollectUserDataModel() { public AssistantCollectUserDataModel() {
super(DELEGATE, WEB_CONTENTS, VISIBLE, SHIPPING_ADDRESS, PAYMENT_METHOD, CONTACT_DETAILS, super(DELEGATE, WEB_CONTENTS, VISIBLE, SHIPPING_ADDRESS, PAYMENT_METHOD, CONTACT_DETAILS,
LOGIN_SECTION_TITLE, SELECTED_LOGIN, TERMS_STATUS, DEFAULT_EMAIL, REQUEST_NAME, LOGIN_SECTION_TITLE, SELECTED_LOGIN, TERMS_STATUS, DEFAULT_EMAIL, REQUEST_NAME,
...@@ -111,7 +126,9 @@ public class AssistantCollectUserDataModel extends PropertyModel { ...@@ -111,7 +126,9 @@ public class AssistantCollectUserDataModel extends PropertyModel {
ACCEPT_TERMS_AND_CONDITIONS_TEXT, SHOW_TERMS_AS_CHECKBOX, REQUEST_LOGIN_CHOICE, ACCEPT_TERMS_AND_CONDITIONS_TEXT, SHOW_TERMS_AS_CHECKBOX, REQUEST_LOGIN_CHOICE,
AVAILABLE_PROFILES, AVAILABLE_AUTOFILL_PAYMENT_METHODS, AVAILABLE_PROFILES, AVAILABLE_AUTOFILL_PAYMENT_METHODS,
SUPPORTED_BASIC_CARD_NETWORKS, SUPPORTED_PAYMENT_METHODS, AVAILABLE_LOGINS, SUPPORTED_BASIC_CARD_NETWORKS, SUPPORTED_PAYMENT_METHODS, AVAILABLE_LOGINS,
EXPANDED_SECTION, REQUIRE_BILLING_POSTAL_CODE, BILLING_POSTAL_CODE_MISSING_TEXT); EXPANDED_SECTION, REQUIRE_BILLING_POSTAL_CODE, BILLING_POSTAL_CODE_MISSING_TEXT,
REQUEST_DATE_RANGE, DATE_RANGE_START, DATE_RANGE_START_LABEL, DATE_RANGE_END,
DATE_RANGE_END_LABEL);
/** /**
* Set initial state for basic type properties (others are implicitly null). * Set initial state for basic type properties (others are implicitly null).
...@@ -127,6 +144,8 @@ public class AssistantCollectUserDataModel extends PropertyModel { ...@@ -127,6 +144,8 @@ public class AssistantCollectUserDataModel extends PropertyModel {
set(REQUEST_LOGIN_CHOICE, false); set(REQUEST_LOGIN_CHOICE, false);
set(REQUIRE_BILLING_POSTAL_CODE, false); set(REQUIRE_BILLING_POSTAL_CODE, false);
set(DEFAULT_EMAIL, ""); set(DEFAULT_EMAIL, "");
set(DATE_RANGE_START_LABEL, "");
set(DATE_RANGE_END_LABEL, "");
} }
@CalledByNative @CalledByNative
...@@ -215,7 +234,7 @@ public class AssistantCollectUserDataModel extends PropertyModel { ...@@ -215,7 +234,7 @@ public class AssistantCollectUserDataModel extends PropertyModel {
return new ArrayList<>(); return new ArrayList<>();
} }
/** Appends a login choice to {@code logins}. */ /** Appends a login choice to {@code loginChoices}. */
@CalledByNative @CalledByNative
private void addLoginChoice(List<AssistantLoginChoice> loginChoices, String identifier, private void addLoginChoice(List<AssistantLoginChoice> loginChoices, String identifier,
String label, int priority) { String label, int priority) {
...@@ -227,4 +246,44 @@ public class AssistantCollectUserDataModel extends PropertyModel { ...@@ -227,4 +246,44 @@ public class AssistantCollectUserDataModel extends PropertyModel {
private void setLoginChoices(List<AssistantLoginChoice> loginChoices) { private void setLoginChoices(List<AssistantLoginChoice> loginChoices) {
set(AVAILABLE_LOGINS, loginChoices); set(AVAILABLE_LOGINS, loginChoices);
} }
@CalledByNative
private void setRequestDateRange(boolean requestDateRange) {
set(REQUEST_DATE_RANGE, requestDateRange);
}
/** Create an instance of {@code AssistantDateTime}. */
@CalledByNative
private static AssistantDateTime createAssistantDateTime(
int year, int month, int day, int hour, int minute, int second) {
return new AssistantDateTime(year, month, day, hour, minute, second);
}
/** Configures the start of the date/time range. */
@CalledByNative
private void setDateTimeRangeStart(AssistantDateTime initialValue, AssistantDateTime minValue,
AssistantDateTime maxValue) {
AssistantDateChoiceOptions options =
new AssistantDateChoiceOptions(initialValue, minValue, maxValue);
set(DATE_RANGE_START, options);
}
/** Configures the end of the date/time range. */
@CalledByNative
private void setDateTimeRangeEnd(AssistantDateTime initialValue, AssistantDateTime minValue,
AssistantDateTime maxValue) {
AssistantDateChoiceOptions options =
new AssistantDateChoiceOptions(initialValue, minValue, maxValue);
set(DATE_RANGE_END, options);
}
@CalledByNative
private void setDateTimeRangeStartLabel(String label) {
set(DATE_RANGE_START_LABEL, label);
}
@CalledByNative
private void setDateTimeRangeEndLabel(String label) {
set(DATE_RANGE_END_LABEL, label);
}
} }
...@@ -96,6 +96,28 @@ public class AssistantCollectUserDataNativeDelegate implements AssistantCollectU ...@@ -96,6 +96,28 @@ public class AssistantCollectUserDataNativeDelegate implements AssistantCollectU
} }
} }
@Override
public void onDateTimeRangeStartChanged(
int year, int month, int day, int hour, int minute, int second) {
if (mNativeAssistantCollectUserDataDelegate != 0) {
AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeStartChanged(
mNativeAssistantCollectUserDataDelegate,
AssistantCollectUserDataNativeDelegate.this, year, month, day, hour, minute,
second);
}
}
@Override
public void onDateTimeRangeEndChanged(
int year, int month, int day, int hour, int minute, int second) {
if (mNativeAssistantCollectUserDataDelegate != 0) {
AssistantCollectUserDataNativeDelegateJni.get().onDateTimeRangeEndChanged(
mNativeAssistantCollectUserDataDelegate,
AssistantCollectUserDataNativeDelegate.this, year, month, day, hour, minute,
second);
}
}
@CalledByNative @CalledByNative
private void clearNativePtr() { private void clearNativePtr() {
mNativeAssistantCollectUserDataDelegate = 0; mNativeAssistantCollectUserDataDelegate = 0;
...@@ -118,5 +140,11 @@ public class AssistantCollectUserDataNativeDelegate implements AssistantCollectU ...@@ -118,5 +140,11 @@ public class AssistantCollectUserDataNativeDelegate implements AssistantCollectU
AssistantCollectUserDataNativeDelegate caller, int link); AssistantCollectUserDataNativeDelegate caller, int link);
void onLoginChoiceChanged(long nativeAssistantCollectUserDataDelegate, void onLoginChoiceChanged(long nativeAssistantCollectUserDataDelegate,
AssistantCollectUserDataNativeDelegate caller, String choice); AssistantCollectUserDataNativeDelegate caller, String choice);
void onDateTimeRangeStartChanged(long nativeAssistantCollectUserDataDelegate,
AssistantCollectUserDataNativeDelegate caller, int year, int month, int day,
int hour, int minute, int second);
void onDateTimeRangeEndChanged(long nativeAssistantCollectUserDataDelegate,
AssistantCollectUserDataNativeDelegate caller, int year, int month, int day,
int hour, int minute, int second);
} }
} }
// Copyright 2019 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_assistant.user_data;
/**
* Represents a request to let the user choose a single date/time value.
*/
public class AssistantDateChoiceOptions {
private final AssistantDateTime mInitialValue;
private final AssistantDateTime mMinValue;
private final AssistantDateTime mMaxValue;
/**
*
* @param initialValue The initial value for the date/time value.
* @param minValue The minimum allowed value for the date/time value.
* @param maxValue The maximum allowed value for the date/time value.
*/
public AssistantDateChoiceOptions(AssistantDateTime initialValue, AssistantDateTime minValue,
AssistantDateTime maxValue) {
mInitialValue = initialValue;
mMinValue = minValue;
mMaxValue = maxValue;
}
AssistantDateTime getInitialValue() {
return mInitialValue;
}
AssistantDateTime getMinValue() {
return mMinValue;
}
AssistantDateTime getMaxValue() {
return mMaxValue;
}
}
// Copyright 2019 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_assistant.user_data;
import android.content.Context;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander.ChevronStyle;
import org.chromium.content.browser.picker.InputDialogContainer;
import org.chromium.content.browser.picker.InputDialogContainer.InputActionDelegate;
import org.chromium.ui.base.ime.TextInputType;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
/**
* A section which allows the user to specify a single date/time value.
*/
public class AssistantDateSection {
interface Delegate {
void onDateTimeChanged(int year, int month, int day, int hour, int minute, int second);
}
private final Context mContext;
private final AssistantVerticalExpander mSectionExpander;
private final View mSummaryView;
private final int mTitleToContentPadding;
private final Locale mLocale;
private final DateFormat mDateTimeFormat;
@Nullable
private Delegate mDelegate;
@Nullable
private AssistantDateChoiceOptions mDateChoiceOptions;
private AssistantDateTime mCurrentValue;
AssistantDateSection(Context context, ViewGroup parent, Locale locale, DateFormat dateFormat) {
mContext = context;
mLocale = locale;
mCurrentValue = new AssistantDateTime(Calendar.getInstance());
mDateTimeFormat = dateFormat;
mTitleToContentPadding = context.getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_payment_request_title_padding);
LayoutInflater inflater = LayoutInflater.from(context);
mSectionExpander = new AssistantVerticalExpander(context, null);
View sectionTitle =
inflater.inflate(R.layout.autofill_assistant_payment_request_section_title, null);
mSummaryView = inflater.inflate(R.layout.autofill_assistant_datetime, null);
View.OnClickListener onClickListener = unusedView -> {
if (mDateChoiceOptions == null) {
return;
}
InputDialogContainer inputDialogContainer =
new InputDialogContainer(mContext, new InputActionDelegate() {
@Override
public void cancelDateTimeDialog() {
// Do nothing.
}
@Override
public void replaceDateTime(double value) {
// User tapped the 'clear' button.
if (Double.isNaN(value)) {
setCurrentValue(mDateChoiceOptions.getInitialValue());
} else {
setCurrentValue((long) value);
}
}
});
inputDialogContainer.showDialog(TextInputType.DATE_TIME,
mCurrentValue.getTimeInUtcMillis(),
mDateChoiceOptions.getMinValue().getTimeInUtcMillis(),
mDateChoiceOptions.getMaxValue().getTimeInUtcMillis(), -1, null);
};
mSummaryView.setOnClickListener(onClickListener);
sectionTitle.setOnClickListener(onClickListener);
mSectionExpander.getChevronButton().setOnClickListener(onClickListener);
mSectionExpander.setTitleView(sectionTitle,
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mSectionExpander.setCollapsedView(mSummaryView,
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
// Adjust margins such that title and collapsed views are indented.
int horizontalMargin = context.getResources().getDimensionPixelSize(
R.dimen.autofill_assistant_bottombar_horizontal_spacing);
setHorizontalMargins(sectionTitle, horizontalMargin, horizontalMargin);
setHorizontalMargins(mSectionExpander.getChevronButton(), 0, horizontalMargin);
setHorizontalMargins(mSummaryView, horizontalMargin, 0);
mSectionExpander.findViewById(R.id.section_title_add_button).setVisibility(View.GONE);
mSectionExpander.setFixed(true);
mSectionExpander.setChevronStyle(ChevronStyle.ALWAYS);
updateTextView();
parent.addView(mSectionExpander,
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
void setDateChoiceOptions(AssistantDateChoiceOptions dateChoiceOptions) {
mDateChoiceOptions = dateChoiceOptions;
setCurrentValue(mDateChoiceOptions.getInitialValue());
}
void setDelegate(Delegate delegate) {
mDelegate = delegate;
}
View getView() {
return mSectionExpander;
}
void setVisible(boolean visible) {
mSectionExpander.setVisibility(visible ? View.VISIBLE : View.GONE);
}
void setPaddings(int topPadding, int bottomPadding) {
View titleView = mSectionExpander.getTitleView();
titleView.setPadding(titleView.getPaddingLeft(), topPadding, titleView.getPaddingRight(),
mTitleToContentPadding);
mSectionExpander.getCollapsedView().setPadding(
mSectionExpander.getCollapsedView().getPaddingLeft(),
mSectionExpander.getCollapsedView().getPaddingTop(),
mSectionExpander.getCollapsedView().getPaddingRight(), bottomPadding);
}
void setTitle(String title) {
TextView titleView = mSectionExpander.getTitleView().findViewById(R.id.section_title);
titleView.setText(title);
}
AssistantDateTime getCurrentValue() {
return mCurrentValue;
}
void setCurrentValue(AssistantDateTime value) {
setCurrentValue(value.getTimeInUtcMillis());
}
void setCurrentValue(long millisUtc) {
mCurrentValue = new AssistantDateTime(millisUtc);
updateTextView();
if (mDelegate != null) {
mDelegate.onDateTimeChanged(mCurrentValue.getYear(), mCurrentValue.getMonth(),
mCurrentValue.getDay(), mCurrentValue.getHour(), mCurrentValue.getMinute(),
mCurrentValue.getSecond());
}
}
private void updateTextView() {
TextView dateTimeView = mSummaryView.findViewById(R.id.datetime);
Calendar calendar = Calendar.getInstance(mLocale);
calendar.setTimeInMillis(mCurrentValue.getTimeInMillis(mLocale));
dateTimeView.setText(mDateTimeFormat.format(calendar.getTime()));
}
private void setHorizontalMargins(View view, int marginStart, int marginEnd) {
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
lp.setMarginStart(marginStart);
lp.setMarginEnd(marginEnd);
view.setLayoutParams(lp);
}
}
// Copyright 2019 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_assistant.user_data;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
/**
* A single date/time value which directly corresponds to the proto definition in {@code
* DateTimeProto}.
*
* Note that this class does not make any guarantees with respect to the validity of the represented
* date/time.
*/
public class AssistantDateTime {
/** Year, e.g., 2019. */
private int mYear;
/** Month in [1-12]. */
private int mMonth;
/** Day in [1-31]. */
private int mDay;
/** Hour in [0-23]. */
private int mHour;
/** Minute in [0-59]. */
private int mMinute;
/** Second in [0-59]. */
private int mSecond;
public AssistantDateTime(Calendar calendar) {
this(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE), calendar.get(Calendar.SECOND));
}
public AssistantDateTime(int year, int month, int day, int hour, int minute, int second) {
set(year, month, day, hour, minute, second);
}
public AssistantDateTime(long millisUtc) {
setFromUtcMillis(millisUtc);
}
public void setFromUtcMillis(long value) {
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
calendar.setTimeInMillis(value);
mYear = calendar.get(Calendar.YEAR);
mMonth = calendar.get(Calendar.MONTH) + 1;
mDay = calendar.get(Calendar.DAY_OF_MONTH);
mHour = calendar.get(Calendar.HOUR_OF_DAY);
mMinute = calendar.get(Calendar.MINUTE);
mSecond = calendar.get(Calendar.SECOND);
}
public void set(int year, int month, int day, int hour, int minute, int second) {
mYear = year;
mMonth = month;
mDay = day;
mHour = hour;
mMinute = minute;
mSecond = second;
}
public long getTimeInMillis(Locale locale) {
Calendar calendar = Calendar.getInstance(locale);
calendar.clear();
calendar.set(mYear, mMonth - 1, mDay, mHour, mMinute, mSecond);
return calendar.getTimeInMillis();
}
public long getTimeInUtcMillis() {
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
calendar.clear();
calendar.set(mYear, mMonth - 1, mDay, mHour, mMinute, mSecond);
return calendar.getTimeInMillis();
}
public int getYear() {
return mYear;
}
public int getMonth() {
return mMonth;
}
public int getDay() {
return mDay;
}
public int getHour() {
return mHour;
}
public int getMinute() {
return mMinute;
}
public int getSecond() {
return mSecond;
}
}
\ No newline at end of file
...@@ -28,6 +28,13 @@ import org.chromium.chrome.browser.ui.widget.TintedDrawable; ...@@ -28,6 +28,13 @@ import org.chromium.chrome.browser.ui.widget.TintedDrawable;
* parameters for disambiguation, otherwise the child won't be added at all! * parameters for disambiguation, otherwise the child won't be added at all!
*/ */
public class AssistantVerticalExpander extends LinearLayout { public class AssistantVerticalExpander extends LinearLayout {
/** Controls whether the chevron should be visible. */
enum ChevronStyle {
AUTO, /** visible if the expander has an expanded view, else invisible. */
ALWAYS,
NEVER
}
private final ViewGroup mTitleContainer; private final ViewGroup mTitleContainer;
private final ViewGroup mCollapsedContainer; private final ViewGroup mCollapsedContainer;
private final ViewGroup mExpandedContainer; private final ViewGroup mExpandedContainer;
...@@ -39,6 +46,7 @@ public class AssistantVerticalExpander extends LinearLayout { ...@@ -39,6 +46,7 @@ public class AssistantVerticalExpander extends LinearLayout {
private View mExpandedView; private View mExpandedView;
private boolean mExpanded; private boolean mExpanded;
private boolean mFixed; private boolean mFixed;
private ChevronStyle mChevronStyle = ChevronStyle.AUTO;
public AssistantVerticalExpander(Context context, AttributeSet attrs) { public AssistantVerticalExpander(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
...@@ -130,6 +138,13 @@ public class AssistantVerticalExpander extends LinearLayout { ...@@ -130,6 +138,13 @@ public class AssistantVerticalExpander extends LinearLayout {
} }
} }
public void setChevronStyle(ChevronStyle style) {
if (style != mChevronStyle) {
mChevronStyle = style;
update();
}
}
public boolean isExpanded() { public boolean isExpanded() {
return mExpanded; return mExpanded;
} }
...@@ -185,7 +200,18 @@ public class AssistantVerticalExpander extends LinearLayout { ...@@ -185,7 +200,18 @@ public class AssistantVerticalExpander extends LinearLayout {
} }
private void update() { private void update() {
mChevronButton.setVisibility(!mFixed && mExpandedView != null ? View.VISIBLE : View.GONE); switch (mChevronStyle) {
case AUTO:
mChevronButton.setVisibility(
!mFixed && mExpandedView != null ? View.VISIBLE : View.GONE);
break;
case ALWAYS:
mChevronButton.setVisibility(View.VISIBLE);
break;
case NEVER:
mChevronButton.setVisibility(View.GONE);
break;
}
if (mExpandedView != null) { if (mExpandedView != null) {
mExpandedView.setVisibility(mExpanded ? VISIBLE : GONE); mExpandedView.setVisibility(mExpanded ? VISIBLE : GONE);
......
...@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; ...@@ -20,6 +20,7 @@ import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataCoordinator;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataDelegate; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantCollectUserDataDelegate;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantDateTime;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantLoginChoice; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantLoginChoice;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantTermsAndConditionsState; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantTermsAndConditionsState;
import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander; import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantVerticalExpander;
...@@ -46,6 +47,8 @@ public class AutofillAssistantCollectUserDataTestHelper { ...@@ -46,6 +47,8 @@ public class AutofillAssistantCollectUserDataTestHelper {
final AssistantVerticalExpander mPaymentSection; final AssistantVerticalExpander mPaymentSection;
final AssistantVerticalExpander mShippingSection; final AssistantVerticalExpander mShippingSection;
final AssistantVerticalExpander mLoginsSection; final AssistantVerticalExpander mLoginsSection;
final AssistantVerticalExpander mDateRangeStartSection;
final AssistantVerticalExpander mDateRangeEndSection;
final AssistantChoiceList mContactList; final AssistantChoiceList mContactList;
final AssistantChoiceList mPaymentMethodList; final AssistantChoiceList mPaymentMethodList;
final AssistantChoiceList mShippingAddressList; final AssistantChoiceList mShippingAddressList;
...@@ -63,6 +66,10 @@ public class AutofillAssistantCollectUserDataTestHelper { ...@@ -63,6 +66,10 @@ public class AutofillAssistantCollectUserDataTestHelper {
AssistantTagsForTesting.COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG); AssistantTagsForTesting.COLLECT_USER_DATA_SHIPPING_ADDRESS_SECTION_TAG);
mLoginsSection = coordinator.getView().findViewWithTag( mLoginsSection = coordinator.getView().findViewWithTag(
AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG); AssistantTagsForTesting.COLLECT_USER_DATA_LOGIN_SECTION_TAG);
mDateRangeStartSection = coordinator.getView().findViewWithTag(
AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_START_TAG);
mDateRangeEndSection = coordinator.getView().findViewWithTag(
AssistantTagsForTesting.COLLECT_USER_DATA_DATE_RANGE_END_TAG);
mDividers = findViewsWithTag(coordinator.getView(), DIVIDER_TAG); mDividers = findViewsWithTag(coordinator.getView(), DIVIDER_TAG);
mContactList = (AssistantChoiceList) (findViewsWithTag( mContactList = (AssistantChoiceList) (findViewsWithTag(
mContactSection, COLLECT_USER_DATA_CHOICE_LIST) mContactSection, COLLECT_USER_DATA_CHOICE_LIST)
...@@ -89,6 +96,8 @@ public class AutofillAssistantCollectUserDataTestHelper { ...@@ -89,6 +96,8 @@ public class AutofillAssistantCollectUserDataTestHelper {
AutofillAddress mAddress; AutofillAddress mAddress;
AutofillPaymentInstrument mPaymentMethod; AutofillPaymentInstrument mPaymentMethod;
AssistantLoginChoice mLoginChoice; AssistantLoginChoice mLoginChoice;
AssistantDateTime mDateRangeStart;
AssistantDateTime mDateRangeEnd;
@AssistantTermsAndConditionsState @AssistantTermsAndConditionsState
int mTermsStatus; int mTermsStatus;
@Nullable @Nullable
...@@ -123,6 +132,18 @@ public class AutofillAssistantCollectUserDataTestHelper { ...@@ -123,6 +132,18 @@ public class AutofillAssistantCollectUserDataTestHelper {
public void onTermsAndConditionsLinkClicked(int link) { public void onTermsAndConditionsLinkClicked(int link) {
mLastLinkClicked = link; mLastLinkClicked = link;
} }
@Override
public void onDateTimeRangeStartChanged(
int year, int month, int day, int hour, int minute, int second) {
mDateRangeStart = new AssistantDateTime(year, month, day, hour, minute, second);
}
@Override
public void onDateTimeRangeEndChanged(
int year, int month, int day, int hour, int minute, int second) {
mDateRangeEnd = new AssistantDateTime(year, month, day, hour, minute, second);
}
} }
public AutofillAssistantCollectUserDataTestHelper() public AutofillAssistantCollectUserDataTestHelper()
......
...@@ -129,6 +129,32 @@ void AssistantCollectUserDataDelegate::OnLoginChoiceChanged( ...@@ -129,6 +129,32 @@ void AssistantCollectUserDataDelegate::OnLoginChoiceChanged(
ui_controller_->OnLoginChoiceChanged(identifier); ui_controller_->OnLoginChoiceChanged(identifier);
} }
void AssistantCollectUserDataDelegate::OnDateTimeRangeStartChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint year,
jint month,
jint day,
jint hour,
jint minute,
jint second) {
ui_controller_->OnDateTimeRangeStartChanged(year, month, day, hour, minute,
second);
}
void AssistantCollectUserDataDelegate::OnDateTimeRangeEndChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint year,
jint month,
jint day,
jint hour,
jint minute,
jint second) {
ui_controller_->OnDateTimeRangeEndChanged(year, month, day, hour, minute,
second);
}
base::android::ScopedJavaGlobalRef<jobject> base::android::ScopedJavaGlobalRef<jobject>
AssistantCollectUserDataDelegate::GetJavaObject() { AssistantCollectUserDataDelegate::GetJavaObject() {
return java_assistant_collect_user_data_delegate_; return java_assistant_collect_user_data_delegate_;
......
...@@ -46,6 +46,26 @@ class AssistantCollectUserDataDelegate { ...@@ -46,6 +46,26 @@ class AssistantCollectUserDataDelegate {
const base::android::JavaParamRef<jobject>& jcaller, const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jstring>& jidentifier); const base::android::JavaParamRef<jstring>& jidentifier);
void OnDateTimeRangeStartChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint year,
jint month,
jint day,
jint hour,
jint minute,
jint second);
void OnDateTimeRangeEndChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint year,
jint month,
jint day,
jint hour,
jint minute,
jint second);
base::android::ScopedJavaGlobalRef<jobject> GetJavaObject(); base::android::ScopedJavaGlobalRef<jobject> GetJavaObject();
private: private:
......
...@@ -70,6 +70,14 @@ std::vector<float> ToFloatVector(const std::vector<RectF>& areas) { ...@@ -70,6 +70,14 @@ std::vector<float> ToFloatVector(const std::vector<RectF>& areas) {
return flattened; return flattened;
} }
base::android::ScopedJavaLocalRef<jobject> CreateJavaDateTime(
JNIEnv* env,
const DateTimeProto& proto) {
return Java_AssistantCollectUserDataModel_createAssistantDateTime(
env, (int)proto.date().year(), proto.date().month(), proto.date().day(),
proto.time().hour(), proto.time().minute(), proto.time().second());
}
} // namespace } // namespace
// static // static
...@@ -723,6 +731,24 @@ void UiControllerAndroid::OnTermsAndConditionsLinkClicked(int link) { ...@@ -723,6 +731,24 @@ void UiControllerAndroid::OnTermsAndConditionsLinkClicked(int link) {
ui_delegate_->OnTermsAndConditionsLinkClicked(link); ui_delegate_->OnTermsAndConditionsLinkClicked(link);
} }
void UiControllerAndroid::OnDateTimeRangeStartChanged(int year,
int month,
int day,
int hour,
int minute,
int second) {
ui_delegate_->SetDateTimeRangeStart(year, month, day, hour, minute, second);
}
void UiControllerAndroid::OnDateTimeRangeEndChanged(int year,
int month,
int day,
int hour,
int minute,
int second) {
ui_delegate_->SetDateTimeRangeEnd(year, month, day, hour, minute, second);
}
void UiControllerAndroid::OnCollectUserDataOptionsChanged( void UiControllerAndroid::OnCollectUserDataOptionsChanged(
const CollectUserDataOptions* collect_user_data_options) { const CollectUserDataOptions* collect_user_data_options) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
...@@ -775,6 +801,30 @@ void UiControllerAndroid::OnCollectUserDataOptionsChanged( ...@@ -775,6 +801,30 @@ void UiControllerAndroid::OnCollectUserDataOptionsChanged(
} }
Java_AssistantCollectUserDataModel_setLoginChoices(env, jmodel, jlist); Java_AssistantCollectUserDataModel_setLoginChoices(env, jmodel, jlist);
} }
Java_AssistantCollectUserDataModel_setRequestDateRange(
env, jmodel, collect_user_data_options->request_date_time_range);
if (collect_user_data_options->request_date_time_range) {
auto jstart_date = CreateJavaDateTime(
env, collect_user_data_options->date_time_range.start());
auto jend_date = CreateJavaDateTime(
env, collect_user_data_options->date_time_range.end());
auto jmin_date = CreateJavaDateTime(
env, collect_user_data_options->date_time_range.min());
auto jmax_date = CreateJavaDateTime(
env, collect_user_data_options->date_time_range.max());
Java_AssistantCollectUserDataModel_setDateTimeRangeStart(
env, jmodel, jstart_date, jmin_date, jmax_date);
Java_AssistantCollectUserDataModel_setDateTimeRangeEnd(
env, jmodel, jend_date, jmin_date, jmax_date);
Java_AssistantCollectUserDataModel_setDateTimeRangeStartLabel(
env, jmodel,
base::android::ConvertUTF8ToJavaString(
env, collect_user_data_options->date_time_range.start_label()));
Java_AssistantCollectUserDataModel_setDateTimeRangeEndLabel(
env, jmodel,
base::android::ConvertUTF8ToJavaString(
env, collect_user_data_options->date_time_range.end_label()));
}
Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true); Java_AssistantCollectUserDataModel_setVisible(env, jmodel, true);
} }
......
...@@ -120,6 +120,18 @@ class UiControllerAndroid : public ControllerObserver { ...@@ -120,6 +120,18 @@ class UiControllerAndroid : public ControllerObserver {
void OnTermsAndConditionsChanged(TermsAndConditionsState state); void OnTermsAndConditionsChanged(TermsAndConditionsState state);
void OnLoginChoiceChanged(std::string identifier); void OnLoginChoiceChanged(std::string identifier);
void OnTermsAndConditionsLinkClicked(int link); void OnTermsAndConditionsLinkClicked(int link);
void OnDateTimeRangeStartChanged(int year,
int month,
int day,
int hour,
int minute,
int second);
void OnDateTimeRangeEndChanged(int year,
int month,
int day,
int hour,
int minute,
int second);
// Called by AssistantFormDelegate: // Called by AssistantFormDelegate:
void OnCounterChanged(int input_index, int counter_index, int value); void OnCounterChanged(int input_index, int counter_index, int value);
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
namespace { namespace {
using autofill_assistant::CollectUserDataOptions; using autofill_assistant::CollectUserDataOptions;
using autofill_assistant::DateTimeProto;
using autofill_assistant::TermsAndConditionsState; using autofill_assistant::TermsAndConditionsState;
bool IsCompleteContact( bool IsCompleteContact(
const autofill::AutofillProfile* profile, const autofill::AutofillProfile* profile,
...@@ -153,6 +154,31 @@ bool IsValidTermsChoice( ...@@ -153,6 +154,31 @@ bool IsValidTermsChoice(
terms_state != TermsAndConditionsState::NOT_SELECTED; terms_state != TermsAndConditionsState::NOT_SELECTED;
} }
// Comparison function for |DateTimeProto|.
// Returns 0 if equal, < 0 if |first| < |second|, > 0 if |second| > |first|.
int CompareDateTimes(const DateTimeProto& first, const DateTimeProto& second) {
auto first_tuple = std::make_tuple(
first.date().year(), first.date().month(), first.date().day(),
first.time().hour(), first.time().minute(), first.time().second());
auto second_tuple = std::make_tuple(
second.date().year(), second.date().month(), second.date().day(),
second.time().hour(), second.time().minute(), second.time().second());
if (first_tuple < second_tuple) {
return -1;
} else if (second_tuple < first_tuple) {
return 1;
}
return 0;
}
bool IsValidDateTimeRange(
const DateTimeProto& start,
const DateTimeProto& end,
const CollectUserDataOptions& collect_user_data_options) {
return !collect_user_data_options.request_date_time_range ||
CompareDateTimes(start, end) < 0;
}
} // namespace } // namespace
namespace autofill_assistant { namespace autofill_assistant {
...@@ -403,6 +429,13 @@ void CollectUserDataAction::OnGetUserData( ...@@ -403,6 +429,13 @@ void CollectUserDataAction::OnGetUserData(
->set_login_payload(login_details->second->payload); ->set_login_payload(login_details->second->payload);
} }
if (collect_user_data.has_date_time_range()) {
*processed_action_proto_->mutable_collect_user_data_result()
->mutable_date_time_start() = user_data->date_time_range_start;
*processed_action_proto_->mutable_collect_user_data_result()
->mutable_date_time_end() = user_data->date_time_range_end;
}
processed_action_proto_->mutable_collect_user_data_result() processed_action_proto_->mutable_collect_user_data_result()
->set_is_terms_and_conditions_accepted( ->set_is_terms_and_conditions_accepted(
user_data->terms_and_conditions == user_data->terms_and_conditions ==
...@@ -500,6 +533,22 @@ CollectUserDataAction::CreateOptionsFromProto() { ...@@ -500,6 +533,22 @@ CollectUserDataAction::CreateOptionsFromProto() {
} }
} }
if (collect_user_data.has_date_time_range()) {
if (!collect_user_data.date_time_range().has_start_label() ||
!collect_user_data.date_time_range().has_end_label() ||
!collect_user_data.date_time_range().has_start() ||
!collect_user_data.date_time_range().has_end() ||
!collect_user_data.date_time_range().has_min() ||
!collect_user_data.date_time_range().has_max()) {
DVLOG(1) << "Invalid action: missing one or more of the required fields "
"'start', 'end', 'min', 'max', 'start_label', end_label'.";
return nullptr;
}
collect_user_data_options->request_date_time_range = true;
collect_user_data_options->date_time_range =
collect_user_data.date_time_range();
}
// TODO(crbug.com/806868): Maybe we could refactor this to make the confirm // TODO(crbug.com/806868): Maybe we could refactor this to make the confirm
// chip and direct_action part of the additional_actions. // chip and direct_action part of the additional_actions.
std::string confirm_text = collect_user_data.confirm_button_text(); std::string confirm_text = collect_user_data.confirm_button_text();
...@@ -598,7 +647,9 @@ bool CollectUserDataAction::IsUserDataComplete( ...@@ -598,7 +647,9 @@ bool CollectUserDataAction::IsUserDataComplete(
IsCompleteCreditCard(personal_data_manager, user_data.card.get(), IsCompleteCreditCard(personal_data_manager, user_data.card.get(),
options) && options) &&
IsValidLoginChoice(user_data.login_choice_identifier, options) && IsValidLoginChoice(user_data.login_choice_identifier, options) &&
IsValidTermsChoice(user_data.terms_and_conditions, options); IsValidTermsChoice(user_data.terms_and_conditions, options) &&
IsValidDateTimeRange(user_data.date_time_range_start,
user_data.date_time_range_end, options);
} }
void CollectUserDataAction::OnPersonalDataChanged() { void CollectUserDataAction::OnPersonalDataChanged() {
......
...@@ -30,6 +30,28 @@ const char kMemoryLocation[] = "billing"; ...@@ -30,6 +30,28 @@ const char kMemoryLocation[] = "billing";
namespace autofill_assistant { namespace autofill_assistant {
namespace { namespace {
void SetDateTimeProto(DateTimeProto* proto,
int year,
int month,
int day,
int hour,
int minute,
int second) {
proto->mutable_date()->set_year(year);
proto->mutable_date()->set_month(month);
proto->mutable_date()->set_day(day);
proto->mutable_time()->set_hour(hour);
proto->mutable_time()->set_minute(minute);
proto->mutable_time()->set_second(second);
}
MATCHER_P(EqualsProto, message, "") {
std::string expected_serialized, actual_serialized;
message.SerializeToString(&expected_serialized);
arg.SerializeToString(&actual_serialized);
return expected_serialized == actual_serialized;
}
using ::base::test::RunOnceCallback; using ::base::test::RunOnceCallback;
using ::testing::_; using ::testing::_;
using ::testing::Eq; using ::testing::Eq;
...@@ -483,5 +505,66 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_ShippingAddress) { ...@@ -483,5 +505,66 @@ TEST_F(CollectUserDataActionTest, UserDataComplete_ShippingAddress) {
&mock_personal_data_manager_, user_data, options)); &mock_personal_data_manager_, user_data, options));
} }
TEST_F(CollectUserDataActionTest, UserDataComplete_DateTimeRange) {
UserData user_data;
CollectUserDataOptions options;
options.request_date_time_range = true;
EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
&mock_personal_data_manager_, user_data, options));
SetDateTimeProto(&user_data.date_time_range_start, 2019, 12, 31, 10, 30, 0);
SetDateTimeProto(&user_data.date_time_range_end, 2019, 1, 28, 16, 0, 0);
// Start date not before end date.
EXPECT_FALSE(CollectUserDataAction::IsUserDataComplete(
&mock_personal_data_manager_, user_data, options));
user_data.date_time_range_end.mutable_date()->set_year(2020);
EXPECT_TRUE(CollectUserDataAction::IsUserDataComplete(
&mock_personal_data_manager_, user_data, options));
}
TEST_F(CollectUserDataActionTest, SelectDateTimeRange) {
ActionProto action_proto;
auto* collect_user_data_proto = action_proto.mutable_collect_user_data();
collect_user_data_proto->set_request_terms_and_conditions(false);
auto* date_time_proto = collect_user_data_proto->mutable_date_time_range();
SetDateTimeProto(date_time_proto->mutable_start(), 2019, 10, 21, 8, 0, 0);
SetDateTimeProto(date_time_proto->mutable_end(), 2019, 11, 5, 16, 0, 0);
SetDateTimeProto(date_time_proto->mutable_min(), 2019, 11, 5, 16, 0, 0);
SetDateTimeProto(date_time_proto->mutable_max(), 2020, 11, 5, 16, 0, 0);
date_time_proto->set_start_label("Pick up");
date_time_proto->set_end_label("Return");
DateTimeProto actual_pickup_time;
DateTimeProto actual_return_time;
SetDateTimeProto(&actual_pickup_time, 2019, 10, 21, 7, 0, 0);
SetDateTimeProto(&actual_return_time, 2019, 10, 25, 19, 0, 0);
ON_CALL(mock_action_delegate_, CollectUserData(_, _))
.WillByDefault(Invoke(
[&](std::unique_ptr<CollectUserDataOptions> collect_user_data_options,
std::unique_ptr<UserData> user_data) {
user_data->succeed = true;
user_data->date_time_range_start = actual_pickup_time;
user_data->date_time_range_end = actual_return_time;
std::move(collect_user_data_options->confirm_callback)
.Run(std::move(user_data));
}));
EXPECT_CALL(
callback_,
Run(Pointee(
AllOf(Property(&ProcessedActionProto::status, ACTION_APPLIED),
Property(&ProcessedActionProto::collect_user_data_result,
Property(&CollectUserDataResultProto::date_time_start,
EqualsProto(actual_pickup_time))),
Property(&ProcessedActionProto::collect_user_data_result,
Property(&CollectUserDataResultProto::date_time_end,
EqualsProto(actual_return_time)))))));
CollectUserDataAction action(&mock_action_delegate_, action_proto);
action.ProcessAction(callback_.Get());
}
} // namespace } // namespace
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -85,6 +85,22 @@ bool StateEndsFlow(AutofillAssistantState state) { ...@@ -85,6 +85,22 @@ bool StateEndsFlow(AutofillAssistantState state) {
return false; return false;
} }
// Convenience method to set all fields of a |DateTimeProto|.
void SetDateTimeProto(DateTimeProto* proto,
int year,
int month,
int day,
int hour,
int minute,
int second) {
proto->mutable_date()->set_year(year);
proto->mutable_date()->set_month(month);
proto->mutable_date()->set_day(day);
proto->mutable_time()->set_hour(hour);
proto->mutable_time()->set_minute(minute);
proto->mutable_time()->set_second(second);
}
} // namespace } // namespace
Controller::Controller(content::WebContents* web_contents, Controller::Controller(content::WebContents* web_contents,
...@@ -968,6 +984,40 @@ void Controller::OnTermsAndConditionsLinkClicked(int link) { ...@@ -968,6 +984,40 @@ void Controller::OnTermsAndConditionsLinkClicked(int link) {
std::move(callback).Run(link); std::move(callback).Run(link);
} }
void Controller::SetDateTimeRangeStart(int year,
int month,
int day,
int hour,
int minute,
int second) {
if (!user_data_)
return;
SetDateTimeProto(&user_data_->date_time_range_start, year, month, day, hour,
minute, second);
for (ControllerObserver& observer : observers_) {
observer.OnUserDataChanged(user_data_.get());
}
UpdateCollectUserDataActions();
}
void Controller::SetDateTimeRangeEnd(int year,
int month,
int day,
int hour,
int minute,
int second) {
if (!user_data_)
return;
SetDateTimeProto(&user_data_->date_time_range_end, year, month, day, hour,
minute, second);
for (ControllerObserver& observer : observers_) {
observer.OnUserDataChanged(user_data_.get());
}
UpdateCollectUserDataActions();
}
void Controller::SetShippingAddress( void Controller::SetShippingAddress(
std::unique_ptr<autofill::AutofillProfile> address) { std::unique_ptr<autofill::AutofillProfile> address) {
if (!user_data_) if (!user_data_)
......
...@@ -157,6 +157,19 @@ class Controller : public ScriptExecutorDelegate, ...@@ -157,6 +157,19 @@ class Controller : public ScriptExecutorDelegate,
TermsAndConditionsState terms_and_conditions) override; TermsAndConditionsState terms_and_conditions) override;
void SetLoginOption(std::string identifier) override; void SetLoginOption(std::string identifier) override;
void OnTermsAndConditionsLinkClicked(int link) override; void OnTermsAndConditionsLinkClicked(int link) override;
void SetDateTimeRangeStart(int year,
int month,
int day,
int hour,
int minute,
int second) override;
void SetDateTimeRangeEnd(int year,
int month,
int day,
int hour,
int minute,
int second) override;
void GetTouchableArea(std::vector<RectF>* area) const override; void GetTouchableArea(std::vector<RectF>* area) const override;
void GetRestrictedArea(std::vector<RectF>* area) const override; void GetRestrictedArea(std::vector<RectF>* area) const override;
void GetVisualViewport(RectF* visual_viewport) const override; void GetVisualViewport(RectF* visual_viewport) const override;
......
...@@ -458,6 +458,10 @@ message CollectUserDataResultProto { ...@@ -458,6 +458,10 @@ message CollectUserDataResultProto {
optional int32 terms_link = 5; optional int32 terms_link = 5;
// The payload of the chosen login option. // The payload of the chosen login option.
optional bytes login_payload = 6; optional bytes login_payload = 6;
// The start of the date/time range, if requested.
optional DateTimeProto date_time_start = 7;
// The end of the date/time range, if requested.
optional DateTimeProto date_time_end = 8;
} }
message ProcessedActionProto { message ProcessedActionProto {
...@@ -1195,6 +1199,21 @@ message LoginDetailsProto { ...@@ -1195,6 +1199,21 @@ message LoginDetailsProto {
repeated LoginOptionProto login_options = 2; repeated LoginOptionProto login_options = 2;
} }
message DateTimeRangeProto {
// The start value of the date/time range.
optional DateTimeProto start = 1;
// The end value of the date/time range.
optional DateTimeProto end = 2;
// The minimum allowed value of the date/time range.
optional DateTimeProto min = 3;
// The maximum allowed value of the date/time range.
optional DateTimeProto max = 4;
// The label of the start date/time value (e.g., 'Pick-up').
optional string start_label = 5;
// The label of the end date/time value (e.g., 'Return').
optional string end_label = 6;
}
// Asks to provide the data used by UseAddressAction and // Asks to provide the data used by UseAddressAction and
// UseCreditCardAction. // UseCreditCardAction.
message CollectUserDataProto { message CollectUserDataProto {
...@@ -1253,6 +1272,8 @@ message CollectUserDataProto { ...@@ -1253,6 +1272,8 @@ message CollectUserDataProto {
optional string billing_postal_code_missing_text = 15; optional string billing_postal_code_missing_text = 15;
// The login details that should be gathered. // The login details that should be gathered.
optional LoginDetailsProto login_details = 16; optional LoginDetailsProto login_details = 16;
// The date/time range that should be gathered.
optional DateTimeRangeProto date_time_range = 17;
} }
// Resets Autofill Assistant: clears any state and server payload. // Resets Autofill Assistant: clears any state and server payload.
......
...@@ -123,6 +123,22 @@ class UiDelegate { ...@@ -123,6 +123,22 @@ class UiDelegate {
// Called when the user clicks a link on the terms & conditions message. // Called when the user clicks a link on the terms & conditions message.
virtual void OnTermsAndConditionsLinkClicked(int link) = 0; virtual void OnTermsAndConditionsLinkClicked(int link) = 0;
// Sets the start of the date/time range.
virtual void SetDateTimeRangeStart(int year,
int month,
int day,
int hour,
int minute,
int second) = 0;
// Sets the end of the date/time range.
virtual void SetDateTimeRangeEnd(int year,
int month,
int day,
int hour,
int minute,
int second) = 0;
// Adds the rectangles that correspond to the current touchable area to // Adds the rectangles that correspond to the current touchable area to
// the given vector. // the given vector.
// //
......
...@@ -55,6 +55,8 @@ struct UserData { ...@@ -55,6 +55,8 @@ struct UserData {
std::unique_ptr<autofill::AutofillProfile> billing_address; std::unique_ptr<autofill::AutofillProfile> billing_address;
std::string login_choice_identifier; std::string login_choice_identifier;
TermsAndConditionsState terms_and_conditions = NOT_SELECTED; TermsAndConditionsState terms_and_conditions = NOT_SELECTED;
DateTimeProto date_time_range_start;
DateTimeProto date_time_range_end;
}; };
// Struct for holding the payment request options. // Struct for holding the payment request options.
...@@ -68,6 +70,7 @@ struct CollectUserDataOptions { ...@@ -68,6 +70,7 @@ struct CollectUserDataOptions {
bool request_shipping = false; bool request_shipping = false;
bool request_payment_method = false; bool request_payment_method = false;
bool request_login_choice = false; bool request_login_choice = false;
bool request_date_time_range = false;
bool require_billing_postal_code = false; bool require_billing_postal_code = false;
std::string billing_postal_code_missing_text; std::string billing_postal_code_missing_text;
...@@ -83,6 +86,7 @@ struct CollectUserDataOptions { ...@@ -83,6 +86,7 @@ struct CollectUserDataOptions {
UserActionProto confirm_action; UserActionProto confirm_action;
std::vector<UserActionProto> additional_actions; std::vector<UserActionProto> additional_actions;
TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED; TermsAndConditionsState initial_terms_and_conditions = NOT_SELECTED;
DateTimeRangeProto date_time_range;
base::OnceCallback<void(std::unique_ptr<UserData>)> confirm_callback; base::OnceCallback<void(std::unique_ptr<UserData>)> confirm_callback;
base::OnceCallback<void(int)> additional_actions_callback; base::OnceCallback<void(int)> additional_actions_callback;
......
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