Commit ecca6dd3 authored by Li Lin's avatar Li Lin Committed by Chromium LUCI CQ

Add Projector Controllers.

This CL includes adding:
- Feature flag for gating Projector feature.
- ProjectorController: the main controller
- ProjectorUiController: the controller responsible for interacting
with the UI.
- ProjectorMetadataController: the controller responsible for recording
and saving the screencast metadata.
- ProjectorMetadata: model of the projector metadata.

Follow up CLs will add more implementations and unit tests. More detail
about Projector at: go/projector-prd

Bug: 1164029
Test: unit tests
Change-Id: I808a044c444a999ecfdbcf1615949cdd4df182b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2627441
Commit-Queue: Li Lin <llin@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarYilkal Abe <yilkal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845315}
parent 59d680fc
...@@ -720,6 +720,14 @@ component("ash") { ...@@ -720,6 +720,14 @@ component("ash") {
"policy/policy_recommendation_restorer.h", "policy/policy_recommendation_restorer.h",
"power/hid_battery_util.cc", "power/hid_battery_util.cc",
"power/hid_battery_util.h", "power/hid_battery_util.h",
"projector/projector_controller.cc",
"projector/projector_controller.h",
"projector/projector_metadata_controller.cc",
"projector/projector_metadata_controller.h",
"projector/projector_metadata_model.cc",
"projector/projector_metadata_model.h",
"projector/projector_ui_controller.cc",
"projector/projector_ui_controller.h",
"quick_answers/quick_answers_controller_impl.cc", "quick_answers/quick_answers_controller_impl.cc",
"quick_answers/quick_answers_controller_impl.h", "quick_answers/quick_answers_controller_impl.h",
"quick_answers/quick_answers_ui_controller.cc", "quick_answers/quick_answers_ui_controller.cc",
...@@ -1746,6 +1754,7 @@ component("ash") { ...@@ -1746,6 +1754,7 @@ component("ash") {
"//ash/public/cpp/resources:ash_public_unscaled_resources", "//ash/public/cpp/resources:ash_public_unscaled_resources",
"//ash/resources/vector_icons", "//ash/resources/vector_icons",
"//ash/strings", "//ash/strings",
"//chromeos/services/machine_learning/public/mojom",
"//chromeos/services/multidevice_setup/public/mojom", "//chromeos/services/multidevice_setup/public/mojom",
"//chromeos/ui/vector_icons", "//chromeos/ui/vector_icons",
"//components/discardable_memory/public/mojom", "//components/discardable_memory/public/mojom",
...@@ -2149,6 +2158,8 @@ test("ash_unittests") { ...@@ -2149,6 +2158,8 @@ test("ash_unittests") {
"multi_device_setup/multi_device_notification_presenter_unittest.cc", "multi_device_setup/multi_device_notification_presenter_unittest.cc",
"policy/policy_recommendation_restorer_unittest.cc", "policy/policy_recommendation_restorer_unittest.cc",
"power/hid_battery_util_unittest.cc", "power/hid_battery_util_unittest.cc",
"projector/projector_controller_unittest.cc",
"projector/projector_metadata_model_unittest.cc",
"quick_answers/quick_answers_controller_unittest.cc", "quick_answers/quick_answers_controller_unittest.cc",
"quick_answers/quick_answers_ui_controller_unittest.cc", "quick_answers/quick_answers_ui_controller_unittest.cc",
"quick_answers/ui/quick_answers_view_unittest.cc", "quick_answers/ui/quick_answers_view_unittest.cc",
...@@ -2646,6 +2657,10 @@ static_library("test_support") { ...@@ -2646,6 +2657,10 @@ static_library("test_support") {
"metrics/task_switch_time_tracker_test_api.h", "metrics/task_switch_time_tracker_test_api.h",
"metrics/user_metrics_recorder_test_api.cc", "metrics/user_metrics_recorder_test_api.cc",
"metrics/user_metrics_recorder_test_api.h", "metrics/user_metrics_recorder_test_api.h",
"projector/test/mock_projector_metadata_controller.cc",
"projector/test/mock_projector_metadata_controller.h",
"projector/test/mock_projector_ui_controller.cc",
"projector/test/mock_projector_ui_controller.h",
"public/cpp/test/app_list_test_api.h", "public/cpp/test/app_list_test_api.h",
"public/cpp/test/shell_test_api.h", "public/cpp/test/shell_test_api.h",
"rotator/screen_rotation_animator_test_api.cc", "rotator/screen_rotation_animator_test_api.cc",
......
...@@ -78,6 +78,7 @@ include_rules = [ ...@@ -78,6 +78,7 @@ include_rules = [
"+chromeos/network", "+chromeos/network",
"+chromeos/services/assistant/public" , "+chromeos/services/assistant/public" ,
"+chromeos/services/assistant/test_support", "+chromeos/services/assistant/test_support",
"+chromeos/services/machine_learning/public",
"+chromeos/services/multidevice_setup/public", "+chromeos/services/multidevice_setup/public",
"+chromeos/services/nearby/public", "+chromeos/services/nearby/public",
"+chromeos/services/network_config/public", "+chromeos/services/network_config/public",
......
...@@ -1226,6 +1226,9 @@ This file contains the strings for ash. ...@@ -1226,6 +1226,9 @@ This file contains the strings for ash.
<message name="IDS_ASH_PHONE_HUB_ENABLE_HOTSPOT_NO_RECEPTION_STATE_TOOLTIP" desc="Tooltip message that indicates to the user that the Enable Hotspot feature is disabled because their phone does not have mobile data."> <message name="IDS_ASH_PHONE_HUB_ENABLE_HOTSPOT_NO_RECEPTION_STATE_TOOLTIP" desc="Tooltip message that indicates to the user that the Enable Hotspot feature is disabled because their phone does not have mobile data.">
Your phone must have mobile data to provide a hotspot Your phone must have mobile data to provide a hotspot
</message> </message>
<message name="IDS_ASH_PROJECTOR_KEY_IDEA_MARKED" desc="Message content on the toast that appears when a key idea is marked">
Marked as a key idea
</message>
<message name="IDS_ASH_STYLUS_BATTERY_LOW_LABEL" desc="The label next to the stylus battery indicator when the battery level is below 24%"> <message name="IDS_ASH_STYLUS_BATTERY_LOW_LABEL" desc="The label next to the stylus battery indicator when the battery level is below 24%">
Low Low
</message> </message>
......
2347a3da02b87db4c009f0f68b7f4f4f54868575
\ No newline at end of file
// Copyright 2021 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/projector/projector_controller.h"
#include "ash/projector/projector_metadata_controller.h"
#include "ash/projector/projector_ui_controller.h"
namespace ash {
ProjectorController::ProjectorController()
: ui_controller_(std::make_unique<ash::ProjectorUiController>()),
metadata_controller_(
std::make_unique<ash::ProjectorMetadataController>()) {}
ProjectorController::~ProjectorController() = default;
void ProjectorController::ShowToolbar() {
ui_controller_->ShowToolbar();
}
void ProjectorController::SetCaptionState(bool is_on) {
if (is_on == is_caption_on_)
return;
is_caption_on_ = is_on;
}
void ProjectorController::OnRecordingStarted() {
StartSpeechRecognition();
metadata_controller_->OnRecordingStarted();
}
void ProjectorController::SaveScreencast(
const base::FilePath& saved_video_path) {
metadata_controller_->SaveMetadata(saved_video_path);
}
void ProjectorController::OnTranscription(
chromeos::machine_learning::mojom::SpeechRecognizerEventPtr
speech_recognizer_event) {
bool is_final = speech_recognizer_event->is_final_result();
std::string transcript;
if (is_final) {
auto& final_result = speech_recognizer_event->get_final_result();
if (final_result->final_hypotheses.size() > 0) {
// Get the first result which is the most likely.
transcript = final_result->final_hypotheses.at(0);
}
// Records final transcript.
metadata_controller_->RecordTranscription(
transcript, final_result->timing_event->audio_start_time,
final_result->timing_event->event_end_time,
final_result->timing_event->word_alignments);
} else if (speech_recognizer_event->is_partial_result()) {
auto& partial_text =
speech_recognizer_event->get_partial_result()->partial_text;
if (partial_text.size() > 0) {
// Get the first result which is the most likely.
transcript = partial_text.at(0);
}
} else {
LOG(ERROR) << "No valid speech recognition result.";
return;
}
// Render transcription.
if (is_caption_on_) {
ui_controller_->OnTranscription(transcript, is_final);
}
}
void ProjectorController::SetProjectorUiControllerForTest(
std::unique_ptr<ProjectorUiController> ui_controller) {
ui_controller_ = std::move(ui_controller);
}
void ProjectorController::SetProjectorMetadataControllerForTest(
std::unique_ptr<ProjectorMetadataController> metadata_controller) {
metadata_controller_ = std::move(metadata_controller);
}
void ProjectorController::MarkKeyIdea() {
metadata_controller_->RecordKeyIdea();
ui_controller_->OnKeyIdeaMarked();
}
void ProjectorController::StartSpeechRecognition() {
// TODO(crbug.com/1165437): Enable speech reognition for mic input.
}
} // namespace ash
// Copyright 2021 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_PROJECTOR_PROJECTOR_CONTROLLER_H_
#define ASH_PROJECTOR_PROJECTOR_CONTROLLER_H_
#include <string>
#include <vector>
#include "ash/ash_export.h"
#include "chromeos/services/machine_learning/public/mojom/soda.mojom.h"
namespace base {
class FilePath;
} // namespace base
namespace ash {
class ProjectorUiController;
class ProjectorMetadataController;
// A controller to handle projector functionalities.
class ASH_EXPORT ProjectorController {
public:
ProjectorController();
ProjectorController(const ProjectorController&) = delete;
ProjectorController& operator=(const ProjectorController&) = delete;
~ProjectorController();
// Shows projector toolbar.
void ShowToolbar();
// Set caption on/off state.
void SetCaptionState(bool is_on);
// Mark a key idea.
void MarkKeyIdea();
// TODO(crbug.com/1165435): Consider updating to be delegate of recording
// service after finalizing on the integration plan with recording mode.
// Invoked when recording is started to start a screencast session.
void OnRecordingStarted();
// Saves the screencast including metadata.
void SaveScreencast(const base::FilePath& saved_video_path);
// TODO(crbug.com/1165437): Update the interface once SODA integration is
// available.
// Invoked when transcription result is available to record the transcript
// and maybe update the UI.
void OnTranscription(
chromeos::machine_learning::mojom::SpeechRecognizerEventPtr
speech_recognizer_event);
void SetProjectorUiControllerForTest(
std::unique_ptr<ProjectorUiController> ui_controller);
void SetProjectorMetadataControllerForTest(
std::unique_ptr<ProjectorMetadataController> metadata_controller);
private:
// Starts the speech recognition session.
void StartSpeechRecognition();
std::unique_ptr<ProjectorUiController> ui_controller_;
std::unique_ptr<ProjectorMetadataController> metadata_controller_;
bool is_caption_on_ = false;
};
} // namespace ash
#endif // ASH_PROJECTOR_PROJECTOR_CONTROLLER_H_
// Copyright 2021 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/projector/projector_controller.h"
#include <memory>
#include <string>
#include <vector>
#include "ash/projector/test/mock_projector_metadata_controller.h"
#include "ash/projector/test/mock_projector_ui_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/files/file_path.h"
#include "base/json/json_writer.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chromeos/services/machine_learning/public/mojom/soda.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
using chromeos::machine_learning::mojom::FinalResult;
using chromeos::machine_learning::mojom::PartialResult;
using chromeos::machine_learning::mojom::SpeechRecognizerEvent;
using chromeos::machine_learning::mojom::SpeechRecognizerEventPtr;
using chromeos::machine_learning::mojom::TimingInfo;
using testing::_;
using testing::ElementsAre;
SpeechRecognizerEventPtr BuildFinalResultEvent() {
auto final_result = FinalResult::New();
final_result->final_hypotheses.push_back("transcript text 1");
final_result->final_hypotheses.push_back("transcript text 2");
auto timing_event = TimingInfo::New();
timing_event->audio_start_time = base::TimeDelta::FromMilliseconds(0);
timing_event->event_end_time = base::TimeDelta::FromMilliseconds(3000);
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(1000));
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(2000)),
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(2500));
final_result->timing_event = std::move(timing_event);
return SpeechRecognizerEvent::NewFinalResult(std::move(final_result));
}
SpeechRecognizerEventPtr BuildPartialResultEvent() {
auto partial_result = PartialResult::New();
partial_result->partial_text.push_back("transcript partial text 1");
partial_result->partial_text.push_back("transcript partial text 2");
auto timing_event = TimingInfo::New();
timing_event->audio_start_time = base::TimeDelta::FromMilliseconds(0);
timing_event->event_end_time = base::TimeDelta::FromMilliseconds(3000);
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(1000));
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(2000)),
timing_event->word_alignments.push_back(
base::TimeDelta::FromMilliseconds(2500));
partial_result->timing_event = std::move(timing_event);
return SpeechRecognizerEvent::NewPartialResult(std::move(partial_result));
}
} // namespace
class ProjectorControllerTest : public AshTestBase {
public:
ProjectorControllerTest() = default;
ProjectorControllerTest(const ProjectorControllerTest&) = delete;
ProjectorControllerTest& operator=(const ProjectorControllerTest&) = delete;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
controller_ = std::make_unique<ProjectorController>();
auto mock_ui_controller = std::make_unique<MockProjectorUiController>();
mock_ui_controller_ = mock_ui_controller.get();
controller_->SetProjectorUiControllerForTest(std::move(mock_ui_controller));
auto mock_metadata_controller =
std::make_unique<MockProjectorMetadataController>();
mock_metadata_controller_ = mock_metadata_controller.get();
controller_->SetProjectorMetadataControllerForTest(
std::move(mock_metadata_controller));
}
protected:
MockProjectorUiController* mock_ui_controller_ = nullptr;
MockProjectorMetadataController* mock_metadata_controller_ = nullptr;
std::unique_ptr<ProjectorController> controller_;
};
TEST_F(ProjectorControllerTest, ShowToolbar) {
// Verify that |ShowToolbar| in |ProjectorUiController| is called.
EXPECT_CALL(*mock_ui_controller_, ShowToolbar()).Times(1);
controller_->ShowToolbar();
}
TEST_F(ProjectorControllerTest, SaveScreencast) {
base::FilePath saved_path;
// Verify that |SaveMetadata| in |ProjectorMetadataController| is called.
EXPECT_CALL(*mock_metadata_controller_, SaveMetadata(saved_path)).Times(1);
controller_->SaveScreencast(saved_path);
}
TEST_F(ProjectorControllerTest, OnTranscription) {
auto speech_recognition_event = BuildFinalResultEvent();
// Verify that |RecordTranscription| in |ProjectorMetadataController| is
// called to record the transcript.
EXPECT_CALL(
*mock_metadata_controller_,
RecordTranscription("transcript text 1",
testing::Eq(base::TimeDelta::FromMilliseconds(0)),
testing::Eq(base::TimeDelta::FromMilliseconds(3000)),
ElementsAre(base::TimeDelta::FromMilliseconds(1000),
base::TimeDelta::FromMilliseconds(2000),
base::TimeDelta::FromMilliseconds(2500))))
.Times(1);
// Verify that |OnTranscription| in |ProjectorUiController| is not called
// since capton is off.
EXPECT_CALL(*mock_ui_controller_, OnTranscription(_, _)).Times(0);
controller_->OnTranscription(std::move(speech_recognition_event));
}
TEST_F(ProjectorControllerTest, OnTranscriptionPartialResult) {
auto speech_recognition_event = BuildPartialResultEvent();
// Verify that |RecordTranscription| in |ProjectorMetadataController| is not
// called since it is not a final result.
EXPECT_CALL(*mock_metadata_controller_, RecordTranscription(_, _, _, _))
.Times(0);
// Verify that |OnTranscription| in |ProjectorUiController| is not called
// since caption is off.
EXPECT_CALL(*mock_ui_controller_, OnTranscription(_, _)).Times(0);
controller_->OnTranscription(std::move(speech_recognition_event));
}
TEST_F(ProjectorControllerTest, OnTranscriptionCaptionOn) {
auto speech_recognition_event = BuildFinalResultEvent();
// Verify that |SaveMetadata| in |ProjectorMetadataController| is called to
// record the transcript.
EXPECT_CALL(
*mock_metadata_controller_,
RecordTranscription("transcript text 1",
testing::Eq(base::TimeDelta::FromMilliseconds(0)),
testing::Eq(base::TimeDelta::FromMilliseconds(3000)),
ElementsAre(base::TimeDelta::FromMilliseconds(1000),
base::TimeDelta::FromMilliseconds(2000),
base::TimeDelta::FromMilliseconds(2500))))
.Times(1);
// Verify that |OnTranscription| in |ProjectorUiController| is called since
// capton is on.
EXPECT_CALL(*mock_ui_controller_, OnTranscription("transcript text 1", true))
.Times(1);
controller_->SetCaptionState(true);
controller_->OnTranscription(std::move(speech_recognition_event));
}
TEST_F(ProjectorControllerTest, OnTranscriptionCaptionOnPartialResult) {
auto speech_recognition_event = BuildPartialResultEvent();
// Verify that |RecordTranscription| in |ProjectorMetadataController| is
// called.
EXPECT_CALL(*mock_metadata_controller_, RecordTranscription(_, _, _, _))
.Times(0);
// Verify that |OnTranscription| in |ProjectorUiController| is called since
// capton is on.
EXPECT_CALL(*mock_ui_controller_,
OnTranscription("transcript partial text 1", false))
.Times(1);
controller_->SetCaptionState(true);
controller_->OnTranscription(std::move(speech_recognition_event));
}
} // namespace ash
// Copyright 2021 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/projector/projector_metadata_controller.h"
#include "ash/projector/projector_controller.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/task/current_thread.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
namespace ash {
namespace {
// Writes the given |data| in a file with |path|. Returns true if saving
// succeeded, or false otherwise.
bool SaveFile(const std::string& content, const base::FilePath& path) {
DCHECK(!base::CurrentUIThread::IsSet());
DCHECK(!path.empty());
if (!base::PathExists(path.DirName()) &&
!base::CreateDirectory(path.DirName())) {
LOG(ERROR) << "Failed to create path: " << path.DirName();
return false;
}
return base::WriteFile(path, content);
}
} // namespace
ProjectorMetadataController::ProjectorMetadataController()
: blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
ProjectorMetadataController::~ProjectorMetadataController() = default;
void ProjectorMetadataController::OnRecordingStarted() {
metadata_ = std::make_unique<ProjectorMetadata>();
}
void ProjectorMetadataController::RecordTranscription(
const std::string& transcription,
const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::vector<base::TimeDelta>& word_alignments) {
DCHECK(metadata_);
metadata_->AddTranscript(std::make_unique<ProjectorTranscript>(
start_time, end_time, transcription, word_alignments));
}
void ProjectorMetadataController::RecordKeyIdea() {
DCHECK(metadata_);
metadata_->MarkKeyIdea();
}
void ProjectorMetadataController::SaveMetadata(
const base::FilePath& video_file_path) {
DCHECK(metadata_);
// TODO(crbug.com/1165439): Finalize on the metadata file naming convention.
const base::FilePath path = video_file_path.ReplaceExtension(".txt");
metadata_->SetName(
video_file_path.RemoveExtension().BaseName().AsUTF8Unsafe());
// Save metadata.
auto metadata_str = metadata_->Serialize();
// TODO(crbug.com/1165439): Update after finalizing on the storage strategy.
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&SaveFile, metadata_str, path),
base::BindOnce(
[](const base::FilePath& path, bool success) {
if (!success) {
LOG(ERROR) << "Failed to save the metadata file: " << path;
return;
}
// TODO(crbug.com/1165439): Make screencast metadata indexable.
},
path));
}
void ProjectorMetadataController::SetProjectorMetadataModelForTest(
std::unique_ptr<ProjectorMetadata> metadata) {
metadata_ = std::move(metadata);
}
} // namespace ash
// Copyright 2021 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_PROJECTOR_PROJECTOR_METADATA_CONTROLLER_H_
#define ASH_PROJECTOR_PROJECTOR_METADATA_CONTROLLER_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "ash/projector/projector_metadata_model.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequenced_task_runner.h"
namespace base {
class FilePath;
class TimeDelta;
} // namespace base
namespace ash {
// The controller in charge of recording and saving screencast metadata.
class ASH_EXPORT ProjectorMetadataController {
public:
ProjectorMetadataController();
ProjectorMetadataController(const ProjectorMetadataController&) = delete;
ProjectorMetadataController& operator=(const ProjectorMetadataController&) =
delete;
virtual ~ProjectorMetadataController();
// Invoked when recording is started to create a new instance of metadata
// model. Virtual for testing.
virtual void OnRecordingStarted();
// Records the transcript in metadata. Virtual for testing.
virtual void RecordTranscription(
const std::string& transcription,
const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::vector<base::TimeDelta>& word_alignments);
// Marks the next transcript as the beginning of a key idea.
// Virtual for testing.
virtual void RecordKeyIdea();
// Serializes the metadata and saves in storage. Virtual for testing.
virtual void SaveMetadata(const base::FilePath& video_file_path);
void SetProjectorMetadataModelForTest(
std::unique_ptr<ProjectorMetadata> metadata);
private:
std::unique_ptr<ProjectorMetadata> metadata_;
// A blocking task runner for file IO operations.
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
base::WeakPtrFactory<ProjectorMetadataController> weak_factory_{this};
};
} // namespace ash
#endif // ASH_PROJECTOR_PROJECTOR_METADATA_CONTROLLER_H_
// Copyright 2021 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/projector/projector_metadata_model.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
namespace ash {
namespace {
using base::ListValue;
using base::Value;
constexpr base::StringPiece kStartOffsetKey = "startOffset";
constexpr base::StringPiece kEndOffsetKey = "endOffset";
constexpr base::StringPiece kTextKey = "text";
constexpr base::StringPiece kWordAlignmentKey = "wordAlignment";
constexpr base::StringPiece kNameKey = "name";
constexpr base::StringPiece kCaptionsKey = "captions";
constexpr base::StringPiece kKeyIdeasKey = "tableOfContent";
} // namespace
MetadataItem::MetadataItem(const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text)
: start_time_(start_time), end_time_(end_time), text_(text) {}
MetadataItem::~MetadataItem() = default;
ProjectorKeyIdea::ProjectorKeyIdea(const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text)
: MetadataItem(start_time, end_time, text) {}
ProjectorKeyIdea::~ProjectorKeyIdea() = default;
// The JSON we generate looks like this:
// {
// "startOffset": 100
// "endOffset": 2100
// "text": "Today I'd like to teach..."
// }
//
// Which is:
// DICT
// "startOffset": INT
// "endOffset": INT
// "text": STRING
base::Value ProjectorKeyIdea::ToJson() {
base::Value transcript(base::Value::Type::DICTIONARY);
transcript.SetIntKey(kStartOffsetKey, start_time_.InMilliseconds());
transcript.SetIntKey(kEndOffsetKey, end_time_.InMilliseconds());
transcript.SetStringKey(kTextKey, text_);
return transcript;
}
ProjectorTranscript::ProjectorTranscript(
const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text,
const std::vector<base::TimeDelta>& word_alignments)
: MetadataItem(start_time, end_time, text),
word_alignments_(word_alignments) {}
ProjectorTranscript::~ProjectorTranscript() = default;
// The JSON we generate looks like this:
// {
// "startOffset": 100
// "endOffset": 2100
// "text": "Today I'd like to teach..."
// "wordAlignments": [
// 100,
// 1500,
// ]
// }
//
// Which is:
// DICT
// "startOffset": INT
// "endOffset": INT
// "text": STRING
// "wordAlignments": LIST
base::Value ProjectorTranscript::ToJson() {
base::Value transcript(base::Value::Type::DICTIONARY);
transcript.SetIntKey(kStartOffsetKey, start_time_.InMilliseconds());
transcript.SetIntKey(kEndOffsetKey, end_time_.InMilliseconds());
transcript.SetStringKey(kTextKey, text_);
base::Value word_alignments_value(base::Value::Type::LIST);
for (auto& word_alignment : word_alignments_)
word_alignments_value.Append((int)word_alignment.InMilliseconds());
transcript.SetKey(kWordAlignmentKey, std::move(word_alignments_value));
return transcript;
}
ProjectorMetadata::ProjectorMetadata() = default;
ProjectorMetadata::~ProjectorMetadata() = default;
void ProjectorMetadata::AddTranscript(
std::unique_ptr<ProjectorTranscript> transcript) {
if (should_mark_key_idea_) {
key_ideas_.push_back(std::make_unique<ProjectorKeyIdea>(
transcript->start_time(), transcript->end_time()));
}
transcripts_.push_back(std::move(transcript));
should_mark_key_idea_ = false;
}
void ProjectorMetadata::MarkKeyIdea() {
should_mark_key_idea_ = true;
}
void ProjectorMetadata::SetName(const std::string& name) {
name_ = name;
}
std::string ProjectorMetadata::Serialize() {
std::string metadata_str;
base::JSONWriter::Write(ToJson(), &metadata_str);
return metadata_str;
}
// The JSON we generate looks like this:
// {
// "name": "Constructivist Learning Theory"
// "captions": [{
// "startOffset": 100
// "endOffset": 2100
// "text": "Today I'd like to teach you about a central pillar of a
// construction learning theory it's called the debugging Loop...",
// "wordAlignments": [
// 100,
// 1500,
// ]
// }],
// "tableOfContent": [
// {
// "text": "Making a creation",
// "startOffset": 4400,
// "encodingFormat": "text/markdown",
// },
// ]
// }
//
// Which is:
// DICT
// "@type": STRING
// "text": STRING
// "captions": LIST
// "tableOfContent": LIST
base::Value ProjectorMetadata::ToJson() {
base::Value metadata(base::Value::Type::DICTIONARY);
metadata.SetStringKey(kNameKey, name_);
base::Value captions_value(base::Value::Type::LIST);
for (auto& transcript : transcripts_)
captions_value.Append(transcript->ToJson());
metadata.SetKey(kCaptionsKey, std::move(captions_value));
base::Value key_ideas_value(base::Value::Type::LIST);
for (auto& key_idea : key_ideas_)
key_ideas_value.Append(key_idea->ToJson());
metadata.SetKey(kKeyIdeasKey, std::move(key_ideas_value));
return metadata;
}
} // namespace ash
// Copyright 2021 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_PROJECTOR_PROJECTOR_METADATA_MODEL_H_
#define ASH_PROJECTOR_PROJECTOR_METADATA_MODEL_H_
#include <memory>
#include <string>
#include <vector>
#include "ash/ash_export.h"
#include "base/time/time.h"
namespace base {
class Value;
} // namespace base
namespace ash {
// Base class to describe a metadata item.
class MetadataItem {
public:
explicit MetadataItem(const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text);
MetadataItem(const MetadataItem&) = delete;
MetadataItem& operator=(const MetadataItem&) = delete;
virtual ~MetadataItem();
base::TimeDelta& start_time() { return start_time_; }
base::TimeDelta& end_time() { return end_time_; }
// Return the serialized metadata item. This is used for storage.
virtual base::Value ToJson() = 0;
protected:
// The start time of the metadata item from the start of the recording
// session.
base::TimeDelta start_time_;
// The end time of the metadata item from the start of the recording session.
base::TimeDelta end_time_;
// Text data of the metadata item.
std::string text_;
};
// Class to describe a key idea.
class ASH_EXPORT ProjectorKeyIdea : public MetadataItem {
public:
explicit ProjectorKeyIdea(const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text = std::string());
ProjectorKeyIdea(const ProjectorKeyIdea&) = delete;
ProjectorKeyIdea& operator=(const ProjectorKeyIdea&) = delete;
~ProjectorKeyIdea() override;
base::Value ToJson() override;
};
// Class to describe a transcription.
class ASH_EXPORT ProjectorTranscript : public MetadataItem {
public:
explicit ProjectorTranscript(
const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::string& text,
const std::vector<base::TimeDelta>& word_alignments);
ProjectorTranscript(const ProjectorTranscript&) = delete;
ProjectorTranscript& operator=(const ProjectorTranscript&) = delete;
~ProjectorTranscript() override;
base::Value ToJson() override;
private:
std::vector<base::TimeDelta> word_alignments_;
};
// Class to describe a projector metadata of a screencast session, including
// name, transcriptions, key_ideas, etc
class ASH_EXPORT ProjectorMetadata {
public:
explicit ProjectorMetadata();
ProjectorMetadata(const ProjectorMetadata&) = delete;
ProjectorMetadata& operator=(const ProjectorMetadata&) = delete;
~ProjectorMetadata();
// Adds the transcript to the metadata. Virtual for testing.
void AddTranscript(std::unique_ptr<ProjectorTranscript> transcript);
// Marks a beginning of a key idea. The timing info of the next transcript
// will be used as the timing of the key idea. Virtual for testing.
void MarkKeyIdea();
// Sets the name of the screencast. Virtual for testing.
void SetName(const std::string& name);
// Serializes the metadata for storage. Virtual for testing.
std::string Serialize();
private:
base::Value ToJson();
std::vector<std::unique_ptr<ProjectorTranscript>> transcripts_;
std::vector<std::unique_ptr<ProjectorKeyIdea>> key_ideas_;
std::string name_;
// True if user mark the transcript as a key idea. It will be reset to false
// when the final recognition result is received and recorded as a key idea.
bool should_mark_key_idea_ = false;
};
} // namespace ash
#endif // ASH_PROJECTOR_PROJECTOR_METADATA_MODEL_H_
// Copyright 2021 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/projector/projector_metadata_model.h"
#include <memory>
#include <string>
#include <vector>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ash {
namespace {
constexpr char kSerializedKeyIdeaTemplate[] = R"({
"endOffset": %i,
"startOffset": %i,
"text": "%s"
})";
constexpr char kSerializedTranscriptTemplate[] = R"({
"endOffset": %i,
"startOffset": %i,
"text": "%s",
"wordAlignment": %s
})";
void AssertSerializedString(const std::string& expected,
const std::string& actual) {
base::Optional<base::Value> expected_value = base::JSONReader::Read(expected);
ASSERT_TRUE(expected_value);
std::string expected_serialized_value;
base::JSONWriter::Write(expected_value.value(), &expected_serialized_value);
EXPECT_EQ(expected_serialized_value, actual);
}
std::string BuildKeyIdeaJson(int start_offset,
int end_offset,
const std::string& text) {
return base::StringPrintf(kSerializedKeyIdeaTemplate, end_offset,
start_offset, text.c_str());
}
std::string BuildTranscriptJson(int start_offset,
int end_offset,
const std::string& text,
const std::string& words_alignments_str) {
return base::StringPrintf(kSerializedTranscriptTemplate, end_offset,
start_offset, text.c_str(),
words_alignments_str.c_str());
}
} // namespace
class ProjectorKeyIdeaTest : public testing::Test {
public:
ProjectorKeyIdeaTest() = default;
ProjectorKeyIdeaTest(const ProjectorKeyIdeaTest&) = delete;
ProjectorKeyIdeaTest& operator=(const ProjectorKeyIdeaTest&) = delete;
};
TEST_F(ProjectorKeyIdeaTest, ToJson) {
ProjectorKeyIdea key_idea(
/*start_time=*/base::TimeDelta::FromMilliseconds(1000),
/*end_time=*/base::TimeDelta::FromMilliseconds(3000));
std::string key_idea_str;
base::JSONWriter::Write(key_idea.ToJson(), &key_idea_str);
AssertSerializedString(BuildKeyIdeaJson(1000, 3000, std::string()),
key_idea_str);
}
TEST_F(ProjectorKeyIdeaTest, ToJsonWithText) {
ProjectorKeyIdea key_idea(
/*start_time=*/base::TimeDelta::FromMilliseconds(1000),
/*end_time=*/base::TimeDelta::FromMilliseconds(3000), "Key idea text");
std::string key_idea_str;
base::JSONWriter::Write(key_idea.ToJson(), &key_idea_str);
AssertSerializedString(BuildKeyIdeaJson(1000, 3000, "Key idea text"),
key_idea_str);
}
class ProjectorTranscriptTest : public testing::Test {
public:
ProjectorTranscriptTest() = default;
ProjectorTranscriptTest(const ProjectorTranscriptTest&) = delete;
ProjectorTranscriptTest& operator=(const ProjectorTranscriptTest&) = delete;
};
TEST_F(ProjectorTranscriptTest, ToJson) {
ProjectorTranscript transcript(
/*start_time=*/base::TimeDelta::FromMilliseconds(1000),
/*end_time=*/base::TimeDelta::FromMilliseconds(3000), "transcript text",
{base::TimeDelta::FromMilliseconds(1000),
base::TimeDelta::FromMilliseconds(2000)});
std::string transcript_str;
base::JSONWriter::Write(transcript.ToJson(), &transcript_str);
AssertSerializedString(
BuildTranscriptJson(1000, 3000, "transcript text", "[1000,2000]"),
transcript_str);
}
class ProjectorMetadataTest : public testing::Test {
public:
ProjectorMetadataTest() = default;
ProjectorMetadataTest(const ProjectorMetadataTest&) = delete;
ProjectorMetadataTest& operator=(const ProjectorMetadataTest&) = delete;
};
TEST_F(ProjectorMetadataTest, Serialize) {
const char kExpectedMetaData[] = R"({
"name": "Screen Recording 1",
"captions": [
{
"endOffset": 3000,
"startOffset": 1000,
"text": "transcript text",
"wordAlignment": [1000, 2000]
},
{
"endOffset": 5000,
"startOffset": 3000,
"text": "transcript text 2",
"wordAlignment":[3200, 4200, 4500]
}
],
"tableOfContent":[
{
"endOffset": 5000,
"startOffset": 3000,
"text": ""
}
]
})";
ProjectorMetadata metadata;
metadata.SetName("Screen Recording 1");
metadata.AddTranscript(std::make_unique<ProjectorTranscript>(
/*start_time=*/base::TimeDelta::FromMilliseconds(1000),
/*end_time=*/base::TimeDelta::FromMilliseconds(3000), "transcript text",
std::initializer_list<base::TimeDelta>(
{base::TimeDelta::FromMilliseconds(1000),
base::TimeDelta::FromMilliseconds(2000)})));
metadata.MarkKeyIdea();
metadata.AddTranscript(std::make_unique<ProjectorTranscript>(
/*start_time=*/base::TimeDelta::FromMilliseconds(3000),
/*end_time=*/base::TimeDelta::FromMilliseconds(5000), "transcript text 2",
std::initializer_list<base::TimeDelta>(
{base::TimeDelta::FromMilliseconds(3200),
base::TimeDelta::FromMilliseconds(4200),
base::TimeDelta::FromMilliseconds(4500)})));
AssertSerializedString(kExpectedMetaData, metadata.Serialize());
}
} // namespace ash
// Copyright 2021 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/projector/projector_ui_controller.h"
#include "ash/projector/projector_controller.h"
#include "ash/public/cpp/toast_data.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/toast/toast_manager_impl.h"
#include "ui/base/l10n/l10n_util.h"
namespace ash {
namespace {
constexpr char kMarkedKeyIdeaToastId[] = "projector_marked_key_idea";
void ShowToast(const std::string& id, int message_id, int32_t duration_ms) {
DCHECK(Shell::Get());
DCHECK(Shell::Get()->toast_manager());
ToastData toast(id, l10n_util::GetStringUTF16(message_id), duration_ms,
l10n_util::GetStringUTF16(IDS_ASH_TOAST_DISMISS_BUTTON));
Shell::Get()->toast_manager()->Show(toast);
}
} // namespace
ProjectorUiController::ProjectorUiController() = default;
ProjectorUiController::~ProjectorUiController() = default;
void ProjectorUiController::ShowToolbar() {}
void ProjectorUiController::OnKeyIdeaMarked() {
ShowToast(kMarkedKeyIdeaToastId, IDS_ASH_PROJECTOR_KEY_IDEA_MARKED,
ToastData::kInfiniteDuration);
}
void ProjectorUiController::OnTranscription(const std::string& transcription,
bool is_final) {}
} // namespace ash
// Copyright 2021 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_PROJECTOR_PROJECTOR_UI_CONTROLLER_H_
#define ASH_PROJECTOR_PROJECTOR_UI_CONTROLLER_H_
#include <string>
#include <vector>
#include "ash/ash_export.h"
namespace ash {
// The controller in charge of UI.
class ASH_EXPORT ProjectorUiController {
public:
ProjectorUiController();
ProjectorUiController(const ProjectorUiController&) = delete;
ProjectorUiController& operator=(const ProjectorUiController&) = delete;
virtual ~ProjectorUiController();
// Virtual for testing.
virtual void ShowToolbar();
// Invoked when key idea is marked to show a toast. Virtual for testing.
virtual void OnKeyIdeaMarked();
// Invoked when transcription is available for rendering. Virtual for testing.
virtual void OnTranscription(const std::string& transcription, bool is_final);
};
} // namespace ash
#endif // ASH_PROJECTOR_PROJECTOR_UI_CONTROLLER_H_
// Copyright 2021 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/projector/test/mock_projector_metadata_controller.h"
namespace ash {
MockProjectorMetadataController::MockProjectorMetadataController() = default;
MockProjectorMetadataController::~MockProjectorMetadataController() = default;
} // namespace ash
\ No newline at end of file
// Copyright 2021 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_PROJECTOR_TEST_MOCK_PROJECTOR_METADATA_CONTROLLER_H_
#define ASH_PROJECTOR_TEST_MOCK_PROJECTOR_METADATA_CONTROLLER_H_
#include "ash/projector/projector_metadata_controller.h"
#include "base/files/file_path.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace ash {
// A mock implementation of ProjectorMetadataController for use in tests.
class ASH_EXPORT MockProjectorMetadataController
: public ProjectorMetadataController {
public:
MockProjectorMetadataController();
MockProjectorMetadataController(const MockProjectorMetadataController&) =
delete;
MockProjectorMetadataController& operator=(
const MockProjectorMetadataController&) = delete;
~MockProjectorMetadataController() override;
// ProjectorMetadataController:
MOCK_METHOD0(OnRecordingStarted, void());
MOCK_METHOD4(RecordTranscription,
void(const std::string& transcription,
const base::TimeDelta start_time,
const base::TimeDelta end_time,
const std::vector<base::TimeDelta>& word_alignments));
MOCK_METHOD0(RecordKeyIdea, void());
MOCK_METHOD1(SaveMetadata, void(const base::FilePath& video_file_path));
};
} // namespace ash
#endif // ASH_PROJECTOR_TEST_MOCK_PROJECTOR_METADATA_CONTROLLER_H_
// Copyright 2021 The Chromium Authors. All rights reserved.
// Copyright 2021 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/projector/test/mock_projector_ui_controller.h"
namespace ash {
MockProjectorUiController::MockProjectorUiController() = default;
MockProjectorUiController::~MockProjectorUiController() = default;
} // namespace ash
// Copyright 2021 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_PROJECTOR_TEST_MOCK_PROJECTOR_UI_CONTROLLER_H_
#define ASH_PROJECTOR_TEST_MOCK_PROJECTOR_UI_CONTROLLER_H_
#include "ash/projector/projector_ui_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace ash {
// A mock implementation of ProjectorUiController for use in tests.
class ASH_EXPORT MockProjectorUiController : public ProjectorUiController {
public:
MockProjectorUiController();
MockProjectorUiController(const MockProjectorUiController&) = delete;
MockProjectorUiController& operator=(const MockProjectorUiController&) =
delete;
~MockProjectorUiController() override;
// ProjectorUiController:
MOCK_METHOD0(ShowToolbar, void());
MOCK_METHOD0(OnKeyIdeaMarked, void());
MOCK_METHOD2(OnTranscription,
void(const std::string& transcription, bool is_final));
};
} // namespace ash
#endif // ASH_PROJECTOR_TEST_MOCK_PROJECTOR_UI_CONTROLLER_H_
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
#include "ash/media/media_notification_controller_impl.h" #include "ash/media/media_notification_controller_impl.h"
#include "ash/multi_device_setup/multi_device_notification_presenter.h" #include "ash/multi_device_setup/multi_device_notification_presenter.h"
#include "ash/policy/policy_recommendation_restorer.h" #include "ash/policy/policy_recommendation_restorer.h"
#include "ash/projector/projector_controller.h"
#include "ash/public/cpp/ash_constants.h" #include "ash/public/cpp/ash_constants.h"
#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_prefs.h" #include "ash/public/cpp/ash_prefs.h"
...@@ -839,6 +840,7 @@ Shell::~Shell() { ...@@ -839,6 +840,7 @@ Shell::~Shell() {
display_color_manager_.reset(); display_color_manager_.reset();
projecting_observer_.reset(); projecting_observer_.reset();
projector_controller_.reset();
if (display_change_observer_) if (display_change_observer_)
display_manager_->configurator()->RemoveObserver( display_manager_->configurator()->RemoveObserver(
...@@ -1259,6 +1261,10 @@ void Shell::Init( ...@@ -1259,6 +1261,10 @@ void Shell::Init(
std::make_unique<DisplayAlignmentController>(); std::make_unique<DisplayAlignmentController>();
} }
if (chromeos::features::IsProjectorEnabled()) {
projector_controller_ = std::make_unique<ProjectorController>();
}
// Injects the factory which fulfills the implementation of the text context // Injects the factory which fulfills the implementation of the text context
// menu exclusive to CrOS. // menu exclusive to CrOS.
views::ViewsTextServicesContextMenuChromeos::SetImplFactory( views::ViewsTextServicesContextMenuChromeos::SetImplFactory(
......
...@@ -160,6 +160,7 @@ class PowerEventObserver; ...@@ -160,6 +160,7 @@ class PowerEventObserver;
class PowerPrefs; class PowerPrefs;
class PrivacyScreenController; class PrivacyScreenController;
class ProjectingObserver; class ProjectingObserver;
class ProjectorController;
class QuickAnswersController; class QuickAnswersController;
class ResizeShadowController; class ResizeShadowController;
class ResolutionNotificationController; class ResolutionNotificationController;
...@@ -558,6 +559,10 @@ class ASH_EXPORT Shell : public SessionObserver, ...@@ -558,6 +559,10 @@ class ASH_EXPORT Shell : public SessionObserver,
return frame_throttling_controller_.get(); return frame_throttling_controller_.get();
} }
ProjectorController* projector_controller() {
return projector_controller_.get();
}
// Force the shelf to query for it's current visibility state. // Force the shelf to query for it's current visibility state.
// TODO(jamescook): Move to Shelf. // TODO(jamescook): Move to Shelf.
void UpdateShelfVisibility(); void UpdateShelfVisibility();
...@@ -851,6 +856,8 @@ class ASH_EXPORT Shell : public SessionObserver, ...@@ -851,6 +856,8 @@ class ASH_EXPORT Shell : public SessionObserver,
std::unique_ptr<FrameThrottlingController> frame_throttling_controller_; std::unique_ptr<FrameThrottlingController> frame_throttling_controller_;
std::unique_ptr<ProjectorController> projector_controller_;
// For testing only: simulate that a modal window is open // For testing only: simulate that a modal window is open
bool simulate_modal_window_open_for_test_ = false; bool simulate_modal_window_open_for_test_ = false;
......
...@@ -541,6 +541,9 @@ const base::Feature kPrintSaveToDrive{"PrintSaveToDrive", ...@@ -541,6 +541,9 @@ const base::Feature kPrintSaveToDrive{"PrintSaveToDrive",
const base::Feature kPrintServerScaling{"PrintServerScaling", const base::Feature kPrintServerScaling{"PrintServerScaling",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether to enable projector.
const base::Feature kProjector{"Projector", base::FEATURE_DISABLED_BY_DEFAULT};
// Controls whether to enable quick answers. // Controls whether to enable quick answers.
const base::Feature kQuickAnswers{"QuickAnswers", const base::Feature kQuickAnswers{"QuickAnswers",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
...@@ -835,6 +838,10 @@ bool IsPinAutosubmitBackfillFeatureEnabled() { ...@@ -835,6 +838,10 @@ bool IsPinAutosubmitBackfillFeatureEnabled() {
return base::FeatureList::IsEnabled(kQuickUnlockPinAutosubmitBackfill); return base::FeatureList::IsEnabled(kQuickUnlockPinAutosubmitBackfill);
} }
bool IsProjectorEnabled() {
return base::FeatureList::IsEnabled(kProjector);
}
bool IsQuickAnswersDogfood() { bool IsQuickAnswersDogfood() {
return base::FeatureList::IsEnabled(kQuickAnswersDogfood); return base::FeatureList::IsEnabled(kQuickAnswersDogfood);
} }
......
...@@ -240,6 +240,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) ...@@ -240,6 +240,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kPrinterStatus; extern const base::Feature kPrinterStatus;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kPrinterStatusDialog; extern const base::Feature kPrinterStatusDialog;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kProjector;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kQuickAnswers; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kQuickAnswers;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kQuickAnswersDogfood; extern const base::Feature kQuickAnswersDogfood;
...@@ -362,6 +363,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsPinSetupForFamilyLinkEnabled(); ...@@ -362,6 +363,7 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsPinSetupForFamilyLinkEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
bool IsPinAutosubmitBackfillFeatureEnabled(); bool IsPinAutosubmitBackfillFeatureEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsProjectorEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersDogfood(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersDogfood();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersEnabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersEnabled();
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersRichUiEnabled(); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) bool IsQuickAnswersRichUiEnabled();
......
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