Commit 39c5924b authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[CRD iOS] Basic support for physical keyboard

This CL has these changes:
* Added an PhysicalKeyboardDetector for detecting physical keyboard.
* When the client is connected to the host and the physical keyboard is
  presented, we hide the Show/Hide Keyboard button and add immediately
  add the ClientKeyboard view to the HostView so that it can capture
  key input.

Bug: 749964
Change-Id: I2af7932d9fab2302e7d5cf8b7b59732087acf827
Reviewed-on: https://chromium-review.googlesource.com/590829Reviewed-by: default avatarScott Nichols <nicholss@chromium.org>
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Cr-Commit-Position: refs/heads/master@{#490603}
parent 1324644a
......@@ -49,6 +49,8 @@ source_set("common_source_set") {
"host_view_controller.h",
"host_view_controller.mm",
"main.mm",
"physical_keyboard_detector.h",
"physical_keyboard_detector.mm",
"pin_entry_view.h",
"pin_entry_view.mm",
"remoting_theme.h",
......
......@@ -11,6 +11,7 @@
#include <memory>
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "remoting/ios/app/physical_keyboard_detector.h"
#import "remoting/ios/app/remoting_theme.h"
#import "remoting/ios/app/settings/remoting_settings_view_controller.h"
#import "remoting/ios/client_gestures.h"
......@@ -92,6 +93,11 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
[self.view addSubview:_floatingButton];
[self applyInputMode];
_clientKeyboard = [[ClientKeyboard alloc] init];
_clientKeyboard.delegate = self;
[self.view addSubview:_clientKeyboard];
// TODO(nicholss): need to pass some keyboard injection interface here.
}
- (void)viewDidUnload {
......@@ -118,6 +124,19 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
// Call onSurfaceChanged here to cover that case.
[_client surfaceChanged:self.view.frame];
[self resizeHostToFitIfNeeded];
[PhysicalKeyboardDetector detectOnView:self.view
callback:^(BOOL hasPhysicalKeyboard) {
if (hasPhysicalKeyboard) {
_clientKeyboard.hasPhysicalKeyboard =
hasPhysicalKeyboard;
// Directly make the client keyboard first
// responder so that it can immediately
// handle key inputs.
[self showKeyboard];
}
}];
}
- (void)viewWillAppear:(BOOL)animated {
......@@ -170,19 +189,10 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
#pragma mark - Keyboard
- (BOOL)isKeyboardActive {
if (_clientKeyboard) {
return [_clientKeyboard isFirstResponder];
}
return NO;
return [_clientKeyboard isFirstResponder];
}
- (void)showKeyboard {
if (!_clientKeyboard) {
_clientKeyboard = [[ClientKeyboard alloc] init];
_clientKeyboard.delegate = self;
[self.view addSubview:_clientKeyboard];
// TODO(nicholss): need to pass some keyboard injection interface here.
}
[_clientKeyboard becomeFirstResponder];
}
......@@ -304,24 +314,27 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
if ([self isKeyboardActive]) {
void (^hideKeyboardHandler)(UIAlertAction*) = ^(UIAlertAction*) {
[self hideKeyboard];
[_actionImageView setActive:NO animated:YES];
};
[alert addAction:[UIAlertAction actionWithTitle:l10n_util::GetNSString(
IDS_HIDE_KEYBOARD)
style:UIAlertActionStyleDefault
handler:hideKeyboardHandler]];
} else {
void (^showKeyboardHandler)(UIAlertAction*) = ^(UIAlertAction*) {
[self showKeyboard];
[_actionImageView setActive:NO animated:YES];
};
[alert addAction:[UIAlertAction actionWithTitle:l10n_util::GetNSString(
IDS_SHOW_KEYBOARD)
style:UIAlertActionStyleDefault
handler:showKeyboardHandler]];
if (!_clientKeyboard.hasPhysicalKeyboard) {
// These are only needed for soft keyboard.
if ([self isKeyboardActive]) {
void (^hideKeyboardHandler)(UIAlertAction*) = ^(UIAlertAction*) {
[self hideKeyboard];
[_actionImageView setActive:NO animated:YES];
};
[alert addAction:[UIAlertAction actionWithTitle:l10n_util::GetNSString(
IDS_HIDE_KEYBOARD)
style:UIAlertActionStyleDefault
handler:hideKeyboardHandler]];
} else {
void (^showKeyboardHandler)(UIAlertAction*) = ^(UIAlertAction*) {
[self showKeyboard];
[_actionImageView setActive:NO animated:YES];
};
[alert addAction:[UIAlertAction actionWithTitle:l10n_util::GetNSString(
IDS_SHOW_KEYBOARD)
style:UIAlertActionStyleDefault
handler:showKeyboardHandler]];
}
}
remoting::GestureInterpreter::InputMode currentInputMode =
......
// 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.
#ifndef REMOTING_IOS_PHYSICAL_KEYBOARD_DETECTOR_H_
#define REMOTING_IOS_PHYSICAL_KEYBOARD_DETECTOR_H_
#import <UIKit/UIKit.h>
// A class for detecting whether an physical keyboard is presented.
@interface PhysicalKeyboardDetector : NSObject
// |callback| will be called with YES if an physical keyboard is presented.
+ (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback;
@end
#endif // REMOTING_IOS_PHYSICAL_KEYBOARD_DETECTOR_H_
// 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.
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
#import "remoting/ios/app/physical_keyboard_detector.h"
// A view to do measurement to figure out whether a physical keyboard is
// presented.
//
// The logic is hacky since iOS doesn't provide any API that tells you whether
// a physical keyboard is being used. We infer that in these steps:
//
// 1. Add a hidden text field (this view) to the view to be detected and
// register the keyboardWillShow notification.
// 2. Make the text field first responder.
// 3. keyboardWillShow will get called. The keyboard's end frame will go
// offscreen if the physical keyboard is presented.
// 4. Pass that information to the callback and remove the hidden text field.
// The view will not flicker as long as we immedately remove the text field
// in keyboardWillShow.
//
// Unfortunately there is no easy way to know immediately when the user connects
// or disconnects the keyboard.
@interface PhysicalKeyboardDetectorView : UITextField {
void (^_callback)(BOOL);
}
- (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback;
@end
@implementation PhysicalKeyboardDetectorView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.hidden = YES;
// This is to force keyboardWillShow to always be called.
self.inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback {
_callback = callback;
[view addSubview:self];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[self becomeFirstResponder];
}
#pragma mark - Private
- (void)keyboardWillShow:(NSNotification*)notification {
if (!self.isFirstResponder) {
// Don't handle the notification if it is not for the detector view.
return;
}
CGRect keyboardFrame = [(NSValue*)[notification.userInfo
objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
// iPad will still show the toolbar at the top of the soft keyboard even if
// the physical keyboard is presented, so the safer check is to see if the
// bottom of the keyboard goes under the screen.
int keyboardBottom = keyboardFrame.origin.y + keyboardFrame.size.height;
BOOL isKeyboardOffScreen =
keyboardBottom > UIScreen.mainScreen.bounds.size.height;
[self resignFirstResponder];
[self removeFromSuperview];
_callback(isKeyboardOffScreen);
}
@end
#pragma mark - PhysicalKeyboardDetector
@implementation PhysicalKeyboardDetector
+ (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback {
PhysicalKeyboardDetectorView* detectorView =
[[PhysicalKeyboardDetectorView alloc] initWithFrame:CGRectZero];
[detectorView detectOnView:view callback:callback];
}
@end
......@@ -21,6 +21,10 @@
@property(nonatomic) UITextAutocorrectionType autocorrectionType;
@property(nonatomic) UITextSpellCheckingType spellCheckingType;
// This determines whether or how the soft keyboard should be shown. The default
// value is NO.
@property(nonatomic) BOOL hasPhysicalKeyboard;
// This delegate is used to call back to handler key entry.
@property(weak, nonatomic) id<ClientKeyboardDelegate> delegate;
......
......@@ -13,6 +13,11 @@
// TODO(nicholss): Look into inputView - The custom input view to display when
// the receiver becomes the first responder
@interface ClientKeyboard () {
UIView* _inputView;
}
@end
@implementation ClientKeyboard
@synthesize autocapitalizationType = _autocapitalizationType;
......@@ -21,6 +26,8 @@
@synthesize keyboardType = _keyboardType;
@synthesize spellCheckingType = _spellCheckingType;
@synthesize hasPhysicalKeyboard = _hasPhysicalKeyboard;
@synthesize delegate = _delegate;
// TODO(nicholss): For physical keyboard, look at UIKeyCommand
......@@ -34,6 +41,8 @@
_autocorrectionType = UITextAutocorrectionTypeNo;
_keyboardType = UIKeyboardTypeDefault;
_spellCheckingType = UITextSpellCheckingTypeNo;
self.hasPhysicalKeyboard = NO;
}
return self;
}
......@@ -62,6 +71,23 @@
return nil;
}
- (UIView*)inputView {
return _inputView;
}
#pragma mark - UITextInputTraits
#pragma mark - Properties
- (void)setHasPhysicalKeyboard:(BOOL)hasPhysicalKeyboard {
_hasPhysicalKeyboard = hasPhysicalKeyboard;
// If the physical keyboard is presented, we hide the soft keyboard by
// replacing it with an empty view (nil will show the default soft keyboard).
// iPad will show a soft keyboard with only a toolbar when the physical
// keyboard is presented.
_inputView =
hasPhysicalKeyboard ? [[UIView alloc] initWithFrame:CGRectZero] : nil;
}
@end
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