blob: 83d285efeb8f7035b4ddc8d3df625bbd780799fe [file] [log] [blame]
// Copyright 2017 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/system/palette/tools/metalayer_mode.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/palette/palette_ids.h"
#include "ash/system/palette/palette_utils.h"
#include "ash/system/toast/toast_data.h"
#include "ash/system/toast/toast_manager.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/voice_interaction/voice_interaction_controller.h"
#include "base/bind.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/events/event.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/view.h"
namespace ash {
namespace {
const char kToastId[] = "palette_metalayer_mode";
const int kToastDurationMs = 2500;
// If the last stroke happened within this amount of time,
// assume writing/sketching usage.
const int kMaxStrokeGapWhenWritingMs = 1000;
} // namespace
MetalayerMode::MetalayerMode(Delegate* delegate)
: CommonPaletteTool(delegate),
weak_factory_(this) {
Shell::Get()->AddPreTargetHandler(this);
Shell::Get()->voice_interaction_controller()->AddLocalObserver(this);
Shell::Get()->highlighter_controller()->AddObserver(this);
}
MetalayerMode::~MetalayerMode() {
Shell::Get()->highlighter_controller()->RemoveObserver(this);
Shell::Get()->voice_interaction_controller()->RemoveLocalObserver(this);
Shell::Get()->RemovePreTargetHandler(this);
}
PaletteGroup MetalayerMode::GetGroup() const {
return PaletteGroup::MODE;
}
PaletteToolId MetalayerMode::GetToolId() const {
return PaletteToolId::METALAYER;
}
void MetalayerMode::OnEnable() {
CommonPaletteTool::OnEnable();
HighlighterController* controller = Shell::Get()->highlighter_controller();
controller->SetExitCallback(
base::BindOnce(&MetalayerMode::OnMetalayerSessionComplete,
weak_factory_.GetWeakPtr()),
!activated_via_button_ /* no retries if activated via button */);
controller->UpdateEnabledState(HighlighterEnabledState::kEnabled);
delegate()->HidePalette();
}
void MetalayerMode::OnDisable() {
CommonPaletteTool::OnDisable();
activated_via_button_ = false;
HighlighterController* controller = Shell::Get()->highlighter_controller();
if (controller->enabled_state() != HighlighterEnabledState::kEnabled)
return;
// Call UpdateEnabledState() only when it hasn't been disabled to ensure this
// emits the disabling signal by deselecting on palette tool.
Shell::Get()->highlighter_controller()->UpdateEnabledState(
HighlighterEnabledState::kDisabledByUser);
}
const gfx::VectorIcon& MetalayerMode::GetActiveTrayIcon() const {
return kPaletteTrayIconMetalayerIcon;
}
const gfx::VectorIcon& MetalayerMode::GetPaletteIcon() const {
return kPaletteModeMetalayerIcon;
}
views::View* MetalayerMode::CreateView() {
views::View* view = CreateDefaultView(base::string16());
UpdateView();
return view;
}
void MetalayerMode::OnTouchEvent(ui::TouchEvent* event) {
if (!feature_enabled())
return;
if (!palette_utils::IsInUserSession())
return;
// The metalayer tool is already selected, no need to do anything.
if (enabled())
return;
if (event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_PEN)
return;
if (event->type() == ui::ET_TOUCH_RELEASED) {
previous_stroke_end_ = event->time_stamp();
return;
}
if (event->type() != ui::ET_TOUCH_PRESSED)
return;
if (event->time_stamp() - previous_stroke_end_ <
base::TimeDelta::FromMilliseconds(kMaxStrokeGapWhenWritingMs)) {
// The press is happening too soon after the release, the user is most
// likely writing/sketching and does not want the metalayer to activate.
return;
}
// The stylus "barrel" button press is encoded as ui::EF_LEFT_MOUSE_BUTTON
if (!(event->flags() & ui::EF_LEFT_MOUSE_BUTTON))
return;
if (palette_utils::PaletteContainsPointInScreen(event->root_location()))
return;
if (loading()) {
// Repetitive presses will create toasts with the same id which will be
// ignored.
ToastData toast(
kToastId,
l10n_util::GetStringUTF16(IDS_ASH_STYLUS_TOOLS_METALAYER_TOAST_LOADING),
kToastDurationMs, base::Optional<base::string16>());
Shell::Get()->toast_manager()->Show(toast);
} else {
delegate()->RecordPaletteOptionsUsage(
PaletteToolIdToPaletteTrayOptions(GetToolId()),
PaletteInvocationMethod::SHORTCUT);
activated_via_button_ = true;
delegate()->EnableTool(GetToolId());
}
event->StopPropagation();
}
void MetalayerMode::OnVoiceInteractionStatusChanged(
mojom::VoiceInteractionState state) {
voice_interaction_state_ = state;
UpdateState();
}
void MetalayerMode::OnVoiceInteractionSettingsEnabled(bool enabled) {
voice_interaction_enabled_ = enabled;
UpdateState();
}
void MetalayerMode::OnVoiceInteractionContextEnabled(bool enabled) {
voice_interaction_context_enabled_ = enabled;
UpdateState();
}
void MetalayerMode::OnAssistantFeatureAllowedChanged(
mojom::AssistantAllowedState state) {
assistant_allowed_state_ = state;
UpdateState();
}
void MetalayerMode::OnHighlighterEnabledChanged(HighlighterEnabledState state) {
// OnHighlighterEnabledChanged is used by the caller that disables highlighter
// enabled state to disable the palette tool only.
if (state == HighlighterEnabledState::kEnabled)
return;
// TODO(warx): We disable palette tool of metalayer mode when receiving the
// disabled state of highlighter controller. And this class also calls
// HighlighterController's UpdateEnabledState method to send the state signal.
// We shall think of removing this circular dependency.
delegate()->DisableTool(GetToolId());
}
void MetalayerMode::UpdateState() {
if (enabled() && !selectable())
delegate()->DisableTool(GetToolId());
if (!loading())
Shell::Get()->toast_manager()->Cancel(kToastId);
UpdateView();
}
void MetalayerMode::UpdateView() {
if (!highlight_view_)
return;
const base::string16 text = l10n_util::GetStringUTF16(
loading() ? IDS_ASH_STYLUS_TOOLS_METALAYER_MODE_LOADING
: IDS_ASH_STYLUS_TOOLS_METALAYER_MODE);
highlight_view_->text_label()->SetText(text);
highlight_view_->SetAccessibleName(text);
highlight_view_->SetEnabled(selectable());
TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::DETAILED_VIEW_LABEL,
false /* use_unified_theme */);
style.set_color_style(highlight_view_->enabled()
? TrayPopupItemStyle::ColorStyle::ACTIVE
: TrayPopupItemStyle::ColorStyle::DISABLED);
style.SetupLabel(highlight_view_->text_label());
highlight_view_->left_icon()->SetImage(
CreateVectorIcon(GetPaletteIcon(), kMenuIconSize, style.GetIconColor()));
}
void MetalayerMode::OnMetalayerSessionComplete() {
Shell::Get()->highlighter_controller()->UpdateEnabledState(
HighlighterEnabledState::kDisabledBySessionComplete);
}
} // namespace ash