physical_keyboard_detector.mm 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #if !defined(__has_feature) || !__has_feature(objc_arc)
  5. #error "This file requires ARC support."
  6. #endif
  7. #import "remoting/ios/app/physical_keyboard_detector.h"
  8. // A view to do measurement to figure out whether a physical keyboard is
  9. // presented.
  10. //
  11. // The logic is hacky since iOS doesn't provide any API that tells you whether
  12. // a physical keyboard is being used. We infer that in these steps:
  13. //
  14. // 1. Add a hidden text field (this view) to the view to be detected and
  15. // register the keyboardWillShow notification.
  16. // 2. Make the text field first responder.
  17. // 3. keyboardWillShow will get called. The keyboard's end frame will go
  18. // offscreen if the physical keyboard is presented.
  19. // 4. Pass that information to the callback and remove the hidden text field.
  20. // The view will not flicker as long as we immediately remove the text field
  21. // in keyboardWillShow.
  22. //
  23. // Unfortunately there is no easy way to know immediately when the user connects
  24. // or disconnects the keyboard.
  25. @interface PhysicalKeyboardDetectorView : UITextField {
  26. void (^_callback)(BOOL);
  27. }
  28. - (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback;
  29. @end
  30. @implementation PhysicalKeyboardDetectorView
  31. - (instancetype)initWithFrame:(CGRect)frame {
  32. if (self = [super initWithFrame:frame]) {
  33. self.hidden = YES;
  34. // This is to force keyboardWillShow to always be called.
  35. self.inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
  36. }
  37. return self;
  38. }
  39. - (void)dealloc {
  40. [[NSNotificationCenter defaultCenter] removeObserver:self];
  41. }
  42. - (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback {
  43. _callback = callback;
  44. [view addSubview:self];
  45. [[NSNotificationCenter defaultCenter]
  46. addObserver:self
  47. selector:@selector(keyboardWillShow:)
  48. name:UIKeyboardWillShowNotification
  49. object:nil];
  50. [self becomeFirstResponder];
  51. }
  52. #pragma mark - Private
  53. - (void)keyboardWillShow:(NSNotification*)notification {
  54. if (!self.isFirstResponder) {
  55. // Don't handle the notification if it is not for the detector view.
  56. return;
  57. }
  58. CGRect keyboardFrame = [(NSValue*)[notification.userInfo
  59. objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
  60. // iPad will still show the toolbar at the top of the soft keyboard even if
  61. // the physical keyboard is presented, so the safer check is to see if the
  62. // bottom of the keyboard goes under the screen.
  63. int keyboardBottom = keyboardFrame.origin.y + keyboardFrame.size.height;
  64. BOOL isKeyboardOffScreen =
  65. keyboardBottom > UIScreen.mainScreen.bounds.size.height;
  66. [self resignFirstResponder];
  67. [self removeFromSuperview];
  68. _callback(isKeyboardOffScreen);
  69. }
  70. @end
  71. #pragma mark - PhysicalKeyboardDetector
  72. @implementation PhysicalKeyboardDetector
  73. + (void)detectOnView:(UIView*)view callback:(void (^)(BOOL))callback {
  74. PhysicalKeyboardDetectorView* detectorView =
  75. [[PhysicalKeyboardDetectorView alloc] initWithFrame:CGRectZero];
  76. [detectorView detectOnView:view callback:callback];
  77. }
  78. @end