chromium/chrome/browser/ash/input_method/editor_panel_manager.cc

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

#include "chrome/browser/ash/input_method/editor_panel_manager.h"

#include <string_view>
#include <utility>

#include "ash/constants/ash_features.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/notreached.h"
#include "chrome/browser/ash/input_method/editor_consent_enums.h"
#include "chrome/browser/ash/input_method/editor_metrics_enums.h"
#include "chrome/browser/ash/input_method/editor_metrics_recorder.h"
#include "chromeos/crosapi/mojom/editor_panel.mojom.h"

namespace ash::input_method {

namespace {

crosapi::mojom::EditorPanelPresetQueryCategory ToEditorPanelQueryCategory(
    const orca::mojom::PresetTextQueryType query_type) {
  switch (query_type) {
    case orca::mojom::PresetTextQueryType::kUnknown:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kUnknown;
    case orca::mojom::PresetTextQueryType::kShorten:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kShorten;
    case orca::mojom::PresetTextQueryType::kElaborate:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kElaborate;
    case orca::mojom::PresetTextQueryType::kRephrase:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kRephrase;
    case orca::mojom::PresetTextQueryType::kFormalize:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kFormalize;
    case orca::mojom::PresetTextQueryType::kEmojify:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kEmojify;
    case orca::mojom::PresetTextQueryType::kProofread:
      return crosapi::mojom::EditorPanelPresetQueryCategory::kProofread;
  }
}

crosapi::mojom::EditorPanelMode GetEditorPanelMode(EditorMode editor_mode) {
  switch (editor_mode) {
    case EditorMode::kHardBlocked:
      return crosapi::mojom::EditorPanelMode::kHardBlocked;
    case EditorMode::kSoftBlocked:
      return crosapi::mojom::EditorPanelMode::kSoftBlocked;
    case EditorMode::kConsentNeeded:
      return crosapi::mojom::EditorPanelMode::kPromoCard;
    case EditorMode::kRewrite:
      return crosapi::mojom::EditorPanelMode::kRewrite;
    case EditorMode::kWrite:
      return crosapi::mojom::EditorPanelMode::kWrite;
  }
}

crosapi::mojom::EditorPanelPresetTextQueryPtr ToEditorPanelQuery(
    const orca::mojom::PresetTextQueryPtr& orca_query) {
  auto editor_panel_query = crosapi::mojom::EditorPanelPresetTextQuery::New();
  editor_panel_query->text_query_id = orca_query->id;
  editor_panel_query->name = orca_query->label;
  editor_panel_query->description = orca_query->description;
  editor_panel_query->category = ToEditorPanelQueryCategory(orca_query->type);
  return editor_panel_query;
}

}  // namespace

EditorPanelManager::EditorPanelManager(Delegate* delegate)
    : delegate_(delegate) {}

EditorPanelManager::~EditorPanelManager() = default;

void EditorPanelManager::BindReceiver(
    mojo::PendingReceiver<crosapi::mojom::EditorPanelManager>
        pending_receiver) {
  receivers_.Add(this, std::move(pending_receiver));
}

void EditorPanelManager::BindEditorClient() {
  if (editor_client_remote_.is_bound() &&
      !base::FeatureList::IsEnabled(ash::features::kOrcaServiceConnection)) {
    return;
  }

  editor_client_remote_.reset();
  delegate_->BindEditorClient(
      editor_client_remote_.BindNewPipeAndPassReceiver());
  editor_client_remote_.reset_on_disconnect();
}

void EditorPanelManager::GetEditorPanelContext(
    GetEditorPanelContextCallback callback) {
  const auto editor_panel_mode = GetEditorPanelMode(delegate_->GetEditorMode());

  if (editor_panel_mode != crosapi::mojom::EditorPanelMode::kSoftBlocked &&
      editor_panel_mode != crosapi::mojom::EditorPanelMode::kHardBlocked &&
      editor_client_remote_.is_bound()) {
    editor_client_remote_->GetPresetTextQueries(
        base::BindOnce(&EditorPanelManager::OnGetPresetTextQueriesResult,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                       editor_panel_mode));
    return;
  }

  auto context = crosapi::mojom::EditorPanelContext::New();
  context->editor_panel_mode = editor_panel_mode;
  context->consent_status_settled =
      delegate_->GetConsentStatus() != ConsentStatus::kUnset;
  std::move(callback).Run(std::move(context));
}

void EditorPanelManager::OnPromoCardDismissed() {}

void EditorPanelManager::OnPromoCardDeclined() {
  delegate_->OnPromoCardDeclined();
  delegate_->GetMetricsRecorder()->LogEditorState(
      EditorStates::kPromoCardExplicitDismissal);
}

void EditorPanelManager::OnConsentRejected() {
  delegate_->ProcessConsentAction(ConsentAction::kDecline);
}

void EditorPanelManager::StartEditingFlow() {
  delegate_->HandleTrigger(/*preset_query_id=*/std::nullopt,
                           /*freeform_text=*/std::nullopt);
}

void EditorPanelManager::StartEditingFlowWithPreset(
    const std::string& text_query_id) {
  delegate_->HandleTrigger(/*preset_query_id=*/text_query_id,
                           /*freeform_text=*/std::nullopt);
}

void EditorPanelManager::StartEditingFlowWithFreeform(const std::string& text) {
  delegate_->HandleTrigger(/*preset_query_id=*/std::nullopt,
                           /*freeform_text=*/text);
}

void EditorPanelManager::OnGetPresetTextQueriesResult(
    GetEditorPanelContextCallback callback,
    crosapi::mojom::EditorPanelMode panel_mode,
    std::vector<orca::mojom::PresetTextQueryPtr> queries) {
  auto context = crosapi::mojom::EditorPanelContext::New();
  context->editor_panel_mode = panel_mode;
  context->consent_status_settled =
      delegate_->GetConsentStatus() != ConsentStatus::kUnset;
  for (const auto& query : queries) {
    context->preset_text_queries.push_back(ToEditorPanelQuery(query));
  }
  std::move(callback).Run(std::move(context));
}

void EditorPanelManager::OnEditorMenuVisibilityChanged(bool visible) {
  is_editor_menu_visible_ = visible;
}

bool EditorPanelManager::IsEditorMenuVisible() const {
  return is_editor_menu_visible_;
}

void EditorPanelManager::LogEditorMode(crosapi::mojom::EditorPanelMode mode) {
  EditorOpportunityMode opportunity_mode =
      delegate_->GetEditorOpportunityMode();
  EditorMetricsRecorder* logger = delegate_->GetMetricsRecorder();
  logger->SetMode(opportunity_mode);
  logger->SetTone(EditorTone::kUnset);
  if (opportunity_mode == EditorOpportunityMode::kRewrite ||
      opportunity_mode == EditorOpportunityMode::kWrite) {
    logger->LogEditorState(EditorStates::kNativeUIShowOpportunity);
  }

  if (mode == crosapi::mojom::EditorPanelMode::kRewrite ||
      mode == crosapi::mojom::EditorPanelMode::kWrite) {
    logger->LogEditorState(EditorStates::kNativeUIShown);
  }

  if (mode == crosapi::mojom::EditorPanelMode::kHardBlocked ||
      mode == crosapi::mojom::EditorPanelMode::kSoftBlocked) {
    logger->LogEditorState(EditorStates::kBlocked);
    for (EditorBlockedReason blocked_reason : delegate_->GetBlockedReasons()) {
      logger->LogEditorState(ToEditorStatesMetric(blocked_reason));
    }
  }

  if (mode == crosapi::mojom::EditorPanelMode::kPromoCard) {
    logger->LogEditorState(EditorStates::kPromoCardImpression);
  }
}

void EditorPanelManager::BindEditorObserver(
    mojo::PendingRemote<crosapi::mojom::EditorObserver>
        pending_observer_remote) {
  observer_remotes_.Add(std::move(pending_observer_remote));
}

void EditorPanelManager::AddObserver(EditorPanelManager::Observer* observer) {
  observers_.AddObserver(observer);
}

void EditorPanelManager::RemoveObserver(
    EditorPanelManager::Observer* observer) {
  observers_.RemoveObserver(observer);
}

void EditorPanelManager::NotifyEditorModeChanged(const EditorMode& mode) {
  for (const mojo::Remote<crosapi::mojom::EditorObserver>& obs :
       observer_remotes_) {
    obs->OnEditorPanelModeChanged(GetEditorPanelMode(mode));
  }
  for (EditorPanelManager::Observer& obs : observers_) {
    obs.OnEditorModeChanged(mode);
  }
}

void EditorPanelManager::RequestCacheContext() {
  delegate_->CacheContext();
}

void EditorPanelManager::OnConsentApproved() {
  delegate_->ProcessConsentAction(ConsentAction::kApprove);
}

void EditorPanelManager::OnMagicBoostPromoCardDeclined() {
  // Reject consent and follow the normal workflow similar to when Orca's promo
  // card is declined.
  OnConsentRejected();
  OnPromoCardDeclined();
}

void EditorPanelManager::SetEditorClientForTesting(
    mojo::PendingRemote<orca::mojom::EditorClient> client_for_testing) {
  editor_client_remote_.Bind(std::move(client_for_testing));
}

}  // namespace ash::input_method