Commit af332606 authored by Xing Liu's avatar Xing Liu Committed by Commit Bot

Download later: Move location dialog logic to its own coordinator.

DownloadDialogBridge currently holds the location dialog UI logic.

This CL moves the location dialog UI logic to its own class.
DownloadDialogBridge will be the place to glue the download later
dialog and location dialog.

Bug: 1078454
Change-Id: Ieafda28ec6feb2a4f7afcaf098ea34b789d33abd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2236135
Commit-Queue: Xing Liu <xingliu@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarMin Qin <qinmin@chromium.org>
Reviewed-by: default avatarHesen Zhang <hesen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#776815}
parent 886ba6de
...@@ -726,6 +726,7 @@ junit_binary("chrome_junit_tests") { ...@@ -726,6 +726,7 @@ junit_binary("chrome_junit_tests") {
"//chrome/android/webapk/test:junit_test_support", "//chrome/android/webapk/test:junit_test_support",
"//chrome/browser/android/lifecycle:java", "//chrome/browser/android/lifecycle:java",
"//chrome/browser/download/android:java", "//chrome/browser/download/android:java",
"//chrome/browser/download/android:junit_tests",
"//chrome/browser/flags:flags_junit_tests", "//chrome/browser/flags:flags_junit_tests",
"//chrome/browser/flags:java", "//chrome/browser/flags:java",
"//chrome/browser/image_fetcher:java", "//chrome/browser/image_fetcher:java",
......
...@@ -15,7 +15,6 @@ android_library("java") { ...@@ -15,7 +15,6 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/download/DownloadFileProvider.java", "java/src/org/chromium/chrome/browser/download/DownloadFileProvider.java",
"java/src/org/chromium/chrome/browser/download/DownloadFilter.java", "java/src/org/chromium/chrome/browser/download/DownloadFilter.java",
"java/src/org/chromium/chrome/browser/download/DownloadInfo.java", "java/src/org/chromium/chrome/browser/download/DownloadInfo.java",
"java/src/org/chromium/chrome/browser/download/DownloadLocationCustomView.java",
"java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java", "java/src/org/chromium/chrome/browser/download/DownloadManagerBridge.java",
"java/src/org/chromium/chrome/browser/download/DownloadStartupUtils.java", "java/src/org/chromium/chrome/browser/download/DownloadStartupUtils.java",
"java/src/org/chromium/chrome/browser/download/DownloadStatus.java", "java/src/org/chromium/chrome/browser/download/DownloadStatus.java",
...@@ -27,6 +26,9 @@ android_library("java") { ...@@ -27,6 +26,9 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogCoordinator.java", "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogCoordinator.java",
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogProperties.java", "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogProperties.java",
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogView.java", "java/src/org/chromium/chrome/browser/download/dialogs/DownloadLaterDialogView.java",
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationCustomView.java",
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogController.java",
"java/src/org/chromium/chrome/browser/download/dialogs/DownloadLocationDialogCoordinator.java",
"java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java", "java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinator.java",
"java/src/org/chromium/chrome/browser/download/home/DownloadManagerUiConfig.java", "java/src/org/chromium/chrome/browser/download/home/DownloadManagerUiConfig.java",
"java/src/org/chromium/chrome/browser/download/home/FaviconProvider.java", "java/src/org/chromium/chrome/browser/download/home/FaviconProvider.java",
...@@ -110,6 +112,24 @@ android_library("download_java_tests") { ...@@ -110,6 +112,24 @@ android_library("download_java_tests") {
] ]
} }
android_library("junit_tests") {
# Platform checks are broken for Robolectric. See https://crbug.com/1071638.
bypass_platform_checks = true
testonly = true
sources = [ "java/src/org/chromium/chrome/browser/download/DownloadDialogBridgeUnitTest.java" ]
deps = [
":java",
"//base:base_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//third_party/junit",
"//third_party/mockito:mockito_java",
"//ui/android:ui_full_java",
]
}
android_resources("java_resources") { android_resources("java_resources") {
sources = [ sources = [
"java/res/color/tint_on_blue_bg.xml", "java/res/color/tint_on_blue_bg.xml",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<org.chromium.chrome.browser.download.DownloadLocationCustomView <org.chromium.chrome.browser.download.dialogs.DownloadLocationCustomView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
...@@ -75,5 +75,4 @@ ...@@ -75,5 +75,4 @@
</LinearLayout> </LinearLayout>
</org.chromium.chrome.browser.download.DownloadLocationCustomView> </org.chromium.chrome.browser.download.dialogs.DownloadLocationCustomView>
// Copyright 2020 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.download;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.download.dialogs.DownloadLocationDialogCoordinator;
import org.chromium.ui.base.WindowAndroid;
/**
* Unit test for {@link DownloadDialogBridge}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class DownloadDialogBridgeUnitTest {
private static final int FAKE_NATIVE_HODLER = 1;
private static final long TOTAL_BYTES = 100;
private static final @DownloadLocationDialogType int LOCATION_DIALOG_TYPE =
org.chromium.chrome.browser.download.DownloadLocationDialogType.DEFAULT;
private static final String SUGGESTED_PATH = "sdcard/download.txt";
private DownloadDialogBridge mBridge;
@Rule
public JniMocker mJniMocker = new JniMocker();
@Mock
private DownloadDialogBridge.Natives mNativeMock;
@Mock
WindowAndroid mWindowAndroid;
@Mock
DownloadLocationDialogCoordinator mLocationDialog;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mJniMocker.mock(DownloadDialogBridgeJni.TEST_HOOKS, mNativeMock);
mBridge = new DownloadDialogBridge(FAKE_NATIVE_HODLER, mLocationDialog);
}
@After
public void tearDown() {
mBridge = null;
}
@Test
public void testShowDialog() {
mBridge.showDialog(mWindowAndroid, TOTAL_BYTES, LOCATION_DIALOG_TYPE, SUGGESTED_PATH);
verify(mLocationDialog)
.showDialog(eq(mWindowAndroid), eq(TOTAL_BYTES), eq(LOCATION_DIALOG_TYPE),
eq(SUGGESTED_PATH));
}
@Test
public void testDestroy() {
mBridge.destroy();
verify(mLocationDialog).destroy();
}
@Test
public void testLocationDialogComplete() {
mBridge.onComplete(SUGGESTED_PATH);
verify(mNativeMock).onComplete(anyLong(), any(), eq(SUGGESTED_PATH), eq(false), eq(-1L));
}
@Test
public void testLocationDialogCanceled() {
Assert.assertNotNull(mBridge);
mBridge.onCancel();
verify(mNativeMock).onCanceled(anyLong(), any());
}
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
package org.chromium.chrome.browser.download; package org.chromium.chrome.browser.download.dialogs;
import static org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID; import static org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter.NO_SELECTED_ITEM_ID;
...@@ -18,6 +18,11 @@ import android.widget.TextView; ...@@ -18,6 +18,11 @@ import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.chrome.browser.download.DirectoryOption;
import org.chromium.chrome.browser.download.DownloadDialogBridge;
import org.chromium.chrome.browser.download.DownloadLocationDialogType;
import org.chromium.chrome.browser.download.DownloadPromptStatus;
import org.chromium.chrome.browser.download.R;
import org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter; import org.chromium.chrome.browser.download.settings.DownloadDirectoryAdapter;
import org.chromium.components.browser_ui.widget.text.AlertDialogEditText; import org.chromium.components.browser_ui.widget.text.AlertDialogEditText;
......
// Copyright 2020 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.download.dialogs;
/**
* Receives events from download location dialog.
*/
public interface DownloadLocationDialogController {
/**
* Called when the user finished download location selection flow.
* @param returnedPath The download file path picked by the user.
*/
void onComplete(String returnedPath);
/**
* Called when the user cancel or dismiss the download location dialog.
*/
void onCancel();
}
// Copyright 2020 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.download.dialogs;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.download.DirectoryOption;
import org.chromium.chrome.browser.download.DownloadDialogBridge;
import org.chromium.chrome.browser.download.DownloadDirectoryProvider;
import org.chromium.chrome.browser.download.DownloadLocationDialogType;
import org.chromium.chrome.browser.download.DownloadPromptStatus;
import org.chromium.chrome.browser.download.R;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modaldialog.DialogDismissalCause;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modaldialog.ModalDialogManagerHolder;
import org.chromium.ui.modaldialog.ModalDialogProperties;
import org.chromium.ui.modelutil.PropertyModel;
import java.io.File;
import java.util.ArrayList;
/**
* The factory class that contains all dependencies for the download location dialog.
* Also provides the public functionalties to interact with dialog.
*/
// TODO(xingliu): Refactor download location dialog to fully use clank MVC.
public class DownloadLocationDialogCoordinator implements ModalDialogProperties.Controller {
@NonNull
private DownloadLocationDialogController mController;
private PropertyModel mDialogModel;
private DownloadLocationCustomView mCustomView;
private ModalDialogManager mModalDialogManager;
private long mTotalBytes;
private @DownloadLocationDialogType int mDialogType;
private String mSuggestedPath;
private Context mContext;
/**
* Initializes the download location dialog.
* @param controller Receives events from download location dialog.
*/
public void initialize(DownloadLocationDialogController controller) {
mController = controller;
}
/**
* Shows the download location dialog.
* @param windowAndroid The window android handle to provide the activity.
* @param totalBytes The total download file size. May be 0 if not available.
* @param dialogType The type of the location dialog.
* @param suggestedPath The suggested file path used by the location dialog.
*/
public void showDialog(WindowAndroid windowAndroid, long totalBytes,
@DownloadLocationDialogType int dialogType, String suggestedPath) {
Activity activity = windowAndroid.getActivity().get();
// If the activity has gone away, just clean up the native pointer.
if (activity == null) {
onDismiss(null, DialogDismissalCause.ACTIVITY_DESTROYED);
return;
}
mModalDialogManager = ((ModalDialogManagerHolder) activity).getModalDialogManager();
mContext = activity;
mTotalBytes = totalBytes;
mDialogType = dialogType;
mSuggestedPath = suggestedPath;
DownloadDirectoryProvider.getInstance().getAllDirectoriesOptions(
(ArrayList<DirectoryOption> dirs) -> { onDirectoryOptionsRetrieved(dirs); });
}
/**
* Destroy the location dialog.
*/
public void destroy() {
if (mModalDialogManager != null) {
mModalDialogManager.dismissDialog(
mDialogModel, DialogDismissalCause.DISMISSED_BY_NATIVE);
}
}
@Override
public void onClick(PropertyModel model, int buttonType) {
switch (buttonType) {
case ModalDialogProperties.ButtonType.POSITIVE:
mModalDialogManager.dismissDialog(
model, DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
break;
case ModalDialogProperties.ButtonType.NEGATIVE:
mModalDialogManager.dismissDialog(
model, DialogDismissalCause.NEGATIVE_BUTTON_CLICKED);
break;
default:
}
}
@Override
public void onDismiss(PropertyModel model, int dismissalCause) {
switch (dismissalCause) {
case DialogDismissalCause.POSITIVE_BUTTON_CLICKED:
handleResponses(mCustomView.getFileName(), mCustomView.getDirectoryOption(),
mCustomView.getDontShowAgain());
break;
default:
cancel();
break;
}
mDialogModel = null;
mCustomView = null;
}
/**
* Called after retrieved the download directory options.
* @param dirs An list of available download directories.
*/
private void onDirectoryOptionsRetrieved(ArrayList<DirectoryOption> dirs) {
// If there is only one directory available, don't show the default dialog, and set the
// download directory to default. Dialog will still show for other types of dialogs, like
// name conflict or disk error.
if (dirs.size() == 1 && mDialogType == DownloadLocationDialogType.DEFAULT) {
final DirectoryOption dir = dirs.get(0);
if (dir.type == DirectoryOption.DownloadLocationDirectoryType.DEFAULT) {
assert (!TextUtils.isEmpty(dir.location));
DownloadDialogBridge.setDownloadAndSaveFileDefaultDirectory(dir.location);
mController.onComplete(mSuggestedPath);
}
return;
}
// Already showing the dialog.
if (mDialogModel != null) return;
// Actually show the dialog.
mCustomView = (DownloadLocationCustomView) LayoutInflater.from(mContext).inflate(
R.layout.download_location_dialog, null);
mCustomView.initialize(mDialogType, new File(mSuggestedPath));
Resources resources = mContext.getResources();
mDialogModel =
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();
mModalDialogManager.showDialog(mDialogModel, 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(
org.chromium.components.browser_ui.util.DownloadUtils.getStringForBytes(
mContext, totalBytes));
title = stringBuilder.toString();
}
return title;
}
assert false;
return null;
}
/**
* Pass along information from location dialog to native.
*
* @param fileName Name the user gave the file.
* @param directoryOption Location the user wants the file saved to.
* @param dontShowAgain Whether the user wants the "Save download to..." dialog shown again.
*/
private void handleResponses(
String fileName, DirectoryOption directoryOption, boolean dontShowAgain) {
// If there's no file location, treat as a cancellation.
if (directoryOption == null || directoryOption.location == null || fileName == null) {
cancel();
return;
}
// Update native with new path.
DownloadDialogBridge.setDownloadAndSaveFileDefaultDirectory(directoryOption.location);
RecordHistogram.recordEnumeratedHistogram("MobileDownload.Location.Dialog.DirectoryType",
directoryOption.type, DirectoryOption.DownloadLocationDirectoryType.NUM_ENTRIES);
File file = new File(directoryOption.location, fileName);
assert mController != null;
mController.onComplete(file.getAbsolutePath());
// Update preference to show prompt based on whether checkbox is checked only when the user
// click the positive button.
if (dontShowAgain) {
DownloadDialogBridge.setPromptForDownloadAndroid(DownloadPromptStatus.DONT_SHOW);
} else {
DownloadDialogBridge.setPromptForDownloadAndroid(DownloadPromptStatus.SHOW_PREFERENCE);
}
}
private void cancel() {
assert mController != null;
mController.onCancel();
}
}
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