Skip to content

Commit

Permalink
[iOS] Disable voice search keyboard accessory view for VoiceOver
Browse files Browse the repository at this point in the history
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/+/2075045
Reviewed-by: Gauthier 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}
  • Loading branch information
Kurt Horimoto authored and Commit Bot committed Mar 4, 2020
1 parent 9b5f028 commit baa0415
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 26 deletions.
21 changes: 21 additions & 0 deletions ios/chrome/browser/ui/toolbar/keyboard_assist/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -21,28 +23,30 @@

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];
button.layer.shadowColor = [UIColor blackColor].CGColor;
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;
Expand All @@ -51,15 +55,17 @@
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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];
Expand All @@ -50,9 +53,10 @@
@"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 ];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// 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_
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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_
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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
Loading

0 comments on commit baa0415

Please sign in to comment.