Skip to content

Commit

Permalink
Provide data to the Android save card bottom sheet.
Browse files Browse the repository at this point in the history
* Adds an Java equivalent of AutofillSaveCardUiInfo (bridged by the new
  AutofillSaveCardUiInfoAndroid C++ and Java classes).
* Provides AutofillSaveCardUiInfo to
  AutofillSaveCardBottomSheetBridge.
* AutofillSaveCardBottomSheetBridge provides callbacks into the
  AutofillSaveCardDelegate.
* Makes WebContents a forward declaration in
  autofill_save_card_bottom_sheet_bridge.h.

Low-Coverage-Reason: Jni/Bridge code not covered
Low-Coverage-Reason: https://crbug.com/1475704
Bug: 1454271
Change-Id: I5c4e45107088a7628edb99125d3df72beb0dc833
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4721766
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Jan Keitel <jkeitel@google.com>
Commit-Queue: Slobodan Pejic <slobodan@chromium.org>
Reviewed-by: Friedrich Horschig <fhorschig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1188322}
  • Loading branch information
Slobodan Pejic authored and Chromium LUCI CQ committed Aug 25, 2023
1 parent b3be984 commit 0aac0ec
Show file tree
Hide file tree
Showing 11 changed files with 459 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.components.autofill.payments.AutofillSaveCardUiInfo;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
Expand All @@ -22,12 +24,15 @@
*/
@JNINamespace("autofill")
public class AutofillSaveCardBottomSheetBridge {
private long mNativeAutofillSaveCardBottomSheetBridge;
private WindowAndroid mWindow;
private BottomSheetController mBottomSheetController;

@CalledByNative
@VisibleForTesting
/* package */ AutofillSaveCardBottomSheetBridge(WindowAndroid window) {
/* package */ AutofillSaveCardBottomSheetBridge(
long nativeAutofillSaveCardBottomSheetBridge, WindowAndroid window) {
mNativeAutofillSaveCardBottomSheetBridge = nativeAutofillSaveCardBottomSheetBridge;
mWindow = window;
mBottomSheetController = BottomSheetControllerProvider.from(window);
}
Expand All @@ -41,7 +46,7 @@ public class AutofillSaveCardBottomSheetBridge {
* @return True if shown.
*/
@CalledByNative
public boolean requestShowContent() {
public boolean requestShowContent(AutofillSaveCardUiInfo uiInfo) {
return mBottomSheetController.requestShowContent(
new BottomSheetContentImpl(mWindow.getApplicationContext()), /* animate= */ true);
}
Expand Down Expand Up @@ -104,4 +109,12 @@ public int getSheetClosedAccessibilityStringId() {
return android.R.string.ok;
}
}

@NativeMethods
public interface Natives {
void onUiShown(long nativeAutofillSaveCardBottomSheetBridge);
void onUiAccepted(long nativeAutofillSaveCardBottomSheetBridge);
void onUiCanceled(long nativeAutofillSaveCardBottomSheetBridge);
void onUiIgnored(long nativeAutofillSaveCardBottomSheetBridge);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
/** Unit tests for {@link AutofillSaveCardBottomSheetBridge}. */
@RunWith(BaseRobolectricTestRunner.class)
public final class AutofillSaveCardBottomSheetBridgeTest {
private static final long MOCK_POINTER = 0xb00fb00f;

@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();

Expand All @@ -51,7 +53,8 @@ public void setUp() {
Context mApplicationContext = ApplicationProvider.getApplicationContext();
mWindow = new WindowAndroid(mApplicationContext);
BottomSheetControllerFactory.attach(mWindow, mBottomSheetController);
mAutofillSaveCardBottomSheetBridge = new AutofillSaveCardBottomSheetBridge(mWindow);
mAutofillSaveCardBottomSheetBridge =
new AutofillSaveCardBottomSheetBridge(MOCK_POINTER, mWindow);
}

@After
Expand All @@ -63,7 +66,7 @@ public void tearDown() {
@Test
@SmallTest
public void requestShowContent_callsControllerRequestShowContent() {
mAutofillSaveCardBottomSheetBridge.requestShowContent();
mAutofillSaveCardBottomSheetBridge.requestShowContent(/*saveCardDelegate=*/null);

verify(mBottomSheetController)
.requestShowContent(
Expand All @@ -74,7 +77,7 @@ public void requestShowContent_callsControllerRequestShowContent() {
@Test
@SmallTest
public void requestShowContent_bottomSheetContentImplIsStubbed() {
mAutofillSaveCardBottomSheetBridge.requestShowContent();
mAutofillSaveCardBottomSheetBridge.requestShowContent(/*saveCardDelegate=*/null);

ArgumentCaptor<AutofillSaveCardBottomSheetBridge.BottomSheetContentImpl> contentCaptor =
ArgumentCaptor.forClass(
Expand Down
1 change: 1 addition & 0 deletions chrome/browser/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@ static_library("ui") {
"//chrome/browser/ui/android/toolbar:jni_headers",
"//chrome/browser/ui/webui/feed_internals:mojo_bindings",
"//components/autofill/android:payments_autofill_cc",
"//components/autofill/android:payments_jni_headers",
"//components/browser_ui/accessibility/android",
"//components/browser_ui/client_certificate/android",
"//components/browser_ui/share/android",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,66 @@

#include "chrome/browser/ui/android/autofill/autofill_save_card_bottom_sheet_bridge.h"

#include <jni.h>
#include <memory>

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "chrome/android/chrome_jni_headers/AutofillSaveCardBottomSheetBridge_jni.h"
#include "chrome/browser/android/resource_mapper.h"
#include "components/autofill/android/payments/legal_message_line_android.h"
#include "components/autofill/android/payments_jni_headers/AutofillSaveCardUiInfo_jni.h"
#include "components/autofill/core/browser/payments/autofill_save_card_delegate.h"
#include "components/autofill/core/browser/payments/autofill_save_card_ui_info.h"
#include "content/public/browser/web_contents.h"
#include "ui/android/window_android.h"

namespace autofill {

namespace {

static base::android::ScopedJavaLocalRef<jobject> ConvertUiInfoToJavaObject(
JNIEnv* env,
const AutofillSaveCardUiInfo& ui_info) {
// LINT.IfChange
return Java_AutofillSaveCardUiInfo_Constructor(
env, ui_info.is_for_upload,
ResourceMapper::MapToJavaDrawableId(ui_info.logo_icon_id),
ResourceMapper::MapToJavaDrawableId(ui_info.issuer_icon_id),
LegalMessageLineAndroid::ConvertToJavaLinkedList(
ui_info.legal_message_lines),
base::android::ConvertUTF16ToJavaString(env, ui_info.card_label),
base::android::ConvertUTF16ToJavaString(env, ui_info.card_sub_label),
base::android::ConvertUTF16ToJavaString(env, ui_info.title_text),
base::android::ConvertUTF16ToJavaString(env, ui_info.confirm_text),
base::android::ConvertUTF16ToJavaString(env, ui_info.cancel_text),
ui_info.is_google_pay_branding_enabled,
base::android::ConvertUTF16ToJavaString(env, ui_info.description_text));
// LINT.ThenChange(//components/autofill/android/java/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfo.java)
}

} // namespace

AutofillSaveCardBottomSheetBridge::AutofillSaveCardBottomSheetBridge(
content::WebContents* web_contents) {
auto* window = web_contents->GetNativeView()->GetWindowAndroid();
ui::WindowAndroid* window_android) {
CHECK(window_android);
java_autofill_save_card_bottom_sheet_bridge_ =
Java_AutofillSaveCardBottomSheetBridge_Constructor(
base::android::AttachCurrentThread(), window->GetJavaObject());
base::android::AttachCurrentThread(), reinterpret_cast<jlong>(this),
window_android->GetJavaObject());
}

AutofillSaveCardBottomSheetBridge::~AutofillSaveCardBottomSheetBridge() =
default;

bool AutofillSaveCardBottomSheetBridge::RequestShowContent() {
return Java_AutofillSaveCardBottomSheetBridge_requestShowContent(
base::android::AttachCurrentThread(),
java_autofill_save_card_bottom_sheet_bridge_);
void AutofillSaveCardBottomSheetBridge::RequestShowContent(
const AutofillSaveCardUiInfo& ui_info,
std::unique_ptr<AutofillSaveCardDelegate> delegate) {
JNIEnv* env = base::android::AttachCurrentThread();
save_card_delegate_ = std::move(delegate);
Java_AutofillSaveCardBottomSheetBridge_requestShowContent(
env, java_autofill_save_card_bottom_sheet_bridge_,
ConvertUiInfoToJavaObject(env, ui_info));
}

AutofillSaveCardBottomSheetBridge::AutofillSaveCardBottomSheetBridge(
Expand All @@ -36,4 +72,23 @@ AutofillSaveCardBottomSheetBridge::AutofillSaveCardBottomSheetBridge(
: java_autofill_save_card_bottom_sheet_bridge_(
java_autofill_save_card_bottom_sheet_bridge) {}

void AutofillSaveCardBottomSheetBridge::OnUiShown(JNIEnv* env) {
save_card_delegate_->OnUiShown();
}

void AutofillSaveCardBottomSheetBridge::OnUiAccepted(JNIEnv* env) {
save_card_delegate_->OnUiAccepted();
save_card_delegate_.reset(nullptr);
}

void AutofillSaveCardBottomSheetBridge::OnUiCanceled(JNIEnv* env) {
save_card_delegate_->OnUiCanceled();
save_card_delegate_.reset(nullptr);
}

void AutofillSaveCardBottomSheetBridge::OnUiIgnored(JNIEnv* env) {
save_card_delegate_->OnUiIgnored();
save_card_delegate_.reset(nullptr);
}

} // namespace autofill
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
#ifndef CHROME_BROWSER_UI_ANDROID_AUTOFILL_AUTOFILL_SAVE_CARD_BOTTOM_SHEET_BRIDGE_H_
#define CHROME_BROWSER_UI_ANDROID_AUTOFILL_AUTOFILL_SAVE_CARD_BOTTOM_SHEET_BRIDGE_H_

#include <jni.h>

#include "base/android/scoped_java_ref.h"
#include "content/public/browser/web_contents_user_data.h"
#include "ui/android/window_android.h"

namespace ui {
class WindowAndroid;
}

namespace autofill {

class AutofillSaveCardDelegate;
struct AutofillSaveCardUiInfo;

// Bridge class owned by ChromeAutofillClient providing an entry point
// to trigger the save card bottom sheet on Android.
class AutofillSaveCardBottomSheetBridge {
public:
explicit AutofillSaveCardBottomSheetBridge(content::WebContents* contents);
// The window must not be null.
explicit AutofillSaveCardBottomSheetBridge(ui::WindowAndroid* window_android);

AutofillSaveCardBottomSheetBridge(const AutofillSaveCardBottomSheetBridge&) =
delete;
Expand All @@ -25,9 +33,20 @@ class AutofillSaveCardBottomSheetBridge {
virtual ~AutofillSaveCardBottomSheetBridge();

// Requests to show the save card bottom sheet.
// Returns true if the bottom sheet was shown.
// Overridden in tests.
virtual bool RequestShowContent();
virtual void RequestShowContent(
const AutofillSaveCardUiInfo& ui_info,
std::unique_ptr<AutofillSaveCardDelegate> delegate);

// -- JNI calls bridged to AutofillSaveCardDelegate --
// Called when the UI is shown.
void OnUiShown(JNIEnv* env);
// Called when the user has accepted the prompt.
void OnUiAccepted(JNIEnv* env);
// Called when the user explicitly cancelled the prompt.
void OnUiCanceled(JNIEnv* env);
// Called if the user has ignored the prompt.
void OnUiIgnored(JNIEnv* env);

protected:
// Used in tests to inject dependencies.
Expand All @@ -38,6 +57,7 @@ class AutofillSaveCardBottomSheetBridge {
private:
base::android::ScopedJavaGlobalRef<jobject>
java_autofill_save_card_bottom_sheet_bridge_;
std::unique_ptr<AutofillSaveCardDelegate> save_card_delegate_;
};

} // namespace autofill
Expand Down
25 changes: 16 additions & 9 deletions chrome/browser/ui/autofill/chrome_autofill_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -772,15 +772,6 @@ void ChromeAutofillClient::ConfirmSaveCreditCardToCloud(
UploadSaveCardPromptCallback callback) {
#if BUILDFLAG(IS_ANDROID)
DCHECK(options.show_prompt);
if (base::FeatureList::IsEnabled(
features::kAutofillEnablePaymentsAndroidBottomSheet)) {
if (!autofill_save_card_bottom_sheet_bridge_) {
autofill_save_card_bottom_sheet_bridge_ =
std::make_unique<AutofillSaveCardBottomSheetBridge>(web_contents());
}
autofill_save_card_bottom_sheet_bridge_->RequestShowContent();
return;
}
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(GetProfile());
AccountInfo account_info = identity_manager->FindExtendedAccountInfo(
Expand All @@ -789,6 +780,22 @@ void ChromeAutofillClient::ConfirmSaveCreditCardToCloud(
options, card, legal_message_lines, account_info);
auto common_delegate =
std::make_unique<AutofillSaveCardDelegate>(std::move(callback), options);
if (base::FeatureList::IsEnabled(
features::kAutofillEnablePaymentsAndroidBottomSheet)) {
if (!autofill_save_card_bottom_sheet_bridge_) {
// During shutdown the window may be null. There is no need to show the
// bottom sheet during shutdown.
auto* window_android = web_contents()->GetTopLevelNativeWindow();
if (!window_android) {
return;
}
autofill_save_card_bottom_sheet_bridge_ =
std::make_unique<AutofillSaveCardBottomSheetBridge>(window_android);
}
autofill_save_card_bottom_sheet_bridge_->RequestShowContent(
ui_info, std::move(common_delegate));
return;
}
infobars::ContentInfoBarManager::FromWebContents(web_contents())
->AddInfoBar(CreateSaveCardInfoBarMobile(
std::make_unique<AutofillSaveCardInfoBarDelegateMobile>(
Expand Down
22 changes: 20 additions & 2 deletions chrome/browser/ui/autofill/chrome_autofill_client_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/android/autofill/autofill_save_card_bottom_sheet_bridge.h"
#include "components/autofill/core/browser/payments/autofill_save_card_delegate.h"
#include "components/autofill/core/browser/payments/autofill_save_card_ui_info.h"
#endif

namespace autofill {
Expand All @@ -45,7 +47,11 @@ class MockAutofillSaveCardBottomSheetBridge
: AutofillSaveCardBottomSheetBridge(
base::android::ScopedJavaGlobalRef<jobject>(nullptr)) {}

MOCK_METHOD(bool, RequestShowContent, (), (override));
MOCK_METHOD(void,
RequestShowContent,
(const AutofillSaveCardUiInfo&,
std::unique_ptr<AutofillSaveCardDelegate>),
(override));
};
#endif

Expand Down Expand Up @@ -216,13 +222,25 @@ TEST_F(ChromeAutofillClientTestWithPaymentsAndroidBottomSheetFeature,
auto* bottom_sheet_bridge =
autofill_client->InjectMockAutofillSaveCardBottomSheetBridge();

EXPECT_CALL(*bottom_sheet_bridge, RequestShowContent());
EXPECT_CALL(*bottom_sheet_bridge,
RequestShowContent(testing::An<const AutofillSaveCardUiInfo&>(),
testing::NotNull()));

autofill_client->ConfirmSaveCreditCardToCloud(
CreditCard(), LegalMessageLines(),
ChromeAutofillClient::SaveCreditCardOptions().with_show_prompt(true),
base::DoNothing());
}

TEST_F(ChromeAutofillClientTestWithPaymentsAndroidBottomSheetFeature,
ConfirmSaveCreditCardToCloud_DoesNotFailWithoutAWindow) {
TestChromeAutofillClient* autofill_client = client();

EXPECT_NO_FATAL_FAILURE(autofill_client->ConfirmSaveCreditCardToCloud(
CreditCard(), LegalMessageLines(),
ChromeAutofillClient::SaveCreditCardOptions().with_show_prompt(true),
base::DoNothing()));
}
#endif

} // namespace
Expand Down
7 changes: 7 additions & 0 deletions components/autofill/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -202,32 +202,39 @@ android_library("payments_autofill_java") {
sources = [
"java/src/org/chromium/components/autofill/Completable.java",
"java/src/org/chromium/components/autofill/EditableOption.java",
"java/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfo.java",
"java/src/org/chromium/components/autofill/payments/CardDetail.java",
"java/src/org/chromium/components/autofill/payments/LegalMessageLine.java",
]
deps = [
"//base:jni_java",
"//third_party/android_deps:guava_android_java",
"//third_party/androidx:androidx_annotation_annotation_java",
]
srcjar_deps = [ ":payments_jni_headers" ]
}

robolectric_library("payments_autofill_junit_tests") {
sources = [
"javatests/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfoTest.java",
"javatests/src/org/chromium/components/autofill/payments/CardDetailTest.java",
"javatests/src/org/chromium/components/autofill/payments/LegalMessageLineTest.java",
]
deps = [
":payments_autofill_java",
"//base:base_java_test_support",
"//base:base_junit_test_support",
"//third_party/android_deps:guava_android_java",
"//third_party/hamcrest:hamcrest_core_java",
"//third_party/hamcrest:hamcrest_library_java",
"//third_party/junit:junit",
"//third_party/mockito:mockito_java",
]
}

generate_jni("payments_jni_headers") {
sources = [
"java/src/org/chromium/components/autofill/payments/AutofillSaveCardUiInfo.java",
"java/src/org/chromium/components/autofill/payments/LegalMessageLine.java",
]
deps = [ "//base:jni_java" ]
Expand Down

0 comments on commit 0aac0ec

Please sign in to comment.