123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // 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 "ash/assistant/model/assistant_response.h"
- #include <algorithm>
- #include <utility>
- #include "ash/assistant/model/assistant_response_observer.h"
- #include "ash/assistant/model/ui/assistant_error_element.h"
- #include "ash/assistant/model/ui/assistant_ui_element.h"
- #include "base/bind.h"
- #include "base/memory/weak_ptr.h"
- #include "base/unguessable_token.h"
- #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
- #include "chromeos/ash/services/assistant/public/cpp/features.h"
- namespace ash {
- // AssistantResponse::PendingUiElement -----------------------------------------
- struct AssistantResponse::PendingUiElement {
- public:
- PendingUiElement() = default;
- ~PendingUiElement() = default;
- PendingUiElement(const PendingUiElement&) = delete;
- PendingUiElement& operator=(const PendingUiElement&) = delete;
- std::unique_ptr<AssistantUiElement> ui_element;
- bool is_processing = false;
- };
- // AssistantResponse::Processor ------------------------------------------------
- class AssistantResponse::Processor {
- public:
- Processor(AssistantResponse* response, ProcessingCallback callback)
- : response_(response), callback_(std::move(callback)) {}
- Processor(const Processor& copy) = delete;
- Processor& operator=(const Processor& assign) = delete;
- ~Processor() {
- if (callback_)
- std::move(callback_).Run(/*is_completed=*/false);
- }
- void Process() {
- // Responses should only be processed once.
- DCHECK_EQ(ProcessingState::kUnprocessed, response_->processing_state());
- response_->set_processing_state(ProcessingState::kProcessing);
- // Completion of |response_| processing is indicated by |processing_count_|
- // reaching zero. This value is decremented as each UI element is processed.
- processing_count_ = response_->GetUiElements().size();
- // Try finishing directly if there are no UI elements to be processed.
- if (processing_count_ == 0) {
- TryFinishing();
- return;
- }
- for (const auto& ui_element : response_->GetUiElements()) {
- // Start asynchronous processing of the UI element. Note that if the UI
- // element does not require any pre-rendering processing the callback may
- // be run synchronously. Also we must use WeakPtr here because |this| will
- // destroy before |ui_element| by design.
- ui_element->Process(
- base::BindOnce(&AssistantResponse::Processor::OnFinishedProcessing,
- weak_ptr_factory_.GetWeakPtr()));
- }
- }
- private:
- void OnFinishedProcessing() {
- // We handle success/failure cases the same because failures will be skipped
- // in view handling. We decrement our |processing_count_| and attempt to
- // finish response processing. This will no-op if elements are still
- // processing.
- --processing_count_;
- TryFinishing();
- }
- void TryFinishing() {
- // No-op if we are already finished or if elements are still processing.
- if (!callback_ || processing_count_ > 0)
- return;
- // Notify processing completion.
- response_->set_processing_state(ProcessingState::kProcessed);
- std::move(callback_).Run(/*is_completed=*/true);
- }
- // |response_| should outlive the Processor.
- AssistantResponse* const response_;
- ProcessingCallback callback_;
- int processing_count_ = 0;
- base::WeakPtrFactory<AssistantResponse::Processor> weak_ptr_factory_{this};
- };
- // AssistantResponse -----------------------------------------------------------
- AssistantResponse::AssistantResponse() = default;
- AssistantResponse::~AssistantResponse() {
- // Reset |processor_| explicitly in the destructor to guarantee the correct
- // lifecycle where |this| should outlive the |processor_|. This can also force
- // |processor_| to be destroyed before |ui_elements_| as we want regardless of
- // the declaration order.
- processor_.reset();
- }
- void AssistantResponse::AddObserver(AssistantResponseObserver* observer) const {
- observers_.AddObserver(observer);
- }
- void AssistantResponse::RemoveObserver(
- AssistantResponseObserver* observer) const {
- observers_.RemoveObserver(observer);
- }
- void AssistantResponse::AddUiElement(
- std::unique_ptr<AssistantUiElement> ui_element) {
- // In processing v2, UI elements are first cached in a pending state...
- auto pending_ui_element = std::make_unique<PendingUiElement>();
- pending_ui_element->ui_element = std::move(ui_element);
- pending_ui_element->is_processing = true;
- pending_ui_elements_.push_back(std::move(pending_ui_element));
- // ...while we perform any pre-processing necessary prior to rendering.
- pending_ui_elements_.back()->ui_element->Process(base::BindOnce(
- [](const base::WeakPtr<AssistantResponse>& self,
- PendingUiElement* pending_ui_element) {
- if (!self)
- return;
- // Indicate that |pending_ui_element| has finished processing.
- pending_ui_element->is_processing = false;
- // Add any UI elements that are ready for rendering to the response.
- // Note that this may or may not include the |pending_ui_element| which
- // just finished processing as we are required to add renderable UI
- // elements to the response in the same order that they were initially
- // pended to avoid inadvertently shuffling the response.
- while (!self->pending_ui_elements_.empty() &&
- !self->pending_ui_elements_.front()->is_processing) {
- self->ui_elements_.push_back(
- std::move(self->pending_ui_elements_.front()->ui_element));
- self->pending_ui_elements_.pop_front();
- self->NotifyUiElementAdded(self->ui_elements_.back().get());
- }
- },
- weak_factory_.GetWeakPtr(),
- base::Unretained(pending_ui_elements_.back().get())));
- }
- const std::vector<std::unique_ptr<AssistantUiElement>>&
- AssistantResponse::GetUiElements() const {
- return ui_elements_;
- }
- void AssistantResponse::AddSuggestions(
- const std::vector<AssistantSuggestion>& suggestions) {
- for (const auto& suggestion : suggestions)
- suggestions_.push_back(suggestion);
- NotifySuggestionsAdded(suggestions);
- }
- const chromeos::assistant::AssistantSuggestion*
- AssistantResponse::GetSuggestionById(const base::UnguessableToken& id) const {
- for (auto& suggestion : suggestions_) {
- if (suggestion.id == id)
- return &suggestion;
- }
- return nullptr;
- }
- const std::vector<chromeos::assistant::AssistantSuggestion>&
- AssistantResponse::GetSuggestions() const {
- return suggestions_;
- }
- void AssistantResponse::Process(ProcessingCallback callback) {
- processor_ = std::make_unique<Processor>(this, std::move(callback));
- processor_->Process();
- }
- void AssistantResponse::NotifyUiElementAdded(
- const AssistantUiElement* ui_element) {
- for (auto& observer : observers_)
- observer.OnUiElementAdded(ui_element);
- }
- void AssistantResponse::NotifySuggestionsAdded(
- const std::vector<AssistantSuggestion>& suggestions) {
- for (auto& observer : observers_)
- observer.OnSuggestionsAdded(suggestions);
- }
- bool AssistantResponse::ContainsUiElement(
- const AssistantUiElement* element) const {
- DCHECK(element);
- bool contains_element =
- std::any_of(ui_elements_.cbegin(), ui_elements_.cend(),
- [element](const std::unique_ptr<AssistantUiElement>& other) {
- return *other == *element;
- });
- return contains_element || ContainsPendingUiElement(element);
- }
- bool AssistantResponse::ContainsPendingUiElement(
- const AssistantUiElement* element) const {
- DCHECK(element);
- return std::any_of(pending_ui_elements_.cbegin(), pending_ui_elements_.cend(),
- [element](const std::unique_ptr<PendingUiElement>& other) {
- return *other->ui_element == *element;
- });
- }
- } // namespace ash
|