Commit 9943f625 authored by wutao's avatar wutao Committed by Commit Bot

cros: Make Keyboard Shortcut Viewer searchable in launcher

This cl adds the mechanism to make Keyboard Shortcut Viewer (KSV)
searchable in launcher.
1. Adds an InternalDataSource.
2. Adds an InternalAppResult.
3. Adds unit tests for searching internal app.

Bug: 790748
Test: manual and AppSearchProviderTest.FetchInternalApp
Change-Id: I8cae318b51d75516575c9b02cb7565b1dc748951
Reviewed-on: https://chromium-review.googlesource.com/982995
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#547054}
parent 44c2c323
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<grit-part> <grit-part>
<!-- Title --> <!-- Title -->
<message name="IDS_KSV_TITLE" desc="The tooltip text of the keyboard shortcut viewer shelf icon."> <message name="IDS_KSV_TITLE" desc="The tooltip text of the keyboard shortcut viewer shelf icon.">
Keyboard Shortcut Viewer Keyboard Shortcut Helper
</message> </message>
<!-- Accessibility --> <!-- Accessibility -->
......
...@@ -95,6 +95,8 @@ struct EnumTraits<ash::mojom::SearchResultType, ash::SearchResultType> { ...@@ -95,6 +95,8 @@ struct EnumTraits<ash::mojom::SearchResultType, ash::SearchResultType> {
return ash::mojom::SearchResultType::kPlayStoreApp; return ash::mojom::SearchResultType::kPlayStoreApp;
case ash::SearchResultType::kInstantApp: case ash::SearchResultType::kInstantApp:
return ash::mojom::SearchResultType::kInstantApp; return ash::mojom::SearchResultType::kInstantApp;
case ash::SearchResultType::kInternalApp:
return ash::mojom::SearchResultType::kInternalApp;
case ash::SearchResultType::kUnknown: case ash::SearchResultType::kUnknown:
break; break;
} }
...@@ -114,6 +116,9 @@ struct EnumTraits<ash::mojom::SearchResultType, ash::SearchResultType> { ...@@ -114,6 +116,9 @@ struct EnumTraits<ash::mojom::SearchResultType, ash::SearchResultType> {
case ash::mojom::SearchResultType::kInstantApp: case ash::mojom::SearchResultType::kInstantApp:
*out = ash::SearchResultType::kInstantApp; *out = ash::SearchResultType::kInstantApp;
return true; return true;
case ash::mojom::SearchResultType::kInternalApp:
*out = ash::SearchResultType::kInternalApp;
return true;
} }
NOTREACHED(); NOTREACHED();
return false; return false;
......
...@@ -38,6 +38,7 @@ enum class SearchResultType { ...@@ -38,6 +38,7 @@ enum class SearchResultType {
kInstalledApp, // Installed apps. kInstalledApp, // Installed apps.
kPlayStoreApp, // Uninstalled apps from playstore. kPlayStoreApp, // Uninstalled apps from playstore.
kInstantApp, // Instant apps. kInstantApp, // Instant apps.
kInternalApp, // Chrome OS apps.
// Add new values here. // Add new values here.
}; };
......
...@@ -56,6 +56,7 @@ enum SearchResultType { ...@@ -56,6 +56,7 @@ enum SearchResultType {
kInstalledApp, // Installed apps. kInstalledApp, // Installed apps.
kPlayStoreApp, // Uninstalled apps from playstore. kPlayStoreApp, // Uninstalled apps from playstore.
kInstantApp, // Instant apps. kInstantApp, // Instant apps.
kInternalApp, // Chrome OS apps.
// Add new values here. // Add new values here.
}; };
......
...@@ -4841,6 +4841,11 @@ ...@@ -4841,6 +4841,11 @@
Lock screen notes are automatically saved to <ph name="LOCK_SCREEN_APP_NAME">$1<ex>Lock Screen Enabled App</ex></ph>. Your most recent note will remain on the lock screen. Lock screen notes are automatically saved to <ph name="LOCK_SCREEN_APP_NAME">$1<ex>Lock Screen Enabled App</ex></ph>. Your most recent note will remain on the lock screen.
</message> </message>
<!-- Launcher Searchable Chrome OS apps -->
<message name="IDS_LAUNCHER_SEARCHABLE_APP_KEYBOARD_SHORTCUT_VIEWER" desc="The string can be used to search keyboard shortcut viewer app.">
Keyboard Shortcut Helper
</message>
<message name="IDS_CROSTINI_INSTALLER_TITLE" desc="Title of the Crostini installer, a dialog for installing the 'Terminal'."> <message name="IDS_CROSTINI_INSTALLER_TITLE" desc="Title of the Crostini installer, a dialog for installing the 'Terminal'.">
<ph name="APP_NAME">$1<ex>Terminal</ex></ph> for <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> <ph name="APP_NAME">$1<ex>Terminal</ex></ph> for <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph>
</message> </message>
......
...@@ -111,6 +111,9 @@ ...@@ -111,6 +111,9 @@
<include name="IDR_APPS_FOLDER_OVERLAY_128" file="mac/apps_folder_overlay_128.png" type="BINDATA" /> <include name="IDR_APPS_FOLDER_OVERLAY_128" file="mac/apps_folder_overlay_128.png" type="BINDATA" />
<include name="IDR_APPS_FOLDER_OVERLAY_512" file="mac/apps_folder_overlay_512.png" type="BINDATA" /> <include name="IDR_APPS_FOLDER_OVERLAY_512" file="mac/apps_folder_overlay_512.png" type="BINDATA" />
</if> </if>
<if expr="chromeos">
<include name="IDR_KEYBOARD_SHORTCUT_VIEWER_LOGO_192" file="cros/keyboard_shortcut_viewer_logo_192.png" type="BINDATA" />
</if>
</includes> </includes>
</release> </release>
</grit> </grit>
...@@ -3614,6 +3614,10 @@ split_static_library("ui") { ...@@ -3614,6 +3614,10 @@ split_static_library("ui") {
"app_list/crostini/crostini_util.h", "app_list/crostini/crostini_util.h",
"app_list/search/arc_app_result.cc", "app_list/search/arc_app_result.cc",
"app_list/search/arc_app_result.h", "app_list/search/arc_app_result.h",
"app_list/search/internal_app_metadata.cc",
"app_list/search/internal_app_metadata.h",
"app_list/search/internal_app_result.cc",
"app_list/search/internal_app_result.h",
"ash/launcher/arc_app_deferred_launcher_controller.cc", "ash/launcher/arc_app_deferred_launcher_controller.cc",
"ash/launcher/arc_app_deferred_launcher_controller.h", "ash/launcher/arc_app_deferred_launcher_controller.h",
"ash/launcher/arc_app_deferred_launcher_item_controller.cc", "ash/launcher/arc_app_deferred_launcher_item_controller.cc",
......
...@@ -34,11 +34,14 @@ ...@@ -34,11 +34,14 @@
#include "chrome/browser/ui/app_list/chrome_app_list_item.h" #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
#include "chrome/browser/ui/app_list/search/arc_app_result.h" #include "chrome/browser/ui/app_list/search/arc_app_result.h"
#include "chrome/browser/ui/app_list/search/extension_app_result.h" #include "chrome/browser/ui/app_list/search/extension_app_result.h"
#include "chrome/browser/ui/app_list/search/internal_app_metadata.h"
#include "chrome/browser/ui/app_list/search/internal_app_result.h"
#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h" #include "extensions/common/extension.h"
#include "extensions/common/extension_set.h" #include "extensions/common/extension_set.h"
#include "ui/base/l10n/l10n_util.h"
using extensions::ExtensionRegistry; using extensions::ExtensionRegistry;
...@@ -297,6 +300,36 @@ class ArcDataSource : public AppSearchProvider::DataSource, ...@@ -297,6 +300,36 @@ class ArcDataSource : public AppSearchProvider::DataSource,
DISALLOW_COPY_AND_ASSIGN(ArcDataSource); DISALLOW_COPY_AND_ASSIGN(ArcDataSource);
}; };
class InternalDataSource : public AppSearchProvider::DataSource {
public:
InternalDataSource(Profile* profile, AppSearchProvider* owner)
: AppSearchProvider::DataSource(profile, owner) {}
~InternalDataSource() override = default;
// AppSearchProvider::DataSource overrides:
void AddApps(AppSearchProvider::Apps* apps) override {
const base::Time time;
for (const auto& internal_app : GetInternalAppList()) {
apps->emplace_back(std::make_unique<AppSearchProvider::App>(
this, internal_app.app_id,
l10n_util::GetStringUTF8(internal_app.name_string_resource_id), time,
time));
}
}
std::unique_ptr<AppResult> CreateResult(
const std::string& app_id,
AppListControllerDelegate* list_controller,
bool is_recommended) override {
return std::make_unique<InternalAppResult>(profile(), app_id,
list_controller, is_recommended);
}
private:
DISALLOW_COPY_AND_ASSIGN(InternalDataSource);
};
} // namespace } // namespace
AppSearchProvider::AppSearchProvider(Profile* profile, AppSearchProvider::AppSearchProvider(Profile* profile,
...@@ -311,6 +344,8 @@ AppSearchProvider::AppSearchProvider(Profile* profile, ...@@ -311,6 +344,8 @@ AppSearchProvider::AppSearchProvider(Profile* profile,
std::make_unique<ExtensionDataSource>(profile, this)); std::make_unique<ExtensionDataSource>(profile, this));
if (arc::IsArcAllowedForProfile(profile)) if (arc::IsArcAllowedForProfile(profile))
data_sources_.emplace_back(std::make_unique<ArcDataSource>(profile, this)); data_sources_.emplace_back(std::make_unique<ArcDataSource>(profile, this));
data_sources_.emplace_back(
std::make_unique<InternalDataSource>(profile, this));
} }
AppSearchProvider::~AppSearchProvider() {} AppSearchProvider::~AppSearchProvider() {}
......
// Copyright 2018 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/internal_app_metadata.h"
#include "base/no_destructor.h"
#include "chrome/grit/chrome_unscaled_resources.h"
#include "chrome/grit/generated_resources.h"
namespace app_list {
const std::vector<InternalApp>& GetInternalAppList() {
static const base::NoDestructor<std::vector<InternalApp>> internal_app_list(
{{kInternalAppIdKeyboardShortcutViewer,
IDS_LAUNCHER_SEARCHABLE_APP_KEYBOARD_SHORTCUT_VIEWER,
IDR_KEYBOARD_SHORTCUT_VIEWER_LOGO_192}});
return *internal_app_list;
}
int GetIconResourceIdByAppId(const std::string& app_id) {
for (const auto& app : GetInternalAppList()) {
if (app_id == app.app_id)
return app.icon_resource_id;
}
return 0;
}
} // namespace app_list
// Copyright 2018 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_INTERNAL_APP_METADATA_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_INTERNAL_APP_METADATA_H_
#include <string>
#include <vector>
namespace app_list {
constexpr char kInternalAppIdKeyboardShortcutViewer[] =
"internal://keyboard_shortcut_viewer";
// Metadata about an internal app.
// Internal apps are these apps can run in Chrome OS directly, e.g. Keyboard
// Shortcut Viewer.
struct InternalApp {
const char* app_id;
int name_string_resource_id = 0;
int icon_resource_id = 0;
};
// Returns a list of Chrome OS internal apps, which are searchable in launcher.
const std::vector<InternalApp>& GetInternalAppList();
// Returns the app's icon resource id.
// Returns 0 if |app_id| is invalid.
int GetIconResourceIdByAppId(const std::string& app_id);
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_INTERNAL_APP_METADATA_H_
// Copyright 2018 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/internal_app_result.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/internal_app_metadata.h"
#include "chrome/browser/ui/app_list/search/search_util.h"
#include "chrome/browser/ui/ash/ksv/keyboard_shortcut_viewer_util.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia_operations.h"
namespace app_list {
namespace {
gfx::ImageSkia GetIconForInternalAppId(const std::string& app_id) {
const int resource_id = GetIconResourceIdByAppId(app_id);
if (resource_id == 0)
return gfx::ImageSkia();
gfx::ImageSkia* source =
ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
return gfx::ImageSkiaOperations::CreateResizedImage(
*source, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kTileIconSize, kTileIconSize));
}
} // namespace
InternalAppResult::InternalAppResult(Profile* profile,
const std::string& app_id,
AppListControllerDelegate* controller,
bool is_recommendation)
: AppResult(profile, app_id, controller, is_recommendation) {
set_id(app_id);
set_result_type(ResultType::kInternalApp);
gfx::ImageSkia icon = GetIconForInternalAppId(app_id);
if (!icon.isNull())
SetIcon(icon);
}
InternalAppResult::~InternalAppResult() {}
void InternalAppResult::ExecuteLaunchCommand(int event_flags) {
Open(event_flags);
}
void InternalAppResult::Open(int event_flags) {
// Record the search metric if the result is not a suggested app.
if (display_type() != DisplayType::kRecommendation)
RecordHistogram(APP_SEARCH_RESULT);
if (id() == kInternalAppIdKeyboardShortcutViewer)
keyboard_shortcut_viewer_util::ShowKeyboardShortcutViewer();
}
std::unique_ptr<SearchResult> InternalAppResult::Duplicate() const {
auto copy = std::make_unique<InternalAppResult>(
profile(), app_id(), controller(),
display_type() == DisplayType::kRecommendation);
copy->set_title(title());
copy->set_title_tags(title_tags());
copy->set_relevance(relevance());
return copy;
}
ui::MenuModel* InternalAppResult::GetContextMenuModel() {
return nullptr;
}
} // namespace app_list
// Copyright 2018 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_INTERNAL_APP_RESULT_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_INTERNAL_APP_RESULT_H_
#include <memory>
#include "base/macros.h"
#include "chrome/browser/ui/app_list/search/app_result.h"
class AppListControllerDelegate;
class Profile;
namespace app_list {
class InternalAppResult : public AppResult {
public:
InternalAppResult(Profile* profile,
const std::string& app_id,
AppListControllerDelegate* controller,
bool is_recommendation);
~InternalAppResult() override;
// SearchResult overrides:
void Open(int event_flags) override;
std::unique_ptr<SearchResult> Duplicate() const override;
ui::MenuModel* GetContextMenuModel() override;
// AppContextMenuDelegate overrides:
void ExecuteLaunchCommand(int event_flags) override;
private:
DISALLOW_COPY_AND_ASSIGN(InternalAppResult);
};
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_INTERNAL_APP_RESULT_H_
...@@ -45,6 +45,8 @@ constexpr char kGmailExtensionName[] = "Gmail Ext"; ...@@ -45,6 +45,8 @@ constexpr char kGmailExtensionName[] = "Gmail Ext";
constexpr char kGmailArcPackage[] = "com.google.android.gm"; constexpr char kGmailArcPackage[] = "com.google.android.gm";
constexpr char kGmailArcActivity[] = constexpr char kGmailArcActivity[] =
"com.google.android.gm.ConversationListActivityGmail"; "com.google.android.gm.ConversationListActivityGmail";
constexpr char kKeyboardShortcutHelperInternalName[] =
"Keyboard Shortcut Helper";
} // namespace } // namespace
...@@ -259,19 +261,22 @@ TEST_F(AppSearchProviderTest, FetchRecommendations) { ...@@ -259,19 +261,22 @@ TEST_F(AppSearchProviderTest, FetchRecommendations) {
prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(20)); prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(20));
prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10)); prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5)); prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5));
EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery("")); EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2,Keyboard Shortcut Helper",
RunQuery(""));
prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(5)); prefs->SetLastLaunchTime(kHostedAppId, base::Time::FromInternalValue(5));
prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10)); prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(20)); prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(20));
EXPECT_EQ("Packaged App 2,Packaged App 1,Hosted App", RunQuery("")); EXPECT_EQ("Packaged App 2,Packaged App 1,Hosted App,Keyboard Shortcut Helper",
RunQuery(""));
// Times in the future should just be handled as highest priority. // Times in the future should just be handled as highest priority.
prefs->SetLastLaunchTime(kHostedAppId, prefs->SetLastLaunchTime(kHostedAppId,
kTestCurrentTime + base::TimeDelta::FromSeconds(5)); kTestCurrentTime + base::TimeDelta::FromSeconds(5));
prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10)); prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(10));
prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5)); prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(5));
EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery("")); EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2,Keyboard Shortcut Helper",
RunQuery(""));
} }
TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) { TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) {
...@@ -285,7 +290,8 @@ TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) { ...@@ -285,7 +290,8 @@ TEST_F(AppSearchProviderTest, FetchUnlaunchedRecommendations) {
prefs->SetLastLaunchTime(kHostedAppId, base::Time::Now()); prefs->SetLastLaunchTime(kHostedAppId, base::Time::Now());
prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(0)); prefs->SetLastLaunchTime(kPackagedApp1Id, base::Time::FromInternalValue(0));
prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(0)); prefs->SetLastLaunchTime(kPackagedApp2Id, base::Time::FromInternalValue(0));
EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2", RunQuery("")); EXPECT_EQ("Hosted App,Packaged App 1,Packaged App 2,Keyboard Shortcut Helper",
RunQuery(""));
} }
TEST_F(AppSearchProviderTest, FilterDuplicate) { TEST_F(AppSearchProviderTest, FilterDuplicate) {
...@@ -322,5 +328,12 @@ TEST_F(AppSearchProviderTest, FilterDuplicate) { ...@@ -322,5 +328,12 @@ TEST_F(AppSearchProviderTest, FilterDuplicate) {
EXPECT_EQ(kGmailExtensionName, RunQuery(kGmailQeuery)); EXPECT_EQ(kGmailExtensionName, RunQuery(kGmailQeuery));
} }
TEST_F(AppSearchProviderTest, FetchInternalApp) {
CreateSearch();
EXPECT_EQ(kKeyboardShortcutHelperInternalName, RunQuery("Keyboard"));
EXPECT_EQ(kKeyboardShortcutHelperInternalName, RunQuery("Shortcut"));
EXPECT_EQ(kKeyboardShortcutHelperInternalName, RunQuery("Helper"));
}
} // namespace test } // namespace test
} // namespace app_list } // namespace app_list
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment