Commit 31b3c895 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[ModalDialog] Add ModalDialogModel and ViewBinder

+ Added a ModalDialogModel and ModalDialogViewBinder
+ Migrate DownloadLocationDialog to use the model

Bug: 899318
Change-Id: I61d019cb4d707a65b09d30c2c3e185a63fd6caca
Reviewed-on: https://chromium-review.googlesource.com/c/1313763
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606297}
parent 3e2b2d9a
......@@ -3,7 +3,6 @@
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<!-- TODO(huayinz): rename menu_bg or change the dialog background to the desired one. -->
<org.chromium.chrome.browser.widget.BoundedLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
......@@ -24,6 +23,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="gone"
android:fadeScrollbars="false">
<LinearLayout
......@@ -40,6 +40,7 @@
<org.chromium.ui.widget.TextViewWithLeading
android:id="@+id/message"
android:textAppearance="@style/BlackBody"
android:visibility="gone"
app:leading="20sp"
style="@style/AlertDialogContent" />
......@@ -51,23 +52,28 @@
android:id="@+id/custom"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
android:layout_weight="1"
android:visibility="gone" />
<android.support.v7.widget.ButtonBarLayout
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarStyle">
<android.support.v7.widget.AppCompatButton
android:id="@+id/negative_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarNegativeButtonStyle" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/positive_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
style="?attr/buttonBarPositiveButtonStyle" />
</android.support.v7.widget.ButtonBarLayout>
......
......@@ -3,7 +3,7 @@
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<ScrollView
<org.chromium.chrome.browser.download.DownloadLocationCustomView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
......@@ -74,5 +74,5 @@
</LinearLayout>
</ScrollView>
</org.chromium.chrome.browser.download.DownloadLocationCustomView>
......@@ -7,15 +7,15 @@ package org.chromium.chrome.browser.download;
import static org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID;
import android.content.Context;
import android.view.LayoutInflater;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.preferences.download.DownloadDirectoryAdapter;
import org.chromium.chrome.browser.widget.AlertDialogEditText;
......@@ -28,91 +28,61 @@ import javax.annotation.Nullable;
/**
* Dialog that is displayed to ask user where they want to download the file.
*/
public class DownloadLocationDialog extends ModalDialogView
implements OnCheckedChangeListener, DownloadDirectoryAdapter.Delegate {
public class DownloadLocationCustomView
extends ScrollView implements OnCheckedChangeListener, DownloadDirectoryAdapter.Delegate {
private DownloadDirectoryAdapter mDirectoryAdapter;
private TextView mSubtitleView;
private AlertDialogEditText mFileName;
private Spinner mFileLocation;
private CheckBox mDontShowAgain;
private @DownloadLocationDialogType int mDialogType;
/**
* Create a {@link DownloadLocationDialog} with the given properties.
*
* @param controller Controller that listens to the events from the dialog.
* @param context Context from which the dialog emerged.
* @param totalBytes The total bytes of the file. Can be 0 if size is unknown.
* @param dialogType Type of dialog that should be displayed, dictates title/subtitle.
* @param suggestedPath The path that was automatically generated, used as a starting point.
* @return A {@link DownloadLocationDialog} with the given properties.
*/
public static DownloadLocationDialog create(Controller controller, Context context,
long totalBytes, @DownloadLocationDialogType int dialogType, File suggestedPath) {
Params params = new Params();
params.positiveButtonTextId = R.string.duplicate_download_infobar_download_button;
params.negativeButtonTextId = R.string.cancel;
params.customView =
LayoutInflater.from(context).inflate(R.layout.download_location_dialog, null);
params.title = context.getString(R.string.download_location_dialog_title);
TextView subtitleText = params.customView.findViewById(R.id.subtitle);
subtitleText.setVisibility(
dialogType == DownloadLocationDialogType.DEFAULT ? View.GONE : View.VISIBLE);
public DownloadLocationCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
mDirectoryAdapter = new DownloadDirectoryAdapter(context, this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSubtitleView = findViewById(R.id.subtitle);
mFileName = findViewById(R.id.file_name);
mFileLocation = findViewById(R.id.file_location);
mDontShowAgain = findViewById(R.id.show_again_checkbox);
}
void initialize(@DownloadLocationDialogType int dialogType, File suggestedPath) {
mDialogType = dialogType;
// Automatically check "don't show again" the first time the user is seeing the dialog.
boolean isInitial = PrefServiceBridge.getInstance().getPromptForDownloadAndroid()
== DownloadPromptStatus.SHOW_INITIAL;
mDontShowAgain.setChecked(isInitial);
mDontShowAgain.setOnCheckedChangeListener(this);
mFileName.setText(suggestedPath.getName());
mSubtitleView.setVisibility(
dialogType == DownloadLocationDialogType.DEFAULT ? View.GONE : View.VISIBLE);
switch (dialogType) {
case DownloadLocationDialogType.LOCATION_FULL:
params.title = context.getString(R.string.download_location_not_enough_space);
subtitleText.setText(R.string.download_location_download_to_default_folder);
mSubtitleView.setText(R.string.download_location_download_to_default_folder);
break;
case DownloadLocationDialogType.LOCATION_NOT_FOUND:
params.title = context.getString(R.string.download_location_no_sd_card);
subtitleText.setText(R.string.download_location_download_to_default_folder);
mSubtitleView.setText(R.string.download_location_download_to_default_folder);
break;
case DownloadLocationDialogType.NAME_CONFLICT:
params.title = context.getString(R.string.download_location_download_again);
subtitleText.setText(R.string.download_location_name_exists);
mSubtitleView.setText(R.string.download_location_name_exists);
break;
case DownloadLocationDialogType.NAME_TOO_LONG:
params.title = context.getString(R.string.download_location_rename_file);
subtitleText.setText(R.string.download_location_name_too_long);
break;
case DownloadLocationDialogType.DEFAULT:
if (totalBytes > 0) {
StringBuilder title = new StringBuilder(params.title);
title.append(" ");
title.append(DownloadUtils.getStringForBytes(context, totalBytes));
params.title = title.toString();
}
mSubtitleView.setText(R.string.download_location_name_too_long);
break;
}
return new DownloadLocationDialog(controller, context, dialogType, suggestedPath, params);
}
private DownloadLocationDialog(Controller controller, Context context,
@DownloadLocationDialogType int dialogType, File suggestedPath, Params params) {
super(controller, params);
mFileName = (AlertDialogEditText) params.customView.findViewById(R.id.file_name);
mFileName.setText(suggestedPath.getName());
mFileLocation = (Spinner) params.customView.findViewById(R.id.file_location);
// Automatically check "don't show again" the first time the user is seeing the dialog.
mDontShowAgain = (CheckBox) params.customView.findViewById(R.id.show_again_checkbox);
boolean isInitial = PrefServiceBridge.getInstance().getPromptForDownloadAndroid()
== DownloadPromptStatus.SHOW_INITIAL;
mDontShowAgain.setChecked(isInitial);
mDontShowAgain.setOnCheckedChangeListener(this);
mDialogType = dialogType;
mDirectoryAdapter = new DownloadDirectoryAdapter(context, this);
mDirectoryAdapter.update();
}
......
......@@ -5,15 +5,22 @@
package org.chromium.chrome.browser.download;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.view.LayoutInflater;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.modaldialog.DialogDismissalCause;
import org.chromium.chrome.browser.modaldialog.ModalDialogManager;
import org.chromium.chrome.browser.modaldialog.ModalDialogProperties;
import org.chromium.chrome.browser.modaldialog.ModalDialogView;
import org.chromium.chrome.browser.modaldialog.ModalDialogViewBinder;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.download.R;
import org.chromium.ui.base.WindowAndroid;
import java.io.File;
......@@ -24,7 +31,8 @@ import java.util.ArrayList;
*/
public class DownloadLocationDialogBridge implements ModalDialogView.Controller {
private long mNativeDownloadLocationDialogBridge;
private DownloadLocationDialog mLocationDialog;
private ModalDialogView mLocationDialog;
private DownloadLocationCustomView mCustomView;
private ModalDialogManager mModalDialogManager;
private long mTotalBytes;
private @DownloadLocationDialogType int mDialogType;
......@@ -82,22 +90,21 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
break;
default:
}
mLocationDialog = null;
}
@Override
public void onDismiss(@DialogDismissalCause int dismissalCause) {
switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
handleResponses(mLocationDialog.getFileName(), mLocationDialog.getDirectoryOption(),
mLocationDialog.getDontShowAgain());
handleResponses(mCustomView.getFileName(), mCustomView.getDirectoryOption(),
mCustomView.getDontShowAgain());
break;
default:
cancel();
break;
}
mLocationDialog = null;
mCustomView = null;
}
/**
......@@ -123,11 +130,56 @@ public class DownloadLocationDialogBridge implements ModalDialogView.Controller
if (mLocationDialog != null) return;
// Actually show the dialog.
mLocationDialog = DownloadLocationDialog.create(
this, mContext, mTotalBytes, mDialogType, new File(mSuggestedPath));
mCustomView = (DownloadLocationCustomView) LayoutInflater.from(mContext).inflate(
R.layout.download_location_dialog, null);
mCustomView.initialize(mDialogType, new File(mSuggestedPath));
Resources resources = mContext.getResources();
PropertyModel model =
new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS)
.with(ModalDialogProperties.CONTROLLER, this)
.with(ModalDialogProperties.TITLE, getTitle(mTotalBytes, mDialogType))
.with(ModalDialogProperties.CUSTOM_VIEW, mCustomView)
.with(ModalDialogProperties.POSITIVE_BUTTON_TEXT, resources,
R.string.duplicate_download_infobar_download_button)
.with(ModalDialogProperties.NEGATIVE_BUTTON_TEXT, resources,
R.string.cancel)
.build();
mLocationDialog = new ModalDialogView(mContext);
PropertyModelChangeProcessor.create(model, mLocationDialog, new ModalDialogViewBinder());
mModalDialogManager.showDialog(mLocationDialog, ModalDialogManager.ModalDialogType.APP);
}
private String getTitle(long totalBytes, @DownloadLocationDialogType int dialogType) {
switch (dialogType) {
case DownloadLocationDialogType.LOCATION_FULL:
return mContext.getString(R.string.download_location_not_enough_space);
case DownloadLocationDialogType.LOCATION_NOT_FOUND:
return mContext.getString(R.string.download_location_no_sd_card);
case DownloadLocationDialogType.NAME_CONFLICT:
return mContext.getString(R.string.download_location_download_again);
case DownloadLocationDialogType.NAME_TOO_LONG:
return mContext.getString(R.string.download_location_rename_file);
case DownloadLocationDialogType.DEFAULT:
String title = mContext.getString(R.string.download_location_dialog_title);
if (totalBytes > 0) {
StringBuilder stringBuilder = new StringBuilder(title);
stringBuilder.append(" ");
stringBuilder.append(DownloadUtils.getStringForBytes(mContext, totalBytes));
title = stringBuilder.toString();
}
return title;
}
assert false;
return null;
}
/**
* Pass along information from location dialog to native.
*
......
// 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.modaldialog;
import android.view.View;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel.WritableObjectPropertyKey;
/**
* The model properties for a modal dialog.
*/
public class ModalDialogProperties {
/** The {@link ModalDialogView.Controller} that handles events on user actions. */
public static final ReadableObjectPropertyKey<ModalDialogView.Controller> CONTROLLER =
new ReadableObjectPropertyKey<>();
/** The title of the dialog. */
public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
/** The message of the dialog. */
public static final WritableObjectPropertyKey<String> MESSAGE =
new WritableObjectPropertyKey<>();
/** The customized content view of the dialog. */
public static final WritableObjectPropertyKey<View> CUSTOM_VIEW =
new WritableObjectPropertyKey<>();
/** The text on the positive button. */
public static final WritableObjectPropertyKey<String> POSITIVE_BUTTON_TEXT =
new WritableObjectPropertyKey<>();
/** The enabled state on the positive button. */
public static final WritableBooleanPropertyKey POSITIVE_BUTTON_DISABLED =
new WritableBooleanPropertyKey();
/** The text on the negative button. */
public static final WritableObjectPropertyKey<String> NEGATIVE_BUTTON_TEXT =
new WritableObjectPropertyKey<>();
/** The enabled state on the negative button. */
public static final WritableBooleanPropertyKey NEGATIVE_BUTTON_DISABLED =
new WritableBooleanPropertyKey();
/** Whether the dialog should be dismissed on user tapping the scrim. */
public static final WritableBooleanPropertyKey CANCEL_ON_TOUCH_OUTSIDE =
new WritableBooleanPropertyKey();
/** Whether the title is scrollable with the message. */
public static final WritableBooleanPropertyKey TITLE_SCROLLABLE =
new WritableBooleanPropertyKey();
public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {CONTROLLER, TITLE, MESSAGE,
CUSTOM_VIEW, POSITIVE_BUTTON_TEXT, POSITIVE_BUTTON_DISABLED, NEGATIVE_BUTTON_TEXT,
NEGATIVE_BUTTON_DISABLED, CANCEL_ON_TOUCH_OUTSIDE, TITLE_SCROLLABLE};
}
// 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.modaldialog;
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModel;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
/**
* This class is responsible for binding view properties from {@link ModalDialogProperties} to a
* {@link ModalDialogView}.
*/
public class ModalDialogViewBinder
implements PropertyModelChangeProcessor
.ViewBinder<PropertyModel, ModalDialogView, PropertyKey> {
@Override
public void bind(PropertyModel model, ModalDialogView view, PropertyKey propertyKey) {
if (ModalDialogProperties.TITLE == propertyKey) {
view.setTitle(model.get(ModalDialogProperties.TITLE));
} else if (ModalDialogProperties.MESSAGE == propertyKey) {
view.setMessage(model.get(ModalDialogProperties.MESSAGE));
} else if (ModalDialogProperties.CUSTOM_VIEW == propertyKey) {
view.setCustomView(model.get(ModalDialogProperties.CUSTOM_VIEW));
} else if (ModalDialogProperties.POSITIVE_BUTTON_TEXT == propertyKey) {
view.setButtonText(ModalDialogView.ButtonType.POSITIVE,
model.get(ModalDialogProperties.POSITIVE_BUTTON_TEXT));
} else if (ModalDialogProperties.POSITIVE_BUTTON_DISABLED == propertyKey) {
view.setButtonEnabled(ModalDialogView.ButtonType.POSITIVE,
!model.get(ModalDialogProperties.POSITIVE_BUTTON_DISABLED));
} else if (ModalDialogProperties.NEGATIVE_BUTTON_TEXT == propertyKey) {
view.setButtonText(ModalDialogView.ButtonType.NEGATIVE,
model.get(ModalDialogProperties.NEGATIVE_BUTTON_TEXT));
} else if (ModalDialogProperties.NEGATIVE_BUTTON_DISABLED == propertyKey) {
view.setButtonEnabled(ModalDialogView.ButtonType.NEGATIVE,
!model.get(ModalDialogProperties.NEGATIVE_BUTTON_DISABLED));
} else if (ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE == propertyKey) {
view.setCancelOnTouchOutside(model.get(ModalDialogProperties.CANCEL_ON_TOUCH_OUTSIDE));
} else if (ModalDialogProperties.TITLE_SCROLLABLE == propertyKey) {
view.setTitleScrollable(model.get(ModalDialogProperties.TITLE_SCROLLABLE));
} else if (ModalDialogProperties.CONTROLLER == propertyKey) {
view.setController(model.get(ModalDialogProperties.CONTROLLER));
} else {
assert false : "Unhandled property detected in ModalDialogViewBinder!";
}
}
}
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.modelutil;
import android.content.res.Resources;
import android.support.annotation.StringRes;
import android.support.v4.util.ObjectsCompat;
import org.chromium.base.annotations.RemovableInRelease;
......@@ -241,6 +243,18 @@ public class PropertyModel extends PropertyObservable<PropertyKey> {
return this;
}
/**
* @param key The key of the specified {@link ReadableObjectPropertyKey<String>}.
* @param resources The {@link Resources} for obtaining the specified string resource.
* @param resId The specified string resource id.
* @return The {@link Builder} with the specified key and string resource set.
*/
public Builder with(
ReadableObjectPropertyKey<String> key, Resources resources, @StringRes int resId) {
if (resId != 0) with(key, resources.getString(resId));
return this;
}
public PropertyModel build() {
return new PropertyModel(mData);
}
......
......@@ -458,7 +458,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
"java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java",
"java/src/org/chromium/chrome/browser/download/DownloadItem.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationDialog.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationDialogBridge.java",
"java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java",
"java/src/org/chromium/chrome/browser/download/DownloadManagerService.java",
......@@ -899,7 +899,9 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java",
"java/src/org/chromium/chrome/browser/modaldialog/DialogDismissalCause.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogManager.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogProperties.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogView.java",
"java/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewBinder.java",
"java/src/org/chromium/chrome/browser/modaldialog/TabModalLifetimeHandler.java",
"java/src/org/chromium/chrome/browser/modaldialog/TabModalPresenter.java",
"java/src/org/chromium/chrome/browser/modelutil/ForwardingListObservable.java",
......@@ -1964,6 +1966,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/metrics/StartupLoadingMetricsTest.java",
"javatests/src/org/chromium/chrome/browser/metrics/UkmTest.java",
"javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogManagerTest.java",
"javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewTest.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowTestHelper.java",
"javatests/src/org/chromium/chrome/browser/multiwindow/MultiWindowUtilsTest.java",
......
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