[game search] Add skeleton of search provider and result
This adds: - A stub API for cloud gaming results, to be replaced in the near future - A search provider and result using that API - Filtering logic to deduplicate between omnibox and game search results Bug: 1305880 Change-Id: I0f1e3433c5a5f01ae76d9c8063dd768d40cc7c00 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3527536 Reviewed-by: Rachel Wong <wrong@chromium.org> Commit-Queue: Tony Yeoman <tby@chromium.org> Cr-Commit-Position: refs/heads/main@{#984705}
This commit is contained in:
ash
chrome
@ -4649,6 +4649,9 @@ New install
|
||||
<message name="IDS_APP_LIST_OPEN_TAB_HINT" desc="Shown alongside launcher search results which will take the user to an open tab when clicked.">
|
||||
Go to this tab
|
||||
</message>
|
||||
<message name="IDS_APP_LIST_SEARCH_GAME_PLATFORMS_PREFIX" desc="Shown next to a video game search result, begins a list of cloud gaming platforms the game is playable on.">
|
||||
on
|
||||
</message>
|
||||
<message name="IDS_APP_LIST_START_ASSISTANT" desc="Tooltip for the button that starts Google Assistant from the search box in the app list.">
|
||||
Google Assistant
|
||||
</message>
|
||||
|
@ -0,0 +1 @@
|
||||
2098bdec94d7cc66c749c8caa9127e2ffd1115f2
|
@ -2024,6 +2024,12 @@ static_library("ui") {
|
||||
"app_list/search/files/zero_state_drive_provider.h",
|
||||
"app_list/search/files/zero_state_file_provider.cc",
|
||||
"app_list/search/files/zero_state_file_provider.h",
|
||||
"app_list/search/games/game_provider.cc",
|
||||
"app_list/search/games/game_provider.h",
|
||||
"app_list/search/games/game_result.cc",
|
||||
"app_list/search/games/game_result.h",
|
||||
"app_list/search/games/stub_api.cc",
|
||||
"app_list/search/games/stub_api.h",
|
||||
"app_list/search/help_app_provider.cc",
|
||||
"app_list/search/help_app_provider.h",
|
||||
"app_list/search/keyboard_shortcut_data.cc",
|
||||
|
@ -70,8 +70,7 @@ class FileResult : public ChromeSearchResult {
|
||||
}
|
||||
|
||||
private:
|
||||
// Callback for the result of MaybeRequestThumbnail's call to the
|
||||
// ThumbnailLoader.
|
||||
// Callback for the result of RequestThumbnail's call to the ThumbnailLoader.
|
||||
void OnThumbnailLoaded(const SkBitmap* bitmap, base::File::Error error);
|
||||
|
||||
// Callback for the result of SetDetailsToJustificationString to
|
||||
|
124
chrome/browser/ui/app_list/search/games/game_provider.cc
Normal file
124
chrome/browser/ui/app_list/search/games/game_provider.cc
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2022 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/ui/app_list/search/games/game_provider.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/time/time.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/game_result.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "chromeos/components/string_matching/tokenized_string.h"
|
||||
#include "chromeos/components/string_matching/tokenized_string_match.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
namespace app_list {
|
||||
namespace {
|
||||
|
||||
using chromeos::string_matching::TokenizedString;
|
||||
using chromeos::string_matching::TokenizedStringMatch;
|
||||
|
||||
constexpr double kRelevanceThreshold = 0.7;
|
||||
constexpr size_t kMaxResults = 3u;
|
||||
|
||||
double CalculateTitleRelevance(const TokenizedString& tokenized_query,
|
||||
const std::u16string& game_title) {
|
||||
const TokenizedString tokenized_title(game_title,
|
||||
TokenizedString::Mode::kCamelCase);
|
||||
|
||||
if (tokenized_query.text().empty() || tokenized_title.text().empty()) {
|
||||
static constexpr double kDefaultRelevance = 0.0;
|
||||
return kDefaultRelevance;
|
||||
}
|
||||
|
||||
TokenizedStringMatch match;
|
||||
match.Calculate(tokenized_query, tokenized_title);
|
||||
return match.relevance();
|
||||
}
|
||||
|
||||
std::vector<std::pair<GameData, double>> SearchGames(std::u16string query,
|
||||
GameIndex* index) {
|
||||
TokenizedString tokenized_query(query, TokenizedString::Mode::kCamelCase);
|
||||
|
||||
std::vector<std::pair<GameData, double>> matches;
|
||||
for (const auto& game_data : *index) {
|
||||
double relevance =
|
||||
CalculateTitleRelevance(tokenized_query, game_data.title);
|
||||
if (relevance > kRelevanceThreshold) {
|
||||
matches.push_back(std::make_pair(game_data, relevance));
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GameProvider::GameProvider(Profile* profile,
|
||||
AppListControllerDelegate* list_controller)
|
||||
: profile_(profile), list_controller_(list_controller) {
|
||||
DCHECK(profile_);
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
game_index_manager_ = std::make_unique<GameIndexManager>();
|
||||
game_index_ = game_index_manager_->GetIndex();
|
||||
|
||||
index_observer_.Observe(game_index_manager_.get());
|
||||
}
|
||||
|
||||
GameProvider::~GameProvider() = default;
|
||||
|
||||
ash::AppListSearchResultType GameProvider::ResultType() const {
|
||||
return ash::AppListSearchResultType::kGames;
|
||||
}
|
||||
|
||||
void GameProvider::OnIndexUpdated(const absl::optional<GameIndex>& index) {
|
||||
if (index)
|
||||
game_index_ = index;
|
||||
}
|
||||
|
||||
void GameProvider::Start(const std::u16string& query) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
if (!game_index_)
|
||||
return;
|
||||
|
||||
// Clear results and discard any existing searches.
|
||||
ClearResultsSilently();
|
||||
weak_factory_.InvalidateWeakPtrs();
|
||||
|
||||
base::ThreadPool::PostTaskAndReplyWithResult(
|
||||
FROM_HERE, {base::TaskPriority::USER_BLOCKING},
|
||||
base::BindOnce(&SearchGames, query, &game_index_.value()),
|
||||
base::BindOnce(&GameProvider::OnSearchComplete,
|
||||
weak_factory_.GetWeakPtr(), query));
|
||||
}
|
||||
|
||||
void GameProvider::SetGameIndexForTest(const GameIndex& game_index) {
|
||||
game_index_ = game_index;
|
||||
}
|
||||
|
||||
void GameProvider::OnSearchComplete(
|
||||
std::u16string query,
|
||||
std::vector<std::pair<GameData, double>> matches) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
// Sort matches by descending relevance score.
|
||||
std::sort(matches.begin(), matches.end(),
|
||||
[](const auto& a, const auto& b) { return a.second > b.second; });
|
||||
|
||||
SearchProvider::Results results;
|
||||
for (size_t i = 0; i < std::min(matches.size(), kMaxResults); ++i) {
|
||||
results.emplace_back(std::make_unique<GameResult>(
|
||||
profile_, list_controller_, game_index_manager_.get(), matches[i].first,
|
||||
matches[i].second, query));
|
||||
}
|
||||
SwapResults(&results);
|
||||
}
|
||||
|
||||
} // namespace app_list
|
63
chrome/browser/ui/app_list/search/games/game_provider.h
Normal file
63
chrome/browser/ui/app_list/search/games/game_provider.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_PROVIDER_H_
|
||||
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_PROVIDER_H_
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/scoped_observation.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/time/time.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/stub_api.h"
|
||||
#include "chrome/browser/ui/app_list/search/search_provider.h"
|
||||
#include "chrome/browser/ui/ash/thumbnail_loader.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
class AppListControllerDelegate;
|
||||
class Profile;
|
||||
|
||||
namespace app_list {
|
||||
|
||||
// Provider for cloud gaming search.
|
||||
class GameProvider : public SearchProvider, public GameIndexManager::Observer {
|
||||
public:
|
||||
GameProvider(Profile* profile, AppListControllerDelegate* list_controller);
|
||||
~GameProvider() override;
|
||||
|
||||
GameProvider(const GameProvider&) = delete;
|
||||
GameProvider& operator=(const GameProvider&) = delete;
|
||||
|
||||
// GameIndexManager::Observer:
|
||||
void OnIndexUpdated(const absl::optional<GameIndex>& index) override;
|
||||
|
||||
// SearchProvider:
|
||||
ash::AppListSearchResultType ResultType() const override;
|
||||
void Start(const std::u16string& query) override;
|
||||
|
||||
void SetGameIndexForTest(const GameIndex& game_index);
|
||||
|
||||
private:
|
||||
void OnSearchComplete(std::u16string query,
|
||||
std::vector<std::pair<GameData, double>> matches);
|
||||
|
||||
Profile* const profile_;
|
||||
AppListControllerDelegate* list_controller_;
|
||||
|
||||
std::unique_ptr<GameIndexManager> game_index_manager_;
|
||||
absl::optional<GameIndex> game_index_;
|
||||
|
||||
base::ScopedObservation<GameIndexManager, GameIndexManager::Observer>
|
||||
index_observer_{this};
|
||||
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
base::WeakPtrFactory<GameProvider> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace app_list
|
||||
|
||||
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_PROVIDER_H_
|
@ -0,0 +1,90 @@
|
||||
// Copyright 2022 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/ui/app_list/search/games/game_provider.h"
|
||||
|
||||
#include "ash/public/cpp/app_list/app_list_features.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/stub_api.h"
|
||||
#include "chrome/browser/ui/app_list/search/test/test_search_controller.h"
|
||||
#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
#include "content/public/test/browser_task_environment.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace app_list {
|
||||
namespace {
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::UnorderedElementsAre;
|
||||
|
||||
MATCHER_P(Title, title, "") {
|
||||
return base::UTF16ToUTF8(arg->title()) == title;
|
||||
}
|
||||
|
||||
GameData MakeGameData(const std::u16string& title) {
|
||||
GameData data;
|
||||
data.title = title;
|
||||
data.source = GameSource::kExampleSource;
|
||||
data.launch_url = GURL("https://example.com");
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class GameProviderTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
profile_ = std::make_unique<TestingProfile>();
|
||||
provider_ =
|
||||
std::make_unique<GameProvider>(profile_.get(), &list_controller_);
|
||||
|
||||
search_controller_ = std::make_unique<TestSearchController>();
|
||||
provider_->set_controller(search_controller_.get());
|
||||
}
|
||||
|
||||
const SearchProvider::Results& LastResults() {
|
||||
if (app_list_features::IsCategoricalSearchEnabled()) {
|
||||
return search_controller_->last_results();
|
||||
} else {
|
||||
return provider_->results();
|
||||
}
|
||||
}
|
||||
|
||||
void SetUpTestingIndex() {
|
||||
GameIndex index;
|
||||
index.push_back(MakeGameData(u"First Title"));
|
||||
index.push_back(MakeGameData(u"Second Title"));
|
||||
index.push_back(MakeGameData(u"Third Title"));
|
||||
provider_->SetGameIndexForTest(std::move(index));
|
||||
}
|
||||
|
||||
void Wait() { task_environment_.RunUntilIdle(); }
|
||||
|
||||
content::BrowserTaskEnvironment task_environment_;
|
||||
::test::TestAppListControllerDelegate list_controller_;
|
||||
std::unique_ptr<TestSearchController> search_controller_;
|
||||
std::unique_ptr<Profile> profile_;
|
||||
|
||||
std::unique_ptr<GameProvider> provider_;
|
||||
};
|
||||
|
||||
TEST_F(GameProviderTest, SearchResultsMatchQuery) {
|
||||
SetUpTestingIndex();
|
||||
|
||||
provider_->Start(u"first");
|
||||
Wait();
|
||||
EXPECT_THAT(LastResults(), ElementsAre(Title("First Title")));
|
||||
|
||||
provider_->Start(u"title");
|
||||
Wait();
|
||||
EXPECT_THAT(LastResults(),
|
||||
UnorderedElementsAre(Title("First Title"), Title("Second Title"),
|
||||
Title("Third Title")));
|
||||
}
|
||||
|
||||
} // namespace app_list
|
121
chrome/browser/ui/app_list/search/games/game_result.cc
Normal file
121
chrome/browser/ui/app_list/search/games/game_result.cc
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2022 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/ui/app_list/search/games/game_result.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ash/public/cpp/app_list/app_list_config.h"
|
||||
#include "ash/public/cpp/app_list/app_list_types.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
|
||||
#include "chrome/browser/ui/app_list/search/common/icon_constants.h"
|
||||
#include "chrome/browser/ui/app_list/search/common/search_result_util.h"
|
||||
#include "chrome/browser/ui/app_list/search/search_tags_util.h"
|
||||
#include "chrome/browser/ui/ash/thumbnail_loader.h"
|
||||
#include "chromeos/components/string_matching/tokenized_string.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/page_transition_types.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "ui/gfx/image/image_skia.h"
|
||||
|
||||
namespace app_list {
|
||||
namespace {
|
||||
|
||||
constexpr char16_t kPlatformDelimiter[] = u", ";
|
||||
constexpr char16_t kDetailsDelimiter[] = u" - ";
|
||||
constexpr char16_t kA11yDelimiter[] = u", ";
|
||||
|
||||
} // namespace
|
||||
|
||||
GameResult::GameResult(Profile* profile,
|
||||
AppListControllerDelegate* list_controller,
|
||||
GameIndexManager* index_manager,
|
||||
const GameData& game_data,
|
||||
double relevance,
|
||||
const std::u16string& query)
|
||||
: profile_(profile),
|
||||
list_controller_(list_controller),
|
||||
launch_url_(game_data.launch_url) {
|
||||
DCHECK(profile);
|
||||
DCHECK(list_controller);
|
||||
DCHECK(index_manager);
|
||||
DCHECK(launch_url_.is_valid());
|
||||
|
||||
set_id(launch_url_.spec());
|
||||
set_relevance(relevance);
|
||||
|
||||
SetMetricsType(ash::GAME_SEARCH);
|
||||
SetResultType(ResultType::kGames);
|
||||
SetDisplayType(DisplayType::kList);
|
||||
SetCategory(Category::kGames);
|
||||
|
||||
UpdateText(game_data, query);
|
||||
|
||||
// TODO(crbug.com/1305880): Set a default icon.
|
||||
|
||||
index_manager->GetIcon(game_data.icon_url,
|
||||
base::BindOnce(&GameResult::OnIconLoaded,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
GameResult::~GameResult() = default;
|
||||
|
||||
void GameResult::Open(int event_flags) {
|
||||
list_controller_->OpenURL(profile_, launch_url_, ui::PAGE_TRANSITION_TYPED,
|
||||
ui::DispositionFromEventFlags(event_flags));
|
||||
}
|
||||
|
||||
void GameResult::UpdateText(const GameData& game_data,
|
||||
const std::u16string& query) {
|
||||
SetTitle(game_data.title);
|
||||
SetTitleTags(CalculateTags(query, title()));
|
||||
|
||||
std::vector<ash::SearchResultTextItem> details;
|
||||
std::vector<std::u16string> accessible_name;
|
||||
|
||||
accessible_name.push_back(title());
|
||||
accessible_name.push_back(kA11yDelimiter);
|
||||
|
||||
std::u16string source = GameSourceDisplayString(game_data.source);
|
||||
details.push_back(CreateStringTextItem(source).SetElidable(false));
|
||||
accessible_name.push_back(source);
|
||||
|
||||
if (game_data.platforms) {
|
||||
std::u16string platforms =
|
||||
base::JoinString(game_data.platforms.value(), kPlatformDelimiter);
|
||||
|
||||
details.push_back(CreateStringTextItem(kDetailsDelimiter));
|
||||
details.push_back(
|
||||
CreateStringTextItem(IDS_APP_LIST_SEARCH_GAME_PLATFORMS_PREFIX));
|
||||
details.push_back(CreateStringTextItem(u" "));
|
||||
details.push_back(CreateStringTextItem(platforms));
|
||||
|
||||
accessible_name.push_back(kA11yDelimiter);
|
||||
accessible_name.push_back(
|
||||
l10n_util::GetStringUTF16(IDS_APP_LIST_SEARCH_GAME_PLATFORMS_PREFIX));
|
||||
accessible_name.push_back(u" ");
|
||||
accessible_name.push_back(platforms);
|
||||
}
|
||||
|
||||
SetDetailsTextVector(details);
|
||||
SetAccessibleName(base::StrCat(accessible_name));
|
||||
}
|
||||
|
||||
void GameResult::OnIconLoaded(const SkBitmap* bitmap) {
|
||||
if (!bitmap || bitmap->isNull())
|
||||
return;
|
||||
|
||||
IconInfo icon_info(gfx::ImageSkia::CreateFrom1xBitmap(*bitmap),
|
||||
GetAppIconDimension(), IconShape::kRoundedRectangle);
|
||||
SetIcon(icon_info);
|
||||
}
|
||||
|
||||
} // namespace app_list
|
51
chrome/browser/ui/app_list/search/games/game_result.h
Normal file
51
chrome/browser/ui/app_list/search/games/game_result.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_RESULT_H_
|
||||
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_RESULT_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/stub_api.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
class AppListControllerDelegate;
|
||||
class Profile;
|
||||
class SkBitmap;
|
||||
|
||||
namespace app_list {
|
||||
|
||||
// Search result for cloud gaming search.
|
||||
class GameResult : public ChromeSearchResult {
|
||||
public:
|
||||
GameResult(Profile* profile,
|
||||
AppListControllerDelegate* list_controller,
|
||||
GameIndexManager* index_manager,
|
||||
const GameData& game_data,
|
||||
double relevance,
|
||||
const std::u16string& query);
|
||||
~GameResult() override;
|
||||
|
||||
GameResult(const GameResult&) = delete;
|
||||
GameResult& operator=(const GameResult&) = delete;
|
||||
|
||||
// ChromeSearchResult overrides:
|
||||
void Open(int event_flags) override;
|
||||
|
||||
private:
|
||||
void OnIconLoaded(const SkBitmap* bitmap);
|
||||
|
||||
void UpdateText(const GameData& game_data, const std::u16string& query);
|
||||
|
||||
Profile* profile_;
|
||||
AppListControllerDelegate* list_controller_;
|
||||
|
||||
const GURL launch_url_;
|
||||
|
||||
base::WeakPtrFactory<GameResult> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace app_list
|
||||
|
||||
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_GAME_RESULT_H_
|
@ -0,0 +1,60 @@
|
||||
// Copyright 2022 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/ui/app_list/search/games/game_result.h"
|
||||
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "base/strings/strcat.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/app_list/search/common/search_result_util.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/stub_api.h"
|
||||
#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
|
||||
#include "chrome/test/base/testing_profile.h"
|
||||
#include "content/public/test/browser_task_environment.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace app_list {
|
||||
|
||||
class GameResultTest : public testing::Test {
|
||||
public:
|
||||
GameResultTest() { profile_ = std::make_unique<TestingProfile>(); }
|
||||
|
||||
~GameResultTest() override = default;
|
||||
|
||||
content::BrowserTaskEnvironment task_environment_;
|
||||
test::TestAppListControllerDelegate list_controller_;
|
||||
std::unique_ptr<Profile> profile_;
|
||||
};
|
||||
|
||||
TEST_F(GameResultTest, Basic) {
|
||||
GameIndexManager index_manager;
|
||||
|
||||
GameData game_data;
|
||||
game_data.title = u"Title";
|
||||
game_data.source = GameSource::kExampleSource;
|
||||
game_data.launch_url = GURL("https://test-url.com/");
|
||||
game_data.platforms = {u"A", u"B", u"C"};
|
||||
|
||||
GameResult result(profile_.get(), &list_controller_, &index_manager,
|
||||
game_data, 0.6, u"SomeGame");
|
||||
|
||||
EXPECT_EQ(result.title(), u"Title");
|
||||
|
||||
EXPECT_EQ(StringFromTextVector(result.details_text_vector()),
|
||||
base::StrCat({u"Example source - ",
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_APP_LIST_SEARCH_GAME_PLATFORMS_PREFIX),
|
||||
u" A, B, C"}));
|
||||
|
||||
EXPECT_EQ(result.accessible_name(),
|
||||
base::StrCat({u"Title, Example source, ",
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_APP_LIST_SEARCH_GAME_PLATFORMS_PREFIX),
|
||||
u" A, B, C"}));
|
||||
}
|
||||
|
||||
} // namespace app_list
|
40
chrome/browser/ui/app_list/search/games/stub_api.cc
Normal file
40
chrome/browser/ui/app_list/search/games/stub_api.cc
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2022 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/ui/app_list/search/games/stub_api.h"
|
||||
|
||||
// TODO(crbug.com/1305880)
|
||||
// This whole file is a temporary fake API for the game index manager, that can
|
||||
// be deleted and replaced once the real API is implemented.
|
||||
|
||||
namespace app_list {
|
||||
|
||||
GameData::GameData() = default;
|
||||
|
||||
GameData::~GameData() = default;
|
||||
|
||||
GameData::GameData(const GameData&) = default;
|
||||
|
||||
GameData& GameData::operator=(const GameData&) = default;
|
||||
|
||||
absl::optional<GameIndex> GameIndexManager::GetIndex() {
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
void GameIndexManager::AddObserver(GameIndexManager::Observer* observer) {}
|
||||
|
||||
void GameIndexManager::RemoveObserver(GameIndexManager::Observer* observer) {}
|
||||
|
||||
void GameIndexManager::GetIcon(
|
||||
const GURL& icon_url,
|
||||
base::OnceCallback<void(const SkBitmap*)> callback) {}
|
||||
|
||||
std::u16string GameSourceDisplayString(GameSource source) {
|
||||
switch (source) {
|
||||
case GameSource::kExampleSource:
|
||||
return u"Example source";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace app_list
|
72
chrome/browser/ui/app_list/search/games/stub_api.h
Normal file
72
chrome/browser/ui/app_list/search/games/stub_api.h
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
#ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_STUB_API_H_
|
||||
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_STUB_API_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/observer_list_types.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "third_party/skia/include/core/SkBitmap.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
// TODO(crbug.com/1305880)
|
||||
// This whole file is a temporary fake API for the game index manager, that can
|
||||
// be deleted and replaced once the real API is implemented.
|
||||
|
||||
namespace app_list {
|
||||
|
||||
// Each possible source a game can be provided by.
|
||||
enum class GameSource {
|
||||
kExampleSource,
|
||||
};
|
||||
|
||||
// Metadata for a single game in the index of games.
|
||||
struct GameData {
|
||||
// Game title
|
||||
std::u16string title;
|
||||
|
||||
GameSource source = GameSource::kExampleSource;
|
||||
|
||||
// What platforms the game is available on.
|
||||
absl::optional<std::vector<std::u16string>> platforms;
|
||||
|
||||
// The URL that should be opened when the search result is clicked.
|
||||
GURL launch_url;
|
||||
|
||||
// A token uniquely identifying an icon.
|
||||
GURL icon_url;
|
||||
|
||||
GameData();
|
||||
~GameData();
|
||||
|
||||
GameData(const GameData&);
|
||||
GameData& operator=(const GameData&);
|
||||
};
|
||||
|
||||
// A collection of game data, forming an index.
|
||||
using GameIndex = std::vector<GameData>;
|
||||
|
||||
class GameIndexManager {
|
||||
public:
|
||||
class Observer : public base::CheckedObserver {
|
||||
public:
|
||||
virtual void OnIndexUpdated(const absl::optional<GameIndex>& index) = 0;
|
||||
};
|
||||
|
||||
absl::optional<GameIndex> GetIndex();
|
||||
void GetIcon(const GURL& icon_url,
|
||||
base::OnceCallback<void(const SkBitmap*)> callback);
|
||||
|
||||
void AddObserver(Observer* observer);
|
||||
void RemoveObserver(Observer* observer);
|
||||
};
|
||||
|
||||
std::u16string GameSourceDisplayString(GameSource source);
|
||||
|
||||
} // namespace app_list
|
||||
|
||||
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_GAMES_STUB_API_H_
|
@ -18,6 +18,35 @@
|
||||
namespace app_list {
|
||||
namespace {
|
||||
|
||||
// Given `higher_priority` and `lower_priority` result types, deduplicate
|
||||
// results between the two result types in `results` based on their id,
|
||||
// preserving the ones in `higher_priority`.
|
||||
//
|
||||
// Note this only deduplicates results whose ids are present in both result
|
||||
// types; if two results of one result type have the same id, they will not be
|
||||
// deduplicated.
|
||||
void DeduplicateResults(ResultsMap& results,
|
||||
ResultType higher_priority,
|
||||
ResultType lower_priority) {
|
||||
const auto first_it = results.find(higher_priority);
|
||||
const auto second_it = results.find(lower_priority);
|
||||
if (first_it == results.end() || second_it == results.end())
|
||||
return;
|
||||
const auto& first_results = first_it->second;
|
||||
const auto& second_results = second_it->second;
|
||||
|
||||
base::flat_set<std::string> first_ids;
|
||||
for (const auto& result : first_results) {
|
||||
if (result->result_type() == higher_priority)
|
||||
first_ids.insert(result->id());
|
||||
}
|
||||
|
||||
for (auto& result : second_results) {
|
||||
if (first_ids.contains(result->id()))
|
||||
result->scoring().filter = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DeduplicateDriveFilesAndTabs(ResultsMap& results) {
|
||||
const auto omnibox_it = results.find(ProviderType::kOmnibox);
|
||||
const auto drive_it = results.find(ProviderType::kDriveSearch);
|
||||
@ -91,6 +120,9 @@ void FilteringRanker::UpdateResultRanks(ResultsMap& results,
|
||||
return;
|
||||
FilterOmniboxResults(results);
|
||||
DeduplicateDriveFilesAndTabs(results);
|
||||
// TODO(crbug.com/1305880): Verify that game URLs match the omnibox stripped
|
||||
// URL once game URLs are finalized.
|
||||
DeduplicateResults(results, ResultType::kGames, ResultType::kOmnibox);
|
||||
}
|
||||
|
||||
} // namespace app_list
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "chrome/browser/ui/app_list/search/files/file_search_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/files/zero_state_file_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/games/game_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/help_app_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/keyboard_shortcut_provider.h"
|
||||
#include "chrome/browser/ui/app_list/search/mixer.h"
|
||||
@ -171,6 +172,15 @@ std::unique_ptr<SearchController> CreateSearchController(
|
||||
controller->AddProvider(help_app_group_id,
|
||||
std::make_unique<HelpAppProvider>(profile));
|
||||
|
||||
// TODO(crbug.com/1305880): Move this to its own flag.
|
||||
if (ash::features::IsProductivityLauncherEnabled() &&
|
||||
base::GetFieldTrialParamByFeatureAsBool(
|
||||
ash::features::kProductivityLauncher, "enable_games", false)) {
|
||||
size_t games_group_id = controller->AddGroup(kGenericMaxResults);
|
||||
controller->AddProvider(games_group_id, std::make_unique<GameProvider>(
|
||||
profile, list_controller));
|
||||
}
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
|
@ -2669,9 +2669,8 @@ if (!is_android) {
|
||||
"//third_party/liblouis/wasm/liblouis_wrapper_browsertest.cc",
|
||||
]
|
||||
deps += [ "//chrome/browser/chromeos" ]
|
||||
data_deps += [
|
||||
"//third_party/liblouis:liblouis_test_data",
|
||||
]
|
||||
data_deps += [ "//third_party/liblouis:liblouis_test_data" ]
|
||||
|
||||
# TODO(https://crbug.com/1299021): Implement building these NaCl
|
||||
# targets as ARM32 when Chrome is built for ARM64 (for Linux/Chrome
|
||||
# OS).
|
||||
@ -2692,6 +2691,7 @@ if (!is_android) {
|
||||
# browser process as needed by this test. See http://crbug.com/157312.
|
||||
sources -= [ "../browser/nacl_host/test/gdb_debug_stub_browsertest.cc" ]
|
||||
}
|
||||
|
||||
# TODO(https://crbug.com/1299021): Implement building this NaCl target
|
||||
# as ARM32 when Chrome is built for ARM64 (for Linux/Chrome OS).
|
||||
if ((is_linux || is_chromeos) && target_cpu != "arm64") {
|
||||
@ -7021,6 +7021,8 @@ test("unit_tests") {
|
||||
"../browser/ui/app_list/search/files/justifications_unittest.cc",
|
||||
"../browser/ui/app_list/search/files/zero_state_drive_provider_unittest.cc",
|
||||
"../browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc",
|
||||
"../browser/ui/app_list/search/games/game_provider_unittest.cc",
|
||||
"../browser/ui/app_list/search/games/game_result_unittest.cc",
|
||||
"../browser/ui/app_list/search/help_app_provider_unittest.cc",
|
||||
"../browser/ui/app_list/search/keyboard_shortcut_provider_unittest.cc",
|
||||
"../browser/ui/app_list/search/keyboard_shortcut_result_unittest.cc",
|
||||
|
Reference in New Issue
Block a user