Commit baa04150 authored by Kurt Horimoto's avatar Kurt Horimoto Committed by Commit Bot

[iOS] Disable voice search keyboard accessory view for VoiceOver

crrev.com/c/2005903 was an incomplete solution because the util
functions generating the keyboard accessory views was only called once
per OmniboxCoordinator's lifetime.  This CL updates the coordinator
to observe the voice search availability and reconfigure the keyboard
accessory views when availability changes.

Bug: 998524
Change-Id: Ica88b4690fac0ada1be6cc77bb6ca911f5a245e6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2075045Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Commit-Queue: Kurt Horimoto <kkhorimoto@chromium.org>
Auto-Submit: Kurt Horimoto <kkhorimoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#746855}
parent 9b5f0283
......@@ -17,6 +17,10 @@ source_set("keyboard_assist") {
"toolbar_keyboard_accessory_view.mm",
"toolbar_ui_bar_button_item.h",
"toolbar_ui_bar_button_item.mm",
"voice_search_keyboard_accessory_button.h",
"voice_search_keyboard_accessory_button.mm",
"voice_search_keyboard_bar_button_item.h",
"voice_search_keyboard_bar_button_item.mm",
]
deps = [
"resources:keyboard_accessory_qr_scanner",
......@@ -42,3 +46,20 @@ source_set("keyboard_assist") {
[ "//ios/chrome/browser/ui/omnibox:omnibox_internal" ]
libs = [ "UIKit.framework" ]
}
source_set("unit_tests") {
testonly = true
sources = [
"voice_search_keyboard_accessory_button_unittest.mm",
"voice_search_keyboard_bar_button_item_unittest.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
deps = [
":keyboard_assist",
"//ios/chrome/browser/voice",
"//ios/chrome/browser/voice:test_support",
"//testing/gtest",
]
}
......@@ -6,7 +6,9 @@
#include "base/logging.h"
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_delegate.h"
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/voice/voice_search_availability.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -21,12 +23,11 @@ NSString* const kVoiceSearchInputAccessoryViewID =
namespace {
UIButton* ButtonWithIcon(NSString* iconName) {
void SetUpButtonWithIcon(UIButton* button, NSString* iconName) {
const CGFloat kButtonShadowOpacity = 0.35;
const CGFloat kButtonShadowRadius = 1.0;
const CGFloat kButtonShadowVerticalOffset = 1.0;
UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
UIImage* icon = [UIImage imageNamed:iconName];
[button setImage:icon forState:UIControlStateNormal];
......@@ -34,15 +35,18 @@ UIButton* ButtonWithIcon(NSString* iconName) {
button.layer.shadowOffset = CGSizeMake(0, kButtonShadowVerticalOffset);
button.layer.shadowOpacity = kButtonShadowOpacity;
button.layer.shadowRadius = kButtonShadowRadius;
return button;
}
} // namespace
NSArray<UIButton*>* ToolbarAssistiveKeyboardLeadingButtons(
id<ToolbarAssistiveKeyboardDelegate> delegate) {
UIButton* voiceSearchButton =
ButtonWithIcon(@"keyboard_accessory_voice_search");
NSMutableArray<UIButton*>* buttons = [NSMutableArray<UIButton*> array];
UIButton* voiceSearchButton = [[VoiceSearchKeyboardAccessoryButton alloc]
initWithVoiceSearchAvailability:std::make_unique<
VoiceSearchAvailability>()];
SetUpButtonWithIcon(voiceSearchButton, @"keyboard_accessory_voice_search");
NSString* accessibilityLabel =
l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
voiceSearchButton.accessibilityLabel = accessibilityLabel;
......@@ -51,15 +55,17 @@ NSArray<UIButton*>* ToolbarAssistiveKeyboardLeadingButtons(
addTarget:delegate
action:@selector(keyboardAccessoryVoiceSearchTouchUpInside:)
forControlEvents:UIControlEventTouchUpInside];
[buttons addObject:voiceSearchButton];
UIButton* cameraButton = ButtonWithIcon(@"keyboard_accessory_qr_scanner");
UIButton* cameraButton = [UIButton buttonWithType:UIButtonTypeCustom];
SetUpButtonWithIcon(cameraButton, @"keyboard_accessory_qr_scanner");
[cameraButton addTarget:delegate
action:@selector(keyboardAccessoryCameraSearchTouchUp)
forControlEvents:UIControlEventTouchUpInside];
SetA11yLabelAndUiAutomationName(
cameraButton, IDS_IOS_KEYBOARD_ACCESSORY_VIEW_QR_CODE_SEARCH,
@"QR code Search");
[buttons addObject:cameraButton];
NSArray<UIButton*>* buttons = @[ voiceSearchButton, cameraButton ];
return buttons;
}
......@@ -8,6 +8,7 @@
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views.h"
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_assistive_keyboard_views_utils.h"
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/toolbar_ui_bar_button_item.h"
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/voice/voice_search_availability.h"
#include "ios/chrome/grit/ios_strings.h"
......@@ -18,25 +19,27 @@
#error "This file requires ARC support."
#endif
#pragma mark - Util Functions
NSArray<UIBarButtonItemGroup*>* ToolbarAssistiveKeyboardLeadingBarButtonGroups(
id<ToolbarAssistiveKeyboardDelegate> delegate) {
NSMutableArray<UIBarButtonItem*>* items = [NSMutableArray array];
VoiceSearchAvailability voice_search_availability;
if (voice_search_availability.IsVoiceSearchAvailable()) {
UIImage* voiceSearchIcon =
[[UIImage imageNamed:@"keyboard_accessory_voice_search"]
imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIBarButtonItem* voiceSearchItem = [[UIBarButtonItem alloc]
initWithImage:voiceSearchIcon
style:UIBarButtonItemStylePlain
target:delegate
action:@selector(keyboardAccessoryVoiceSearchTouchUpInside:)];
NSString* accessibilityLabel =
l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
voiceSearchItem.accessibilityLabel = accessibilityLabel;
voiceSearchItem.accessibilityIdentifier = kVoiceSearchInputAccessoryViewID;
[items addObject:voiceSearchItem];
}
UIImage* voiceSearchIcon =
[[UIImage imageNamed:@"keyboard_accessory_voice_search"]
imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIBarButtonItem* voiceSearchItem = [[VoiceSearchKeyboardBarButtonItem alloc]
initWithImage:voiceSearchIcon
style:UIBarButtonItemStylePlain
target:delegate
action:@selector
(keyboardAccessoryVoiceSearchTouchUpInside:)
voiceSearchAvailability:std::make_unique<VoiceSearchAvailability>()];
NSString* accessibilityLabel =
l10n_util::GetNSString(IDS_IOS_KEYBOARD_ACCESSORY_VIEW_VOICE_SEARCH);
voiceSearchItem.accessibilityLabel = accessibilityLabel;
voiceSearchItem.accessibilityIdentifier = kVoiceSearchInputAccessoryViewID;
[items addObject:voiceSearchItem];
UIImage* cameraIcon = [[UIImage imageNamed:@"keyboard_accessory_qr_scanner"]
imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
......@@ -50,9 +53,10 @@ NSArray<UIBarButtonItemGroup*>* ToolbarAssistiveKeyboardLeadingBarButtonGroups(
@"QR code Search");
[items addObject:cameraItem];
UIBarButtonItemGroup* group =
[[UIBarButtonItemGroup alloc] initWithBarButtonItems:items
representativeItem:nil];
UIBarButtonItemGroup* group = [[UIBarButtonItemGroup alloc]
initWithBarButtonItems:@[ voiceSearchItem, cameraItem ]
representativeItem:nil];
return @[ group ];
}
......
// Copyright 2020 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 IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
#define IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
#import <UIKit/UIKit.h>
#include <memory>
class VoiceSearchAvailability;
// A custom button that disables itself when voice search becomes unavailable.
@interface VoiceSearchKeyboardAccessoryButton : UIButton
- (instancetype)initWithVoiceSearchAvailability:
(std::unique_ptr<VoiceSearchAvailability>)availability
NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_ACCESSORY_BUTTON_H_
// Copyright 2020 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.
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
#include "base/logging.h"
#import "ios/chrome/browser/voice/voice_search_availability.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface VoiceSearchKeyboardAccessoryButton () <
VoiceSearchAvailabilityObserver> {
std::unique_ptr<VoiceSearchAvailability> _availability;
}
@end
@implementation VoiceSearchKeyboardAccessoryButton
- (instancetype)initWithVoiceSearchAvailability:
(std::unique_ptr<VoiceSearchAvailability>)availability {
if (self = [super initWithFrame:CGRectZero]) {
_availability = std::move(availability);
DCHECK(_availability);
_availability->AddObserver(self);
}
return self;
}
- (void)dealloc {
_availability->RemoveObserver(self);
}
#pragma mark - UIView
- (void)willMoveToSuperview:(UIView*)newSuperview {
[self updateEnabledState];
}
#pragma mark - VoiceSearchAvailabilityObserver
- (void)voiceSearchAvailability:(VoiceSearchAvailability*)availability
updatedAvailability:(BOOL)available {
[self updateEnabledState];
}
#pragma mark - Private
// Updates the button's enabled state according to its voice search
// availability.
- (void)updateEnabledState {
self.enabled = _availability->IsVoiceSearchAvailable();
}
@end
// Copyright 2020 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.
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_accessory_button.h"
#import "ios/chrome/browser/voice/fake_voice_search_availability.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Test fixture for VoiceSearchKeyboardAccessoryButton.
class VoiceSearchKeyboardAccessoryButtonTest : public PlatformTest {
public:
VoiceSearchKeyboardAccessoryButtonTest()
: superview_([[UIView alloc] initWithFrame:CGRectZero]) {
std::unique_ptr<FakeVoiceSearchAvailability> availability =
std::make_unique<FakeVoiceSearchAvailability>();
availability_ = availability.get();
availability_->SetVoiceOverEnabled(false);
availability_->SetVoiceProviderEnabled(true);
button_ = [[VoiceSearchKeyboardAccessoryButton alloc]
initWithVoiceSearchAvailability:std::move(availability)];
[superview_ addSubview:button_];
}
protected:
FakeVoiceSearchAvailability* availability_ = nullptr;
UIView* superview_ = nil;
VoiceSearchKeyboardAccessoryButton* button_ = nil;
};
// Tests that the button is disabled when VoiceOver is enabled.
TEST_F(VoiceSearchKeyboardAccessoryButtonTest, DisableForVoiceOver) {
ASSERT_TRUE(button_.enabled);
availability_->SetVoiceOverEnabled(true);
EXPECT_FALSE(button_.enabled);
availability_->SetVoiceOverEnabled(false);
EXPECT_TRUE(button_.enabled);
}
// Copyright 2020 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 IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
#define IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
#import <UIKit/UIKit.h>
#include <memory>
class VoiceSearchAvailability;
// A custom bar button item that disables itself when voice search is
// unavailable.
@interface VoiceSearchKeyboardBarButtonItem : UIBarButtonItem
// Initializer for an item that disables itself when |availability| returns
// false for IsVoiceSearchAvailable().
- (instancetype)initWithImage:(UIImage*)image
style:(UIBarButtonItemStyle)style
target:(id)target
action:(SEL)action
voiceSearchAvailability:
(std::unique_ptr<VoiceSearchAvailability>)availability
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder*)coder NS_UNAVAILABLE;
@end
#endif // IOS_CHROME_BROWSER_UI_TOOLBAR_KEYBOARD_ASSIST_VOICE_SEARCH_KEYBOARD_BAR_BUTTON_ITEM_H_
// Copyright 2020 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.
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
#import "ios/chrome/browser/voice/voice_search_availability.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface VoiceSearchKeyboardBarButtonItem () <
VoiceSearchAvailabilityObserver> {
std::unique_ptr<VoiceSearchAvailability> _availability;
}
@end
@implementation VoiceSearchKeyboardBarButtonItem
- (instancetype)initWithImage:(UIImage*)image
style:(UIBarButtonItemStyle)style
target:(id)target
action:(SEL)action
voiceSearchAvailability:
(std::unique_ptr<VoiceSearchAvailability>)availability {
if (self = [super init]) {
self.image = image;
self.style = style;
self.target = target;
self.action = action;
_availability = std::move(availability);
_availability->AddObserver(self);
[self updateEnabledState];
}
return self;
}
- (void)dealloc {
_availability->RemoveObserver(self);
}
#pragma mark - VoiceSearchAvailabilityObserver
- (void)voiceSearchAvailability:(VoiceSearchAvailability*)availability
updatedAvailability:(BOOL)available {
[self updateEnabledState];
}
#pragma mark - Private
// Updates the item's enabled state according to its voice search availability.
- (void)updateEnabledState {
self.enabled = _availability->IsVoiceSearchAvailable();
}
@end
// Copyright 2020 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.
#import "ios/chrome/browser/ui/toolbar/keyboard_assist/voice_search_keyboard_bar_button_item.h"
#import "ios/chrome/browser/voice/fake_voice_search_availability.h"
#include "testing/platform_test.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Test fixture for VoiceSearchKeyboardBarButtonItem.
class VoiceSearchKeyboardBarButtonItemTest : public PlatformTest {
public:
VoiceSearchKeyboardBarButtonItemTest() {
std::unique_ptr<FakeVoiceSearchAvailability> availability =
std::make_unique<FakeVoiceSearchAvailability>();
availability_ = availability.get();
availability_->SetVoiceOverEnabled(false);
availability_->SetVoiceProviderEnabled(true);
item_ = [[VoiceSearchKeyboardBarButtonItem alloc]
initWithImage:nil
style:UIBarButtonItemStylePlain
target:nil
action:nil
voiceSearchAvailability:std::move(availability)];
}
protected:
FakeVoiceSearchAvailability* availability_ = nullptr;
VoiceSearchKeyboardBarButtonItem* item_ = nil;
};
// Tests that the item is disabled when VoiceOver is enabled.
TEST_F(VoiceSearchKeyboardBarButtonItemTest, DisableForVoiceOver) {
ASSERT_TRUE(item_.enabled);
availability_->SetVoiceOverEnabled(true);
EXPECT_FALSE(item_.enabled);
availability_->SetVoiceOverEnabled(false);
EXPECT_TRUE(item_.enabled);
}
......@@ -295,6 +295,7 @@ test("ios_chrome_unittests") {
"//ios/chrome/browser/ui/tabs:unit_tests",
"//ios/chrome/browser/ui/toolbar:unit_tests",
"//ios/chrome/browser/ui/toolbar/fullscreen:unit_tests",
"//ios/chrome/browser/ui/toolbar/keyboard_assist:unit_tests",
"//ios/chrome/browser/ui/toolbar_container:unit_tests",
"//ios/chrome/browser/ui/translate:unit_tests",
"//ios/chrome/browser/ui/util:unit_tests",
......
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