// Copyright (c) 2012 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/input_method/input_method_manager_impl.h" #include // std::find #include "base/basictypes.h" #include "base/bind.h" #include "base/location.h" #include "base/memory/scoped_ptr.h" #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/input_method/candidate_window_controller.h" #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h" #include "chrome/browser/chromeos/input_method/input_method_engine.h" #include "chrome/browser/chromeos/language_preferences.h" #include "chromeos/ime/component_extension_ime_manager.h" #include "chromeos/ime/extension_ime_util.h" #include "chromeos/ime/input_method_delegate.h" #include "chromeos/ime/xkeyboard.h" #include "third_party/icu/source/common/unicode/uloc.h" #include "ui/base/accelerators/accelerator.h" namespace chromeos { namespace input_method { namespace { const char nacl_mozc_jp_id[] = "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp"; bool Contains(const std::vector& container, const std::string& value) { return std::find(container.begin(), container.end(), value) != container.end(); } } // namespace bool InputMethodManagerImpl::IsLoginKeyboard( const std::string& layout) const { const InputMethodDescriptor* ime = util_.GetInputMethodDescriptorFromId(layout); return ime ? ime->is_login_keyboard() : false; } InputMethodManagerImpl::InputMethodManagerImpl( scoped_ptr delegate) : delegate_(delegate.Pass()), state_(STATE_LOGIN_SCREEN), util_(delegate_.get(), GetSupportedInputMethods()), component_extension_ime_manager_(new ComponentExtensionIMEManager()), weak_ptr_factory_(this) { } InputMethodManagerImpl::~InputMethodManagerImpl() { if (candidate_window_controller_.get()) candidate_window_controller_->RemoveObserver(this); } void InputMethodManagerImpl::AddObserver( InputMethodManager::Observer* observer) { observers_.AddObserver(observer); } void InputMethodManagerImpl::AddCandidateWindowObserver( InputMethodManager::CandidateWindowObserver* observer) { candidate_window_observers_.AddObserver(observer); } void InputMethodManagerImpl::RemoveObserver( InputMethodManager::Observer* observer) { observers_.RemoveObserver(observer); } void InputMethodManagerImpl::RemoveCandidateWindowObserver( InputMethodManager::CandidateWindowObserver* observer) { candidate_window_observers_.RemoveObserver(observer); } void InputMethodManagerImpl::SetState(State new_state) { const State old_state = state_; state_ = new_state; switch (state_) { case STATE_LOGIN_SCREEN: break; case STATE_BROWSER_SCREEN: if (old_state == STATE_LOCK_SCREEN) OnScreenUnlocked(); break; case STATE_LOCK_SCREEN: OnScreenLocked(); break; case STATE_TERMINATING: { if (candidate_window_controller_.get()) candidate_window_controller_.reset(); break; } } } scoped_ptr InputMethodManagerImpl::GetSupportedInputMethods() const { return whitelist_.GetSupportedInputMethods(); } scoped_ptr InputMethodManagerImpl::GetActiveInputMethods() const { scoped_ptr result(new InputMethodDescriptors); // Build the active input method descriptors from the active input // methods cache |active_input_method_ids_|. for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { const std::string& input_method_id = active_input_method_ids_[i]; const InputMethodDescriptor* descriptor = util_.GetInputMethodDescriptorFromId(input_method_id); if (descriptor) { result->push_back(*descriptor); } else { std::map::const_iterator ix = extra_input_methods_.find(input_method_id); if (ix != extra_input_methods_.end()) result->push_back(ix->second); else DVLOG(1) << "Descriptor is not found for: " << input_method_id; } } if (result->empty()) { // Initially |active_input_method_ids_| is empty. browser_tests might take // this path. result->push_back( InputMethodUtil::GetFallbackInputMethodDescriptor()); } return result.Pass(); } const std::vector& InputMethodManagerImpl::GetActiveInputMethodIds() const { return active_input_method_ids_; } size_t InputMethodManagerImpl::GetNumActiveInputMethods() const { return active_input_method_ids_.size(); } void InputMethodManagerImpl::EnableLayouts(const std::string& language_code, const std::string& initial_layout) { if (state_ == STATE_TERMINATING) return; std::vector candidates; // Add input methods associated with the language. util_.GetInputMethodIdsFromLanguageCode(language_code, kKeyboardLayoutsOnly, &candidates); // Add the hardware keyboard as well. We should always add this so users // can use the hardware keyboard on the login screen and the screen locker. candidates.push_back(util_.GetHardwareInputMethodId()); std::vector layouts; // First, add the initial input method ID, if it's requested, to // layouts, so it appears first on the list of active input // methods at the input language status menu. if (util_.IsValidInputMethodId(initial_layout) && IsLoginKeyboard(initial_layout)) { layouts.push_back(initial_layout); } else if (!initial_layout.empty()) { DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: " << initial_layout; } // Add candidates to layouts, while skipping duplicates. for (size_t i = 0; i < candidates.size(); ++i) { const std::string& candidate = candidates[i]; // Not efficient, but should be fine, as the two vectors are very // short (2-5 items). if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate)) layouts.push_back(candidate); } active_input_method_ids_.swap(layouts); // Initialize candidate window controller and widgets such as // candidate window, infolist and mode indicator. Note, mode // indicator is used by only keyboard layout input methods. if (active_input_method_ids_.size() > 1) MaybeInitializeCandidateWindowController(); ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|. } // Adds new input method to given list. bool InputMethodManagerImpl::EnableInputMethodImpl( const std::string& input_method_id, std::vector* new_active_input_method_ids) const { DCHECK(new_active_input_method_ids); if (!util_.IsValidInputMethodId(input_method_id)) { DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id; return false; } if (!Contains(*new_active_input_method_ids, input_method_id)) new_active_input_method_ids->push_back(input_method_id); return true; } // Starts or stops the system input method framework as needed. void InputMethodManagerImpl::ReconfigureIMFramework() { if (component_extension_ime_manager_->IsInitialized()) LoadNecessaryComponentExtensions(); const bool need_engine = !ContainsOnlyKeyboardLayout(active_input_method_ids_); // Initialize candidate window controller and widgets such as // candidate window, infolist and mode indicator. Note, mode // indicator is used by only keyboard layout input methods. if (need_engine || active_input_method_ids_.size() > 1) MaybeInitializeCandidateWindowController(); } bool InputMethodManagerImpl::EnableInputMethod( const std::string& input_method_id) { if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_)) return false; ReconfigureIMFramework(); return true; } bool InputMethodManagerImpl::EnableInputMethods( const std::vector& new_active_input_method_ids) { if (state_ == STATE_TERMINATING) return false; // Filter unknown or obsolete IDs. std::vector new_active_input_method_ids_filtered; for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) EnableInputMethodImpl(new_active_input_method_ids[i], &new_active_input_method_ids_filtered); if (new_active_input_method_ids_filtered.empty()) { DVLOG(1) << "EnableInputMethods: No valid input method ID"; return false; } // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to // keep relative order of the extension input method IDs. for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { const std::string& input_method_id = active_input_method_ids_[i]; if (extension_ime_util::IsExtensionIME(input_method_id)) new_active_input_method_ids_filtered.push_back(input_method_id); } active_input_method_ids_.swap(new_active_input_method_ids_filtered); ReconfigureIMFramework(); // If |current_input_method| is no longer in |active_input_method_ids_|, // ChangeInputMethod() picks the first one in |active_input_method_ids_|. ChangeInputMethod(current_input_method_.id()); return true; } void InputMethodManagerImpl::ChangeInputMethod( const std::string& input_method_id) { ChangeInputMethodInternal(input_method_id, false); } bool InputMethodManagerImpl::ChangeInputMethodInternal( const std::string& input_method_id, bool show_message) { if (state_ == STATE_TERMINATING) return false; std::string input_method_id_to_switch = input_method_id; // Sanity check. if (!InputMethodIsActivated(input_method_id)) { scoped_ptr input_methods(GetActiveInputMethods()); DCHECK(!input_methods->empty()); input_method_id_to_switch = input_methods->at(0).id(); if (!input_method_id.empty()) { DVLOG(1) << "Can't change the current input method to " << input_method_id << " since the engine is not enabled. " << "Switch to " << input_method_id_to_switch << " instead."; } } if (!component_extension_ime_manager_->IsInitialized() && !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) { // We can't change input method before the initialization of // component extension ime manager. ChangeInputMethod will be // called with |pending_input_method_| when the initialization is // done. pending_input_method_ = input_method_id_to_switch; return false; } pending_input_method_.clear(); // Hide candidate window and info list. if (candidate_window_controller_.get()) candidate_window_controller_->Hide(); // Disable the current engine handler. IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetCurrentEngineHandler(); if (engine) engine->Disable(); // Configure the next engine handler. if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) { IBusBridge::Get()->SetCurrentEngineHandler(NULL); } else { IBusEngineHandlerInterface* next_engine = IBusBridge::Get()->SetCurrentEngineHandlerById( input_method_id_to_switch); if (next_engine) next_engine->Enable(); } // TODO(komatsu): Check if it is necessary to perform the above routine // when the current input method is equal to |input_method_id_to_swich|. if (current_input_method_.id() != input_method_id_to_switch) { // Clear property list. Property list would be updated by // extension IMEs via InputMethodEngine::(Set|Update)MenuItems. // If the current input method is a keyboard layout, empty // properties are sufficient. const InputMethodPropertyList empty_property_list; SetCurrentInputMethodProperties(empty_property_list); const InputMethodDescriptor* descriptor = NULL; if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) { DCHECK(extra_input_methods_.find(input_method_id_to_switch) != extra_input_methods_.end()); descriptor = &(extra_input_methods_[input_method_id_to_switch]); } else { descriptor = util_.GetInputMethodDescriptorFromId(input_method_id_to_switch); } DCHECK(descriptor); previous_input_method_ = current_input_method_; current_input_method_ = *descriptor; } // Change the keyboard layout to a preferred layout for the input method. if (!xkeyboard_->SetCurrentKeyboardLayoutByName( current_input_method_.GetPreferredKeyboardLayout())) { LOG(ERROR) << "Failed to change keyboard layout to " << current_input_method_.GetPreferredKeyboardLayout(); } // Update input method indicators (e.g. "US", "DV") in Chrome windows. FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_, InputMethodChanged(this, show_message)); return true; } void InputMethodManagerImpl::OnComponentExtensionInitialized( scoped_ptr delegate) { DCHECK(thread_checker_.CalledOnValidThread()); component_extension_ime_manager_->Initialize(delegate.Pass()); util_.SetComponentExtensions( component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor()); LoadNecessaryComponentExtensions(); if (!pending_input_method_.empty()) ChangeInputMethodInternal(pending_input_method_, false); } void InputMethodManagerImpl::LoadNecessaryComponentExtensions() { if (!component_extension_ime_manager_->IsInitialized()) return; // Load component extensions but also update |active_input_method_ids_| as // some component extension IMEs may have been removed from the Chrome OS // image. If specified component extension IME no longer exists, falling back // to an existing IME. std::vector unfiltered_input_method_ids = active_input_method_ids_; active_input_method_ids_.clear(); for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) { if (!extension_ime_util::IsComponentExtensionIME( unfiltered_input_method_ids[i])) { // Legacy IMEs or xkb layouts are alwayes active. active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); } else if (component_extension_ime_manager_->IsWhitelisted( unfiltered_input_method_ids[i])) { component_extension_ime_manager_->LoadComponentExtensionIME( unfiltered_input_method_ids[i]); active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); } } } void InputMethodManagerImpl::ActivateInputMethodProperty( const std::string& key) { DCHECK(!key.empty()); for (size_t i = 0; i < property_list_.size(); ++i) { if (property_list_[i].key == key) { IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetCurrentEngineHandler(); if (engine) engine->PropertyActivate(key); return; } } DVLOG(1) << "ActivateInputMethodProperty: unknown key: " << key; } void InputMethodManagerImpl::AddInputMethodExtension( const std::string& id, InputMethodEngineInterface* engine) { if (state_ == STATE_TERMINATING) return; if (!extension_ime_util::IsExtensionIME(id) && !extension_ime_util::IsComponentExtensionIME(id)) { DVLOG(1) << id << " is not a valid extension input method ID."; return; } DCHECK(engine); const InputMethodDescriptor& descriptor = engine->GetDescriptor(); extra_input_methods_[id] = descriptor; if (Contains(enabled_extension_imes_, id) && !extension_ime_util::IsComponentExtensionIME(id)) { if (!Contains(active_input_method_ids_, id)) { active_input_method_ids_.push_back(id); } else { DVLOG(1) << "AddInputMethodExtension: alread added: " << id << ", " << descriptor.name(); // Call Start() anyway, just in case. } // Ensure that the input method daemon is running. MaybeInitializeCandidateWindowController(); } IBusBridge::Get()->SetEngineHandler(id, engine); } void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) { if (!extension_ime_util::IsExtensionIME(id)) DVLOG(1) << id << " is not a valid extension input method ID."; std::vector::iterator i = std::find( active_input_method_ids_.begin(), active_input_method_ids_.end(), id); if (i != active_input_method_ids_.end()) active_input_method_ids_.erase(i); extra_input_methods_.erase(id); // If |current_input_method| is no longer in |active_input_method_ids_|, // switch to the first one in |active_input_method_ids_|. ChangeInputMethod(current_input_method_.id()); if (IBusBridge::Get()->GetCurrentEngineHandler() == IBusBridge::Get()->GetEngineHandler(id)) IBusBridge::Get()->SetCurrentEngineHandler(NULL); } void InputMethodManagerImpl::GetInputMethodExtensions( InputMethodDescriptors* result) { // Build the extension input method descriptors from the extra input // methods cache |extra_input_methods_|. std::map::iterator iter; for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end(); ++iter) { if (extension_ime_util::IsExtensionIME(iter->first)) result->push_back(iter->second); } } void InputMethodManagerImpl::SetEnabledExtensionImes( std::vector* ids) { enabled_extension_imes_.clear(); enabled_extension_imes_.insert(enabled_extension_imes_.end(), ids->begin(), ids->end()); bool active_imes_changed = false; for (std::map::iterator extra_iter = extra_input_methods_.begin(); extra_iter != extra_input_methods_.end(); ++extra_iter) { if (extension_ime_util::IsComponentExtensionIME( extra_iter->first)) continue; // Do not filter component extension. std::vector::iterator active_iter = std::find( active_input_method_ids_.begin(), active_input_method_ids_.end(), extra_iter->first); bool active = active_iter != active_input_method_ids_.end(); bool enabled = Contains(enabled_extension_imes_, extra_iter->first); if (active && !enabled) active_input_method_ids_.erase(active_iter); if (!active && enabled) active_input_method_ids_.push_back(extra_iter->first); if (active == !enabled) active_imes_changed = true; } if (active_imes_changed) { MaybeInitializeCandidateWindowController(); // If |current_input_method| is no longer in |active_input_method_ids_|, // switch to the first one in |active_input_method_ids_|. ChangeInputMethod(current_input_method_.id()); } } void InputMethodManagerImpl::SetInputMethodDefault() { // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty // and US dvorak keyboard layouts. if (g_browser_process && g_browser_process->local_state()) { const std::string locale = g_browser_process->GetApplicationLocale(); // If the preferred keyboard for the login screen has been saved, use it. PrefService* prefs = g_browser_process->local_state(); std::string initial_input_method_id = prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout); if (initial_input_method_id.empty()) { // If kPreferredKeyboardLayout is not specified, use the hardware layout. initial_input_method_id = GetInputMethodUtil()->GetHardwareInputMethodId(); } EnableLayouts(locale, initial_input_method_id); } } bool InputMethodManagerImpl::SwitchToNextInputMethod() { // Sanity checks. if (active_input_method_ids_.empty()) { DVLOG(1) << "active input method is empty"; return false; } if (current_input_method_.id().empty()) { DVLOG(1) << "current_input_method_ is unknown"; return false; } // Do not consume key event if there is only one input method is enabled. // Ctrl+Space or Alt+Shift may be used by other application. if (active_input_method_ids_.size() == 1) return false; // Find the next input method and switch to it. SwitchToNextInputMethodInternal(active_input_method_ids_, current_input_method_.id()); return true; } bool InputMethodManagerImpl::SwitchToPreviousInputMethod( const ui::Accelerator& accelerator) { // Sanity check. if (active_input_method_ids_.empty()) { DVLOG(1) << "active input method is empty"; return false; } // Do not consume key event if there is only one input method is enabled. // Ctrl+Space or Alt+Shift may be used by other application. if (active_input_method_ids_.size() == 1) return false; if (accelerator.type() == ui::ET_KEY_RELEASED) return true; if (previous_input_method_.id().empty() || previous_input_method_.id() == current_input_method_.id()) { return SwitchToNextInputMethod(); } std::vector::const_iterator iter = std::find(active_input_method_ids_.begin(), active_input_method_ids_.end(), previous_input_method_.id()); if (iter == active_input_method_ids_.end()) { // previous_input_method_ is not supported. return SwitchToNextInputMethod(); } ChangeInputMethodInternal(*iter, true); return true; } bool InputMethodManagerImpl::SwitchInputMethod( const ui::Accelerator& accelerator) { // Sanity check. if (active_input_method_ids_.empty()) { DVLOG(1) << "active input method is empty"; return false; } // Get the list of input method ids for the |accelerator|. For example, get // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR. std::vector input_method_ids_to_switch; switch (accelerator.key_code()) { case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard input_method_ids_to_switch.push_back(nacl_mozc_jp_id); break; case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard input_method_ids_to_switch.push_back("xkb:jp::jpn"); break; case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard case ui::VKEY_DBE_DBCSCHAR: input_method_ids_to_switch.push_back(nacl_mozc_jp_id); input_method_ids_to_switch.push_back("xkb:jp::jpn"); break; default: NOTREACHED(); break; } if (input_method_ids_to_switch.empty()) { DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code(); return false; } // Obtain the intersection of input_method_ids_to_switch and // active_input_method_ids_. The order of IDs in active_input_method_ids_ is // preserved. std::vector ids; for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) { const std::string& id = input_method_ids_to_switch[i]; if (Contains(active_input_method_ids_, id)) ids.push_back(id); } if (ids.empty()) { // No input method for the accelerator is active. For example, we should // just ignore VKEY_HANGUL when mozc-hangul is not active. return false; } SwitchToNextInputMethodInternal(ids, current_input_method_.id()); return true; // consume the accelerator. } void InputMethodManagerImpl::SwitchToNextInputMethodInternal( const std::vector& input_method_ids, const std::string& current_input_method_id) { std::vector::const_iterator iter = std::find(input_method_ids.begin(), input_method_ids.end(), current_input_method_id); if (iter != input_method_ids.end()) ++iter; if (iter == input_method_ids.end()) iter = input_method_ids.begin(); ChangeInputMethodInternal(*iter, true); } InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const { if (current_input_method_.id().empty()) return InputMethodUtil::GetFallbackInputMethodDescriptor(); return current_input_method_; } InputMethodPropertyList InputMethodManagerImpl::GetCurrentInputMethodProperties() const { // This check is necessary since an IME property (e.g. for Pinyin) might be // sent from ibus-daemon AFTER the current input method is switched to XKB. if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id())) return InputMethodPropertyList(); // Empty list. return property_list_; } void InputMethodManagerImpl::SetCurrentInputMethodProperties( const InputMethodPropertyList& property_list) { property_list_ = property_list; PropertyChanged(); } XKeyboard* InputMethodManagerImpl::GetXKeyboard() { return xkeyboard_.get(); } InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() { return &util_; } ComponentExtensionIMEManager* InputMethodManagerImpl::GetComponentExtensionIMEManager() { DCHECK(thread_checker_.CalledOnValidThread()); return component_extension_ime_manager_.get(); } void InputMethodManagerImpl::InitializeComponentExtension() { ComponentExtensionIMEManagerImpl* impl = new ComponentExtensionIMEManagerImpl(); scoped_ptr delegate(impl); impl->InitializeAsync(base::Bind( &InputMethodManagerImpl::OnComponentExtensionInitialized, weak_ptr_factory_.GetWeakPtr(), base::Passed(&delegate))); } void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) { DCHECK(thread_checker_.CalledOnValidThread()); xkeyboard_.reset(XKeyboard::Create()); // We can't call impl->Initialize here, because file thread is not available // at this moment. ui_task_runner->PostTask( FROM_HERE, base::Bind(&InputMethodManagerImpl::InitializeComponentExtension, weak_ptr_factory_.GetWeakPtr())); } void InputMethodManagerImpl::SetCandidateWindowControllerForTesting( CandidateWindowController* candidate_window_controller) { candidate_window_controller_.reset(candidate_window_controller); candidate_window_controller_->AddObserver(this); } void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) { xkeyboard_.reset(xkeyboard); } void InputMethodManagerImpl::InitializeComponentExtensionForTesting( scoped_ptr delegate) { OnComponentExtensionInitialized(delegate.Pass()); } void InputMethodManagerImpl::PropertyChanged() { FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_, InputMethodPropertyChanged(this)); } void InputMethodManagerImpl::CandidateClicked(int index) { IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetCurrentEngineHandler(); if (engine) engine->CandidateClicked(index); } void InputMethodManagerImpl::CandidateWindowOpened() { FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, candidate_window_observers_, CandidateWindowOpened(this)); } void InputMethodManagerImpl::CandidateWindowClosed() { FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, candidate_window_observers_, CandidateWindowClosed(this)); } void InputMethodManagerImpl::OnScreenLocked() { saved_previous_input_method_ = previous_input_method_; saved_current_input_method_ = current_input_method_; saved_active_input_method_ids_ = active_input_method_ids_; const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId(); // We'll add the hardware keyboard if it's not included in // |active_input_method_list| so that the user can always use the hardware // keyboard on the screen locker. bool should_add_hardware_keyboard = true; active_input_method_ids_.clear(); for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) { const std::string& input_method_id = saved_active_input_method_ids_[i]; // Skip if it's not a keyboard layout. Drop input methods including // extension ones. if (!IsLoginKeyboard(input_method_id)) continue; active_input_method_ids_.push_back(input_method_id); if (input_method_id == hardware_keyboard_id) should_add_hardware_keyboard = false; } if (should_add_hardware_keyboard) active_input_method_ids_.push_back(hardware_keyboard_id); ChangeInputMethod(current_input_method_.id()); } void InputMethodManagerImpl::OnScreenUnlocked() { previous_input_method_ = saved_previous_input_method_; current_input_method_ = saved_current_input_method_; active_input_method_ids_ = saved_active_input_method_ids_; ChangeInputMethod(current_input_method_.id()); } bool InputMethodManagerImpl::InputMethodIsActivated( const std::string& input_method_id) { return Contains(active_input_method_ids_, input_method_id); } bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout( const std::vector& value) { for (size_t i = 0; i < value.size(); ++i) { if (!InputMethodUtil::IsKeyboardLayout(value[i])) return false; } return true; } void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() { if (candidate_window_controller_.get()) return; candidate_window_controller_.reset( CandidateWindowController::CreateCandidateWindowController()); candidate_window_controller_->AddObserver(this); } } // namespace input_method } // namespace chromeos