Commit 8b418eb6 authored by Yuichiro Hanada's avatar Yuichiro Hanada Committed by Commit Bot

Implement ArcInputMethodManagerService::OnImeInfoChanged().

OnImeInfoChanged() receives a list of installed IMEs in the ARC
container and registers them as valid IMEs to InputMethodManager.

Bug: 845079
Test: unit_tests
Change-Id: Ic7252c5d9021252228697e351a1c251c1f167e52
Reviewed-on: https://chromium-review.googlesource.com/1105684
Commit-Queue: Yuichiro Hanada <yhanada@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568745}
parent ed63fc04
......@@ -7,7 +7,12 @@
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_features.h"
#include "components/crx_file/id_util.h"
#include "components/prefs/pref_service.h"
#include "ui/base/ime/chromeos/extension_ime_util.h"
namespace arc {
......@@ -20,6 +25,11 @@ namespace {
constexpr char kChromeOSIMEIdInArcContainer[] =
"org.chromium.arc.ime/.ArcInputMethodService";
// The name of the proxy IME extension that is used when registering ARC IMEs to
// InputMethodManager.
constexpr char kArcIMEProxyExtensionName[] =
"org.chromium.arc.inputmethod.proxy";
// Singleton factory for ArcInputMethodManagerService
class ArcInputMethodManagerServiceFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
......@@ -59,15 +69,25 @@ ArcInputMethodManagerService::GetForBrowserContextForTesting(
ArcInputMethodManagerService::ArcInputMethodManagerService(
content::BrowserContext* context,
ArcBridgeService* bridge_service)
: imm_bridge_(
: profile_(Profile::FromBrowserContext(context)),
imm_bridge_(
std::make_unique<ArcInputMethodManagerBridgeImpl>(this,
bridge_service)) {
bridge_service)),
proxy_ime_extension_id_(
crx_file::id_util::GenerateId(kArcIMEProxyExtensionName)),
proxy_ime_engine_(std::make_unique<chromeos::InputMethodEngine>()) {
auto* imm = chromeos::input_method::InputMethodManager::Get();
imm->AddObserver(this);
imm->AddImeMenuObserver(this);
}
ArcInputMethodManagerService::~ArcInputMethodManagerService() {
// Remove any Arc IME entry from preferences before shutting down.
// IME states (installed/enabled/disabled) are stored in Android's settings,
// that will be restored after Arc container starts next time.
RemoveArcIMEFromPrefs();
profile_->GetPrefs()->CommitPendingWrite();
auto* imm = chromeos::input_method::InputMethodManager::Get();
imm->RemoveImeMenuObserver(this);
imm->RemoveObserver(this);
......@@ -86,8 +106,28 @@ void ArcInputMethodManagerService::OnActiveImeChanged(
void ArcInputMethodManagerService::OnImeInfoChanged(
std::vector<mojom::ImeInfoPtr> ime_info_array) {
// Please see https://crbug.com/845079.
NOTIMPLEMENTED();
using namespace chromeos::input_method;
if (!base::FeatureList::IsEnabled(kEnableInputMethodFeature))
return;
scoped_refptr<InputMethodManager::State> state =
InputMethodManager::Get()->GetActiveIMEState();
// Remove the old registered entry.
state->RemoveInputMethodExtension(proxy_ime_extension_id_);
// Convert ime_info_array to InputMethodDescriptors.
InputMethodDescriptors descriptors;
for (const auto& ime_info : ime_info_array)
descriptors.push_back(BuildInputMethodDescriptor(ime_info.get()));
if (descriptors.empty()) {
// If no ARC IME is installed, remove ARC IME entry from preferences.
RemoveArcIMEFromPrefs();
return;
}
// Add the proxy IME entry to InputMethodManager if any ARC IME is installed.
state->AddInputMethodExtension(proxy_ime_extension_id_, descriptors,
proxy_ime_engine_.get());
}
void ArcInputMethodManagerService::ImeMenuListChanged() {
......@@ -122,7 +162,8 @@ void ArcInputMethodManagerService::InputMethodChanged(
chromeos::input_method::InputMethodManager* manager,
Profile* profile,
bool /* show_message */) {
auto state = manager->GetActiveIMEState();
scoped_refptr<chromeos::input_method::InputMethodManager::State> state =
manager->GetActiveIMEState();
if (!state)
return;
SwitchImeTo(state->GetCurrentInputMethod().id());
......@@ -165,4 +206,53 @@ void ArcInputMethodManagerService::SwitchImeTo(const std::string& ime_id) {
ime_id, component_id));
}
chromeos::input_method::InputMethodDescriptor
ArcInputMethodManagerService::BuildInputMethodDescriptor(
const mojom::ImeInfo* info) {
// TODO(yhanada): Set the special layout/language value for ARC IMEs after
// making settings and IME menu tray support it.
std::vector<std::string> layouts{"us"};
std::vector<std::string> languages{"en-US"};
const std::string display_name = info->display_name;
const std::string& input_method_id =
chromeos::extension_ime_util::GetArcInputMethodID(proxy_ime_extension_id_,
info->ime_id);
// TODO(yhanada): Set the indicator string after the UI spec is finalized.
return chromeos::input_method::InputMethodDescriptor(
input_method_id, display_name, std::string() /* indicator */, layouts,
languages, false /* is_login_keyboard */, GURL(info->settings_url),
GURL() /* input_view_url */);
}
void ArcInputMethodManagerService::RemoveArcIMEFromPrefs() {
RemoveArcIMEFromPref(prefs::kLanguageEnabledExtensionImes);
RemoveArcIMEFromPref(prefs::kLanguagePreloadEngines);
PrefService* prefs = profile_->GetPrefs();
if (chromeos::extension_ime_util::IsArcIME(
prefs->GetString(prefs::kLanguageCurrentInputMethod))) {
prefs->SetString(prefs::kLanguageCurrentInputMethod, std::string());
}
if (chromeos::extension_ime_util::IsArcIME(
prefs->GetString(prefs::kLanguagePreviousInputMethod))) {
prefs->SetString(prefs::kLanguagePreviousInputMethod, std::string());
}
}
void ArcInputMethodManagerService::RemoveArcIMEFromPref(const char* pref_name) {
const std::string ime_ids = profile_->GetPrefs()->GetString(pref_name);
std::vector<std::string> ime_id_list = base::SplitString(
ime_ids, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
auto iter = std::remove_if(
ime_id_list.begin(), ime_id_list.end(), [](const std::string& id) {
return chromeos::extension_ime_util::IsArcIME(id);
});
ime_id_list.erase(iter, ime_id_list.end());
profile_->GetPrefs()->SetString(pref_name,
base::JoinString(ime_id_list, ","));
}
} // namespace arc
......@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_bridge.h"
#include "chrome/browser/chromeos/input_method/input_method_engine.h"
#include "components/arc/common/input_method_manager.mojom.h"
#include "components/keyed_service/core/keyed_service.h"
#include "ui/base/ime/chromeos/input_method_manager.h"
......@@ -64,10 +65,26 @@ class ArcInputMethodManagerService
private:
void EnableIme(const std::string& ime_id, bool enable);
void SwitchImeTo(const std::string& ime_id);
chromeos::input_method::InputMethodDescriptor BuildInputMethodDescriptor(
const mojom::ImeInfo* info);
// Removes ARC IME from IME related prefs that are current active IME pref,
// previous active IME pref, enabled IME list pref and preloading IME list
// pref.
void RemoveArcIMEFromPrefs();
void RemoveArcIMEFromPref(const char* pref_name);
Profile* const profile_;
std::unique_ptr<ArcInputMethodManagerBridge> imm_bridge_;
std::set<std::string> active_arc_ime_ids_;
// ArcInputMethodManager installs a proxy IME to redirect IME related events
// from/to ARC IMEs in the container. The below two variables are for the
// proxy IME.
const std::string proxy_ime_extension_id_;
std::unique_ptr<chromeos::InputMethodEngine> proxy_ime_engine_;
DISALLOW_COPY_AND_ASSIGN(ArcInputMethodManagerService);
};
......
......@@ -11,6 +11,8 @@
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/arc/arc_features.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/test/test_browser_context.h"
......@@ -31,7 +33,8 @@ class TestInputMethodManager : public im::MockInputMethodManager {
// The fake im::InputMethodManager::State implementation for testing.
class TestState : public im::MockInputMethodManager::State {
public:
TestState() : active_input_method_ids_() {}
TestState()
: added_input_method_extensions_(), active_input_method_ids_() {}
const std::vector<std::string>& GetActiveInputMethodIds() const override {
return active_input_method_ids_;
......@@ -45,6 +48,18 @@ class TestInputMethodManager : public im::MockInputMethodManager {
return descriptor;
}
void AddInputMethodExtension(
const std::string& extension_id,
const im::InputMethodDescriptors& descriptors,
ui::IMEEngineHandlerInterface* instance) override {
added_input_method_extensions_.push_back(
std::make_tuple(extension_id, descriptors));
}
void RemoveInputMethodExtension(const std::string& extension_id) override {
removed_input_method_extensions_.push_back(extension_id);
}
void AddActiveInputMethodId(const std::string& ime_id) {
if (!std::count(active_input_method_ids_.begin(),
active_input_method_ids_.end(), ime_id)) {
......@@ -62,6 +77,10 @@ class TestInputMethodManager : public im::MockInputMethodManager {
active_ime_id_ = ime_id;
}
std::vector<std::tuple<std::string, im::InputMethodDescriptors>>
added_input_method_extensions_;
std::vector<std::string> removed_input_method_extensions_;
protected:
friend base::RefCounted<InputMethodManager::State>;
~TestState() override = default;
......@@ -116,7 +135,7 @@ class ArcInputMethodManagerServiceTest : public testing::Test {
protected:
ArcInputMethodManagerServiceTest()
: arc_service_manager_(std::make_unique<ArcServiceManager>()) {}
~ArcInputMethodManagerServiceTest() override {}
~ArcInputMethodManagerServiceTest() override = default;
ArcInputMethodManagerService* service() { return service_; }
......@@ -124,16 +143,15 @@ class ArcInputMethodManagerServiceTest : public testing::Test {
TestInputMethodManager* imm() { return input_method_manager_; }
TestingProfile* profile() { return profile_.get(); }
void SetUp() override {
input_method_manager_ = new TestInputMethodManager();
chromeos::input_method::InputMethodManager::Initialize(
input_method_manager_);
context_ = std::make_unique<TestBrowserContext>();
profile_ = std::make_unique<TestingProfile>();
service_ = ArcInputMethodManagerService::GetForBrowserContextForTesting(
context_.get());
profile_.get());
test_bridge_ = new TestInputMethodManagerBridge();
service_->SetInputMethodManagerBridgeForTesting(
base::WrapUnique(test_bridge_));
......@@ -142,16 +160,14 @@ class ArcInputMethodManagerServiceTest : public testing::Test {
void TearDown() override {
test_bridge_ = nullptr;
service_->Shutdown();
context_.reset(nullptr);
profile_.reset(nullptr);
chromeos::input_method::InputMethodManager::Shutdown();
}
private:
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<ArcServiceManager> arc_service_manager_;
std::unique_ptr<TestBrowserContext> context_;
std::unique_ptr<TestingProfile> profile_;
TestInputMethodManager* input_method_manager_ = nullptr;
TestInputMethodManagerBridge* test_bridge_ = nullptr; // Owned by |service_|
......@@ -257,4 +273,88 @@ TEST_F(ArcInputMethodManagerServiceTest, SwitchImeTo) {
EXPECT_EQ("ime.id.in.arc.container", bridge()->switch_ime_to_calls_[2]);
}
TEST_F(ArcInputMethodManagerServiceTest, OnImeInfoChanged) {
using namespace chromeos::extension_ime_util;
base::test::ScopedFeatureList feature;
feature.InitAndEnableFeature(kEnableInputMethodFeature);
// Preparing 2 ImeInfo.
const std::string android_ime_id1 = "test.arc.ime";
const std::string display_name1 = "DisplayName";
const std::string settings_url1 = "url_to_settings";
mojom::ImeInfoPtr info1 = mojom::ImeInfo::New();
info1->ime_id = android_ime_id1;
info1->display_name = display_name1;
info1->enabled = false;
info1->settings_url = settings_url1;
const std::string android_ime_id2 = "test.arc.ime2";
const std::string display_name2 = "DisplayName2";
const std::string settings_url2 = "url_to_settings2";
mojom::ImeInfoPtr info2 = mojom::ImeInfo::New();
info2->ime_id = android_ime_id2;
info2->display_name = display_name2;
info2->enabled = true;
info2->settings_url = settings_url2;
std::vector<
std::tuple<std::string, chromeos::input_method::InputMethodDescriptors>>&
added_extensions = imm()->state()->added_input_method_extensions_;
ASSERT_EQ(0u, added_extensions.size());
{
// Passing empty info_array shouldn't call AddInputMethodExtension.
std::vector<mojom::ImeInfoPtr> info_array{};
service()->OnImeInfoChanged(std::move(info_array));
EXPECT_TRUE(added_extensions.empty());
}
{
// Adding one ARC IME.
std::vector<mojom::ImeInfoPtr> info_array;
info_array.push_back(info1.Clone());
service()->OnImeInfoChanged(std::move(info_array));
ASSERT_EQ(1u, added_extensions.size());
ASSERT_EQ(1u, std::get<1>(added_extensions[0]).size());
EXPECT_EQ(android_ime_id1, GetComponentIDByInputMethodID(
std::get<1>(added_extensions[0])[0].id()));
EXPECT_EQ(display_name1, std::get<1>(added_extensions[0])[0].name());
// Emulate enabling ARC IME from chrome://settings.
const std::string& arc_ime_id = std::get<1>(added_extensions[0])[0].id();
profile()->GetPrefs()->SetString(prefs::kLanguageEnabledExtensionImes,
arc_ime_id);
EXPECT_EQ(arc_ime_id, profile()->GetPrefs()->GetString(
prefs::kLanguageEnabledExtensionImes));
// Removing the ARC IME should clear the pref
std::vector<mojom::ImeInfoPtr> empty_info_array;
service()->OnImeInfoChanged(std::move(empty_info_array));
EXPECT_TRUE(profile()
->GetPrefs()
->GetString(prefs::kLanguageEnabledExtensionImes)
.empty());
added_extensions.clear();
}
{
// Adding two ARC IMEs.
std::vector<mojom::ImeInfoPtr> info_array;
info_array.push_back(info1.Clone());
info_array.push_back(info2.Clone());
service()->OnImeInfoChanged(std::move(info_array));
// The ARC IMEs should be registered as two IMEs in one extension.
ASSERT_EQ(1u, added_extensions.size());
ASSERT_EQ(2u, std::get<1>(added_extensions[0]).size());
EXPECT_EQ(android_ime_id1, GetComponentIDByInputMethodID(
std::get<1>(added_extensions[0])[0].id()));
EXPECT_EQ(display_name1, std::get<1>(added_extensions[0])[0].name());
EXPECT_EQ(android_ime_id2, GetComponentIDByInputMethodID(
std::get<1>(added_extensions[0])[1].id()));
EXPECT_EQ(display_name2, std::get<1>(added_extensions[0])[1].name());
added_extensions.clear();
}
}
} // namespace arc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment