react-native-external-keyboard 0.8.5 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +149 -67
  2. package/android/src/main/java/com/externalkeyboard/delegates/FocusOrderDelegate.java +81 -75
  3. package/android/src/main/java/com/externalkeyboard/delegates/FocusOrderDelegateHost.java +14 -0
  4. package/android/src/main/java/com/externalkeyboard/helper/Linking/A11yOrderLinking.java +5 -0
  5. package/android/src/main/java/com/externalkeyboard/modules/ExternalKeyboardModule.java +10 -10
  6. package/android/src/main/java/com/externalkeyboard/services/FocusLinkObserver/FocusLinkObserver.java +26 -35
  7. package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/ExternalKeyboardLockView.java +5 -0
  8. package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardLockView/ExternalKeyboardLockViewManager.java +6 -0
  9. package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardView/ExternalKeyboardView.java +8 -307
  10. package/android/src/main/java/com/externalkeyboard/views/ExternalKeyboardView/ExternalKeyboardViewManager.java +11 -18
  11. package/android/src/main/java/com/externalkeyboard/views/TextInputFocusWrapper/TextInputFocusWrapper.java +208 -101
  12. package/android/src/main/java/com/externalkeyboard/views/TextInputFocusWrapper/TextInputFocusWrapperManager.java +123 -34
  13. package/android/src/main/java/com/externalkeyboard/views/base/FocusHighlightBase.java +38 -0
  14. package/android/src/main/java/com/externalkeyboard/views/base/ViewGroupBase.java +19 -0
  15. package/android/src/main/java/com/externalkeyboard/views/base/ViewOrderGroupBase.java +190 -0
  16. package/android/src/main/java/com/externalkeyboard/views/base/keyboard/ViewFocusChangeBase.java +39 -0
  17. package/android/src/main/java/com/externalkeyboard/views/base/keyboard/ViewFocusRequestBase.java +125 -0
  18. package/android/src/main/java/com/externalkeyboard/views/base/keyboard/ViewKeyHandlerBase.java +40 -0
  19. package/android/src/newarch/TextInputFocusWrapperManagerSpec.java +2 -8
  20. package/android/src/oldarch/ExternalKeyboardLockViewManagerSpec.java +1 -0
  21. package/android/src/oldarch/TextInputFocusWrapperManagerSpec.java +32 -2
  22. package/ios/Delegates/RNCEKVFocusLinkDelegate/RNCEKVFocusLinkDelegate.h +35 -0
  23. package/ios/Delegates/RNCEKVFocusLinkDelegate/RNCEKVFocusLinkDelegate.mm +195 -0
  24. package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusOrderProtocol.h +6 -8
  25. package/ios/Delegates/RNCEKVFocusSequenceDelegate/RNCEKVFocusSequenceDelegate.h +25 -0
  26. package/ios/Delegates/RNCEKVFocusSequenceDelegate/RNCEKVFocusSequenceDelegate.mm +163 -0
  27. package/ios/Delegates/RNCEKVGroupIdentifierDelegate/RNCEKVGroupIdentifierDelegate.h +2 -6
  28. package/ios/Delegates/RNCEKVGroupIdentifierDelegate/RNCEKVGroupIdentifierDelegate.mm +6 -78
  29. package/ios/Delegates/RNCEKVHaloDelegate/RNCEKVHaloDelegate.h +3 -4
  30. package/ios/Delegates/RNCEKVHaloDelegate/RNCEKVHaloDelegate.mm +32 -101
  31. package/ios/Delegates/RNCEKVHaloDelegate/RNCEKVHaloProtocol.h +1 -1
  32. package/ios/Extensions/RCTEnhancedScrollView+RNCEKVExternalKeyboard.mm +1 -1
  33. package/ios/Extensions/RCTTextInputComponentView+RNCEKVExternalKeyboard.mm +15 -0
  34. package/ios/Extensions/RCTViewComponentView+RNCEKVExternalKeyboard.h +9 -6
  35. package/ios/Extensions/RCTViewComponentView+RNCEKVExternalKeyboard.mm +16 -29
  36. package/ios/Extensions/UIViewController+RNCEKVExternalKeyboard.h +1 -0
  37. package/ios/Extensions/UIViewController+RNCEKVExternalKeyboard.mm +8 -0
  38. package/ios/Helpers/RNCEKVNativeProps/RNCEKVNativeProps.h +123 -0
  39. package/ios/Protocols/RNCEKVCustomFocusEffectProtocol.h +15 -0
  40. package/ios/Protocols/RNCEKVCustomGroudIdProtocol.h +15 -0
  41. package/ios/Protocols/RNCEKVKeyboardFocusableProtocol.h +15 -0
  42. package/ios/Services/RNCEKVFocusLinkObserver.mm +2 -3
  43. package/ios/Services/RNCEKVKeyboardOrderManager/RNCEKVOrderRelationship/RNCEKVOrderRelationship.mm +15 -28
  44. package/ios/Services/RNCEKVOrderLinking.mm +43 -51
  45. package/ios/Views/Base/ContextMenu/RNCEKVViewContextMenuBase.h +33 -0
  46. package/ios/Views/Base/ContextMenu/RNCEKVViewContextMenuBase.mm +84 -0
  47. package/ios/Views/Base/FocusChange/RNCEKVViewFocusChangeBase.h +37 -0
  48. package/ios/Views/Base/FocusChange/RNCEKVViewFocusChangeBase.mm +89 -0
  49. package/ios/Views/Base/FocusOrderGroup/RNCEKVViewOrderGroupBase.h +49 -0
  50. package/ios/Views/Base/FocusOrderGroup/RNCEKVViewOrderGroupBase.mm +245 -0
  51. package/ios/Views/Base/FocusRequest/RNCEKVViewFocusRequestBase.h +34 -0
  52. package/ios/Views/Base/FocusRequest/RNCEKVViewFocusRequestBase.mm +112 -0
  53. package/ios/Views/Base/GroupIdentifier/RNCEKVViewGroupIdentifierBase.h +27 -0
  54. package/ios/Views/Base/GroupIdentifier/RNCEKVViewGroupIdentifierBase.mm +69 -0
  55. package/ios/Views/Base/KeyPress/RNCEKVViewKeyPress.h +30 -0
  56. package/ios/Views/Base/KeyPress/RNCEKVViewKeyPress.mm +75 -0
  57. package/ios/Views/Base/KeyboardHallo/RNCEKVExternalKeyboardHalloBase.h +33 -0
  58. package/ios/Views/Base/KeyboardHallo/RNCEKVExternalKeyboardHalloBase.mm +92 -0
  59. package/ios/Views/Base/ViewGroup/RNCEKVViewGroupBase.h +36 -0
  60. package/ios/Views/Base/ViewGroup/RNCEKVViewGroupBase.mm +63 -0
  61. package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockView.h +8 -0
  62. package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockView.mm +105 -2
  63. package/ios/Views/RNCEKVExternalKeyboardLockView/RNCEKVExternalKeyboardLockViewManager.mm +11 -0
  64. package/ios/Views/RNCEKVExternalKeyboardView/RNCEKVExternalKeyboardView.h +7 -82
  65. package/ios/Views/RNCEKVExternalKeyboardView/RNCEKVExternalKeyboardView.mm +23 -493
  66. package/ios/Views/RNCEKVExternalKeyboardView/RNCEKVExternalKeyboardViewManager.mm +5 -7
  67. package/ios/Views/RNCEKVKeyboardFocusGroupView/RNCEKVKeyboardFocusGroup.mm +20 -17
  68. package/ios/Views/RNCEKVTextInputFocusWrapper/RNCEKVTextInputFocusWrapper.h +10 -39
  69. package/ios/Views/RNCEKVTextInputFocusWrapper/RNCEKVTextInputFocusWrapper.mm +40 -73
  70. package/ios/Views/RNCEKVTextInputFocusWrapper/RNCEKVTextInputFocusWrapperManager.mm +76 -8
  71. package/lib/commonjs/components/BaseKeyboardView/BaseKeyboardView.js +35 -7
  72. package/lib/commonjs/components/BaseKeyboardView/BaseKeyboardView.js.map +1 -1
  73. package/lib/commonjs/components/KeyboardExtendedInput/KeyboardExtendedInput.js +79 -1
  74. package/lib/commonjs/components/KeyboardExtendedInput/KeyboardExtendedInput.js.map +1 -1
  75. package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.js +18 -4
  76. package/lib/commonjs/components/KeyboardFocusLock/FocusTrap/FocusTrap.js.map +1 -1
  77. package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js +17 -2
  78. package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js.map +1 -1
  79. package/lib/commonjs/components/KeyboardFocusView/KeyboardFocusView.js +2 -0
  80. package/lib/commonjs/components/KeyboardFocusView/KeyboardFocusView.js.map +1 -1
  81. package/lib/commonjs/nativeSpec/ExternalKeyboardLockViewNativeComponent.ts +1 -0
  82. package/lib/commonjs/nativeSpec/TextInputFocusWrapperNativeComponent.ts +16 -0
  83. package/lib/commonjs/utils/useFocusStyle.js +3 -9
  84. package/lib/commonjs/utils/useFocusStyle.js.map +1 -1
  85. package/lib/commonjs/utils/withKeyboardFocus.js +32 -15
  86. package/lib/commonjs/utils/withKeyboardFocus.js.map +1 -1
  87. package/lib/commonjs/utils/wrapOrderPrefix.js +17 -0
  88. package/lib/commonjs/utils/wrapOrderPrefix.js.map +1 -0
  89. package/lib/module/components/BaseKeyboardView/BaseKeyboardView.js +35 -7
  90. package/lib/module/components/BaseKeyboardView/BaseKeyboardView.js.map +1 -1
  91. package/lib/module/components/KeyboardExtendedInput/KeyboardExtendedInput.js +80 -2
  92. package/lib/module/components/KeyboardExtendedInput/KeyboardExtendedInput.js.map +1 -1
  93. package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.js +18 -4
  94. package/lib/module/components/KeyboardFocusLock/FocusTrap/FocusTrap.js.map +1 -1
  95. package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js +16 -2
  96. package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.js.map +1 -1
  97. package/lib/module/components/KeyboardFocusView/KeyboardFocusView.js +2 -0
  98. package/lib/module/components/KeyboardFocusView/KeyboardFocusView.js.map +1 -1
  99. package/lib/module/nativeSpec/ExternalKeyboardLockViewNativeComponent.ts +1 -0
  100. package/lib/module/nativeSpec/TextInputFocusWrapperNativeComponent.ts +16 -0
  101. package/lib/module/utils/useFocusStyle.js +4 -10
  102. package/lib/module/utils/useFocusStyle.js.map +1 -1
  103. package/lib/module/utils/withKeyboardFocus.js +32 -15
  104. package/lib/module/utils/withKeyboardFocus.js.map +1 -1
  105. package/lib/module/utils/wrapOrderPrefix.js +12 -0
  106. package/lib/module/utils/wrapOrderPrefix.js.map +1 -0
  107. package/lib/typescript/src/components/BaseKeyboardView/BaseKeyboardView.d.ts.map +1 -1
  108. package/lib/typescript/src/components/KeyboardExtendedInput/KeyboardExtendedInput.d.ts.map +1 -1
  109. package/lib/typescript/src/components/KeyboardExtendedInput/KeyboardExtendedInput.types.d.ts +15 -0
  110. package/lib/typescript/src/components/KeyboardExtendedInput/KeyboardExtendedInput.types.d.ts.map +1 -1
  111. package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.d.ts +1 -1
  112. package/lib/typescript/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.d.ts.map +1 -1
  113. package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.d.ts +2 -1
  114. package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.d.ts.map +1 -1
  115. package/lib/typescript/src/components/KeyboardFocusView/KeyboardFocusView.d.ts.map +1 -1
  116. package/lib/typescript/src/index.d.ts +1 -1
  117. package/lib/typescript/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.d.ts +1 -0
  118. package/lib/typescript/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.d.ts.map +1 -1
  119. package/lib/typescript/src/nativeSpec/TextInputFocusWrapperNativeComponent.d.ts +16 -1
  120. package/lib/typescript/src/nativeSpec/TextInputFocusWrapperNativeComponent.d.ts.map +1 -1
  121. package/lib/typescript/src/types/BaseKeyboardView.d.ts +2 -0
  122. package/lib/typescript/src/types/BaseKeyboardView.d.ts.map +1 -1
  123. package/lib/typescript/src/types/KeyboardFocusLock.types.d.ts +1 -0
  124. package/lib/typescript/src/types/KeyboardFocusLock.types.d.ts.map +1 -1
  125. package/lib/typescript/src/types/WithKeyboardFocus.d.ts +11 -2
  126. package/lib/typescript/src/types/WithKeyboardFocus.d.ts.map +1 -1
  127. package/lib/typescript/src/utils/useFocusStyle.d.ts +1 -0
  128. package/lib/typescript/src/utils/useFocusStyle.d.ts.map +1 -1
  129. package/lib/typescript/src/utils/withKeyboardFocus.d.ts.map +1 -1
  130. package/lib/typescript/src/utils/wrapOrderPrefix.d.ts +9 -0
  131. package/lib/typescript/src/utils/wrapOrderPrefix.d.ts.map +1 -0
  132. package/package.json +6 -2
  133. package/src/components/BaseKeyboardView/BaseKeyboardView.tsx +88 -10
  134. package/src/components/KeyboardExtendedInput/KeyboardExtendedInput.tsx +138 -2
  135. package/src/components/KeyboardExtendedInput/KeyboardExtendedInput.types.ts +15 -0
  136. package/src/components/KeyboardFocusLock/FocusTrap/FocusTrap.tsx +21 -4
  137. package/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.tsx +20 -3
  138. package/src/components/KeyboardFocusView/KeyboardFocusView.tsx +2 -0
  139. package/src/nativeSpec/ExternalKeyboardLockViewNativeComponent.ts +1 -0
  140. package/src/nativeSpec/TextInputFocusWrapperNativeComponent.ts +16 -0
  141. package/src/types/BaseKeyboardView.ts +2 -0
  142. package/src/types/KeyboardFocusLock.types.ts +1 -0
  143. package/src/types/WithKeyboardFocus.ts +19 -2
  144. package/src/utils/useFocusStyle.tsx +5 -15
  145. package/src/utils/withKeyboardFocus.tsx +44 -15
  146. package/src/utils/wrapOrderPrefix.ts +16 -0
  147. package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusGuideDelegate/RNCEKVFocusGuideDelegate.h +0 -36
  148. package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusGuideDelegate/RNCEKVFocusGuideDelegate.mm +0 -150
  149. package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusOrderDelegate.h +0 -47
  150. package/ios/Delegates/RNCEKVFocusOrderDelegate/RNCEKVFocusOrderDelegate.mm +0 -326
  151. package/ios/Services/RNCEKVKeyboardOrderManager/RNCEKVKeyboardOrderManager.h +0 -17
  152. package/ios/Services/RNCEKVKeyboardOrderManager/RNCEKVKeyboardOrderManager.mm +0 -15
  153. package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js +0 -22
  154. package/lib/commonjs/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js.map +0 -1
  155. package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js +0 -17
  156. package/lib/module/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.js.map +0 -1
  157. package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.d.ts +0 -4
  158. package/lib/typescript/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.d.ts.map +0 -1
  159. package/src/components/KeyboardFocusLock/KeyboardFocusLockBase/KeyboardFocusLockBase.android.tsx +0 -16
package/README.md CHANGED
@@ -15,50 +15,10 @@ iOS | Android
15
15
  - Key press event handling.
16
16
  - Focus management for `TextInput` and `Pressable` components.
17
17
  - Customization of the `Halo Effect` and `tintColor` for iOS.
18
+ - `defaultFocusHighlightEnabled` support for Android.
18
19
  - Keyboard focus order.
19
20
  - Focus Lock.
20
21
 
21
- ## New Release: Focus Lock
22
-
23
- | iOS | Android |
24
- | :-- | :-- |
25
- | <img src="/.github/images/rnek-focus-lock-ios.gif" height="500" /> | <img src="/.github/images/rnek-focus-lock-android.gif" height="500" /> |
26
-
27
- > A new type of focus lock functionality has been introduced, featuring two new components: `Focus.Frame` and `Focus.Trap`. These components help manage and lock focus within specific areas of the screen.
28
-
29
- <details>
30
- <summary>More Information</summary>
31
-
32
- - On iOS, `Focus.Trap` uses the native `accessibilityViewIsModal` property to keep the focus within a defined area.
33
- - On Android, where no equivalent to `accessibilityViewIsModal` exists, custom logic has been implemented as a workaround. By default, Android uses a custom Activity or Modal to limit focus. While using a Modal is considered the best practice for focus locking on Android, some scenarios—such as issues with React Native's Modal or library-specific constraints—may require alternative implementations.
34
-
35
- #### How It Works
36
-
37
- The focus lock functionality should be used as a pair:
38
-
39
- - `Focus.Frame`: This component is used at the root level of a "screen" to detect focus leaks and ensure that focus remains contained.
40
- - `Focus.Trap`: This component wraps the content area where focus should be explicitly locked.
41
-
42
- | Prop | Description |
43
- | :-- | :-- |
44
- | ViewProps | Includes all standard React Native View properties, such as style, testID, etc. |
45
-
46
- ```tsx
47
- <Focus.Frame>
48
- ...
49
- <Focus.Trap>
50
- <Text accessibilityRole="header">Locked Area</Text>
51
- <Button
52
- title="Confirm"
53
- accessibilityLabel="Confirm action"
54
- />
55
- </Focus.Trap>
56
- ...
57
- </Focus.Frame>
58
- ```
59
-
60
- </details>
61
-
62
22
  ## Installation
63
23
 
64
24
  ```sh
@@ -79,9 +39,9 @@ cd ios && pod install && cd ..
79
39
  The `withKeyboardFocus` HOC is a helper for integrating keyboard focus functionality. It significantly simplifies integration by wrapping the provided component in `KeyboardFocusView` and extending it with additional features, such as focus and blur events.
80
40
 
81
41
  ```js
82
- const KeybardedPressable = withKeyboardFocus(Pressable);
83
- const KeybardedTouchable = withKeyboardFocus(TouchableOpacity);
84
- const KeybardedButton = withKeyboardFocus(Button);
42
+ const KeyboardPressable = withKeyboardFocus(Pressable);
43
+ const KeyboardTouchable = withKeyboardFocus(TouchableOpacity);
44
+ const KeyboardButton = withKeyboardFocus(Button);
85
45
 
86
46
  ...
87
47
 
@@ -123,8 +83,14 @@ focusable?: | Indicates if the component can be focused by keyboard | `boolean |
123
83
  tintColor?: | Color used for tinting the component | `string`
124
84
  tintType?: | Tint behavior type | `'default' \| 'hover' \| 'background' \| 'none'`
125
85
  FocusHoverComponent?: | Component displayed on focus | `\| ReactElement  \| FunctionComponent  \| (() => ReactElement);`
86
+ renderContent?: | Render prop for components whose `children` is itself a render function (e.g. `Pressable`). Receives the component's own render state merged with `{ focused: boolean }`, so you can style content based on both the component state (e.g. `pressed`) and keyboard focus simultaneously. Only available when the wrapped component exposes a render-prop `children`. | `(state: ComponentRenderState & { focused: boolean }) => ReactNode`
87
+ renderFocusable?: | Render prop available on any `withKeyboardFocus`-wrapped component. Replaces `children` and receives `{ focused: boolean }`, allowing you to render different content based on keyboard focus state. Use this when the wrapped component does not expose a render-prop `children`. | `(state: { focused: boolean }) => ReactNode`
126
88
  group?: | Indicates if the component is a focusable group | `boolean`
127
89
  haloEffect?: | Enables halo effect on focus (iOS only) | `boolean`
90
+ defaultFocusHighlightEnabled?: | **Android only.** Enables Android's default focus highlight for the focused native view. | `boolean \| undefined`, default: `true`
91
+ haloCornerRadius?: | Corner radius of the halo ring (iOS only) | `number`
92
+ haloExpendX?: | Horizontal expansion of the halo ring in points (iOS only) | `number`
93
+ haloExpendY?: | Vertical expansion of the halo ring in points (iOS only) | `number`
128
94
  ref?: | Provides a reference to the component, allowing programmatic focus control | `{ focus: () => void}`
129
95
  viewRef?: | Provides a reference to the underlying view component | `RefObject<View>`
130
96
  onBubbledContextMenuPress | Handler for bubbled long-press events triggered by the context menu command (iOS only) | () => void;
@@ -140,12 +106,51 @@ orderLeft? | ID of the target for navigation to the left. | `string`
140
106
  orderRight? | ID of the target for navigation to the right. | `string`
141
107
  orderUp? | ID of the target for navigation upward. | `string`
142
108
  orderDown? | ID of the target for navigation downward. | `string`
109
+ orderGroup? | The name of the group for index-based focus ordering. | `string`
110
+ orderIndex? | The order index of the element within its group. | `number`
143
111
  lockFocus? | An array of directions to lock focus. | Array of 'left' \| 'right' \| 'up' \| 'down' \| 'forward' \| 'backward' \| 'first' \| 'last'
144
112
  ...rest | Remaining component props  | `Type of Component`
145
113
 
114
+ #### renderContent — Pressable with pressed + focused state
115
+
116
+ `Pressable` passes a `{ pressed }` state to its `children` render prop. Use `renderContent` to access both `pressed` and the keyboard `focused` state at the same time:
117
+
118
+ ```tsx
119
+ const KeyboardPressable = withKeyboardFocus(Pressable);
120
+
121
+ <KeyboardPressable
122
+ onPress={onPress}
123
+ renderContent={({ pressed, focused }) => (
124
+ <View style={[
125
+ styles.button,
126
+ pressed && styles.pressed,
127
+ focused && styles.focused,
128
+ ]}>
129
+ <Text>{pressed ? 'Pressed' : focused ? 'Focused' : 'Default'}</Text>
130
+ </View>
131
+ )}
132
+ />
133
+ ```
134
+
135
+ #### renderFocusable — TouchableOpacity and other components
136
+
137
+ `TouchableOpacity` and similar components do not expose a render-prop `children`, so `renderContent` is not available. Use `renderFocusable` instead — it receives only `{ focused }`:
138
+
139
+ ```tsx
140
+ const KeyboardTouchable = withKeyboardFocus(TouchableOpacity);
141
+
142
+ <KeyboardTouchable
143
+ onPress={onPress}
144
+ renderFocusable={({ focused }) => (
145
+ <View style={[styles.button, focused && styles.focused]}>
146
+ <Text>{focused ? 'Focused' : 'Default'}</Text>
147
+ </View>
148
+ )}
149
+ />
150
+ ```
146
151
 
147
152
  > [!NOTE]
148
- > You may discover that `long press on spacebar` does not trigger a long press event on `iOS`. This is because `iOS` use a `Full Keyboard Access` system that provides commands for interacting with the system. Rather than holding down the spacebar, you can use `Tab+M` (the default action for opening the context menu).
153
+ > You may discover that `long press on spacebar` does not trigger a long press event on `iOS`. This is because `iOS` uses the `Full Keyboard Access` system that provides commands for interacting with the system. Rather than holding down the spacebar, you can use `Tab+M` (the default action for opening the context menu).
149
154
  > You can change `Commands` in: `Full Keyboard Access` -> `Commands`
150
155
 
151
156
 
@@ -187,6 +192,10 @@ tintType?: | Tint behavior type | `'default' \| 'hover' \| 'background' \| 'none
187
192
  FocusHoverComponent?: | Component displayed on focus | `\| ReactElement  \| FunctionComponent  \| (() => ReactElement);`
188
193
  group?: | Indicates if the component is a focusable group | `boolean`
189
194
  haloEffect?: | Enables halo effect on focus (iOS only) | `boolean`
195
+ defaultFocusHighlightEnabled?: | **Android only.** Enables Android's default focus highlight for the focused native view. | `boolean \| undefined`, default: `true`
196
+ haloCornerRadius?: | Corner radius of the halo ring (iOS only) | `number`
197
+ haloExpendX?: | Horizontal expansion of the halo ring in points (iOS only) | `number`
198
+ haloExpendY?: | Vertical expansion of the halo ring in points (iOS only) | `number`
190
199
  triggerCodes?: | `onPress` and `onLongPress` trigger codes | `number[] \| undefined`, spacebar and enter by default
191
200
  enableA11yFocus?: | Can be used to move the screen reader focus within the keyboard using `ref.current.focus`. | `boolean \| undefined`
192
201
  screenAutoA11yFocus?: | Enables screen reader auto-focus functionality. | `boolean \| undefined`
@@ -199,6 +208,8 @@ orderLeft? | ID of the target for navigation to the left. | `string`
199
208
  orderRight? | ID of the target for navigation to the right. | `string`
200
209
  orderUp? | ID of the target for navigation upward. | `string`
201
210
  orderDown? | ID of the target for navigation downward. | `string`
211
+ orderGroup? | The name of the group for index-based focus ordering. | `string`
212
+ orderIndex? | The order index of the element within its group. | `number`
202
213
  lockFocus? | An array of directions to lock focus. | Array of 'left' \| 'right' \| 'up' \| 'down' \| 'forward' \| 'backward' \| 'first' \| 'last'
203
214
  ...rest | Remaining View props  | `View`
204
215
 
@@ -225,6 +236,7 @@ onFocusChange?: | Callback for focus change handling | `(isFocused: boolean) =>
225
236
  focusType?: | Focus type can be `default`, `auto`, or `press`. Based on investigation, Android and iOS typically have different default behaviors. On Android, the `TextInput` is focused by default, while on iOS, you need to press to focus. `auto` is used for automatic focusing, while keyboard focus targets the input. With `press`, you need to press the spacebar to focus an input. | `"default" \\| "press" \\| "auto"`
226
237
  blurType?: | Only for iOS. This defines the behavior for blurring input when focus moves away from the component. By default, iOS allows typing when the keyboard focus is on another component. You can use disable to blur input when focus moves away. (Further investigation is needed for Android.) | `"default"\\| "disable" \\| "auto"`
227
238
  haloEffect?: | Enables halo effect on focus (iOS only) | `boolean`
239
+ defaultFocusHighlightEnabled?: | **Android only.** Enables Android's default focus highlight for the focused native view. | `boolean \| undefined`, default: `true`
228
240
  tintColor?: | Color used for tinting the component | `string`
229
241
  style?: | Style for the inner TextInput | `StyleProp<ViewStyle>`
230
242
  focusStyle? | Style applied to the inner TextInput when focused | `FocusStyle`
@@ -263,6 +275,9 @@ onKeyDownPress | Handler for the key-down event | `(e: OnKeyPress) => void`
263
275
  onContextMenuPress?: | Handler for long press events triggered by the context menu command (iOS only) | () => void;
264
276
  onBubbledContextMenuPress | Handler for bubbled long-press events triggered by the context menu command (iOS only) | () => void;
265
277
  haloEffect | Enables halo effect on focus (iOS only) | `boolean \| undefined`
278
+ haloCornerRadius? | Corner radius of the halo ring (iOS only) | `number`
279
+ haloExpendX? | Horizontal expansion of the halo ring in points (iOS only) | `number`
280
+ haloExpendY? | Vertical expansion of the halo ring in points (iOS only) | `number`
266
281
  autoFocus | Indicates if the component should automatically gain focus | `boolean \| undefined`
267
282
  tintColor | Color used for tinting the component | `string`
268
283
  ref->focus | Command to programmatically focus the component | () => void;
@@ -328,6 +343,40 @@ import { Keyboard } from 'react-native-external-keyboard';
328
343
 
329
344
  This is needed for hiding the soft keyboard using a hardware keyboard. Additionally, the soft keyboard can be hidden from the settings or by pressing `Alt + K`.
330
345
 
346
+ ### Focus.Frame and Focus.Trap
347
+
348
+ `Focus.Frame` and `Focus.Trap` are two components that help manage and lock focus within specific areas of the screen.
349
+
350
+ - On iOS, `Focus.Trap` uses the native `accessibilityViewIsModal` property to keep the screen reader focus within a defined area. For stronger containment — such as preventing focus from reaching system elements like navigation bars or headers — pass `forceLock`. It observes focus changes and moves focus back into the trap when it escapes. Note: because focus is corrected reactively, VoiceOver may briefly jump to an element outside the trap before being returned.
351
+ - On Android, where no equivalent to `accessibilityViewIsModal` exists, custom logic has been implemented as a workaround. By default, Android uses a custom Activity or Modal to limit focus. While using a Modal is considered the best practice for focus locking on Android, some scenarios—such as issues with React Native's Modal or library-specific constraints—may require alternative implementations.
352
+
353
+ #### How It Works
354
+
355
+ The focus lock functionality should be used as a pair:
356
+
357
+ - `Focus.Frame`: This component is used at the root level of a "screen" to detect focus leaks and ensure that focus remains contained.
358
+ - `Focus.Trap`: This component wraps the content area where focus should be explicitly locked.
359
+
360
+ | Prop | Description |
361
+ | :-- | :-- |
362
+ | ViewProps | Includes all standard React Native View properties, such as style, testID, etc. |
363
+ | forceLock? | **iOS only.** Strengthens focus containment beyond `accessibilityViewIsModal` by observing focus changes and returning focus back into the trap whenever it escapes to system elements (e.g. navigation bars, headers). Note: because focus is corrected reactively, VoiceOver may briefly jump to an element outside the trap before being returned. |
364
+ | lockDisabled? | **Android only.** Disables the focus lock when `true`. |
365
+
366
+ ```tsx
367
+ <Focus.Frame>
368
+ ...
369
+ <Focus.Trap forceLock>
370
+ <Text accessibilityRole="header">Locked Area</Text>
371
+ <Button
372
+ title="Confirm"
373
+ accessibilityLabel="Confirm action"
374
+ />
375
+ </Focus.Trap>
376
+ ...
377
+ </Focus.Frame>
378
+ ```
379
+
331
380
  ## Focus order features
332
381
 
333
382
  ## Link Focus Order
@@ -361,6 +410,37 @@ This is needed for hiding the soft keyboard using a hardware keyboard. Additiona
361
410
  </View>
362
411
  ```
363
412
 
413
+ > [!IMPORTANT]
414
+ > `orderId` values are global. If the same IDs appear more than once on screen — e.g. in a list where each row renders the same component — duplicate IDs will cause incorrect focus jumps. Use `KeyboardOrderFocusGroup` or `orderPrefix` to keep IDs unique per instance.
415
+ >
416
+ > When a link prop is used without any prefix, a console warning is shown reminding you to add one.
417
+ >
418
+ > **`KeyboardOrderFocusGroup` (auto namespace).** Wraps a component tree and automatically namespaces all `orderId` values inside. Ideal for screens and containers where uniqueness is needed but the exact prefix doesn't matter.
419
+ >
420
+ > ```tsx
421
+ > // Each card gets its own isolated namespace
422
+ > {items.map((item) => (
423
+ > <KeyboardOrderFocusGroup key={item.id}>
424
+ > <Pressable orderId="title" orderForward="action">…</Pressable>
425
+ > <Pressable orderId="action" orderBackward="title">…</Pressable>
426
+ > </KeyboardOrderFocusGroup>
427
+ > ))}
428
+ > ```
429
+ >
430
+ > **Static namespace with `groupId` / `orderPrefix`.** Use an explicit string when you need a stable, predictable namespace — for example, to create intentional focus links between two sibling components that know about each other.
431
+ >
432
+ > ```tsx
433
+ > // groupId on KeyboardOrderFocusGroup
434
+ > <KeyboardOrderFocusGroup groupId="card_42">
435
+ > <Pressable orderId="title" orderForward="action">…</Pressable>
436
+ > <Pressable orderId="action" orderBackward="title">…</Pressable>
437
+ > </KeyboardOrderFocusGroup>
438
+ >
439
+ > // or orderPrefix directly on each component
440
+ > <Pressable orderPrefix="card_42" orderId="title" orderForward="action">…</Pressable>
441
+ > <Pressable orderPrefix="card_42" orderId="action" orderBackward="title">…</Pressable>
442
+ > ```
443
+
364
444
  You can find more examples here: [Focus Link Order](https://github.com/ArturKalach/react-native-external-keyboard/blob/release/0.6.0-rc/example/src/components/FocusOrderExample/FocusLinkOrder.tsx), [DPad Order](https://github.com/ArturKalach/react-native-external-keyboard/blob/release/0.6.0-rc/example/src/components/FocusOrderExample/FocusDPadOrder.tsx)
365
445
 
366
446
  | Props | Description | Type |
@@ -372,45 +452,48 @@ You can find more examples here: [Focus Link Order](https://github.com/ArturKala
372
452
  | orderRight? | ID of the target for navigation to the right. | `string` |
373
453
  | orderUp? | ID of the target for navigation upward. | `string` |
374
454
  | orderDown? | ID of the target for navigation downward. | `string` |
455
+ | orderPrefix? | Prefix prepended to this component's `orderId` and all `order*` target IDs. Use to namespace IDs in repeated components (lists, cards) or alongside a static `groupId`. | `string` |
375
456
 
376
457
  ## Indexes Focus Order
377
458
 
378
- Linking is one of the best ways to set up focus order. However, there may be cases where you need to define the order of multiple elements, such as groups. As an alternative solution, you can use Indexes.
459
+ Linking is one of the best ways to set up focus order. However, there may be cases where you need to define the order of multiple elements within a group. As an alternative, you can use index-based ordering.
460
+
461
+ ### KeyboardOrderFocusGroup
462
+
463
+ `KeyboardOrderFocusGroup` is a context provider that defines a named focus group. All children that declare `orderIndex` will be ordered within that group. You can optionally provide a `groupId`; if omitted, a unique ID is generated automatically.
379
464
 
380
465
  ```tsx
466
+ import { KeyboardOrderFocusGroup } from 'react-native-external-keyboard';
467
+
381
468
  <KeyboardOrderFocusGroup>
382
469
  <View>
383
- <Pressable
384
- onPress={onPress}
385
- orderIndex={0}
386
- >
470
+ <Pressable onPress={onPress} orderIndex={0}>
387
471
  <Text>First</Text>
388
472
  </Pressable>
389
- <Pressable
390
- onPress={onPress}
391
- orderIndex={2}
392
- >
473
+ <Pressable onPress={onPress} orderIndex={2}>
393
474
  <Text>Third</Text>
394
475
  </Pressable>
395
- <Pressable
396
- onPress={onPress}
397
- orderIndex={1}
398
- >
476
+ <Pressable onPress={onPress} orderIndex={1}>
399
477
  <Text>Second</Text>
400
478
  </Pressable>
401
479
  </View>
402
480
  </KeyboardOrderFocusGroup>
403
481
  ```
404
- Indexing requres `orderGroup` param for proper order set, you can use `KeyboardOrderFocusGroup` or provide `orderGroup` to the component.
482
+
483
+ | Props | Description | Type |
484
+ | :-- | :-- | :-- |
485
+ | groupId? | Optional explicit group name. Auto-generated when omitted. | `string` |
486
+ | children? | Child components | `ReactNode` |
487
+
488
+ Alternatively, provide `orderGroup` directly on each component to skip the wrapper:
405
489
 
406
490
  ```tsx
407
- <Pressable
408
- orderGroup="main"
409
- onPress={onPress}
410
- orderIndex={2}
411
- >
412
- <Text>Back</Text>
413
- </Pressable>
491
+ <Pressable orderGroup="main" onPress={onPress} orderIndex={0}>
492
+ <Text>First</Text>
493
+ </Pressable>
494
+ <Pressable orderGroup="main" onPress={onPress} orderIndex={1}>
495
+ <Text>Second</Text>
496
+ </Pressable>
414
497
  ```
415
498
 
416
499
  | Props | Description | Type |
@@ -418,7 +501,6 @@ Indexing requres `orderGroup` param for proper order set, you can use `Keyboard
418
501
  | orderGroup? | The name of the group containing ordered elements. | `string` |
419
502
  | orderIndex? | The order index of the element within the group. | `number` |
420
503
 
421
-
422
504
  You can find more examples here: [Focus Order via indexes](https://github.com/ArturKalach/react-native-external-keyboard/blob/release/0.6.0-rc/example/src/components/FocusOrderExample/FocusOrder.tsx)
423
505
 
424
506
  ## Focus Lock
@@ -5,10 +5,22 @@ import android.view.View;
5
5
  import com.externalkeyboard.helper.Linking.A11yOrderLinking;
6
6
  import com.externalkeyboard.services.FocusLinkObserver.FocusLinkObserver;
7
7
  import com.externalkeyboard.services.FocusLinkObserver.FocusLinkObserverSingleton;
8
- import com.externalkeyboard.views.ExternalKeyboardView.ExternalKeyboardView;
8
+
9
+ import java.lang.ref.WeakReference;
9
10
 
10
11
  public class FocusOrderDelegate {
11
- private final ExternalKeyboardView delegate;
12
+ private final FocusOrderDelegateHost delegate;
13
+
14
+ // The view that was registered during the last link() call. Stored here so
15
+ // that unlink() does not depend on getFirstChild() returning the same value
16
+ // (it may have changed by the time unlink() is called, e.g. after reactEditText
17
+ // is cleared or focusType switches between FOCUS_BY_PRESS and regular focus).
18
+ // WeakReference so a detached view is not kept alive by this delegate.
19
+ private WeakReference<View> linkedChild = null;
20
+
21
+ private View getLinkedChild() {
22
+ return linkedChild != null ? linkedChild.get() : null;
23
+ }
12
24
 
13
25
  FocusLinkObserver.LinkUpdatedCallback leftUpdated = null;
14
26
  FocusLinkObserver.LinkRemovedCallback leftRemoved = null;
@@ -19,14 +31,15 @@ public class FocusOrderDelegate {
19
31
  FocusLinkObserver.LinkUpdatedCallback downUpdated = null;
20
32
  FocusLinkObserver.LinkRemovedCallback downRemoved = null;
21
33
 
22
-
23
- public FocusOrderDelegate(ExternalKeyboardView delegate) {
34
+ public FocusOrderDelegate(FocusOrderDelegateHost delegate) {
24
35
  super();
25
36
  this.delegate = delegate;
26
37
  }
27
38
 
28
39
  public void link() {
29
40
  View child = delegate.getFirstChild();
41
+ this.linkedChild = child != null ? new WeakReference<>(child) : null;
42
+
30
43
  String orderGroup = delegate.getOrderGroup();
31
44
  Integer orderIndex = delegate.getOrderIndex();
32
45
  String orderId = delegate.getOrderId();
@@ -41,6 +54,8 @@ public class FocusOrderDelegate {
41
54
  A11yOrderLinking.getInstance().addOrderLink(child, orderId);
42
55
  }
43
56
 
57
+ // Lambdas capture the local strong reference — fine for the subscription lifetime;
58
+ // subscriptions are released in unlink().
44
59
  if (delegate.getOrderLeft() != null && child != null) {
45
60
  leftUpdated = link -> child.setNextFocusLeftId(link.getId());
46
61
  leftRemoved = () -> child.setNextFocusLeftId(View.NO_ID);
@@ -66,8 +81,44 @@ public class FocusOrderDelegate {
66
81
  }
67
82
  }
68
83
 
84
+ public void unlink() {
85
+ View child = getLinkedChild();
86
+ String orderGroup = delegate.getOrderGroup();
87
+ Integer orderIndex = delegate.getOrderIndex();
88
+ String orderId = delegate.getOrderId();
89
+
90
+ if (orderGroup != null && orderIndex != null) {
91
+ A11yOrderLinking.getInstance().removeRelationship(orderGroup, orderIndex);
92
+ }
93
+
94
+ if (orderId != null) {
95
+ FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
96
+ View storedView = A11yOrderLinking.getInstance().getOrderLink(orderId);
97
+ if (storedView == child) {
98
+ observer.emitRemove(orderId);
99
+ A11yOrderLinking.getInstance().removeOrderLink(orderId);
100
+ }
101
+ }
102
+
103
+ FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
104
+ if (delegate.getOrderLeft() != null) {
105
+ observer.unsubscribe(delegate.getOrderLeft(), leftUpdated, leftRemoved);
106
+ }
107
+ if (delegate.getOrderRight() != null) {
108
+ observer.unsubscribe(delegate.getOrderRight(), rightUpdated, rightRemoved);
109
+ }
110
+ if (delegate.getOrderUp() != null) {
111
+ observer.unsubscribe(delegate.getOrderUp(), upUpdated, upRemoved);
112
+ }
113
+ if (delegate.getOrderDown() != null) {
114
+ observer.unsubscribe(delegate.getOrderDown(), downUpdated, downRemoved);
115
+ }
116
+
117
+ this.linkedChild = null;
118
+ }
119
+
69
120
  public void refreshOrder() {
70
- View child = delegate.getFirstChild();
121
+ View child = getLinkedChild();
71
122
  String orderGroup = delegate.getOrderGroup();
72
123
  Integer orderIndex = delegate.getOrderIndex();
73
124
 
@@ -78,15 +129,13 @@ public class FocusOrderDelegate {
78
129
 
79
130
  public void refreshLeft(String prev, String next) {
80
131
  FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
132
+ View child = getLinkedChild();
81
133
  if (prev != null && leftUpdated != null && leftRemoved != null) {
82
134
  observer.unsubscribe(prev, leftUpdated, leftRemoved);
83
- View child = delegate.getFirstChild();
84
- if(child != null) {
135
+ if (child != null) {
85
136
  child.setNextFocusLeftId(View.NO_ID);
86
137
  }
87
138
  }
88
-
89
- View child = delegate.getFirstChild();
90
139
  if (next != null && child != null) {
91
140
  leftUpdated = link -> child.setNextFocusLeftId(link.getId());
92
141
  leftRemoved = () -> child.setNextFocusLeftId(View.NO_ID);
@@ -96,15 +145,13 @@ public class FocusOrderDelegate {
96
145
 
97
146
  public void refreshRight(String prev, String next) {
98
147
  FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
99
- if (rightUpdated != null && rightRemoved != null) {
148
+ View child = getLinkedChild();
149
+ if (prev != null && rightUpdated != null && rightRemoved != null) {
100
150
  observer.unsubscribe(prev, rightUpdated, rightRemoved);
101
- View child = delegate.getFirstChild();
102
- if(child != null) {
151
+ if (child != null) {
103
152
  child.setNextFocusRightId(View.NO_ID);
104
153
  }
105
154
  }
106
-
107
- View child = delegate.getFirstChild();
108
155
  if (next != null && child != null) {
109
156
  rightUpdated = link -> child.setNextFocusRightId(link.getId());
110
157
  rightRemoved = () -> child.setNextFocusRightId(View.NO_ID);
@@ -114,16 +161,13 @@ public class FocusOrderDelegate {
114
161
 
115
162
  public void refreshUp(String prev, String next) {
116
163
  FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
164
+ View child = getLinkedChild();
117
165
  if (prev != null && upUpdated != null && upRemoved != null) {
118
166
  observer.unsubscribe(prev, upUpdated, upRemoved);
119
-
120
- View child = delegate.getFirstChild();
121
- if(child != null) {
167
+ if (child != null) {
122
168
  child.setNextFocusUpId(View.NO_ID);
123
169
  }
124
170
  }
125
-
126
- View child = delegate.getFirstChild();
127
171
  if (next != null && child != null) {
128
172
  upUpdated = link -> child.setNextFocusUpId(link.getId());
129
173
  upRemoved = () -> child.setNextFocusUpId(View.NO_ID);
@@ -131,27 +175,15 @@ public class FocusOrderDelegate {
131
175
  }
132
176
  }
133
177
 
134
- public void updateOrderGroup(String prev, String next) {
135
- View child = delegate.getFirstChild();
136
- Integer position = delegate.getOrderIndex();
137
-
138
- if (child != null && position != null) {
139
- A11yOrderLinking.getInstance().updateGroup(prev, next, position, child);
140
- }
141
- }
142
-
143
178
  public void refreshDown(String prev, String next) {
144
179
  FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
180
+ View child = getLinkedChild();
145
181
  if (prev != null && downUpdated != null && downRemoved != null) {
146
182
  observer.unsubscribe(prev, downUpdated, downRemoved);
147
-
148
- View child = delegate.getFirstChild();
149
- if(child != null) {
183
+ if (child != null) {
150
184
  child.setNextFocusDownId(View.NO_ID);
151
185
  }
152
186
  }
153
-
154
- View child = delegate.getFirstChild();
155
187
  if (next != null && child != null) {
156
188
  downUpdated = link -> child.setNextFocusDownId(link.getId());
157
189
  downRemoved = () -> child.setNextFocusDownId(View.NO_ID);
@@ -159,44 +191,11 @@ public class FocusOrderDelegate {
159
191
  }
160
192
  }
161
193
 
162
-
163
- public void unlink(View unlinkView) {
164
- String orderGroup = delegate.getOrderGroup();
165
- Integer orderIndex = delegate.getOrderIndex();
166
- String orderId = delegate.getOrderId();
167
-
168
- if (orderGroup != null && orderIndex != null) {
169
- A11yOrderLinking.getInstance().removeRelationship(orderGroup, orderIndex);
170
- }
171
-
172
- if (orderId != null) {
173
- FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
174
- View storedView = A11yOrderLinking.getInstance().getOrderLink(orderId);
175
- if(storedView == unlinkView) {
176
- observer.emitRemove(orderId);
177
- A11yOrderLinking.getInstance().removeOrderLink(orderId);
178
- }
179
- }
180
-
181
-
182
- if (delegate.getOrderLeft() != null) {
183
- FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
184
- observer.unsubscribe(delegate.getOrderLeft(), leftUpdated, leftRemoved);
185
- }
186
-
187
- if (delegate.getOrderRight() != null) {
188
- FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
189
- observer.unsubscribe(delegate.getOrderRight(), rightUpdated, rightRemoved);
190
- }
191
-
192
- if (delegate.getOrderUp() != null) {
193
- FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
194
- observer.unsubscribe(delegate.getOrderUp(), upUpdated, upRemoved);
195
- }
196
-
197
- if (delegate.getOrderDown() != null) {
198
- FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
199
- observer.unsubscribe(delegate.getOrderDown(), downUpdated, downRemoved);
194
+ public void updateOrderGroup(String prev, String next) {
195
+ View child = getLinkedChild();
196
+ Integer position = delegate.getOrderIndex();
197
+ if (child != null && position != null) {
198
+ A11yOrderLinking.getInstance().updateGroup(prev, next, position, child);
200
199
  }
201
200
  }
202
201
 
@@ -204,10 +203,17 @@ public class FocusOrderDelegate {
204
203
  return A11yOrderLinking.getInstance().getOrderLink(linkId);
205
204
  }
206
205
 
207
- public void clear(View view) {
208
- if(view != null) {
209
- unlink(view);
206
+ public void cleanByOrderId(String orderId) {
207
+ if (orderId == null) return;
208
+
209
+ String orderGroup = delegate.getOrderGroup();
210
+ Integer orderIndex = delegate.getOrderIndex();
211
+ if (orderGroup != null && orderIndex != null) {
212
+ A11yOrderLinking.getInstance().removeRelationship(orderGroup, orderIndex);
210
213
  }
211
- }
212
214
 
215
+ FocusLinkObserver observer = FocusLinkObserverSingleton.getInstance();
216
+ observer.emitRemove(orderId);
217
+ A11yOrderLinking.getInstance().removeOrderLink(orderId);
218
+ }
213
219
  }
@@ -0,0 +1,14 @@
1
+ package com.externalkeyboard.delegates;
2
+
3
+ import android.view.View;
4
+
5
+ public interface FocusOrderDelegateHost {
6
+ View getFirstChild();
7
+ String getOrderGroup();
8
+ Integer getOrderIndex();
9
+ String getOrderId();
10
+ String getOrderLeft();
11
+ String getOrderRight();
12
+ String getOrderUp();
13
+ String getOrderDown();
14
+ }
@@ -1,5 +1,6 @@
1
1
  package com.externalkeyboard.helper.Linking;
2
2
 
3
+ import android.util.Log;
3
4
  import android.view.View;
4
5
 
5
6
  import java.util.HashMap;
@@ -59,6 +60,10 @@ public class A11yOrderLinking {
59
60
  if (queue == null) return;
60
61
 
61
62
  queue.removeFromOrder(index);
63
+
64
+ if (queue.isEmpty()) {
65
+ relationships.remove(key);
66
+ }
62
67
  }
63
68
 
64
69
  public void updateGroup(String prev, String next, Integer position, View child) {
@@ -9,33 +9,33 @@ import com.facebook.react.bridge.Promise;
9
9
  import com.facebook.react.bridge.ReactApplicationContext;
10
10
  import com.facebook.react.bridge.ReactMethod;
11
11
 
12
+ import java.lang.ref.WeakReference;
13
+
12
14
  public class ExternalKeyboardModule extends ExternalKeyboardModuleSpec {
13
15
  public static final String NAME = "ExternalKeyboardModule";
14
- private static View focusedView = null;
15
- private final ReactApplicationContext context;
16
-
16
+ private static WeakReference<View> focusedViewRef = new WeakReference<>(null);
17
17
 
18
18
  private boolean dismiss() {
19
- if(focusedView == null) return false;
19
+ View focusedView = focusedViewRef.get();
20
+ if (focusedView == null) return false;
20
21
  try {
21
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
22
+ InputMethodManager imm = (InputMethodManager) getReactApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
22
23
  if (imm != null) {
23
24
  imm.hideSoftInputFromWindow(focusedView.getWindowToken(), 0);
24
25
  return true;
25
26
  }
26
- } catch (Exception ignored){}
27
+ } catch (Exception ignored) {
28
+ }
27
29
 
28
30
  return false;
29
31
  }
30
32
 
31
-
32
- public static void setFocusedTextInput (View _focusedView) {
33
- focusedView = _focusedView;
33
+ public static void setFocusedTextInput(View _focusedView) {
34
+ focusedViewRef = new WeakReference<>(_focusedView);
34
35
  }
35
36
 
36
37
  public ExternalKeyboardModule(ReactApplicationContext reactContext) {
37
38
  super(reactContext);
38
- context = reactContext;
39
39
  }
40
40
 
41
41
  @Override