blob: 2f09d97d22b0a75bac03c9db217435f08cbba299 [file] [log] [blame]
// 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/picker/picker_search_controller.h"
#include <algorithm>
#include <cstddef>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "ash/picker/model/picker_category.h"
#include "ash/picker/model/picker_search_results.h"
#include "ash/picker/views/picker_view_delegate.h"
#include "ash/public/cpp/picker/picker_client.h"
#include "ash/public/cpp/picker/picker_search_result.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "base/check.h"
#include "base/check_deref.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ash/components/emoji/emoji_search.h"
#include "ui/base/models/image_model.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/vector_icon_types.h"
#include "url/gurl.h"
namespace ash {
enum class AppListSearchResultType;
namespace {
base::span<const std::string> FirstNOrLessElements(
base::span<const std::string> container,
size_t n) {
return container.subspan(0, std::min(container.size(), n));
}
} // namespace
PickerSearchController::PickerSearchController(PickerClient* client,
base::TimeDelta burn_in_period)
: client_(CHECK_DEREF(client)), burn_in_period_(burn_in_period) {}
PickerSearchController::~PickerSearchController() = default;
void PickerSearchController::StartSearch(
const std::u16string& query,
std::optional<PickerCategory> category,
PickerViewDelegate::SearchResultsCallback callback) {
current_callback_.Reset();
client_->StopCrosQuery();
ResetResults();
current_callback_ = std::move(callback);
current_query_ = query;
client_->StartCrosSearch(
query, base::BindRepeating(&PickerSearchController::HandleSearchResults,
weak_ptr_factory_.GetWeakPtr()));
std::string utf8_query = base::UTF16ToUTF8(query);
client_->FetchGifSearch(
utf8_query,
base::BindOnce(&PickerSearchController::HandleGifSearchResults,
weak_ptr_factory_.GetWeakPtr(), query));
// Emoji search is currently synchronous.
HandleEmojiSearchResults(emoji_search_.SearchEmoji(utf8_query));
// TODO: b/324154537 - Show a loading animation while waiting for results.
burn_in_timer_.Start(FROM_HERE, burn_in_period_, this,
&PickerSearchController::PublishResults);
}
void PickerSearchController::ResetResults() {
omnibox_results_.clear();
gif_results_.clear();
emoji_search_results_.clear();
}
void PickerSearchController::PublishResults() {
if (!current_callback_) {
return;
}
std::vector<PickerSearchResult> expression_results;
expression_results.reserve(emoji_search_results_.size() +
gif_results_.size());
expression_results.insert(expression_results.end(),
emoji_search_results_.begin(),
emoji_search_results_.end());
expression_results.insert(expression_results.end(), gif_results_.begin(),
gif_results_.end());
std::vector<PickerSearchResults::Section> sections;
if (!expression_results.empty()) {
sections.push_back(PickerSearchResults::Section(u"Matching expressions",
expression_results));
}
if (!omnibox_results_.empty()) {
sections.push_back(
PickerSearchResults::Section(u"Matching links", omnibox_results_));
}
current_callback_.Run(PickerSearchResults(std::move(sections)));
}
void PickerSearchController::HandleSearchResults(
ash::AppListSearchResultType type,
std::vector<PickerSearchResult> results) {
omnibox_results_ = std::move(results);
}
void PickerSearchController::HandleGifSearchResults(
std::u16string query,
std::vector<PickerSearchResult> results) {
// As we cannot stop GIF search result callbacks, check whether the query for
// this request is the current query to prevent showing results from an
// outdated query.
// TODO: b/324992789 - Allow stopping GIF search results.
if (query == current_query_) {
gif_results_ = std::move(results);
}
}
void PickerSearchController::HandleEmojiSearchResults(
emoji::EmojiSearchResult results) {
std::vector<PickerSearchResult> picker_emoji_search_results;
for (const std::string& result : FirstNOrLessElements(results.emojis, 3)) {
picker_emoji_search_results.push_back(
PickerSearchResult::Emoji(base::UTF8ToUTF16(result)));
}
for (const std::string& result : FirstNOrLessElements(results.symbols, 2)) {
picker_emoji_search_results.push_back(
PickerSearchResult::Symbol(base::UTF8ToUTF16(result)));
}
for (const std::string& result : FirstNOrLessElements(results.emoticons, 2)) {
picker_emoji_search_results.push_back(
PickerSearchResult::Emoticon(base::UTF8ToUTF16(result)));
}
emoji_search_results_ = std::move(picker_emoji_search_results);
}
} // namespace ash