blob: 42fda2520d705080a85e06a9a1160f7575e054eb [file] [log] [blame]
// Copyright 2021 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.
#include "chrome/browser/android/autofill_assistant/starter_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/notreached.h"
#include "base/time/default_tick_clock.h"
#include "chrome/android/features/autofill_assistant/jni_headers/AssistantOnboardingHelperImpl_jni.h"
#include "chrome/android/features/autofill_assistant/jni_headers_public/Starter_jni.h"
#include "chrome/browser/android/autofill_assistant/client_android.h"
#include "chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h"
#include "chrome/browser/android/autofill_assistant/ui_controller_android_utils.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "components/autofill_assistant/browser/public/runtime_manager_impl.h"
#include "components/autofill_assistant/browser/script_parameters.h"
#include "components/autofill_assistant/browser/website_login_manager_impl.h"
#include "components/version_info/android/channel_getter.h"
#include "components/version_info/channel.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "url/gurl.h"
using base::android::JavaParamRef;
using base::android::ScopedJavaLocalRef;
namespace autofill_assistant {
static jlong JNI_Starter_FromWebContents(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents) {
auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
CHECK(web_contents);
StarterAndroid::CreateForWebContents(web_contents);
auto* tab_helper_android = StarterAndroid::FromWebContents(web_contents);
return reinterpret_cast<intptr_t>(tab_helper_android);
}
StarterAndroid::StarterAndroid(content::WebContents* web_contents)
: content::WebContentsUserData<StarterAndroid>(*web_contents),
website_login_manager_(std::make_unique<WebsiteLoginManagerImpl>(
ChromePasswordManagerClient::FromWebContents(web_contents),
web_contents)) {}
StarterAndroid::~StarterAndroid() = default;
void StarterAndroid::Attach(JNIEnv* env, const JavaParamRef<jobject>& jcaller) {
Detach(env, jcaller);
java_object_ = base::android::ScopedJavaGlobalRef<jobject>(jcaller);
starter_ = std::make_unique<Starter>(
&GetWebContents(), this, ukm::UkmRecorder::Get(),
RuntimeManagerImpl::GetForWebContents(&GetWebContents())->GetWeakPtr(),
base::DefaultTickClock::GetInstance());
}
void StarterAndroid::Detach(JNIEnv* env, const JavaParamRef<jobject>& jcaller) {
java_object_ = nullptr;
java_dependencies_ = nullptr;
starter_.reset();
}
std::unique_ptr<TriggerScriptCoordinator::UiDelegate>
StarterAndroid::CreateTriggerScriptUiDelegate() {
CreateJavaDependenciesIfNecessary();
return std::make_unique<TriggerScriptBridgeAndroid>(
base::android::AttachCurrentThread(),
GetWebContents().GetJavaWebContents(), java_dependencies_);
}
std::unique_ptr<ServiceRequestSender>
StarterAndroid::GetTriggerScriptRequestSenderToInject() {
DCHECK(GetFeatureModuleInstalled());
return ui_controller_android_utils::GetServiceRequestSenderToInject(
base::android::AttachCurrentThread());
}
WebsiteLoginManager* StarterAndroid::GetWebsiteLoginManager() const {
return website_login_manager_.get();
}
version_info::Channel StarterAndroid::GetChannel() const {
return version_info::android::GetChannel();
}
bool StarterAndroid::GetFeatureModuleInstalled() const {
return Java_Starter_getFeatureModuleInstalled(
base::android::AttachCurrentThread());
}
void StarterAndroid::InstallFeatureModule(
bool show_ui,
base::OnceCallback<void(Metrics::FeatureModuleInstallation result)>
callback) {
DCHECK(java_object_);
feature_module_installation_finished_callback_ = std::move(callback);
Java_Starter_installFeatureModule(base::android::AttachCurrentThread(),
java_object_, show_ui);
}
void StarterAndroid::OnFeatureModuleInstalled(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jint result) {
DCHECK(feature_module_installation_finished_callback_);
std::move(feature_module_installation_finished_callback_)
.Run(static_cast<Metrics::FeatureModuleInstallation>(result));
}
void StarterAndroid::OnInteractabilityChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean is_interactable) {
if (!starter_) {
return;
}
starter_->OnTabInteractabilityChanged(is_interactable);
}
void StarterAndroid::OnActivityAttachmentChanged(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller) {
java_dependencies_ = nullptr;
if (!starter_) {
return;
}
// Notify the starter. Some flows are only available in CCT or in regular tab,
// so we need to cancel ongoing flows if they are no longer supported.
starter_->OnDependenciesInvalidated();
}
bool StarterAndroid::GetIsFirstTimeUser() const {
return Java_Starter_getIsFirstTimeUser(base::android::AttachCurrentThread());
}
void StarterAndroid::SetIsFirstTimeUser(bool first_time_user) {
Java_Starter_setIsFirstTimeUser(base::android::AttachCurrentThread(),
first_time_user);
}
bool StarterAndroid::GetOnboardingAccepted() const {
return Java_Starter_getOnboardingAccepted(
base::android::AttachCurrentThread());
}
void StarterAndroid::SetOnboardingAccepted(bool accepted) {
Java_Starter_setOnboardingAccepted(base::android::AttachCurrentThread(),
accepted);
}
void StarterAndroid::ShowOnboarding(
bool use_dialog_onboarding,
const TriggerContext& trigger_context,
base::OnceCallback<void(bool shown, OnboardingResult result)> callback) {
CreateJavaDependenciesIfNecessary();
if (onboarding_finished_callback_) {
DCHECK(false) << "onboarding requested while already being shown";
std::move(callback).Run(false, OnboardingResult::DISMISSED);
return;
}
onboarding_finished_callback_ = std::move(callback);
std::vector<std::string> keys;
std::vector<std::string> values;
for (const auto& param : trigger_context.GetScriptParameters().ToProto()) {
keys.emplace_back(param.name());
values.emplace_back(param.value());
}
JNIEnv* env = base::android::AttachCurrentThread();
Java_Starter_showOnboarding(env, java_object_, java_onboarding_helper_,
use_dialog_onboarding,
base::android::ConvertUTF8ToJavaString(
env, trigger_context.GetExperimentIds()),
base::android::ToJavaArrayOfStrings(env, keys),
base::android::ToJavaArrayOfStrings(env, values));
}
void StarterAndroid::HideOnboarding() {
if (!java_dependencies_) {
return;
}
Java_Starter_hideOnboarding(base::android::AttachCurrentThread(),
java_object_, java_onboarding_helper_);
}
void StarterAndroid::OnOnboardingFinished(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
jboolean shown,
jint result) {
// Currently, java may end up attempting to notify the native starter more
// than once. The first notification is the user-triggered one, the others are
// due to secondary effects, e.g., due to the dialog being hidden.
// TODO(arbesser): fix this such that the callback is only called once.
if (!onboarding_finished_callback_) {
return;
}
std::move(onboarding_finished_callback_)
.Run(shown, static_cast<OnboardingResult>(result));
}
bool StarterAndroid::GetProactiveHelpSettingEnabled() const {
return Java_Starter_getProactiveHelpSettingEnabled(
base::android::AttachCurrentThread());
}
void StarterAndroid::SetProactiveHelpSettingEnabled(bool enabled) {
Java_Starter_setProactiveHelpSettingEnabled(
base::android::AttachCurrentThread(), enabled);
}
bool StarterAndroid::GetMakeSearchesAndBrowsingBetterEnabled() const {
if (!java_object_) {
// Failsafe, should never happen.
NOTREACHED();
return false;
}
return Java_Starter_getMakeSearchesAndBrowsingBetterSettingEnabled(
base::android::AttachCurrentThread(), java_object_);
}
bool StarterAndroid::GetIsCustomTab() const {
return ui_controller_android_utils::IsCustomTab(
const_cast<content::WebContents*>(&GetWebContents()));
}
bool StarterAndroid::GetIsTabCreatedByGSA() const {
if (!java_object_) {
// Failsafe, should never happen.
NOTREACHED();
return false;
}
return Java_Starter_getIsTabCreatedByGSA(base::android::AttachCurrentThread(),
java_object_);
}
void StarterAndroid::CreateJavaDependenciesIfNecessary() {
if (java_dependencies_) {
return;
}
base::android::JavaObjectArrayReader<jobject> array(
Java_Starter_getOrCreateDependenciesAndOnboardingHelper(
base::android::AttachCurrentThread(), java_object_));
DCHECK_EQ(array.size(), 2);
if (array.size() != 2) {
return;
}
java_dependencies_ = *array.begin();
java_onboarding_helper_ = *(++array.begin());
}
void StarterAndroid::Start(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaRef<jstring>& jexperiment_ids,
const base::android::JavaRef<jobjectArray>& jparameter_names,
const base::android::JavaRef<jobjectArray>& jparameter_values,
const base::android::JavaRef<jobjectArray>& jdevice_only_parameter_names,
const base::android::JavaRef<jobjectArray>& jdevice_only_parameter_values,
const base::android::JavaRef<jstring>& jinitial_url) {
DCHECK(starter_);
auto trigger_context = ui_controller_android_utils::CreateTriggerContext(
env, &GetWebContents(), jexperiment_ids, jparameter_names,
jparameter_values, jdevice_only_parameter_names,
jdevice_only_parameter_values,
/* onboarding_shown = */ false, /* is_direct_action = */ false,
jinitial_url);
starter_->Start(std::move(trigger_context));
}
void StarterAndroid::StartRegularScript(
GURL url,
std::unique_ptr<TriggerContext> trigger_context,
const absl::optional<TriggerScriptProto>& trigger_script) {
CreateJavaDependenciesIfNecessary();
ClientAndroid::CreateForWebContents(&GetWebContents(), java_dependencies_);
auto* client_android = ClientAndroid::FromWebContents(&GetWebContents());
DCHECK(client_android);
JNIEnv* env = base::android::AttachCurrentThread();
client_android->Start(
url, std::move(trigger_context),
ui_controller_android_utils::GetServiceToInject(env, client_android),
Java_AssistantOnboardingHelperImpl_transferOnboardingOverlayCoordinator(
env, java_onboarding_helper_),
trigger_script);
}
bool StarterAndroid::IsRegularScriptRunning() const {
const auto* client_android =
ClientAndroid::FromWebContents(&GetWebContents());
if (!client_android) {
return false;
}
return client_android->IsRunning();
}
bool StarterAndroid::IsRegularScriptVisible() const {
const auto* client_android =
ClientAndroid::FromWebContents(&GetWebContents());
if (!client_android) {
return false;
}
return client_android->IsVisible();
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(StarterAndroid);
} // namespace autofill_assistant