assistant_response.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/assistant/model/assistant_response.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "ash/assistant/model/assistant_response_observer.h"
  8. #include "ash/assistant/model/ui/assistant_error_element.h"
  9. #include "ash/assistant/model/ui/assistant_ui_element.h"
  10. #include "base/bind.h"
  11. #include "base/memory/weak_ptr.h"
  12. #include "base/unguessable_token.h"
  13. #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
  14. #include "chromeos/ash/services/assistant/public/cpp/features.h"
  15. namespace ash {
  16. // AssistantResponse::PendingUiElement -----------------------------------------
  17. struct AssistantResponse::PendingUiElement {
  18. public:
  19. PendingUiElement() = default;
  20. ~PendingUiElement() = default;
  21. PendingUiElement(const PendingUiElement&) = delete;
  22. PendingUiElement& operator=(const PendingUiElement&) = delete;
  23. std::unique_ptr<AssistantUiElement> ui_element;
  24. bool is_processing = false;
  25. };
  26. // AssistantResponse::Processor ------------------------------------------------
  27. class AssistantResponse::Processor {
  28. public:
  29. Processor(AssistantResponse* response, ProcessingCallback callback)
  30. : response_(response), callback_(std::move(callback)) {}
  31. Processor(const Processor& copy) = delete;
  32. Processor& operator=(const Processor& assign) = delete;
  33. ~Processor() {
  34. if (callback_)
  35. std::move(callback_).Run(/*is_completed=*/false);
  36. }
  37. void Process() {
  38. // Responses should only be processed once.
  39. DCHECK_EQ(ProcessingState::kUnprocessed, response_->processing_state());
  40. response_->set_processing_state(ProcessingState::kProcessing);
  41. // Completion of |response_| processing is indicated by |processing_count_|
  42. // reaching zero. This value is decremented as each UI element is processed.
  43. processing_count_ = response_->GetUiElements().size();
  44. // Try finishing directly if there are no UI elements to be processed.
  45. if (processing_count_ == 0) {
  46. TryFinishing();
  47. return;
  48. }
  49. for (const auto& ui_element : response_->GetUiElements()) {
  50. // Start asynchronous processing of the UI element. Note that if the UI
  51. // element does not require any pre-rendering processing the callback may
  52. // be run synchronously. Also we must use WeakPtr here because |this| will
  53. // destroy before |ui_element| by design.
  54. ui_element->Process(
  55. base::BindOnce(&AssistantResponse::Processor::OnFinishedProcessing,
  56. weak_ptr_factory_.GetWeakPtr()));
  57. }
  58. }
  59. private:
  60. void OnFinishedProcessing() {
  61. // We handle success/failure cases the same because failures will be skipped
  62. // in view handling. We decrement our |processing_count_| and attempt to
  63. // finish response processing. This will no-op if elements are still
  64. // processing.
  65. --processing_count_;
  66. TryFinishing();
  67. }
  68. void TryFinishing() {
  69. // No-op if we are already finished or if elements are still processing.
  70. if (!callback_ || processing_count_ > 0)
  71. return;
  72. // Notify processing completion.
  73. response_->set_processing_state(ProcessingState::kProcessed);
  74. std::move(callback_).Run(/*is_completed=*/true);
  75. }
  76. // |response_| should outlive the Processor.
  77. AssistantResponse* const response_;
  78. ProcessingCallback callback_;
  79. int processing_count_ = 0;
  80. base::WeakPtrFactory<AssistantResponse::Processor> weak_ptr_factory_{this};
  81. };
  82. // AssistantResponse -----------------------------------------------------------
  83. AssistantResponse::AssistantResponse() = default;
  84. AssistantResponse::~AssistantResponse() {
  85. // Reset |processor_| explicitly in the destructor to guarantee the correct
  86. // lifecycle where |this| should outlive the |processor_|. This can also force
  87. // |processor_| to be destroyed before |ui_elements_| as we want regardless of
  88. // the declaration order.
  89. processor_.reset();
  90. }
  91. void AssistantResponse::AddObserver(AssistantResponseObserver* observer) const {
  92. observers_.AddObserver(observer);
  93. }
  94. void AssistantResponse::RemoveObserver(
  95. AssistantResponseObserver* observer) const {
  96. observers_.RemoveObserver(observer);
  97. }
  98. void AssistantResponse::AddUiElement(
  99. std::unique_ptr<AssistantUiElement> ui_element) {
  100. // In processing v2, UI elements are first cached in a pending state...
  101. auto pending_ui_element = std::make_unique<PendingUiElement>();
  102. pending_ui_element->ui_element = std::move(ui_element);
  103. pending_ui_element->is_processing = true;
  104. pending_ui_elements_.push_back(std::move(pending_ui_element));
  105. // ...while we perform any pre-processing necessary prior to rendering.
  106. pending_ui_elements_.back()->ui_element->Process(base::BindOnce(
  107. [](const base::WeakPtr<AssistantResponse>& self,
  108. PendingUiElement* pending_ui_element) {
  109. if (!self)
  110. return;
  111. // Indicate that |pending_ui_element| has finished processing.
  112. pending_ui_element->is_processing = false;
  113. // Add any UI elements that are ready for rendering to the response.
  114. // Note that this may or may not include the |pending_ui_element| which
  115. // just finished processing as we are required to add renderable UI
  116. // elements to the response in the same order that they were initially
  117. // pended to avoid inadvertently shuffling the response.
  118. while (!self->pending_ui_elements_.empty() &&
  119. !self->pending_ui_elements_.front()->is_processing) {
  120. self->ui_elements_.push_back(
  121. std::move(self->pending_ui_elements_.front()->ui_element));
  122. self->pending_ui_elements_.pop_front();
  123. self->NotifyUiElementAdded(self->ui_elements_.back().get());
  124. }
  125. },
  126. weak_factory_.GetWeakPtr(),
  127. base::Unretained(pending_ui_elements_.back().get())));
  128. }
  129. const std::vector<std::unique_ptr<AssistantUiElement>>&
  130. AssistantResponse::GetUiElements() const {
  131. return ui_elements_;
  132. }
  133. void AssistantResponse::AddSuggestions(
  134. const std::vector<AssistantSuggestion>& suggestions) {
  135. for (const auto& suggestion : suggestions)
  136. suggestions_.push_back(suggestion);
  137. NotifySuggestionsAdded(suggestions);
  138. }
  139. const chromeos::assistant::AssistantSuggestion*
  140. AssistantResponse::GetSuggestionById(const base::UnguessableToken& id) const {
  141. for (auto& suggestion : suggestions_) {
  142. if (suggestion.id == id)
  143. return &suggestion;
  144. }
  145. return nullptr;
  146. }
  147. const std::vector<chromeos::assistant::AssistantSuggestion>&
  148. AssistantResponse::GetSuggestions() const {
  149. return suggestions_;
  150. }
  151. void AssistantResponse::Process(ProcessingCallback callback) {
  152. processor_ = std::make_unique<Processor>(this, std::move(callback));
  153. processor_->Process();
  154. }
  155. void AssistantResponse::NotifyUiElementAdded(
  156. const AssistantUiElement* ui_element) {
  157. for (auto& observer : observers_)
  158. observer.OnUiElementAdded(ui_element);
  159. }
  160. void AssistantResponse::NotifySuggestionsAdded(
  161. const std::vector<AssistantSuggestion>& suggestions) {
  162. for (auto& observer : observers_)
  163. observer.OnSuggestionsAdded(suggestions);
  164. }
  165. bool AssistantResponse::ContainsUiElement(
  166. const AssistantUiElement* element) const {
  167. DCHECK(element);
  168. bool contains_element =
  169. std::any_of(ui_elements_.cbegin(), ui_elements_.cend(),
  170. [element](const std::unique_ptr<AssistantUiElement>& other) {
  171. return *other == *element;
  172. });
  173. return contains_element || ContainsPendingUiElement(element);
  174. }
  175. bool AssistantResponse::ContainsPendingUiElement(
  176. const AssistantUiElement* element) const {
  177. DCHECK(element);
  178. return std::any_of(pending_ui_elements_.cbegin(), pending_ui_elements_.cend(),
  179. [element](const std::unique_ptr<PendingUiElement>& other) {
  180. return *other->ui_element == *element;
  181. });
  182. }
  183. } // namespace ash