// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/language_packs/language_packs_impl.h"
#include <optional>
#include <string>
#include "base/no_destructor.h"
#include "chromeos/ash/components/language_packs/language_pack_manager.h"
#include "chromeos/ash/components/language_packs/public/mojom/language_packs.mojom-shared.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
namespace ash::language_packs {
using ::ash::language::mojom::BasePackInfo;
using ::ash::language::mojom::ErrorCode;
using ::ash::language::mojom::FeatureId;
using ::ash::language::mojom::LanguagePackInfo;
using ::ash::language::mojom::LanguagePacks;
using ::ash::language::mojom::PackState;
namespace {
std::optional<std::string> ConvertMojoFeatureToPackId(FeatureId mojo_id) {
switch (mojo_id) {
case FeatureId::HANDWRITING_RECOGNITION:
return kHandwritingFeatureId;
case FeatureId::TTS:
return kTtsFeatureId;
// Catch all unknown cases here.
default:
return std::nullopt;
}
}
PackState GetPackStateFromStatusCode(const PackResult::StatusCode status_code) {
switch (status_code) {
case PackResult::StatusCode::kNotInstalled:
return PackState::NOT_INSTALLED;
case PackResult::StatusCode::kInProgress:
return PackState::INSTALLING;
case PackResult::StatusCode::kInstalled:
return PackState::INSTALLED;
// Catch all remaining cases as error.
default:
// TODO: b/294162606 - Deprecate this value and use UNKNOWN instead.
return PackState::ERROR;
}
}
ErrorCode GetMojoErrorFromPackError(const PackResult::ErrorCode pack_error) {
// This conversion is exhaustive. We don't use a default: case so that we can
// catch missing values at compile time.
switch (pack_error) {
case PackResult::ErrorCode::kNone:
return ErrorCode::kNone;
case PackResult::ErrorCode::kOther:
return ErrorCode::kOther;
case PackResult::ErrorCode::kWrongId:
return ErrorCode::kWrongId;
case PackResult::ErrorCode::kNeedReboot:
return ErrorCode::kNeedReboot;
case PackResult::ErrorCode::kAllocation:
return ErrorCode::kAllocation;
}
}
FeatureId ToFeatureId(std::string_view feature_id) {
if (feature_id == kHandwritingFeatureId) {
return FeatureId::HANDWRITING_RECOGNITION;
} else if (feature_id == kTtsFeatureId) {
return FeatureId::TTS;
}
return FeatureId::UNSUPPORTED_UNKNOWN;
}
// Called when GetPackState() or InstallPack() functions from Language Packs
// are complete.
void OnOperationComplete(LanguagePacksImpl::GetPackInfoCallback mojo_callback,
const PackResult& pack_result) {
auto info = LanguagePackInfo::New();
info->pack_state = GetPackStateFromStatusCode(pack_result.pack_state);
info->error = GetMojoErrorFromPackError(pack_result.operation_error);
info->feature_id = ToFeatureId(pack_result.feature_id);
if (pack_result.pack_state == PackResult::StatusCode::kInstalled) {
info->path = pack_result.path;
}
std::move(mojo_callback).Run(std::move(info));
}
// Called when InstallBasePack() from Language Packs is complete.
void OnInstallBasePackComplete(
LanguagePacksImpl::InstallBasePackCallback mojo_callback,
const PackResult& pack_result) {
auto info = BasePackInfo::New();
info->pack_state = GetPackStateFromStatusCode(pack_result.pack_state);
info->error = GetMojoErrorFromPackError(pack_result.operation_error);
if (pack_result.pack_state == PackResult::StatusCode::kInstalled) {
info->path = pack_result.path;
}
std::move(mojo_callback).Run(std::move(info));
}
void OnUninstallComplete(LanguagePacksImpl::UninstallPackCallback mojo_callback,
const PackResult& result) {
std::move(mojo_callback).Run();
}
} // namespace
LanguagePacksImpl::LanguagePacksImpl() {
auto* manager = LanguagePackManager::GetInstance();
if (manager) {
// Note: RemoveObserver is never called here because this class should
// never be destroyed (see LanguagePacksImpl::GetInstance).
manager->AddObserver(this);
}
}
LanguagePacksImpl::~LanguagePacksImpl() = default;
LanguagePacksImpl& LanguagePacksImpl::GetInstance() {
static base::NoDestructor<LanguagePacksImpl> impl;
return *impl;
}
void LanguagePacksImpl::BindReceiver(
mojo::PendingReceiver<language::mojom::LanguagePacks> receiver) {
receivers_.Add(this, std::move(receiver));
}
void LanguagePacksImpl::GetPackInfo(FeatureId feature_id,
const std::string& language,
GetPackInfoCallback mojo_callback) {
const std::optional<std::string> pack_id =
ConvertMojoFeatureToPackId(feature_id);
if (pack_id.has_value()) {
LanguagePackManager::GetPackState(
pack_id.value(), language,
base::BindOnce(&OnOperationComplete, std::move(mojo_callback)));
} else {
auto info = LanguagePackInfo::New();
info->pack_state = PackState::ERROR;
info->feature_id = feature_id;
std::move(mojo_callback).Run(std::move(info));
}
}
void LanguagePacksImpl::InstallPack(FeatureId feature_id,
const std::string& language,
InstallPackCallback mojo_callback) {
const std::optional<std::string> pack_id =
ConvertMojoFeatureToPackId(feature_id);
if (pack_id.has_value()) {
LanguagePackManager::InstallPack(
pack_id.value(), language,
base::BindOnce(&OnOperationComplete, std::move(mojo_callback)));
} else {
auto info = LanguagePackInfo::New();
info->pack_state = PackState::ERROR;
info->feature_id = feature_id;
std::move(mojo_callback).Run(std::move(info));
}
}
void LanguagePacksImpl::InstallBasePack(FeatureId feature_id,
InstallBasePackCallback mojo_callback) {
const std::optional<std::string> pack_id =
ConvertMojoFeatureToPackId(feature_id);
if (pack_id.has_value()) {
LanguagePackManager::InstallBasePack(
*pack_id,
base::BindOnce(&OnInstallBasePackComplete, std::move(mojo_callback)));
} else {
auto info = BasePackInfo::New();
info->pack_state = PackState::ERROR;
std::move(mojo_callback).Run(std::move(info));
}
}
void LanguagePacksImpl::UninstallPack(FeatureId feature_id,
const std::string& language,
UninstallPackCallback mojo_callback) {
const std::optional<std::string> pack_id =
ConvertMojoFeatureToPackId(feature_id);
// We ignore the request if the input parameters are incorrect.
if (pack_id.has_value()) {
LanguagePackManager::RemovePack(
pack_id.value(), language,
base::BindOnce(&OnUninstallComplete, std::move(mojo_callback)));
}
}
void LanguagePacksImpl::AddObserver(
mojo::PendingAssociatedRemote<ash::language::mojom::LanguagePacksObserver>
observer) {
observers_.Add(std::move(observer));
}
void LanguagePacksImpl::OnPackStateChanged(const PackResult& pack_result) {
auto info = LanguagePackInfo::New();
info->pack_state = GetPackStateFromStatusCode(pack_result.pack_state);
info->error = GetMojoErrorFromPackError(pack_result.operation_error);
if (pack_result.pack_state == PackResult::StatusCode::kInstalled) {
info->path = pack_result.path;
}
info->feature_id = ToFeatureId(pack_result.feature_id);
info->locale = pack_result.language_code;
for (const auto& observer : observers_) {
observer->OnPackStateChanged(info.Clone());
}
}
} // namespace ash::language_packs