blob: 4957d6dc048cc332e1faeb71f2d2e93c2d935671 [file] [log] [blame] [edit]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/quick_insert/quick_insert_suggestions_controller.h"
#include "ash/quick_insert/model/quick_insert_mode_type.h"
#include "ash/quick_insert/model/quick_insert_model.h"
#include "ash/quick_insert/quick_insert_category.h"
#include "ash/quick_insert/quick_insert_client.h"
#include "ash/quick_insert/quick_insert_clipboard_history_provider.h"
#include "ash/quick_insert/quick_insert_search_result.h"
#include "ash/quick_insert/quick_insert_shortcuts.h"
#include "ash/quick_insert/search/quick_insert_date_search.h"
#include "ash/quick_insert/search/quick_insert_math_search.h"
#include "base/containers/to_vector.h"
#include "base/feature_list.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ash/components/emoji/gif_tenor_api_fetcher.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
namespace ash {
namespace {
constexpr int kMaxRecentFiles = 10;
constexpr int kMaxRecentLinks = 10;
constexpr base::TimeDelta kMaxLocalFileSuggestionRecencyDelta = base::Days(30);
constexpr base::TimeDelta kMaxLocalFileCategoryRecencyDelta = base::Days(3652);
std::vector<QuickInsertSearchResult> ConvertToSearchResults(
base::expected<tenor::mojom::PaginatedGifResponsesPtr,
GifTenorApiFetcher::Error> response) {
if (!response.has_value()) {
// TODO: b/325368650 - Add better handling of errors.
return {};
}
size_t rank = 0;
return base::ToVector(
(*response)->results, [&rank](tenor::mojom::GifResponsePtr& result) {
CHECK(result);
tenor::mojom::GifUrlsPtr& urls = result->url;
CHECK(urls);
return QuickInsertSearchResult(QuickInsertGifResult(
std::move(urls->preview), std::move(urls->preview_image),
result->preview_size, std::move(urls->full), result->full_size,
base::UTF8ToUTF16(result->content_description), rank++));
});
}
} // namespace
QuickInsertSuggestionsController::QuickInsertSuggestionsController() = default;
QuickInsertSuggestionsController::~QuickInsertSuggestionsController() = default;
std::vector<QuickInsertSearchResult> GetMostRecentResults(
size_t n,
std::vector<QuickInsertSearchResult> results) {
if (results.size() > n) {
results.erase(results.begin() + n, results.end());
}
return results;
}
void QuickInsertSuggestionsController::GetSuggestions(
QuickInsertClient& client,
const QuickInsertModel& model,
SuggestionsCallback callback) {
if (model.GetMode() == QuickInsertModeType::kUnfocused) {
std::vector<QuickInsertSearchResult> new_window_results;
for (QuickInsertNewWindowResult::Type type : {
QuickInsertNewWindowResult::Type::kDoc,
QuickInsertNewWindowResult::Type::kSheet,
QuickInsertNewWindowResult::Type::kSlide,
QuickInsertNewWindowResult::Type::kChrome,
}) {
new_window_results.push_back(QuickInsertNewWindowResult(type));
}
callback.Run(std::move(new_window_results));
}
if (model.GetMode() == QuickInsertModeType::kUnfocused ||
model.GetMode() == QuickInsertModeType::kNoSelection) {
callback.Run({QuickInsertCapsLockResult(
!model.is_caps_lock_enabled(), GetQuickInsertShortcutForCapsLock())});
}
if (base::Contains(model.GetAvailableCategories(),
QuickInsertCategory::kEditorRewrite)) {
client.GetSuggestedEditorResults(callback);
}
if (base::Contains(model.GetAvailableCategories(),
QuickInsertCategory::kLobsterWithSelectedText)) {
callback.Run({QuickInsertLobsterResult(
QuickInsertLobsterResult::Mode::kWithSelection, /*display_name=*/u"")});
}
if (model.GetMode() == QuickInsertModeType::kHasSelection) {
std::vector<QuickInsertSearchResult> case_transform_results;
for (QuickInsertCaseTransformResult::Type type : {
QuickInsertCaseTransformResult::Type::kUpperCase,
QuickInsertCaseTransformResult::Type::kLowerCase,
QuickInsertCaseTransformResult::Type::kTitleCase,
}) {
case_transform_results.push_back(QuickInsertCaseTransformResult(type));
}
callback.Run(std::move(case_transform_results));
}
// TODO: b/344685737 - Rank and collect suggestions in a more intelligent way.
for (QuickInsertCategory category : model.GetRecentResultsCategories()) {
// Special case certain categories where we can save computation by only
// asking for 1 result.
// TODO: b/357740941: Request only one Drive file once directory filtering
// is implemented inside DriveFS.
// TODO: b/366237507 - Request only one Link result once HistoryService
// supports filtering.
switch (category) {
case QuickInsertCategory::kLinks:
link_suggester_.GetSuggestedLinks(
client.GetHistoryService(), client.GetFaviconService(),
/*max_results=*/10,
base::BindRepeating(&GetMostRecentResults, 1).Then(callback));
break;
case QuickInsertCategory::kLocalFiles: {
const size_t max_results = 3;
client.GetRecentLocalFileResults(
max_results, kMaxLocalFileSuggestionRecencyDelta,
base::BindRepeating(&GetMostRecentResults, max_results)
.Then(callback));
break;
}
case QuickInsertCategory::kDriveFiles:
client.GetRecentDriveFileResults(
/*max_results=*/5,
base::BindRepeating(&GetMostRecentResults, 1).Then(callback));
break;
default:
GetSuggestionsForCategory(
client, category,
base::BindRepeating(&GetMostRecentResults, 1).Then(callback));
break;
}
}
}
void QuickInsertSuggestionsController::GetSuggestionsForCategory(
QuickInsertClient& client,
QuickInsertCategory category,
SuggestionsCallback callback) {
switch (category) {
case QuickInsertCategory::kEditorWrite:
case QuickInsertCategory::kEditorRewrite:
case QuickInsertCategory::kLobsterWithNoSelectedText:
case QuickInsertCategory::kLobsterWithSelectedText:
NOTREACHED();
case QuickInsertCategory::kLinks:
// TODO: b/366237507 - Request only kMaxRecentLinks results once
// HistoryService supports filtering.
link_suggester_.GetSuggestedLinks(
client.GetHistoryService(), client.GetFaviconService(),
kMaxRecentLinks * 3, std::move(callback));
return;
case QuickInsertCategory::kEmojisGifs:
case QuickInsertCategory::kEmojis:
NOTREACHED();
case QuickInsertCategory::kGifs:
GifTenorApiFetcher::FetchFeaturedGifs(
client.GetSharedURLLoaderFactory(),
/*=*/std::nullopt,
base::BindOnce(ConvertToSearchResults).Then(std::move(callback)));
return;
case QuickInsertCategory::kDriveFiles:
client.GetRecentDriveFileResults(kMaxRecentFiles, std::move(callback));
return;
case QuickInsertCategory::kLocalFiles:
client.GetRecentLocalFileResults(kMaxRecentFiles,
kMaxLocalFileCategoryRecencyDelta,
std::move(callback));
return;
case QuickInsertCategory::kDatesTimes:
std::move(callback).Run(QuickInsertSuggestedDateResults());
break;
case QuickInsertCategory::kUnitsMaths:
std::move(callback).Run(QuickInsertMathExamples());
break;
case QuickInsertCategory::kClipboard:
clipboard_provider_.FetchResults(std::move(callback));
return;
}
}
} // namespace ash