chromium/ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/ime/fuchsia/virtual_keyboard_controller_fuchsia.h"

#include <fidl/fuchsia.input.virtualkeyboard/cpp/natural_ostream.h>
#include <lib/async/default.h>
#include <lib/fit/function.h>
#include <lib/sys/cpp/component_context.h>

#include <utility>

#include "base/check.h"
#include "base/fuchsia/fuchsia_component_connect.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "ui/base/ime/text_input_client.h"

namespace ui {

VirtualKeyboardControllerFuchsia::VirtualKeyboardControllerFuchsia(
    fuchsia_ui_views::ViewRef view_ref,
    ui::InputMethodBase* input_method)
    : input_method_(input_method) {
  DCHECK(input_method_);

  auto controller_creator_client_end = base::fuchsia_component::Connect<
      fuchsia_input_virtualkeyboard::ControllerCreator>();
  if (!controller_creator_client_end.is_ok()) {
    LOG(ERROR) << base::FidlConnectionErrorMessage(
        controller_creator_client_end);
    return;
  }
  fidl::Client controller_creator(
      std::move(controller_creator_client_end.value()),
      async_get_default_dispatcher());

  auto controller_endpoints =
      fidl::CreateEndpoints<fuchsia_input_virtualkeyboard::Controller>();
  ZX_CHECK(controller_endpoints.is_ok(), controller_endpoints.status_value());
  auto create_result = controller_creator->Create({{
      .view_ref = std::move(view_ref),
      .text_type = requested_type_,
      .controller_request = std::move(controller_endpoints->server),
  }});

  if (create_result.is_error()) {
    ZX_DLOG(ERROR, create_result.error_value().status());
    return;
  }
  controller_client_.Bind(std::move(controller_endpoints->client),
                          async_get_default_dispatcher(),
                          &controller_error_logger_);

  WatchVisibility();
}

VirtualKeyboardControllerFuchsia::~VirtualKeyboardControllerFuchsia() = default;

bool VirtualKeyboardControllerFuchsia::DisplayVirtualKeyboard() {
  DVLOG(1) << "DisplayVirtualKeyboard (visible= " << keyboard_visible_ << ")";

  if (!controller_client_) {
    return false;
  }

  UpdateTextType();

  requested_visible_ = true;
  auto result = controller_client_->RequestShow();
  if (result.is_error()) {
    ZX_DLOG(ERROR, result.error_value().status())
        << "Error calling RequestShow()";
    return false;
  }

  return true;
}

void VirtualKeyboardControllerFuchsia::DismissVirtualKeyboard() {
  DVLOG(1) << "DismissVirtualKeyboard (visible= " << keyboard_visible_ << ")";

  if (!controller_client_) {
    return;
  }

  if (!requested_visible_ && !keyboard_visible_)
    return;

  requested_visible_ = false;
  auto result = controller_client_->RequestHide();
  if (result.is_error()) {
    ZX_DLOG(ERROR, result.error_value().status())
        << "Error calling RequestHide()";
  }
}

void VirtualKeyboardControllerFuchsia::AddObserver(
    VirtualKeyboardControllerObserver* observer) {}

void VirtualKeyboardControllerFuchsia::RemoveObserver(
    VirtualKeyboardControllerObserver* observer) {}

bool VirtualKeyboardControllerFuchsia::IsKeyboardVisible() {
  return keyboard_visible_;
}

void VirtualKeyboardControllerFuchsia::WatchVisibility() {
  if (!controller_client_) {
    return;
  }

  controller_client_->WatchVisibility().Then(fit::bind_member(
      this, &VirtualKeyboardControllerFuchsia::OnVisibilityChange));
}

void VirtualKeyboardControllerFuchsia::OnVisibilityChange(
    const fidl::Result<
        fuchsia_input_virtualkeyboard::Controller::WatchVisibility>& result) {
  if (result.is_error()) {
    ZX_DLOG(ERROR, result.error_value().status()) << __func__;
    return;
  }
  DVLOG(1) << "OnVisibilityChange " << result->is_visible();
  keyboard_visible_ = result->is_visible();
  WatchVisibility();
}

// Returns the FIDL enum representation of the current InputMode.
fuchsia_input_virtualkeyboard::TextType
VirtualKeyboardControllerFuchsia::GetFocusedTextType() const {
  TextInputClient* client = input_method_->GetTextInputClient();
  // This function should only be called when there's focus, so there should
  // always be a TextInputClient.
  DCHECK(client);

  switch (client->GetTextInputMode()) {
    case TEXT_INPUT_MODE_NUMERIC:
    case TEXT_INPUT_MODE_DECIMAL:
      return fuchsia_input_virtualkeyboard::TextType::kNumeric;

    case TEXT_INPUT_MODE_TEL:
      return fuchsia_input_virtualkeyboard::TextType::kPhone;

    case TEXT_INPUT_MODE_TEXT:
    case TEXT_INPUT_MODE_URL:
    case TEXT_INPUT_MODE_EMAIL:
    case TEXT_INPUT_MODE_SEARCH:
      return fuchsia_input_virtualkeyboard::TextType::kAlphanumeric;

    // Should be handled in InputMethodFuchsia.
    case TEXT_INPUT_MODE_NONE:
      NOTREACHED();

    case TEXT_INPUT_MODE_DEFAULT:
      // Fall-through to using TextInputType.
      break;
  }

  switch (client->GetTextInputType()) {
    case TEXT_INPUT_TYPE_NUMBER:
      return fuchsia_input_virtualkeyboard::TextType::kNumeric;

    case TEXT_INPUT_TYPE_TELEPHONE:
      return fuchsia_input_virtualkeyboard::TextType::kPhone;

    default:
      return fuchsia_input_virtualkeyboard::TextType::kAlphanumeric;
  }
}

void VirtualKeyboardControllerFuchsia::UpdateTextType() {
  // Only send updates if the type has changed.
  auto new_type = GetFocusedTextType();
  DVLOG(1) << "UpdateTextType() called (current: " << requested_type_
           << ", new: " << new_type << ")";
  if (new_type != requested_type_) {
    auto result = controller_client_->SetTextType(new_type);
    if (result.is_error()) {
      ZX_DLOG(ERROR, result.error_value().status())
          << "Error calling SetTextType()";
    }
    requested_type_ = new_type;
  }
}

}  // namespace ui