Skip to content

Commit

Permalink
[M116][UPMLocalPwd] Start the export flow
Browse files Browse the repository at this point in the history
Clicking on the Next button on the sheet starts the export flow and
leaves the sheet open in the background because the fragment owning the
flow must stay present during the export. The sheet will be closed in a
follow-up CL.

(cherry picked from commit 0dea59e)

Bug: 1445065
Change-Id: Iee44ec57eca877af547680cb1a6fdd5757794778
Low-Coverage-Reason: not useful (this is plumbing code)
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4614480
Reviewed-by: Boris Sazonov <bsazonov@chromium.org>
Auto-Submit: Ivana Žužić <izuzic@google.com>
Commit-Queue: Boris Sazonov <bsazonov@chromium.org>
Reviewed-by: Ioana Pandele <ioanap@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1161793}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4641935
Commit-Queue: Ivana Žužić <izuzic@google.com>
Cr-Commit-Position: refs/branch-heads/5845@{#84}
Cr-Branched-From: 5a5dff6-refs/heads/main@{#1160321}
  • Loading branch information
Ivana Žužić authored and Chromium LUCI CQ committed Jun 26, 2023
1 parent 26307bd commit 67db02e
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@

import android.content.Context;

import org.chromium.base.ContextUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.password_manager.settings.ExportFlow;
import org.chromium.chrome.browser.password_manager.settings.PasswordListObserver;
import org.chromium.chrome.browser.password_manager.settings.PasswordManagerHandlerProvider;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.pwd_migration.PasswordMigrationWarningCoordinator;
import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
Expand All @@ -25,10 +29,15 @@ static void showWarning(WindowAndroid windowAndroid, Profile profile) {
if (bottomSheetController == null) return;
Context context = windowAndroid.getContext().get();
if (context == null) return;
// The export flow won't work unless the sheet is started with an Activity as a Context.
if (ContextUtils.activityFromContext(context) == null) return;
PasswordMigrationWarningCoordinator passwordMigrationWarningCoordinator =
new PasswordMigrationWarningCoordinator(context, profile, bottomSheetController,
SyncConsentActivityLauncherImpl.get(), new SettingsLauncherImpl(),
ManageSyncSettings.class);
ManageSyncSettings.class, new ExportFlow(),
(PasswordListObserver observer)
-> PasswordManagerHandlerProvider.getInstance().addObserver(
observer));
passwordMigrationWarningCoordinator.showWarning();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package org.chromium.chrome.browser.password_manager.settings;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
Expand All @@ -15,13 +14,13 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager;

import org.chromium.base.ContentUriUtils;
import org.chromium.base.ContextUtils;
import org.chromium.base.StrictModeContext;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.pwd_migration.ExportFlowInterface;
import org.chromium.ui.widget.Toast;

import java.io.File;
Expand All @@ -45,7 +44,7 @@
* (4) {@link #sendExportIntent} creates an intent chooser for sharing the exported passwords with
* an app of user's choice.
*/
public class ExportFlow {
public class ExportFlow implements ExportFlowInterface {
@IntDef({ExportState.INACTIVE, ExportState.REQUESTED, ExportState.CONFIRMED})
@Retention(RetentionPolicy.SOURCE)
private @interface ExportState {
Expand Down Expand Up @@ -187,34 +186,10 @@ public DialogManager getDialogManagerForTesting() {
return mProgressBarManager;
}

/** The delegate to provide ExportFlow with essential information from the owning fragment. */
public interface Delegate {
/**
* @return The activity associated with the owning fragment.
*/
Activity getActivity();

/**
* @return The fragment manager associated with the owning fragment.
*/
FragmentManager getFragmentManager();

/**
* @return The ID of the root view of the owning fragment.
*/
int getViewId();
}

/** The concrete delegate instance. It is (re)set in {@link #onCreate}. */
private Delegate mDelegate;

/**
* A hook to be used in the onCreate method of the owning {@link Fragment}. I restores the state
* of the flow.
* @param savedInstanceState The {@link Bundle} passed from the fragment's onCreate
* method.
* @param delegate The {@link Delegate} for this ExportFlow.
*/
@Override
public void onCreate(Bundle savedInstanceState, Delegate delegate) {
mDelegate = delegate;

Expand Down Expand Up @@ -285,12 +260,7 @@ public static String getTargetDirectory() {
}
}

/**
* Starts the password export flow.
* Current state of export flow: the user just tapped the menu item for export
* The next steps are: passing reauthentication, confirming the export, waiting for exported
* data (if needed) and choosing a consumer app for the data.
*/
@Override
public void startExporting() {
assert mExportState == ExportState.INACTIVE;
// Disable re-triggering exporting until the current exporting finishes.
Expand Down Expand Up @@ -517,10 +487,7 @@ private void sendExportIntent() {
mExportFileUri = null;
}

/**
* A hook to be used in a {@link Fragment}'s onResume method. I processes the result of the
* reauthentication.
*/
@Override
public void onResume() {
if (mExportState == ExportState.REQUESTED) {
// If Chrome returns to foreground from being paused (but without being killed), and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,6 @@
* save password) from native code.
*/
public interface PasswordManagerHandler {
/**
* An interface which a client can use to listen to changes to password and password exception
* lists.
*/
interface PasswordListObserver {
/**
* Called when passwords list is updated.
* @param count Number of entries in the password list.
*/
void passwordListAvailable(int count);

/**
* Called when password exceptions list is updated.
* @param count Number of entries in the password exception list.
*/
void passwordExceptionListAvailable(int count);
}

/**
* Called to insert a password entry into the password store.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* managed PasswordManagerHandler instances need to refer to it as an observer. For that reason, the
* provider is a singleton.
*/
public class PasswordManagerHandlerProvider implements PasswordManagerHandler.PasswordListObserver {
public class PasswordManagerHandlerProvider implements PasswordListObserver {
private static final class LazyHolder {
private static final PasswordManagerHandlerProvider INSTANCE =
new PasswordManagerHandlerProvider();
Expand All @@ -41,8 +41,8 @@ public static PasswordManagerHandlerProvider getInstance() {

// This class is itself a PasswordListObserver, listening directly to a PasswordManagerHandler
// implementation. But it also keeps a list of other observers, to which it forwards the events.
private final ObserverList<PasswordManagerHandler.PasswordListObserver> mObservers =
new ObserverList<PasswordManagerHandler.PasswordListObserver>();
private final ObserverList<PasswordListObserver> mObservers =
new ObserverList<PasswordListObserver>();

/**
* Sets a testing implementation of PasswordManagerHandler to be used. It overrides the
Expand Down Expand Up @@ -96,13 +96,13 @@ private void createPasswordManagerHandler() {
/**
* Starts forwarding events from the PasswordManagerHandler implementation to |observer|.
*/
public void addObserver(PasswordManagerHandler.PasswordListObserver observer) {
public void addObserver(PasswordListObserver observer) {
ThreadUtils.assertOnUiThread();
if (getPasswordManagerHandler() == null) createPasswordManagerHandler();
mObservers.addObserver(observer);
}

public void removeObserver(PasswordManagerHandler.PasswordListObserver observer) {
public void removeObserver(PasswordListObserver observer) {
ThreadUtils.assertOnUiThread();
mObservers.removeObserver(observer);
// If this was the last observer of the production implementation of PasswordManagerHandler,
Expand All @@ -116,15 +116,15 @@ public void removeObserver(PasswordManagerHandler.PasswordListObserver observer)
@Override
public void passwordListAvailable(int count) {
ThreadUtils.assertOnUiThread();
for (PasswordManagerHandler.PasswordListObserver observer : mObservers) {
for (PasswordListObserver observer : mObservers) {
observer.passwordListAvailable(count);
}
}

@Override
public void passwordExceptionListAvailable(int count) {
ThreadUtils.assertOnUiThread();
for (PasswordManagerHandler.PasswordListObserver observer : mObservers) {
for (PasswordListObserver observer : mObservers) {
observer.passwordExceptionListAvailable(count);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
* to view saved passwords (just the username and URL), and to delete saved passwords.
*/
public class PasswordSettings extends PreferenceFragmentCompat
implements PasswordManagerHandler.PasswordListObserver,
Preference.OnPreferenceClickListener, SyncService.SyncStateChangedListener,
FragmentHelpAndFeedbackLauncher, ProfileDependentSetting {
implements PasswordListObserver, Preference.OnPreferenceClickListener,
SyncService.SyncStateChangedListener, FragmentHelpAndFeedbackLauncher,
ProfileDependentSetting {
@IntDef({TrustedVaultBannerState.NOT_SHOWN, TrustedVaultBannerState.OFFER_OPT_IN,
TrustedVaultBannerState.OPTED_IN})
@Retention(RetentionPolicy.SOURCE)
Expand Down Expand Up @@ -410,7 +410,10 @@ public void passwordListAvailable(int count) {
PasswordMigrationWarningCoordinator passwordMigrationWarningCoordinator =
new PasswordMigrationWarningCoordinator(getContext(), mProfile,
mBottomSheetController, SyncConsentActivityLauncherImpl.get(),
new SettingsLauncherImpl(), ManageSyncSettings.class);
new SettingsLauncherImpl(), ManageSyncSettings.class, new ExportFlow(),
(PasswordListObserver observer)
-> PasswordManagerHandlerProvider.getInstance().addObserver(
observer));
passwordMigrationWarningCoordinator.showWarning();
}
}
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/password_manager/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ android_library("java") {
"java/src/org/chromium/chrome/browser/password_manager/PasswordStoreCredential.java",
"java/src/org/chromium/chrome/browser/password_manager/PasswordSyncControllerDelegateBridgeImpl.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordAccessReauthenticationHelper.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordListObserver.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/PasswordReauthenticationFragment.java",
"java/src/org/chromium/chrome/browser/password_manager/settings/ReauthenticationManager.java",
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2023 The Chromium Authors
// 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.password_manager.settings;

/**
* An interface which a client can use to listen to changes to password and password exception
* lists.
*/
public interface PasswordListObserver {
/**
* Called when passwords list is updated.
*
* @param count Number of entries in the password list.
*/
void passwordListAvailable(int count);

/**
* Called when password exceptions list is updated.
*
* @param count Number of entries in the password exception list.
*/
void passwordExceptionListAvailable(int count);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ android_library("java") {
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
"//components/version_info/android:version_constants_java",
"//third_party/android_deps/utils:java",
"//third_party/androidx:androidx_annotation_annotation_java",
"//third_party/androidx:androidx_appcompat_appcompat_java",
"//third_party/androidx:androidx_appcompat_appcompat_resources_java",
Expand All @@ -33,6 +34,7 @@ android_library("java") {
]

sources = [
"java/src/org/chromium/chrome/browser/pwd_migration/ExportFlowInterface.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningCoordinator.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningIntroFragment.java",
"java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java",
Expand Down Expand Up @@ -69,6 +71,7 @@ robolectric_library("junit") {
"//components/signin/public/android:signin_java_test_support",
"//components/sync/android:sync_java",
"//components/user_prefs/android:java",
"//third_party/androidx:androidx_fragment_fragment_java",
"//third_party/espresso:espresso_core_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2023 The Chromium Authors
// 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.pwd_migration;

import android.app.Activity;
import android.os.Bundle;

import androidx.fragment.app.FragmentManager;

/**
* An interface for the implementations of {@link ExportFlow}.
*/
public interface ExportFlowInterface {
/** The delegate to provide ExportFlow with essential information from the owning fragment. */
public interface Delegate {
/**
* @return The activity associated with the owning fragment.
*/
Activity getActivity();

/**
* @return The fragment manager associated with the owning fragment.
*/
FragmentManager getFragmentManager();

/**
* @return The ID of the root view of the owning fragment.
*/
int getViewId();
}

/**
* A hook to be used in the onCreate method of the owning {@link Fragment}. I restores the state
* of the flow.
*
* @param savedInstanceState The {@link Bundle} passed from the fragment's onCreate
* method.
* @param delegate The {@link Delegate} for this ExportFlow.
*/
public void onCreate(Bundle savedInstanceState, Delegate delegate);

/**
* Starts the password export flow.
*/
public void startExporting();

/**
* A hook to be used in a {@link Fragment}'s onResume method. I processes the result of the
* reauthentication.
*/
public void onResume();
}

0 comments on commit 67db02e

Please sign in to comment.