#include "chrome/browser/android/webapk/webapk_install_service.h"
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/android/chrome_jni_headers/WebApkInstallService_jni.h"
#include "chrome/browser/android/shortcut_helper.h"
#include "chrome/browser/android/webapk/webapk_installer.h"
#include "components/webapk/webapk.pb.h"
#include "components/webapps/browser/android/shortcut_info.h"
#include "components/webapps/browser/android/webapk/webapk_types.h"
#include "components/webapps/browser/android/webapps_utils.h"
#include "components/webapps/browser/banners/app_banner_manager.h"
#include "components/webapps/browser/features.h"
#include "components/webapps/browser/installable/installable_logging.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/android/java_bitmap.h"
WebApkInstallService::WebApkInstallService(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
WebApkInstallService::~WebApkInstallService() = default;
bool WebApkInstallService::IsInstallInProgress(const GURL& web_manifest_id) {
return install_ids_.count(web_manifest_id);
}
void WebApkInstallService::InstallAsync(
content::WebContents* web_contents,
const webapps::ShortcutInfo& shortcut_info,
const SkBitmap& primary_icon,
webapps::WebappInstallSource install_source) {
if (IsInstallInProgress(shortcut_info.manifest_id)) {
webapps::WebappsUtils::ShowWebApkInstallResultToast(
webapps::WebApkInstallResult::INSTALL_ALREADY_IN_PROGRESS);
return;
}
install_ids_.insert(shortcut_info.manifest_id);
webapps::InstallableMetrics::TrackInstallEvent(install_source);
ShowInstallInProgressNotification(
shortcut_info.manifest_id, shortcut_info.short_name, shortcut_info.url,
primary_icon, shortcut_info.is_primary_icon_maskable);
WebApkInstaller::InstallAsync(
browser_context_, web_contents, shortcut_info, primary_icon,
install_source,
base::BindOnce(&WebApkInstallService::OnFinishedInstall,
weak_ptr_factory_.GetWeakPtr(), web_contents->GetWeakPtr(),
shortcut_info, primary_icon));
}
void WebApkInstallService::InstallRestoreAsync(
content::WebContents* web_contents,
const webapps::ShortcutInfo& shortcut_info,
const SkBitmap& primary_icon,
webapps::WebappInstallSource install_source,
InstallFinishCallback finish_callback) {
if (IsInstallInProgress(shortcut_info.manifest_id)) {
std::move(finish_callback)
.Run(webapps::WebApkInstallResult::INSTALL_ALREADY_IN_PROGRESS);
return;
}
install_ids_.insert(shortcut_info.manifest_id);
webapps::InstallableMetrics::TrackInstallEvent(install_source);
ShowInstallInProgressNotification(
shortcut_info.manifest_id, shortcut_info.short_name, shortcut_info.url,
primary_icon, shortcut_info.is_primary_icon_maskable);
WebApkInstaller::InstallAsync(
browser_context_, web_contents, shortcut_info, primary_icon,
install_source,
base::BindOnce(&WebApkInstallService::OnFinishedInstallRestore,
weak_ptr_factory_.GetWeakPtr(), shortcut_info,
primary_icon, std::move(finish_callback)));
}
void WebApkInstallService::UpdateAsync(
const base::FilePath& update_request_path,
FinishCallback finish_callback) {
WebApkInstaller::UpdateAsync(browser_context_, update_request_path,
std::move(finish_callback));
}
void WebApkInstallService::OnFinishedInstall(
base::WeakPtr<content::WebContents> web_contents,
const webapps::ShortcutInfo& shortcut_info,
const SkBitmap& primary_icon,
webapps::WebApkInstallResult result,
bool relax_updates,
const std::string& webapk_package_name) {
install_ids_.erase(shortcut_info.manifest_id);
bool show_failure_notification = base::FeatureList::IsEnabled(
webapps::features::kWebApkInstallFailureNotification);
HandleFinishInstallNotifications(
shortcut_info.manifest_id, shortcut_info.url, shortcut_info.short_name,
primary_icon, shortcut_info.is_primary_icon_maskable, result,
webapk_package_name, show_failure_notification);
if (result == webapps::WebApkInstallResult::SUCCESS && web_contents) {
webapps::AppBannerManager::FromWebContents(web_contents.get())
->OnInstall(shortcut_info.display, true);
}
if (show_failure_notification) {
return;
}
if (result != webapps::WebApkInstallResult::SUCCESS &&
result != webapps::WebApkInstallResult::PROBABLE_FAILURE) {
if (!web_contents)
return;
ShortcutHelper::AddToLauncherWithSkBitmap(
web_contents.get(), shortcut_info, primary_icon,
webapps::InstallableStatusCode::WEBAPK_INSTALL_FAILED);
}
}
void WebApkInstallService::OnFinishedInstallRestore(
const webapps::ShortcutInfo& shortcut_info,
const SkBitmap& primary_icon,
InstallFinishCallback finish_callback,
webapps::WebApkInstallResult result,
bool ,
const std::string& webapk_package_name) {
install_ids_.erase(shortcut_info.manifest_id);
HandleFinishInstallNotifications(
shortcut_info.manifest_id, shortcut_info.url, shortcut_info.short_name,
primary_icon, shortcut_info.is_primary_icon_maskable, result,
webapk_package_name, true);
std::move(finish_callback).Run(result);
}
void WebApkInstallService::HandleFinishInstallNotifications(
const GURL& notification_id,
const GURL& url,
const std::u16string& short_name,
const SkBitmap& primary_icon,
bool is_primary_icon_maskable,
webapps::WebApkInstallResult result,
const std::string& webapk_package_name,
bool show_failure_notification) {
if (result == webapps::WebApkInstallResult::SUCCESS) {
ShowInstalledNotification(notification_id, short_name, url, primary_icon,
is_primary_icon_maskable, webapk_package_name);
} else if (show_failure_notification) {
ShowInstallFailedNotification(notification_id, short_name, url,
primary_icon, is_primary_icon_maskable,
result);
} else {
JNIEnv* env = base::android::AttachCurrentThread();
Java_WebApkInstallService_cancelNotification(env, notification_id.spec());
}
}
void WebApkInstallService::ShowInstallInProgressNotification(
const GURL& notification_id,
const std::u16string& short_name,
const GURL& url,
const SkBitmap& primary_icon,
bool is_primary_icon_maskable) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> java_primary_icon =
!primary_icon.isNull() ? gfx::ConvertToJavaBitmap(primary_icon) : nullptr;
Java_WebApkInstallService_showInstallInProgressNotification(
env, notification_id.spec(), short_name, url.spec(), java_primary_icon,
is_primary_icon_maskable);
}
void WebApkInstallService::ShowInstalledNotification(
const GURL& notification_id,
const std::u16string& short_name,
const GURL& url,
const SkBitmap& primary_icon,
bool is_primary_icon_maskable,
const std::string& webapk_package_name) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> java_primary_icon =
!primary_icon.isNull() ? gfx::ConvertToJavaBitmap(primary_icon) : nullptr;
Java_WebApkInstallService_showInstalledNotification(
env, webapk_package_name, notification_id.spec(), short_name, url.spec(),
java_primary_icon, is_primary_icon_maskable);
}
void WebApkInstallService::ShowInstallFailedNotification(
const GURL& notification_id,
const std::u16string& short_name,
const GURL& url,
const SkBitmap& primary_icon,
bool is_primary_icon_maskable,
webapps::WebApkInstallResult result) {
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> java_primary_icon =
!primary_icon.isNull() ? gfx::ConvertToJavaBitmap(primary_icon) : nullptr;
Java_WebApkInstallService_showInstallFailedNotification(
env, notification_id.spec(), short_name, url.spec(), java_primary_icon,
is_primary_icon_maskable, static_cast<int>(result));
}
DEFINE_JNI(WebApkInstallService)