Skip to content

Commit

Permalink
Show ChromeVox caption panel when spoken feedback is enabled.
Browse files Browse the repository at this point in the history
This adds a widget to the top of the screen that shows the ChromeVox
caption panel whenever spoken feedback is enabled. It automatically
waits for the web content to load before showing it to avoid flicker.

Depends on: https://codereview.chromium.org/1277183003/

BUG=420795

Review URL: https://codereview.chromium.org/1274563004

Cr-Commit-Position: refs/heads/master@{#359499}
  • Loading branch information
minorninth authored and Commit bot committed Nov 13, 2015
1 parent 1f28f36 commit 94a4f88
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 0 deletions.
12 changes: 12 additions & 0 deletions ash/shelf/shelf_layout_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ ShelfLayoutManager::ShelfLayoutManager(ShelfWidget* shelf)
gesture_drag_amount_(0.f),
gesture_drag_auto_hide_state_(SHELF_AUTO_HIDE_SHOWN),
update_shelf_observer_(NULL),
chromevox_panel_height_(0),
duration_override_in_ms_(0) {
Shell::GetInstance()->AddShellObserver(this);
Shell::GetInstance()->lock_state_controller()->AddObserver(this);
Expand Down Expand Up @@ -541,6 +542,11 @@ bool ShelfLayoutManager::IsHorizontalAlignment() const {
GetAlignment() == SHELF_ALIGNMENT_TOP;
}

void ShelfLayoutManager::SetChromeVoxPanelHeight(int height) {
chromevox_panel_height_ = height;
LayoutShelf();
}

// static
ShelfLayoutManager* ShelfLayoutManager::ForShelf(aura::Window* window) {
ShelfWidget* shelf = RootWindowController::ForShelf(window)->shelf();
Expand Down Expand Up @@ -834,6 +840,12 @@ void ShelfLayoutManager::CalculateTargetBounds(
target_bounds->work_area_insets += dock_insets;
}

// Also push in the work area insets for the ChromeVox panel if it's visible.
if (chromevox_panel_height_) {
gfx::Insets chromevox_insets(chromevox_panel_height_, 0, 0, 0);
target_bounds->work_area_insets += chromevox_insets;
}

target_bounds->opacity =
(gesture_drag_status_ == GESTURE_DRAG_IN_PROGRESS ||
state.visibility_state == SHELF_VISIBLE ||
Expand Down
8 changes: 8 additions & 0 deletions ash/shelf/shelf_layout_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ class ASH_EXPORT ShelfLayoutManager
// Is the shelf's alignment horizontal?
bool IsHorizontalAlignment() const;

// Set the height of the ChromeVox panel, which takes away space from the
// available work area from the top of the screen.
void SetChromeVoxPanelHeight(int height);

// Returns a ShelfLayoutManager on the display which has a shelf for
// given |window|. See RootWindowController::ForShelf for more info.
static ShelfLayoutManager* ForShelf(aura::Window* window);
Expand Down Expand Up @@ -412,6 +416,10 @@ class ASH_EXPORT ShelfLayoutManager
// keyboard.
gfx::Rect user_work_area_bounds_;

// The height of the ChromeVox panel at the top of the screen, which
// needs to be removed from the available work area.
int chromevox_panel_height_;

// The show hide animation duration override or 0 for default.
int duration_override_in_ms_;

Expand Down
57 changes: 57 additions & 0 deletions chrome/browser/chromeos/accessibility/accessibility_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "ash/high_contrast/high_contrast_controller.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/session/session_state_delegate.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/sticky_keys/sticky_keys_controller.h"
#include "ash/system/tray/system_tray_notifier.h"
Expand Down Expand Up @@ -270,6 +271,33 @@ void UnloadChromeVoxExtension(Profile* profile) {

} // namespace

class ChromeVoxPanelWidgetObserver : public views::WidgetObserver {
public:
ChromeVoxPanelWidgetObserver(views::Widget* widget,
AccessibilityManager* manager)
: widget_(widget), manager_(manager) {
widget_->AddObserver(this);
}

void OnWidgetClosing(views::Widget* widget) override {
CHECK_EQ(widget_, widget);
widget->RemoveObserver(this);
manager_->OnChromeVoxPanelClosing();
}

void OnWidgetDestroying(views::Widget* widget) override {
CHECK_EQ(widget_, widget);
widget->RemoveObserver(this);
manager_->OnChromeVoxPanelDestroying();
}

private:
views::Widget* widget_;
AccessibilityManager* manager_;

DISALLOW_COPY_AND_ASSIGN(ChromeVoxPanelWidgetObserver);
};

///////////////////////////////////////////////////////////////////////////////
// AccessibilityStatusEventDetails

Expand Down Expand Up @@ -377,6 +405,7 @@ AccessibilityManager::AccessibilityManager()
braille_display_connected_(false),
scoped_braille_observer_(this),
braille_ime_current_(false),
chromevox_panel_(nullptr),
weak_ptr_factory_(this) {
notification_registrar_.Add(this,
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
Expand Down Expand Up @@ -419,6 +448,11 @@ AccessibilityManager::~AccessibilityManager() {
ui::A11Y_NOTIFICATION_NONE);
NotifyAccessibilityStatusChanged(details);
input_method::InputMethodManager::Get()->RemoveObserver(this);

if (chromevox_panel_) {
chromevox_panel_->Close();
chromevox_panel_ = nullptr;
}
}

bool AccessibilityManager::ShouldShowAccessibilityMenu() {
Expand Down Expand Up @@ -636,6 +670,11 @@ void AccessibilityManager::LoadChromeVoxToLockScreen(
}

void AccessibilityManager::UnloadChromeVox() {
if (chromevox_panel_) {
chromevox_panel_->Close();
chromevox_panel_ = nullptr;
}

if (chrome_vox_loaded_on_lock_screen_)
UnloadChromeVoxFromLockScreen();

Expand Down Expand Up @@ -1138,6 +1177,12 @@ void AccessibilityManager::PostLoadChromeVox(Profile* profile) {

should_speak_chrome_vox_announcements_on_user_screen_ =
chrome_vox_loaded_on_lock_screen_;

if (!chromevox_panel_) {
chromevox_panel_ = new ChromeVoxPanel(profile_);
chromevox_panel_widget_observer_.reset(
new ChromeVoxPanelWidgetObserver(chromevox_panel_->GetWidget(), this));
}
}

void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
Expand All @@ -1148,4 +1193,16 @@ void AccessibilityManager::PostUnloadChromeVox(Profile* profile) {
std::vector<gfx::Rect>());
}

void AccessibilityManager::OnChromeVoxPanelClosing() {
aura::Window* root_window = chromevox_panel_->GetRootWindow();
chromevox_panel_widget_observer_.reset(nullptr);
chromevox_panel_ = nullptr;
ash::ShelfLayoutManager::ForShelf(root_window)->SetChromeVoxPanelHeight(0);
}

void AccessibilityManager::OnChromeVoxPanelDestroying() {
chromevox_panel_widget_observer_.reset(nullptr);
chromevox_panel_ = nullptr;
}

} // namespace chromeos
11 changes: 11 additions & 0 deletions chrome/browser/chromeos/accessibility/accessibility_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "base/scoped_observer.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/accessibility/accessibility_util.h"
#include "chrome/browser/chromeos/accessibility/chromevox_panel.h"
#include "chrome/browser/extensions/api/braille_display_private/braille_controller.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
Expand All @@ -27,6 +28,7 @@
namespace content {
class RenderViewHost;
}

class Profile;

namespace chromeos {
Expand Down Expand Up @@ -68,6 +70,8 @@ typedef base::CallbackList<void(const AccessibilityStatusEventDetails&)>
typedef AccessibilityStatusCallbackList::Subscription
AccessibilityStatusSubscription;

class ChromeVoxPanelWidgetObserver;

// AccessibilityManager changes the statuses of accessibility features
// watching profile notifications and pref-changes.
// TODO(yoshiki): merge MagnificationManager with AccessibilityManager.
Expand Down Expand Up @@ -200,6 +204,10 @@ class AccessibilityManager
// chromeos/audio/chromeos_sounds.h.
void PlayEarcon(int sound_key);

// Called by our widget observer when the ChromeVoxPanel is closing.
void OnChromeVoxPanelClosing();
void OnChromeVoxPanelDestroying();

// Profile having the a11y context.
Profile* profile() { return profile_; }

Expand Down Expand Up @@ -293,6 +301,9 @@ class AccessibilityManager

bool braille_ime_current_;

ChromeVoxPanel* chromevox_panel_;
scoped_ptr<ChromeVoxPanelWidgetObserver> chromevox_panel_widget_observer_;

base::WeakPtrFactory<AccessibilityManager> weak_ptr_factory_;

DISALLOW_COPY_AND_ASSIGN(AccessibilityManager);
Expand Down
146 changes: 146 additions & 0 deletions chrome/browser/chromeos/accessibility/chromevox_panel.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Copyright 2015 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/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/accessibility/chromevox_panel.h"
#include "chrome/common/extensions/extension_constants.h"
#include "content/public/browser/web_contents.h"
#include "ui/chromeos/accessibility_types.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/shadow_types.h"
#include "ui/wm/core/window_animations.h"

namespace {

const int kPanelHeight = 35;
const char kChromeVoxPanelRelativeUrl[] = "/cvox2/background/panel.html";
const char kFullscreenURLFragment[] = "fullscreen";
const char kDisableSpokenFeedbackURLFragment[] = "close";

} // namespace

class ChromeVoxPanelWebContentsObserver : public content::WebContentsObserver {
public:
ChromeVoxPanelWebContentsObserver(content::WebContents* web_contents,
ChromeVoxPanel* panel)
: content::WebContentsObserver(web_contents), panel_(panel) {}
~ChromeVoxPanelWebContentsObserver() override {}

// content::WebContentsObserver overrides.
void DidFirstVisuallyNonEmptyPaint() override {
panel_->DidFirstVisuallyNonEmptyPaint();
}

void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override {
// The ChromeVox panel uses the URL fragment to communicate state
// to this panel host.
std::string fragment = web_contents()->GetLastCommittedURL().ref();
if (fragment == kDisableSpokenFeedbackURLFragment)
panel_->DisableSpokenFeedback();
else if (fragment == kFullscreenURLFragment)
panel_->EnterFullscreen();
else
panel_->ExitFullscreen();
}

private:
ChromeVoxPanel* panel_;

DISALLOW_COPY_AND_ASSIGN(ChromeVoxPanelWebContentsObserver);
};

ChromeVoxPanel::ChromeVoxPanel(content::BrowserContext* browser_context)
: widget_(nullptr), web_view_(nullptr), fullscreen_(false) {
std::string url("chrome-extension://");
url += extension_misc::kChromeVoxExtensionId;
url += kChromeVoxPanelRelativeUrl;

views::WebView* web_view = new views::WebView(browser_context);
web_contents_observer_.reset(
new ChromeVoxPanelWebContentsObserver(web_view->GetWebContents(), this));
web_view->LoadInitialURL(GURL(url));
web_view_ = web_view;

widget_ = new views::Widget();
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
aura::Window* root_window = ash::Shell::GetPrimaryRootWindow();
params.parent = ash::Shell::GetContainer(
root_window, ash::kShellWindowId_SettingBubbleContainer);
params.delegate = this;
params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
params.bounds = gfx::Rect(0, 0, root_window->bounds().width(),
root_window->bounds().height());
widget_->Init(params);
SetShadowType(widget_->GetNativeWindow(), wm::SHADOW_TYPE_RECTANGULAR);

ash::Shell::GetScreen()->AddObserver(this);
}

ChromeVoxPanel::~ChromeVoxPanel() {
ash::Shell::GetScreen()->RemoveObserver(this);
}

aura::Window* ChromeVoxPanel::GetRootWindow() {
return GetWidget()->GetNativeWindow()->GetRootWindow();
}

void ChromeVoxPanel::Close() {
widget_->Close();
}

void ChromeVoxPanel::DidFirstVisuallyNonEmptyPaint() {
widget_->Show();
ash::ShelfLayoutManager::ForShelf(GetRootWindow())
->SetChromeVoxPanelHeight(kPanelHeight);
}

void ChromeVoxPanel::EnterFullscreen() {
fullscreen_ = true;
UpdateWidgetBounds();
}

void ChromeVoxPanel::ExitFullscreen() {
fullscreen_ = false;
UpdateWidgetBounds();
}

void ChromeVoxPanel::DisableSpokenFeedback() {
chromeos::AccessibilityManager::Get()->EnableSpokenFeedback(
false, ui::A11Y_NOTIFICATION_NONE);
}

const views::Widget* ChromeVoxPanel::GetWidget() const {
return widget_;
}

views::Widget* ChromeVoxPanel::GetWidget() {
return widget_;
}

void ChromeVoxPanel::DeleteDelegate() {
delete this;
}

views::View* ChromeVoxPanel::GetContentsView() {
return web_view_;
}

void ChromeVoxPanel::OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t changed_metrics) {
UpdateWidgetBounds();
}

void ChromeVoxPanel::UpdateWidgetBounds() {
gfx::Rect bounds(GetRootWindow()->bounds().size());
if (!fullscreen_)
bounds.set_height(kPanelHeight);
widget_->SetBounds(bounds);
}
Loading

0 comments on commit 94a4f88

Please sign in to comment.