Commit cc3488b6 authored by Jeffrey Young's avatar Jeffrey Young Committed by Commit Bot

assistant: add assistant entry in launcher search

Append a link to the assistant in launcher results when no
tiles are shown. Gated by feature "EnabledEmbeddedAssistantUI".
https://screenshot.googleplex.com/9H5Ds7TjUPmgoxi.png

BUG=1103703

Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: I28dc5c06a85863d0f78eb1da26b214c774008041
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2388001Reviewed-by: default avatarTony Yeoman <tby@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarTao Wu <wutao@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Commit-Queue: Jeffrey Young <cowmoo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808926}
parent 0f626460
...@@ -1177,30 +1177,9 @@ void AppListControllerImpl::OpenSearchResult(const std::string& result_id, ...@@ -1177,30 +1177,9 @@ void AppListControllerImpl::OpenSearchResult(const std::string& result_id,
} }
} }
if (presenter_.IsVisibleDeprecated() && result->is_omnibox_search() && if (client_) {
IsAssistantAllowedAndEnabled() &&
app_list_features::IsAssistantSearchEnabled()) {
// Record the assistant result. Other types of results are recorded in
// |client_| where there is richer data on SearchResultType.
DCHECK_EQ(AppListLaunchedFrom::kLaunchedFromSearchBox, launched_from)
<< "Only log search results which are represented to the user as "
"search results (ie. search results in the search result page) not "
"chips.";
RecordSearchResultOpenTypeHistogram(launched_from, ASSISTANT_OMNIBOX_RESULT,
IsTabletMode());
if (!GetLastQueryLength()) {
RecordZeroStateSuggestionOpenTypeHistogram(ASSISTANT_OMNIBOX_RESULT);
}
AssistantUiController::Get()->ShowUi(
AssistantEntryPoint::kLauncherSearchResult);
AssistantController::Get()->OpenUrl(
assistant::util::CreateAssistantQueryDeepLink(
base::UTF16ToUTF8(result->title())));
} else {
if (client_)
client_->OpenSearchResult(result_id, event_flags, launched_from, client_->OpenSearchResult(result_id, event_flags, launched_from,
launch_type, suggestion_index, launch_type, suggestion_index, launch_as_default);
launch_as_default);
} }
ResetHomeLauncherIfShown(); ResetHomeLauncherIfShown();
......
...@@ -45,83 +45,6 @@ constexpr base::TimeDelta kImpressionThreshold = ...@@ -45,83 +45,6 @@ constexpr base::TimeDelta kImpressionThreshold =
constexpr base::TimeDelta kZeroStateImpressionThreshold = constexpr base::TimeDelta kZeroStateImpressionThreshold =
base::TimeDelta::FromSeconds(1); base::TimeDelta::FromSeconds(1);
constexpr SkColor kListVerticalBarIconColor =
SkColorSetARGB(0xFF, 0xE8, 0xEA, 0xED);
bool IsAssistantSearchEnabled(AppListViewDelegate* view_delegate) {
if (!app_list_features::IsAssistantSearchEnabled())
return false;
return view_delegate && view_delegate->IsAssistantAllowedAndEnabled();
}
// Get the vector icon to update previous Assistant item.
const gfx::VectorIcon* GetPreviousVectorIcon(
int continuous_assistant_item_count) {
if (continuous_assistant_item_count == 2) {
return &kVerticalBarSingleIcon;
} else if (continuous_assistant_item_count > 2) {
return &kVerticalBarEndIcon;
}
NOTREACHED();
return nullptr;
}
// Get the vector icon to update current Assistant item.
const gfx::VectorIcon* GetCurrentVectorIcon(
int continuous_assistant_item_count) {
if (continuous_assistant_item_count == 1) {
return &kAssistantIcon;
} else if (continuous_assistant_item_count == 2) {
return &kVerticalBarStartIcon;
} else if (continuous_assistant_item_count > 2) {
return &kVerticalBarMiddleIcon;
}
NOTREACHED();
return nullptr;
}
// Calculate the display icons for Assistant items.
// We have the following situations:
// Number of consecutive Assistant items:
// 1 item -> Assistant icon.
// 2 items -> Assistant icon + single vertical bar icon.
// 3 items -> Assistant icon + start + end vertical bar icons.
// n >= 4 items -> Assistant icon + start + middle (n - 3) + end vertical bar
// icons.
// This algo sets current result's vertical icon based on the
// |continuous_assistant_item_count|, but also needs to update previous result's
// vertical icon if current result is not an Assisttant item or previous result
// is the last result.
void CalculateDisplayIcons(
const std::vector<SearchResult*>& display_results,
std::vector<const gfx::VectorIcon*>* out_display_icons) {
const size_t display_size = display_results.size();
int continuous_assistant_item_count = 0;
// Index |i| goes beyond the last display result to update its icon.
for (size_t i = 0; i <= display_size; ++i) {
if (i < display_size && display_results[i]->is_omnibox_search()) {
++continuous_assistant_item_count;
} else {
// Update previous result's icon.
if (continuous_assistant_item_count >= 2) {
(*out_display_icons)[i - 1] =
GetPreviousVectorIcon(continuous_assistant_item_count);
}
continuous_assistant_item_count = 0;
}
// Update current result's icon.
if (continuous_assistant_item_count > 0) {
(*out_display_icons)[i] =
GetCurrentVectorIcon(continuous_assistant_item_count);
}
}
}
SearchResultIdWithPositionIndices GetSearchResultsForLogging( SearchResultIdWithPositionIndices GetSearchResultsForLogging(
std::vector<SearchResultView*> search_result_views) { std::vector<SearchResultView*> search_result_views) {
SearchResultIdWithPositionIndices results; SearchResultIdWithPositionIndices results;
...@@ -158,8 +81,11 @@ SearchResultListView::SearchResultListView(AppListMainView* main_view, ...@@ -158,8 +81,11 @@ SearchResultListView::SearchResultListView(AppListMainView* main_view,
results_container_->SetLayoutManager(std::make_unique<views::BoxLayout>( results_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical)); views::BoxLayout::Orientation::kVertical));
for (size_t i = 0; size_t result_count =
i < AppListConfig::instance().max_search_result_list_items(); ++i) { AppListConfig::instance().max_search_result_list_items() +
AppListConfig::instance().max_assistant_search_result_list_items();
for (size_t i = 0; i < result_count; ++i) {
search_result_views_.emplace_back( search_result_views_.emplace_back(
new SearchResultView(this, view_delegate_)); new SearchResultView(this, view_delegate_));
search_result_views_.back()->set_index_in_container(i); search_result_views_.back()->set_index_in_container(i);
...@@ -186,24 +112,14 @@ SearchResultView* SearchResultListView::GetResultViewAt(size_t index) { ...@@ -186,24 +112,14 @@ SearchResultView* SearchResultListView::GetResultViewAt(size_t index) {
int SearchResultListView::DoUpdate() { int SearchResultListView::DoUpdate() {
if (!GetWidget() || !GetWidget()->IsVisible()) { if (!GetWidget() || !GetWidget()->IsVisible()) {
for (size_t i = 0; i < results_container_->children().size(); ++i) { for (auto* result_view : search_result_views_) {
SearchResultView* result_view = GetResultViewAt(i);
result_view->SetResult(nullptr); result_view->SetResult(nullptr);
result_view->SetVisible(false); result_view->SetVisible(false);
} }
return 0; return 0;
} }
std::vector<SearchResult*> display_results = std::vector<SearchResult*> display_results = GetSearchResults();
SearchModel::FilterSearchResultsByDisplayType(
results(), SearchResultDisplayType::kList, /*excludes=*/{},
results_container_->children().size());
const size_t display_size = display_results.size();
std::vector<const gfx::VectorIcon*> assistant_item_icons(display_size,
nullptr);
if (IsAssistantSearchEnabled(view_delegate_))
CalculateDisplayIcons(display_results, &assistant_item_icons);
// TODO(crbug.com/1076270): The logic for zero state and Drive quick access // TODO(crbug.com/1076270): The logic for zero state and Drive quick access
// files below exists only for metrics, and can be folded into the // files below exists only for metrics, and can be folded into the
...@@ -211,7 +127,7 @@ int SearchResultListView::DoUpdate() { ...@@ -211,7 +127,7 @@ int SearchResultListView::DoUpdate() {
bool found_zero_state_file = false; bool found_zero_state_file = false;
bool found_drive_quick_access = false; bool found_drive_quick_access = false;
for (size_t i = 0; i < results_container_->children().size(); ++i) { for (size_t i = 0; i < search_result_views_.size(); ++i) {
SearchResultView* result_view = GetResultViewAt(i); SearchResultView* result_view = GetResultViewAt(i);
if (i < display_results.size()) { if (i < display_results.size()) {
if (IsZeroStateFile(*display_results[i])) { if (IsZeroStateFile(*display_results[i])) {
...@@ -219,24 +135,6 @@ int SearchResultListView::DoUpdate() { ...@@ -219,24 +135,6 @@ int SearchResultListView::DoUpdate() {
} else if (IsDriveQuickAccess(*display_results[i])) { } else if (IsDriveQuickAccess(*display_results[i])) {
found_drive_quick_access = true; found_drive_quick_access = true;
} }
if (assistant_item_icons[i]) {
result_view->SetDisplayIcon(gfx::CreateVectorIcon(
*(assistant_item_icons[i]),
(assistant_item_icons[i] == &kAssistantIcon)
? AppListConfig::instance().search_list_icon_dimension()
: AppListConfig::instance()
.search_list_icon_vertical_bar_dimension(),
kListVerticalBarIconColor));
} else {
// Reset |display_icon_|.
result_view->SetDisplayIcon(gfx::ImageSkia());
}
if (IsAssistantSearchEnabled(view_delegate_) &&
display_results[i]->is_omnibox_search()) {
display_results[i]->set_accessible_name(l10n_util::GetStringFUTF16(
IDS_ASH_ASSISTANT_QUERY_ACCESSIBILITY_ANNOUNCEMENT,
display_results[i]->title()));
}
result_view->SetResult(display_results[i]); result_view->SetResult(display_results[i]);
result_view->SetVisible(true); result_view->SetVisible(true);
...@@ -323,8 +221,12 @@ int SearchResultListView::GetHeightForWidth(int w) const { ...@@ -323,8 +221,12 @@ int SearchResultListView::GetHeightForWidth(int w) const {
void SearchResultListView::SearchResultActivated(SearchResultView* view, void SearchResultListView::SearchResultActivated(SearchResultView* view,
int event_flags, int event_flags,
bool by_button_press) { bool by_button_press) {
if (view_delegate_ && view->result()) { if (!view_delegate_ || !view || !view->result())
RecordSearchResultOpenSource(view->result(), view_delegate_->GetModel(), return;
auto* result = view->result();
RecordSearchResultOpenSource(result, view_delegate_->GetModel(),
view_delegate_->GetSearchModel()); view_delegate_->GetSearchModel());
view_delegate_->LogResultLaunchHistogram( view_delegate_->LogResultLaunchHistogram(
SearchResultLaunchLocation::kResultList, view->index_in_container()); SearchResultLaunchLocation::kResultList, view->index_in_container());
...@@ -332,12 +234,11 @@ void SearchResultListView::SearchResultActivated(SearchResultView* view, ...@@ -332,12 +234,11 @@ void SearchResultListView::SearchResultActivated(SearchResultView* view,
view_delegate_->GetSearchModel()->search_box()->text(), view_delegate_->GetSearchModel()->search_box()->text(),
GetSearchResultsForLogging(search_result_views_), GetSearchResultsForLogging(search_result_views_),
view->index_in_container()); view->index_in_container());
view_delegate_->OpenSearchResult( view_delegate_->OpenSearchResult(
view->result()->id(), event_flags, result->id(), event_flags, AppListLaunchedFrom::kLaunchedFromSearchBox,
AppListLaunchedFrom::kLaunchedFromSearchBox,
AppListLaunchType::kSearchResult, -1 /* suggestion_index */, AppListLaunchType::kSearchResult, -1 /* suggestion_index */,
!by_button_press && view->is_default_result() /* launch_as_default */); !by_button_press && view->is_default_result() /* launch_as_default */);
}
} }
void SearchResultListView::SearchResultActionActivated(SearchResultView* view, void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
...@@ -375,4 +276,44 @@ void SearchResultListView::VisibilityChanged(View* starting_from, ...@@ -375,4 +276,44 @@ void SearchResultListView::VisibilityChanged(View* starting_from,
previous_found_drive_quick_access_ = false; previous_found_drive_quick_access_ = false;
} }
std::vector<SearchResult*> SearchResultListView::GetAssistantResults() {
// Only show Assistant results if there are no tiles. There is not enough
// room in launcher to display Assistant results if there are tiles visible.
bool visible_tiles = !SearchModel::FilterSearchResultsByDisplayType(
results(), SearchResult::DisplayType::kTile,
/*excludes=*/{}, /*max_results=*/1)
.empty();
if (visible_tiles)
return std::vector<SearchResult*>();
return SearchModel::FilterSearchResultsByFunction(
results(), base::BindRepeating([](const SearchResult& search_result) {
return search_result.display_type() == SearchResultDisplayType::kList &&
search_result.result_type() ==
AppListSearchResultType::kAssistantText;
}),
/*max_results=*/
AppListConfig::instance().max_assistant_search_result_list_items());
}
std::vector<SearchResult*> SearchResultListView::GetSearchResults() {
std::vector<SearchResult*> search_results =
SearchModel::FilterSearchResultsByFunction(
results(), base::BindRepeating([](const SearchResult& result) {
return result.display_type() == SearchResultDisplayType::kList &&
result.result_type() !=
AppListSearchResultType::kAssistantText;
}),
/*max_results=*/
AppListConfig::instance().max_search_result_list_items());
std::vector<SearchResult*> assistant_results = GetAssistantResults();
search_results.insert(search_results.end(), assistant_results.begin(),
assistant_results.end());
return search_results;
}
} // namespace ash } // namespace ash
...@@ -71,6 +71,12 @@ class APP_LIST_EXPORT SearchResultListView : public SearchResultContainerView { ...@@ -71,6 +71,12 @@ class APP_LIST_EXPORT SearchResultListView : public SearchResultContainerView {
// after a period of time. // after a period of time.
void LogImpressions(); void LogImpressions();
// Returns search results specific to Assistant if any are available.
std::vector<SearchResult*> GetAssistantResults();
// Returns regular search results with Assistant search results appended.
std::vector<SearchResult*> GetSearchResults();
AppListMainView* main_view_; // Owned by views hierarchy. AppListMainView* main_view_; // Owned by views hierarchy.
AppListViewDelegate* view_delegate_; // Not owned. AppListViewDelegate* view_delegate_; // Not owned.
......
...@@ -57,10 +57,35 @@ class SearchResultListViewTest : public views::test::WidgetTest { ...@@ -57,10 +57,35 @@ class SearchResultListViewTest : public views::test::WidgetTest {
return view_->GetResultViewAt(index); return view_->GetResultViewAt(index);
} }
std::vector<SearchResultView*> GetAssistantResultViews() const {
std::vector<SearchResultView*> results;
for (auto* view : view_->search_result_views_) {
auto* result = view->result();
if (result &&
result->result_type() == AppListSearchResultType::kAssistantText)
results.push_back(view);
}
return results;
}
SearchModel::SearchResults* GetResults() { SearchModel::SearchResults* GetResults() {
return view_delegate_.GetSearchModel()->results(); return view_delegate_.GetSearchModel()->results();
} }
void AddAssistantSearchResult() {
SearchModel::SearchResults* results = GetResults();
std::unique_ptr<TestSearchResult> assistant_result =
std::make_unique<TestSearchResult>();
assistant_result->set_result_type(
ash::AppListSearchResultType::kAssistantText);
assistant_result->set_display_type(ash::SearchResultDisplayType::kList);
assistant_result->set_title(base::UTF8ToUTF16("assistant result"));
results->Add(std::move(assistant_result));
RunPendingMessages();
}
void SetUpSearchResults() { void SetUpSearchResults() {
SearchModel::SearchResults* results = GetResults(); SearchModel::SearchResults* results = GetResults();
for (int i = 0; i < kDefaultSearchItems; ++i) { for (int i = 0; i < kDefaultSearchItems; ++i) {
...@@ -153,5 +178,31 @@ TEST_F(SearchResultListViewTest, ModelObservers) { ...@@ -153,5 +178,31 @@ TEST_F(SearchResultListViewTest, ModelObservers) {
ExpectConsistent(); ExpectConsistent();
} }
TEST_F(SearchResultListViewTest, HidesAssistantResultWhenTilesVisible) {
SetUpSearchResults();
// No assistant results available.
EXPECT_TRUE(GetAssistantResultViews().empty());
AddAssistantSearchResult();
// Assistant result should be set and visible.
for (const auto* view : GetAssistantResultViews()) {
EXPECT_TRUE(view->GetVisible());
EXPECT_EQ(view->result()->title(), base::UTF8ToUTF16("assistant result"));
}
// Add a tile result
std::unique_ptr<TestSearchResult> tile_result =
std::make_unique<TestSearchResult>();
tile_result->set_display_type(ash::SearchResultDisplayType::kTile);
GetResults()->Add(std::move(tile_result));
RunPendingMessages();
// Assistant result should be gone.
EXPECT_TRUE(GetAssistantResultViews().empty());
}
} // namespace test } // namespace test
} // namespace ash } // namespace ash
...@@ -187,6 +187,10 @@ class ASH_PUBLIC_EXPORT AppListConfig { ...@@ -187,6 +187,10 @@ class ASH_PUBLIC_EXPORT AppListConfig {
return max_search_result_list_items_; return max_search_result_list_items_;
} }
size_t max_assistant_search_result_list_items() const {
return max_assistant_search_result_list_items_;
}
double privacy_container_score() const { return privacy_container_score_; } double privacy_container_score() const { return privacy_container_score_; }
double app_tiles_container_score() const { double app_tiles_container_score() const {
return app_tiles_container_score_; return app_tiles_container_score_;
...@@ -510,6 +514,10 @@ class ASH_PUBLIC_EXPORT AppListConfig { ...@@ -510,6 +514,10 @@ class ASH_PUBLIC_EXPORT AppListConfig {
// Max number of search result list items in the launcher suggestion window. // Max number of search result list items in the launcher suggestion window.
const size_t max_search_result_list_items_ = 5; const size_t max_search_result_list_items_ = 5;
// Max number of Assistant search result list items in the launcher suggestion
// window. Appears in the list after normal search results.
const size_t max_assistant_search_result_list_items_ = 1;
// Scores for the containers within the search box view. These are displayed // Scores for the containers within the search box view. These are displayed
// in high-to-low order. // in high-to-low order.
// The privacy container is not always visible, but when available it should // The privacy container is not always visible, but when available it should
......
...@@ -151,6 +151,7 @@ enum class AppListSearchResultType { ...@@ -151,6 +151,7 @@ enum class AppListSearchResultType {
kAssistantChip, // Assistant results in suggestion chips. kAssistantChip, // Assistant results in suggestion chips.
kOsSettings, // OS settings results. kOsSettings, // OS settings results.
kInternalPrivacyInfo, // Result used internally by privacy notices. kInternalPrivacyInfo, // Result used internally by privacy notices.
kAssistantText, // Assistant text results.
// Add new values here. // Add new values here.
}; };
......
...@@ -4,6 +4,8 @@ static_library("test_support") { ...@@ -4,6 +4,8 @@ static_library("test_support") {
sources = [ sources = [
"mock_assistant_controller.cc", "mock_assistant_controller.cc",
"mock_assistant_controller.h", "mock_assistant_controller.h",
"mock_assistant_state.cc",
"mock_assistant_state.h",
] ]
deps = [ deps = [
......
// Copyright 2020 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 "ash/public/cpp/assistant/test_support/mock_assistant_state.h"
namespace ash {
MockAssistantState::MockAssistantState() {
allowed_state_ = chromeos::assistant::AssistantAllowedState::ALLOWED;
settings_enabled_ = true;
}
MockAssistantState::~MockAssistantState() = default;
void MockAssistantState::SetAllowedState(
chromeos::assistant::AssistantAllowedState allowed_state) {
if (allowed_state_ != allowed_state) {
allowed_state_ = allowed_state;
for (auto& observer : observers_)
observer.OnAssistantFeatureAllowedChanged(allowed_state_.value());
}
}
void MockAssistantState::SetSettingsEnabled(bool enabled) {
if (settings_enabled_ != enabled) {
settings_enabled_ = enabled;
for (auto& observer : observers_)
observer.OnAssistantSettingsEnabled(settings_enabled_.value());
}
}
} // namespace ash
// Copyright 2020 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 ASH_PUBLIC_CPP_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_STATE_H_
#define ASH_PUBLIC_CPP_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_STATE_H_
#include "ash/public/cpp/assistant/assistant_state.h"
namespace ash {
class MockAssistantState : public ash::AssistantState {
public:
MockAssistantState();
MockAssistantState(const MockAssistantState&) = delete;
MockAssistantState& operator=(const MockAssistantState&) = delete;
~MockAssistantState() override;
void SetAllowedState(
chromeos::assistant::AssistantAllowedState allowed_state);
void SetSettingsEnabled(bool enabled);
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_ASSISTANT_TEST_SUPPORT_MOCK_ASSISTANT_STATE_H_
...@@ -1719,6 +1719,8 @@ static_library("ui") { ...@@ -1719,6 +1719,8 @@ static_library("ui") {
"app_list/search/arc/arc_playstore_search_result.h", "app_list/search/arc/arc_playstore_search_result.h",
"app_list/search/assistant_search_provider.cc", "app_list/search/assistant_search_provider.cc",
"app_list/search/assistant_search_provider.h", "app_list/search/assistant_search_provider.h",
"app_list/search/assistant_text_search_provider.cc",
"app_list/search/assistant_text_search_provider.h",
"app_list/search/chrome_search_result.cc", "app_list/search/chrome_search_result.cc",
"app_list/search/chrome_search_result.h", "app_list/search/chrome_search_result.h",
"app_list/search/common/url_icon_source.cc", "app_list/search/common/url_icon_source.cc",
......
...@@ -2,4 +2,5 @@ include_rules = [ ...@@ -2,4 +2,5 @@ include_rules = [
"+ash/assistant/model", "+ash/assistant/model",
"+ash/assistant/util", "+ash/assistant/util",
"+ash/public/cpp/vector_icons", "+ash/public/cpp/vector_icons",
"+ash/strings/grit",
] ]
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "ash/public/cpp/assistant/assistant_state.h" #include "ash/public/cpp/assistant/assistant_state.h"
#include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h" #include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
#include "ash/public/cpp/assistant/test_support/mock_assistant_controller.h" #include "ash/public/cpp/assistant/test_support/mock_assistant_controller.h"
#include "ash/public/cpp/assistant/test_support/mock_assistant_state.h"
#include "ash/public/cpp/vector_icons/vector_icons.h" #include "ash/public/cpp/vector_icons/vector_icons.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
...@@ -111,36 +112,6 @@ class ConversationStarterBuilder { ...@@ -111,36 +112,6 @@ class ConversationStarterBuilder {
GURL action_url_; GURL action_url_;
}; };
// TestAssistantState ----------------------------------------------------------
class TestAssistantState : public ash::AssistantState {
public:
TestAssistantState() {
allowed_state_ = chromeos::assistant::AssistantAllowedState::ALLOWED;
settings_enabled_ = true;
}
TestAssistantState(const TestAssistantState&) = delete;
TestAssistantState& operator=(const TestAssistantState&) = delete;
~TestAssistantState() override = default;
void SetAllowedState(AssistantAllowedState allowed_state) {
if (allowed_state_ != allowed_state) {
allowed_state_ = allowed_state;
for (auto& observer : observers_)
observer.OnAssistantFeatureAllowedChanged(allowed_state_.value());
}
}
void SetSettingsEnabled(bool enabled) {
if (settings_enabled_ != enabled) {
settings_enabled_ = enabled;
for (auto& observer : observers_)
observer.OnAssistantSettingsEnabled(settings_enabled_.value());
}
}
};
// TestAssistantSuggestionsController ------------------------------------------ // TestAssistantSuggestionsController ------------------------------------------
class TestAssistantSuggestionsController class TestAssistantSuggestionsController
...@@ -193,7 +164,7 @@ class AssistantSearchProviderTest : public AppListTestBase { ...@@ -193,7 +164,7 @@ class AssistantSearchProviderTest : public AppListTestBase {
AssistantSearchProvider& search_provider() { return search_provider_; } AssistantSearchProvider& search_provider() { return search_provider_; }
TestAssistantState& assistant_state() { return assistant_state_; } ash::MockAssistantState& assistant_state() { return assistant_state_; }
NiceMock<ash::MockAssistantController>& assistant_controller() { NiceMock<ash::MockAssistantController>& assistant_controller() {
return assistant_controller_; return assistant_controller_;
...@@ -204,7 +175,7 @@ class AssistantSearchProviderTest : public AppListTestBase { ...@@ -204,7 +175,7 @@ class AssistantSearchProviderTest : public AppListTestBase {
} }
private: private:
TestAssistantState assistant_state_; ash::MockAssistantState assistant_state_;
NiceMock<ash::MockAssistantController> assistant_controller_; NiceMock<ash::MockAssistantController> assistant_controller_;
TestAssistantSuggestionsController suggestions_controller_; TestAssistantSuggestionsController suggestions_controller_;
AssistantSearchProvider search_provider_; AssistantSearchProvider search_provider_;
......
// Copyright 2020 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/assistant_text_search_provider.h"
#include <memory>
#include <vector>
#include "ash/assistant/util/deep_link_util.h"
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/app_list_controller.h"
#include "ash/public/cpp/app_list/app_list_metrics.h"
#include "ash/public/cpp/assistant/controller/assistant_controller.h"
#include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
#include "ash/public/cpp/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "chromeos/services/assistant/public/cpp/assistant_service.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/paint_vector_icon.h"
namespace app_list {
namespace {
using chromeos::assistant::AssistantAllowedState;
constexpr char kIdPrefix[] = "googleassistant_text://";
// Helpers ---------------------------------------------------------------------
// Returns if the Assistant omnibox search provider is allowed to contribute
// results.
bool AreResultsAllowed() {
ash::AssistantState* assistant_state = ash::AssistantState::Get();
return assistant_state->allowed_state() == AssistantAllowedState::ALLOWED &&
assistant_state->settings_enabled() == true;
}
// AssistantTextSearchResult
// -------------------------------------------------------
class AssistantTextSearchResult : public ChromeSearchResult {
public:
explicit AssistantTextSearchResult(const base::string16& text)
: action_url_(ash::assistant::util::CreateAssistantQueryDeepLink(
base::UTF16ToUTF8(text))) {
set_id(kIdPrefix + base::UTF16ToUTF8(text));
SetDisplayType(ash::SearchResultDisplayType::kList);
SetResultType(ash::AppListSearchResultType::kAssistantText);
SetMetricsType(ash::SearchResultType::ASSISTANT_OMNIBOX_RESULT);
SetTitle(text);
SetAccessibleName(l10n_util::GetStringFUTF16(
IDS_ASH_ASSISTANT_QUERY_ACCESSIBILITY_ANNOUNCEMENT, text));
SetIcon(gfx::CreateVectorIcon(
ash::kAssistantIcon,
ash::AppListConfig::instance().search_list_icon_dimension(),
gfx::kPlaceholderColor));
set_dismiss_view_on_open(false);
}
AssistantTextSearchResult(const AssistantTextSearchResult&) = delete;
AssistantTextSearchResult& operator=(const AssistantTextSearchResult&) =
delete;
~AssistantTextSearchResult() override = default;
private:
void Open(int event_flags) override {
// Opening of |action_url_| is delegated to the Assistant controller as only
// the Assistant controller knows how to handle Assistant deep links.
ash::AssistantController::Get()->OpenUrl(action_url_);
}
const GURL action_url_;
};
} // namespace
// AssistantTextSearchProvider -------------------------------------------------
AssistantTextSearchProvider::AssistantTextSearchProvider() {
UpdateResults();
// Bind observers.
assistant_controller_observer_.Add(ash::AssistantController::Get());
assistant_state_observer_.Add(ash::AssistantState::Get());
}
AssistantTextSearchProvider::~AssistantTextSearchProvider() = default;
ash::AppListSearchResultType AssistantTextSearchProvider::ResultType() {
return ash::AppListSearchResultType::kAssistantText;
}
void AssistantTextSearchProvider::Start(const base::string16& query) {
query_ = query;
UpdateResults();
}
void AssistantTextSearchProvider::OnAssistantControllerDestroying() {
assistant_state_observer_.Remove(ash::AssistantState::Get());
assistant_controller_observer_.Remove(ash::AssistantController::Get());
}
void AssistantTextSearchProvider::OnAssistantFeatureAllowedChanged(
chromeos::assistant::AssistantAllowedState allowed_state) {
UpdateResults();
}
void AssistantTextSearchProvider::OnAssistantSettingsEnabled(bool enabled) {
UpdateResults();
}
void AssistantTextSearchProvider::UpdateResults() {
if (!AreResultsAllowed() || query_.empty()) {
ClearResults();
return;
}
SearchProvider::Results results;
results.push_back(std::make_unique<AssistantTextSearchResult>(query_));
SwapResults(&results);
}
} // namespace app_list
// Copyright 2020 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_ASSISTANT_TEXT_SEARCH_PROVIDER_H_
#define CHROME_BROWSER_UI_APP_LIST_SEARCH_ASSISTANT_TEXT_SEARCH_PROVIDER_H_
#include "ash/public/cpp/assistant/assistant_state.h"
#include "ash/public/cpp/assistant/controller/assistant_controller.h"
#include "ash/public/cpp/assistant/controller/assistant_controller_observer.h"
#include "base/scoped_observer.h"
#include "chrome/browser/ui/app_list/search/search_provider.h"
namespace app_list {
// A search provider implementation serving results from Assistant.
// This is currently only used to provide a single search result that runs an
// Assistant query of the search text. This is displayed when
// kEnableAssistantSearch feature is enabled. This search result does not go
// through normal ranking procedures, but is instead appended to an existing
// list of search results.
class AssistantTextSearchProvider : public SearchProvider,
public ash::AssistantControllerObserver,
public ash::AssistantStateObserver {
public:
AssistantTextSearchProvider();
AssistantTextSearchProvider(const AssistantTextSearchProvider&) = delete;
AssistantTextSearchProvider& operator=(const AssistantTextSearchProvider&) =
delete;
~AssistantTextSearchProvider() override;
// SearchProvider:
void Start(const base::string16& query) override;
private:
// SearchProvider:
ash::AppListSearchResultType ResultType() override;
// ash::AssistantControllerObserver:
void OnAssistantControllerDestroying() override;
// ash::AssistantStateObserver:
void OnAssistantFeatureAllowedChanged(
chromeos::assistant::AssistantAllowedState allowed_state) override;
void OnAssistantSettingsEnabled(bool enabled) override;
// Invoke to update results based on current state.
void UpdateResults();
base::string16 query_;
ScopedObserver<ash::AssistantController, ash::AssistantControllerObserver>
assistant_controller_observer_{this};
ScopedObserver<ash::AssistantStateBase, ash::AssistantStateObserver>
assistant_state_observer_{this};
};
} // namespace app_list
#endif // CHROME_BROWSER_UI_APP_LIST_SEARCH_ASSISTANT_TEXT_SEARCH_PROVIDER_H_
// Copyright 2020 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/assistant_text_search_provider.h"
#include "ash/public/cpp/assistant/test_support/mock_assistant_controller.h"
#include "ash/public/cpp/assistant/test_support/mock_assistant_state.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/app_list/app_list_test_util.h"
#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
namespace app_list {
namespace test {
using chromeos::assistant::AssistantAllowedState;
class AssistantTextSearchProviderTest : public AppListTestBase {
public:
AssistantTextSearchProviderTest() = default;
AssistantTextSearchProviderTest(const AssistantTextSearchProviderTest&) =
delete;
AssistantTextSearchProviderTest& operator=(
const AssistantTextSearchProviderTest&) = delete;
~AssistantTextSearchProviderTest() override = default;
void SendText(const std::string& text) {
search_provider_.Start(base::UTF8ToUTF16(text));
}
AssistantTextSearchProvider& search_provider() { return search_provider_; }
ash::MockAssistantState& assistant_state() { return assistant_state_; }
testing::NiceMock<ash::MockAssistantController>& assistant_controller() {
return assistant_controller_;
}
void VerifyResultAt(size_t index, const std::string& text) {
EXPECT_LT(index, search_provider().results().size());
auto* result = search_provider().results().at(0).get();
EXPECT_EQ(result->title(), base::UTF8ToUTF16(text));
EXPECT_EQ(result->id(), "googleassistant_text://" + text);
EXPECT_EQ(result->accessible_name(),
base::UTF8ToUTF16(text + ", Google Assistant"));
EXPECT_EQ(result->result_type(),
ash::AppListSearchResultType::kAssistantText);
EXPECT_EQ(result->display_type(), ash::SearchResultDisplayType::kList);
}
private:
ash::MockAssistantState assistant_state_;
testing::NiceMock<ash::MockAssistantController> assistant_controller_;
AssistantTextSearchProvider search_provider_;
};
// Tests -----------------------------------------------------------------------
TEST_F(AssistantTextSearchProviderTest, ShouldNotProvideResultForEmptyQuery) {
EXPECT_TRUE(search_provider().results().empty());
SendText("testing");
// Should now have a search result with title "testing".
EXPECT_EQ(search_provider().results().size(), 1u);
VerifyResultAt(0, "testing");
SendText("");
// Should have no search results.
EXPECT_TRUE(search_provider().results().empty());
}
TEST_F(AssistantTextSearchProviderTest,
ShouldUpdateResultsWhenAssistantSettingsChange) {
SendText("testing");
EXPECT_EQ(search_provider().results().size(), 1u);
assistant_state().SetSettingsEnabled(false);
EXPECT_TRUE(search_provider().results().empty());
assistant_state().SetSettingsEnabled(true);
EXPECT_EQ(search_provider().results().size(), 1u);
}
TEST_F(AssistantTextSearchProviderTest,
ShouldUpdateResultsWhenAssistantAllowedStateChanges) {
SendText("testing");
// Test all possible Assistant allowed states.
for (int i = 0; i < static_cast<int>(AssistantAllowedState::MAX_VALUE); ++i) {
if (i == static_cast<int>(AssistantAllowedState::ALLOWED))
continue;
// When Assistant becomes not-allowed, results should be cleared.
assistant_state().SetAllowedState(static_cast<AssistantAllowedState>(i));
EXPECT_TRUE(search_provider().results().empty());
// When Assistant becomes allowed, we should again have a single result.
assistant_state().SetAllowedState(AssistantAllowedState::ALLOWED);
EXPECT_EQ(1u, search_provider().results().size());
}
}
TEST_F(AssistantTextSearchProviderTest, ShouldDeepLinkAssistantQuery) {
SendText("testing query");
GURL url;
bool in_background = true;
bool from_user = true;
EXPECT_CALL(assistant_controller(), OpenUrl)
.WillOnce(testing::DoAll(testing::SaveArg<0>(&url),
testing::SaveArg<1>(&in_background),
testing::SaveArg<2>(&from_user)));
search_provider().results().at(0)->Open(/*event_flags=*/0);
EXPECT_EQ(url, GURL("googleassistant://send-query?q=testing+query"));
EXPECT_FALSE(in_background);
EXPECT_FALSE(from_user);
}
} // namespace test
} // namespace app_list
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.h" #include "chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.h"
#include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.h" #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.h"
#include "chrome/browser/ui/app_list/search/assistant_search_provider.h" #include "chrome/browser/ui/app_list/search/assistant_search_provider.h"
#include "chrome/browser/ui/app_list/search/assistant_text_search_provider.h"
#include "chrome/browser/ui/app_list/search/drive_quick_access_provider.h" #include "chrome/browser/ui/app_list/search/drive_quick_access_provider.h"
#include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h" #include "chrome/browser/ui/app_list/search/launcher_search/launcher_search_provider.h"
#include "chrome/browser/ui/app_list/search/mixer.h" #include "chrome/browser/ui/app_list/search/mixer.h"
...@@ -75,7 +76,9 @@ constexpr size_t kMaxAppShortcutResults = 4; ...@@ -75,7 +76,9 @@ constexpr size_t kMaxAppShortcutResults = 4;
// Assistant provides a single search result when launcher chip integration is // Assistant provides a single search result when launcher chip integration is
// enabled from its internal cache of conversation starters. // enabled from its internal cache of conversation starters.
constexpr size_t kMaxAssistantResults = 1; constexpr size_t kMaxAssistantChipResults = 1;
constexpr size_t kMaxAssistantTextResults = 1;
// TODO(wutao): Need UX spec. // TODO(wutao): Need UX spec.
constexpr size_t kMaxSettingsShortcutResults = 6; constexpr size_t kMaxSettingsShortcutResults = 6;
...@@ -117,11 +120,17 @@ std::unique_ptr<SearchController> CreateSearchController( ...@@ -117,11 +120,17 @@ std::unique_ptr<SearchController> CreateSearchController(
// The Assistant search provider currently only contributes search results // The Assistant search provider currently only contributes search results
// when launcher chip integration is enabled. // when launcher chip integration is enabled.
if (chromeos::assistant::features::IsLauncherChipIntegrationEnabled()) { if (chromeos::assistant::features::IsLauncherChipIntegrationEnabled()) {
size_t assistant_group_id = controller->AddGroup(kMaxAssistantResults); size_t assistant_group_id = controller->AddGroup(kMaxAssistantChipResults);
controller->AddProvider(assistant_group_id, controller->AddProvider(assistant_group_id,
std::make_unique<AssistantSearchProvider>()); std::make_unique<AssistantSearchProvider>());
} }
if (app_list_features::IsAssistantSearchEnabled()) {
size_t assistant_group_id = controller->AddGroup(kMaxAssistantTextResults);
controller->AddProvider(assistant_group_id,
std::make_unique<AssistantTextSearchProvider>());
}
// LauncherSearchProvider is added only when not in guest // LauncherSearchProvider is added only when not in guest
// session and running on Chrome OS. // session and running on Chrome OS.
if (!profile->IsGuestSession()) { if (!profile->IsGuestSession()) {
......
...@@ -33,8 +33,9 @@ RankingItemType RankingItemTypeFromSearchResult( ...@@ -33,8 +33,9 @@ RankingItemType RankingItemTypeFromSearchResult(
case ash::AppListSearchResultType::kAssistantChip: case ash::AppListSearchResultType::kAssistantChip:
case ash::AppListSearchResultType::kOsSettings: case ash::AppListSearchResultType::kOsSettings:
case ash::AppListSearchResultType::kInternalPrivacyInfo: case ash::AppListSearchResultType::kInternalPrivacyInfo:
// NOTE: We don't rank results of type kAssistantChip as the Assistant case ash::AppListSearchResultType::kAssistantText:
// chip result, if present, is always shown in a dedicated slot. // NOTE: We don't rank results of type kAssistantChip, kAssistantText
// as those results, if present, are shown in a dedicated slot.
return RankingItemType::kIgnored; return RankingItemType::kIgnored;
case ash::AppListSearchResultType::kArcAppShortcut: case ash::AppListSearchResultType::kArcAppShortcut:
return RankingItemType::kArcAppShortcut; return RankingItemType::kArcAppShortcut;
......
...@@ -4757,6 +4757,7 @@ test("unit_tests") { ...@@ -4757,6 +4757,7 @@ test("unit_tests") {
"../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc", "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
"../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc", "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
"../browser/ui/app_list/search/assistant_search_provider_unittest.cc", "../browser/ui/app_list/search/assistant_search_provider_unittest.cc",
"../browser/ui/app_list/search/assistant_text_search_provider_unittest.cc",
"../browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc", "../browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker_unittest.cc",
"../browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc", "../browser/ui/app_list/search/cros_action_history/cros_action_recorder_unittest.cc",
"../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc", "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
......
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