blob: 2cdfed2b6390965aa2da81b7c08fb837bb2fb2b9 [file] [log] [blame]
// Copyright 2018 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/chromeos/android_sms/android_sms_app_manager_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "chrome/browser/chromeos/android_sms/android_sms_app_setup_controller.h"
#include "chrome/browser/chromeos/android_sms/android_sms_urls.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chromeos/components/multidevice/logging/logging.h"
#include "extensions/common/extension.h"
namespace chromeos {
namespace android_sms {
namespace {
const PwaDomain kDomains[] = {PwaDomain::kProdAndroid, PwaDomain::kProdGoogle,
PwaDomain::kStaging};
} // namespace
AndroidSmsAppManagerImpl::PwaDelegate::PwaDelegate() = default;
AndroidSmsAppManagerImpl::PwaDelegate::~PwaDelegate() = default;
content::WebContents* AndroidSmsAppManagerImpl::PwaDelegate::OpenApp(
const AppLaunchParams& params) {
// Note: OpenApplications() is not namespaced and is defined in
// application_launch.h.
return OpenApplication(params);
}
bool AndroidSmsAppManagerImpl::PwaDelegate::TransferItemAttributes(
const std::string& from_app_id,
const std::string& to_app_id,
app_list::AppListSyncableService* app_list_syncable_service) {
return app_list_syncable_service->TransferItemAttributes(from_app_id,
to_app_id);
}
AndroidSmsAppManagerImpl::AndroidSmsAppManagerImpl(
Profile* profile,
AndroidSmsAppSetupController* setup_controller,
app_list::AppListSyncableService* app_list_syncable_service,
scoped_refptr<base::TaskRunner> task_runner)
: profile_(profile),
setup_controller_(setup_controller),
app_list_syncable_service_(app_list_syncable_service),
installed_url_at_last_notify_(GetCurrentAppUrl()),
pwa_delegate_(std::make_unique<PwaDelegate>()),
weak_ptr_factory_(this) {
// Post a task to complete initialization. This portion of the flow must be
// posted asynchronously because it accesses the networking stack, which is
// not completely loaded until after this class is instantiated.
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&AndroidSmsAppManagerImpl::CompleteAsyncInitialization,
weak_ptr_factory_.GetWeakPtr()));
}
AndroidSmsAppManagerImpl::~AndroidSmsAppManagerImpl() = default;
base::Optional<GURL> AndroidSmsAppManagerImpl::GetCurrentAppUrl() {
base::Optional<PwaDomain> domain = GetInstalledPwaDomain();
if (!domain)
return base::nullopt;
return GetAndroidMessagesURL(false /* use_install_url */, *domain);
}
void AndroidSmsAppManagerImpl::SetUpAndroidSmsApp() {
// If setup is already in progress, there is nothing else to do.
if (is_new_app_setup_in_progress_)
return;
base::Optional<PwaDomain> migrating_from = GetInstalledPwaDomain();
// If the preferred domain is already installed, no migration is happening at
// all.
if (migrating_from && *migrating_from == GetPreferredPwaDomain())
migrating_from.reset();
is_new_app_setup_in_progress_ = true;
setup_controller_->SetUpApp(
GetAndroidMessagesURL() /* app_url */,
GetAndroidMessagesURL(true /* use_install_url */) /* install_url */,
base::BindOnce(&AndroidSmsAppManagerImpl::OnSetUpNewAppResult,
weak_ptr_factory_.GetWeakPtr(), migrating_from));
}
void AndroidSmsAppManagerImpl::SetUpAndLaunchAndroidSmsApp() {
is_app_launch_pending_ = true;
SetUpAndroidSmsApp();
}
void AndroidSmsAppManagerImpl::TearDownAndroidSmsApp() {
base::Optional<GURL> installed_app_url = GetCurrentAppUrl();
if (!installed_app_url)
return;
setup_controller_->DeleteRememberDeviceByDefaultCookie(*installed_app_url,
base::DoNothing());
}
base::Optional<PwaDomain> AndroidSmsAppManagerImpl::GetInstalledPwaDomain() {
for (auto* it = std::begin(kDomains); it != std::end(kDomains); ++it) {
if (setup_controller_->GetPwa(
GetAndroidMessagesURL(true /* use_install_url */, *it))) {
return *it;
}
}
return base::nullopt;
}
void AndroidSmsAppManagerImpl::CompleteAsyncInitialization() {
base::Optional<PwaDomain> domain = GetInstalledPwaDomain();
// If no app was installed before this object was created, there is nothing
// else to initialize.
if (!domain)
return;
if (GetPreferredPwaDomain() == *domain) {
PA_LOG(INFO) << "AndroidSmsAppManagerImpl::CompleteAsyncInitialization(): "
<< "Currently using: " << *domain;
return;
}
PA_LOG(INFO) << "AndroidSmsAppManagerImpl::CompleteAsyncInitialization(): "
<< "Attempting app migration. From: " << *domain
<< ", to: " << GetPreferredPwaDomain();
SetUpAndroidSmsApp();
}
void AndroidSmsAppManagerImpl::NotifyInstalledAppUrlChangedIfNecessary() {
base::Optional<GURL> installed_app_url = GetCurrentAppUrl();
if (installed_url_at_last_notify_ == installed_app_url)
return;
installed_url_at_last_notify_ = installed_app_url;
NotifyInstalledAppUrlChanged();
}
void AndroidSmsAppManagerImpl::OnSetUpNewAppResult(
const base::Optional<PwaDomain>& migrating_from,
bool success) {
is_new_app_setup_in_progress_ = false;
const extensions::Extension* new_pwa = setup_controller_->GetPwa(
GetAndroidMessagesURL(true /* use_install_url */));
// If the installation succeeded, a PWA should exist at the new URL.
DCHECK_EQ(success, new_pwa != nullptr);
// If the app failed to install, it should no longer be launched.
if (!success) {
is_app_launch_pending_ = false;
return;
}
// If there is no PWA installed at the old URL, no migration is needed and
// setup is finished.
if (!migrating_from) {
HandleAppSetupFinished();
return;
}
const extensions::Extension* old_pwa = setup_controller_->GetPwa(
GetAndroidMessagesURL(true /* use_install_url */, *migrating_from));
// Transfer attributes from the old PWA to the new one. This ensures that the
// PWA's placement in the app launcher and shelf remains constant..
bool transfer_attributes_success = pwa_delegate_->TransferItemAttributes(
old_pwa->id() /* from_app_id */, new_pwa->id() /* to_app_id */,
app_list_syncable_service_);
if (!transfer_attributes_success) {
PA_LOG(ERROR) << "AndroidSmsAppManagerImpl::OnSetUpNewAppResult(): Failed "
<< "to transfer item attributes. From: " << *migrating_from
<< ", to: " << GetPreferredPwaDomain();
}
// Finish the migration by removing the old app now that it has been replaced.
setup_controller_->RemoveApp(
GetAndroidMessagesURL(false /* use_install_url */,
*migrating_from) /* app_url */,
GetAndroidMessagesURL(true /* use_install_url */,
*migrating_from) /* install_url */,
GetAndroidMessagesURL() /* migrated_to_app_url */,
base::BindOnce(&AndroidSmsAppManagerImpl::OnRemoveOldAppResult,
weak_ptr_factory_.GetWeakPtr(), migrating_from));
}
void AndroidSmsAppManagerImpl::OnRemoveOldAppResult(
const base::Optional<PwaDomain>& migrating_from,
bool success) {
// If app removal fails, log an error but continue anyway, since clients
// should still be notified of the URL change.
if (!success) {
PA_LOG(ERROR) << "AndroidSmsAppManagerImpl::OnRemoveOldAppResult(): Failed "
<< "to remove PWA at old domain: " << *migrating_from;
}
HandleAppSetupFinished();
}
void AndroidSmsAppManagerImpl::HandleAppSetupFinished() {
NotifyInstalledAppUrlChangedIfNecessary();
// If no launch was requested, setup is complete.
if (!is_app_launch_pending_)
return;
is_app_launch_pending_ = false;
// If launch was requested but setup failed, there is no app to launch.
base::Optional<PwaDomain> domain = GetInstalledPwaDomain();
if (!domain)
return;
// Otherwise, launch the app.
PA_LOG(VERBOSE) << "AndroidSmsAppManagerImpl::HandleAppSetupFinished(): "
<< "Launching Messages PWA.";
pwa_delegate_->OpenApp(AppLaunchParams(
profile_,
setup_controller_->GetPwa(
GetAndroidMessagesURL(true /* use_install_url */, *domain)),
extensions::LAUNCH_CONTAINER_WINDOW, WindowOpenDisposition::NEW_WINDOW,
extensions::SOURCE_CHROME_INTERNAL));
}
void AndroidSmsAppManagerImpl::SetPwaDelegateForTesting(
std::unique_ptr<PwaDelegate> test_pwa_delegate) {
pwa_delegate_ = std::move(test_pwa_delegate);
}
} // namespace android_sms
} // namespace chromeos