react-native-screens 3.9.0 → 3.11.0
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.
- package/LICENSE +1 -1
- package/README.md +47 -7
- package/android/build.gradle +1 -2
- package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +71 -0
- package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -1
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +35 -52
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +83 -34
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +38 -33
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +77 -42
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +25 -9
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +8 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +107 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +155 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +67 -0
- package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
- package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
- package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
- package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
- package/android/src/main/res/anim/rns_fade_in.xml +7 -0
- package/android/src/main/res/anim/rns_fade_out.xml +7 -0
- package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
- package/createNativeStackNavigator/README.md +12 -0
- package/ios/RNSScreen.h +10 -0
- package/ios/RNSScreen.m +38 -0
- package/ios/RNSScreenContainer.m +5 -0
- package/ios/RNSScreenStack.m +29 -13
- package/ios/RNSScreenStackAnimator.m +45 -14
- package/ios/RNSScreenStackHeaderConfig.m +4 -1
- package/ios/RNSScreenWindowTraits.h +1 -0
- package/ios/RNSScreenWindowTraits.m +20 -0
- package/ios/UIViewController+RNScreens.m +10 -0
- package/lib/commonjs/index.js +17 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.native.js +66 -18
- package/lib/commonjs/index.native.js.map +1 -1
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js +67 -0
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/commonjs/native-stack/views/HeaderConfig.js +46 -4
- package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
- package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +60 -0
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/commonjs/reanimated/ReanimatedScreen.js +7 -79
- package/lib/commonjs/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js +61 -0
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/commonjs/reanimated/index.js +2 -2
- package/lib/commonjs/reanimated/index.js.map +1 -1
- package/lib/commonjs/utils.js +20 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.native.js +65 -19
- package/lib/module/index.native.js.map +1 -1
- package/lib/module/native-stack/utils/useBackPressSubscription.js +50 -0
- package/lib/module/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/module/native-stack/views/HeaderConfig.js +46 -5
- package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.js +33 -4
- package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js +40 -0
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/module/reanimated/ReanimatedScreen.js +6 -73
- package/lib/module/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/module/reanimated/ReanimatedScreenProvider.js +49 -0
- package/lib/module/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/module/reanimated/index.js +1 -1
- package/lib/module/reanimated/index.js.map +1 -1
- package/lib/module/utils.js +8 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/native-stack/types.d.ts +34 -2
- package/lib/typescript/native-stack/utils/useBackPressSubscription.d.ts +16 -0
- package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +5 -0
- package/lib/typescript/reanimated/ReanimatedScreen.d.ts +5 -2
- package/lib/typescript/reanimated/ReanimatedScreenProvider.d.ts +2 -0
- package/lib/typescript/reanimated/index.d.ts +1 -1
- package/lib/typescript/types.d.ts +101 -1
- package/lib/typescript/utils.d.ts +2 -0
- package/native-stack/README.md +70 -8
- package/package.json +2 -1
- package/reanimated/package.json +6 -0
- package/src/index.native.tsx +94 -36
- package/src/index.tsx +4 -0
- package/src/native-stack/types.tsx +34 -2
- package/src/native-stack/utils/useBackPressSubscription.tsx +66 -0
- package/src/native-stack/views/HeaderConfig.tsx +46 -3
- package/src/native-stack/views/NativeStackView.tsx +33 -4
- package/src/reanimated/ReanimatedNativeStackScreen.tsx +61 -0
- package/src/reanimated/ReanimatedScreen.tsx +6 -84
- package/src/reanimated/ReanimatedScreenProvider.tsx +42 -0
- package/src/reanimated/index.tsx +1 -1
- package/src/types.tsx +101 -1
- package/src/utils.ts +12 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2018
|
|
3
|
+
Copyright (c) 2018 Software Mansion <swmansion.com>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -35,6 +35,26 @@ protected void onCreate(Bundle savedInstanceState) {
|
|
|
35
35
|
|
|
36
36
|
For people that must handle cases like this, there is [a more detailed discussion of the difficulties in a series of related comments](https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704633).
|
|
37
37
|
|
|
38
|
+
<details>
|
|
39
|
+
<summary>Need to use a custom Kotlin version?</summary>
|
|
40
|
+
<br>
|
|
41
|
+
|
|
42
|
+
Since `v3.6.0` `react-native-screens` has been rewritten with Kotlin. Kotlin version used in this library defaults to `1.4.10`.
|
|
43
|
+
|
|
44
|
+
If you need to use a different Kotlin version, set `kotlinVersion` ext property in your project's `android/build.gradle` and the library will use this version accordingly:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
buildscript {
|
|
48
|
+
ext {
|
|
49
|
+
...
|
|
50
|
+
kotlinVersion = "1.4.10"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Disclaimer**: `react-native-screens` requires Kotlin `1.3.50` or higher.
|
|
56
|
+
</details>
|
|
57
|
+
|
|
38
58
|
### Windows
|
|
39
59
|
|
|
40
60
|
Installation on Windows should be completely handled with auto-linking when using React Native Windows 0.63+. For earlier versions, you must [manually link](https://microsoft.github.io/react-native-windows/docs/native-modules-using) the native module.
|
|
@@ -68,6 +88,24 @@ Just make sure that the version of [react-navigation](https://github.com/react-n
|
|
|
68
88
|
|
|
69
89
|
You are all set 🎉 – when screens are enabled in your application code react-navigation will automatically use them instead of relying on plain React Native Views.
|
|
70
90
|
|
|
91
|
+
### Experimental support for `react-freeze`
|
|
92
|
+
|
|
93
|
+
> You have to use React Native 0.64 or higher, react-navigation 5.x or 6.x and react-native-screens >= v3.9.0
|
|
94
|
+
|
|
95
|
+
Since `v3.9.0`, `react-native-screens` comes with experimental support for [`react-freeze`](https://github.com/software-mansion-labs/react-freeze). It uses the React `Suspense` mechanism to prevent parts of the React component tree from rendering, while keeping its state untouched.
|
|
96
|
+
|
|
97
|
+
To benefit from this feature, enable it in your entry file (e.g. `App.js`) with this snippet:
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import { enableFreeze } from 'react-native-screens';
|
|
101
|
+
|
|
102
|
+
enableFreeze(true);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Want to know more? Check out [react-freeze README](https://github.com/software-mansion-labs/react-freeze#readme)
|
|
106
|
+
|
|
107
|
+
Found a bug? File an issue [here](https://github.com/software-mansion/react-native-screens/issues) or directly in [react-freeze repository](https://github.com/software-mansion-labs/react-freeze/issues).
|
|
108
|
+
|
|
71
109
|
### Disabling `react-native-screens`
|
|
72
110
|
|
|
73
111
|
If, for whatever reason, you'd like to disable native screens support and use plain React Native Views add the following code in your entry file (e.g. `App.js`):
|
|
@@ -85,8 +123,8 @@ You can also disable the usage of native screens per navigator with [`detachInac
|
|
|
85
123
|
To take advantage of the native stack navigator primitive for React Navigation that leverages `UINavigationController` on iOS and `Fragment` on Android, please refer:
|
|
86
124
|
|
|
87
125
|
- for React Navigation >= v6 to the [Native Stack Navigator part of React Navigation documentation](https://reactnavigation.org/docs/native-stack-navigator)
|
|
88
|
-
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/
|
|
89
|
-
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/
|
|
126
|
+
- for React Navigation v5 to the [README in react-native-screens/native-stack](https://github.com/software-mansion/react-native-screens/tree/main/native-stack)
|
|
127
|
+
- for older versions to the [README in react-native-screens/createNativeStackNavigator](https://github.com/software-mansion/react-native-screens/tree/main/createNativeStackNavigator)
|
|
90
128
|
|
|
91
129
|
## Interop with [react-native-navigation](https://github.com/wix/react-native-navigation)
|
|
92
130
|
|
|
@@ -94,12 +132,12 @@ React-native-navigation library already uses native containers for rendering nav
|
|
|
94
132
|
|
|
95
133
|
## Interop with other libraries
|
|
96
134
|
|
|
97
|
-
This library should work out of the box with all existing react-native libraries. If you experience problems with interoperability please [report an issue](https://github.com/
|
|
135
|
+
This library should work out of the box with all existing react-native libraries. If you experience problems with interoperability please [report an issue](https://github.com/software-mansion/react-native-screens/issues).
|
|
98
136
|
|
|
99
137
|
## Guide for navigation library authors
|
|
100
138
|
|
|
101
139
|
If you are building a navigation library you may want to use `react-native-screens` to have control over which parts of the React component tree are attached to the native view hierarchy.
|
|
102
|
-
To do that, `react-native-screens` provides you with the components documented [here](https://github.com/
|
|
140
|
+
To do that, `react-native-screens` provides you with the components documented [here](https://github.com/software-mansion/react-native-screens/tree/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md).
|
|
103
141
|
|
|
104
142
|
## Common problems
|
|
105
143
|
|
|
@@ -120,10 +158,11 @@ Use `ScrollView` with prop `contentInsetAdjustmentBehavior=“automatic”` as a
|
|
|
120
158
|
| [SVG component becomes transparent when goBack](https://github.com/software-mansion/react-native-screens/issues/773) | [related PRs](https://github.com/software-mansion/react-native-screens/issues/773#issuecomment-783469792) |
|
|
121
159
|
| [Memory leak while moving from one screen to another in the same stack](https://github.com/software-mansion/react-native-screens/issues/843) | [explanation](https://github.com/software-mansion/react-native-screens/issues/843#issuecomment-832034119) |
|
|
122
160
|
| [LargeHeader stays small after pop/goBack/swipe gesture on iOS 14+](https://github.com/software-mansion/react-native-screens/issues/649) | [potential fix](https://github.com/software-mansion/react-native-screens/issues/649#issuecomment-712199895) |
|
|
161
|
+
| [`onScroll` and `onMomentumScrollEnd` of previous screen triggered in bottom tabs](https://github.com/software-mansion/react-native-screens/issues/1183) | [explanation](https://github.com/software-mansion/react-native-screens/issues/1183#issuecomment-949313111) |
|
|
123
162
|
|
|
124
163
|
## Contributing
|
|
125
164
|
|
|
126
|
-
There are many ways to contribute to this project. See [CONTRIBUTING](https://github.com/
|
|
165
|
+
There are many ways to contribute to this project. See [CONTRIBUTING](https://github.com/software-mansion/react-native-screens/tree/main/guides/CONTRIBUTING.md) guide for more information. Thank you for your interest in contributing!
|
|
127
166
|
|
|
128
167
|
## License
|
|
129
168
|
|
|
@@ -131,7 +170,8 @@ React native screens library is licensed under [The MIT License](LICENSE).
|
|
|
131
170
|
|
|
132
171
|
## Credits
|
|
133
172
|
|
|
134
|
-
This project is
|
|
173
|
+
This project has been build and is maintained thanks to the support from [Shopify](https://shopify.com), [Expo.io](https://expo.io) and [Software Mansion](https://swmansion.com)
|
|
135
174
|
|
|
175
|
+
[](https://shopify.com)
|
|
136
176
|
[](https://expo.io)
|
|
137
|
-
[](https://swmansion.com)
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,6 @@ buildscript {
|
|
|
4
4
|
}
|
|
5
5
|
repositories {
|
|
6
6
|
google()
|
|
7
|
-
jcenter()
|
|
8
7
|
mavenCentral()
|
|
9
8
|
}
|
|
10
9
|
dependencies {
|
|
@@ -50,7 +49,6 @@ repositories {
|
|
|
50
49
|
mavenCentral()
|
|
51
50
|
mavenLocal()
|
|
52
51
|
google()
|
|
53
|
-
jcenter()
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
dependencies {
|
|
@@ -60,4 +58,5 @@ dependencies {
|
|
|
60
58
|
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
|
61
59
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
|
|
62
60
|
implementation 'com.google.android.material:material:1.1.0'
|
|
61
|
+
implementation "androidx.core:core-ktx:1.5.0"
|
|
63
62
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.activity.OnBackPressedCallback
|
|
5
|
+
import androidx.appcompat.widget.SearchView
|
|
6
|
+
import androidx.fragment.app.Fragment
|
|
7
|
+
|
|
8
|
+
class CustomSearchView(context: Context, fragment: Fragment) : SearchView(context) {
|
|
9
|
+
/*
|
|
10
|
+
CustomSearchView uses some variables from SearchView. They are listed below with links to documentation
|
|
11
|
+
isIconified - https://developer.android.com/reference/android/widget/SearchView#setIconified(boolean)
|
|
12
|
+
maxWidth - https://developer.android.com/reference/android/widget/SearchView#setMaxWidth(int)
|
|
13
|
+
setOnSearchClickListener - https://developer.android.com/reference/android/widget/SearchView#setOnSearchClickListener(android.view.View.OnClickListener)
|
|
14
|
+
setOnCloseListener - https://developer.android.com/reference/android/widget/SearchView#setOnCloseListener(android.widget.SearchView.OnCloseListener)
|
|
15
|
+
*/
|
|
16
|
+
private var mCustomOnCloseListener: OnCloseListener? = null
|
|
17
|
+
private var mCustomOnSearchClickedListener: OnClickListener? = null
|
|
18
|
+
|
|
19
|
+
private var mOnBackPressedCallback: OnBackPressedCallback =
|
|
20
|
+
object : OnBackPressedCallback(true) {
|
|
21
|
+
override fun handleOnBackPressed() {
|
|
22
|
+
isIconified = true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
private val backPressOverrider = FragmentBackPressOverrider(fragment, mOnBackPressedCallback)
|
|
26
|
+
var overrideBackAction: Boolean
|
|
27
|
+
set(value) {
|
|
28
|
+
backPressOverrider.overrideBackAction = value
|
|
29
|
+
}
|
|
30
|
+
get() = backPressOverrider.overrideBackAction
|
|
31
|
+
|
|
32
|
+
fun focus() {
|
|
33
|
+
isIconified = false
|
|
34
|
+
requestFocusFromTouch()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
override fun setOnCloseListener(listener: OnCloseListener?) {
|
|
38
|
+
mCustomOnCloseListener = listener
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override fun setOnSearchClickListener(listener: OnClickListener?) {
|
|
42
|
+
mCustomOnSearchClickedListener = listener
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
override fun onAttachedToWindow() {
|
|
46
|
+
super.onAttachedToWindow()
|
|
47
|
+
if (!isIconified) {
|
|
48
|
+
backPressOverrider.maybeAddBackCallback()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun onDetachedFromWindow() {
|
|
53
|
+
super.onDetachedFromWindow()
|
|
54
|
+
backPressOverrider.removeBackCallbackIfAdded()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
init {
|
|
58
|
+
super.setOnSearchClickListener { v ->
|
|
59
|
+
mCustomOnSearchClickedListener?.onClick(v)
|
|
60
|
+
backPressOverrider.maybeAddBackCallback()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
super.setOnCloseListener {
|
|
64
|
+
val result = mCustomOnCloseListener?.onClose() ?: false
|
|
65
|
+
backPressOverrider.removeBackCallbackIfAdded()
|
|
66
|
+
result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
maxWidth = Integer.MAX_VALUE
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.appcompat.widget.Toolbar
|
|
5
|
+
|
|
6
|
+
// This class is used to store config closer to search bar
|
|
7
|
+
open class CustomToolbar(context: Context, val config: ScreenStackHeaderConfig) : Toolbar(context)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.swmansion.rnscreens
|
|
2
|
+
|
|
3
|
+
import androidx.activity.OnBackPressedCallback
|
|
4
|
+
import androidx.fragment.app.Fragment
|
|
5
|
+
|
|
6
|
+
class FragmentBackPressOverrider(
|
|
7
|
+
private val fragment: Fragment,
|
|
8
|
+
private val mOnBackPressedCallback: OnBackPressedCallback
|
|
9
|
+
) {
|
|
10
|
+
private var mIsBackCallbackAdded: Boolean = false
|
|
11
|
+
var overrideBackAction: Boolean = true
|
|
12
|
+
|
|
13
|
+
fun maybeAddBackCallback() {
|
|
14
|
+
if (!mIsBackCallbackAdded && overrideBackAction) {
|
|
15
|
+
fragment.activity?.onBackPressedDispatcher?.addCallback(
|
|
16
|
+
fragment,
|
|
17
|
+
mOnBackPressedCallback
|
|
18
|
+
)
|
|
19
|
+
mIsBackCallbackAdded = true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fun removeBackCallbackIfAdded() {
|
|
24
|
+
if (mIsBackCallbackAdded) {
|
|
25
|
+
mOnBackPressedCallback.remove()
|
|
26
|
+
mIsBackCallbackAdded = false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
package com.swmansion.rnscreens
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
|
-
import android.content.Context
|
|
5
4
|
import android.content.pm.ActivityInfo
|
|
6
5
|
import android.graphics.Paint
|
|
7
|
-
import android.os.Build
|
|
8
6
|
import android.os.Parcelable
|
|
9
7
|
import android.util.SparseArray
|
|
10
|
-
import android.view.View
|
|
11
8
|
import android.view.ViewGroup
|
|
12
9
|
import android.view.WindowManager
|
|
13
|
-
import android.view.inputmethod.InputMethodManager
|
|
14
10
|
import android.webkit.WebView
|
|
15
|
-
import android.widget.TextView
|
|
16
11
|
import com.facebook.react.bridge.GuardedRunnable
|
|
17
12
|
import com.facebook.react.bridge.ReactContext
|
|
18
13
|
import com.facebook.react.uimanager.UIManagerModule
|
|
@@ -34,6 +29,8 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
34
29
|
private var mStatusBarHidden: Boolean? = null
|
|
35
30
|
private var mStatusBarTranslucent: Boolean? = null
|
|
36
31
|
private var mStatusBarColor: Int? = null
|
|
32
|
+
private var mNavigationBarColor: Int? = null
|
|
33
|
+
private var mNavigationBarHidden: Boolean? = null
|
|
37
34
|
var isStatusBarAnimated: Boolean? = null
|
|
38
35
|
private var mNativeBackButtonDismissalEnabled = true
|
|
39
36
|
|
|
@@ -51,16 +48,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
51
48
|
layoutParams = WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION)
|
|
52
49
|
}
|
|
53
50
|
|
|
54
|
-
override fun onAnimationStart() {
|
|
55
|
-
super.onAnimationStart()
|
|
56
|
-
fragment?.onViewAnimationStart()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
override fun onAnimationEnd() {
|
|
60
|
-
super.onAnimationEnd()
|
|
61
|
-
fragment?.onViewAnimationEnd()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
51
|
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
|
65
52
|
// do nothing, react native will keep the view hierarchy so no need to serialize/deserialize
|
|
66
53
|
// view's states. The side effect of restoring is that TextInput components would trigger
|
|
@@ -87,28 +74,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
87
74
|
}
|
|
88
75
|
}
|
|
89
76
|
|
|
90
|
-
override fun onAttachedToWindow() {
|
|
91
|
-
super.onAttachedToWindow()
|
|
92
|
-
// This method implements a workaround for RN's autoFocus functionality. Because of the way
|
|
93
|
-
// autoFocus is implemented it sometimes gets triggered before native text view is mounted. As
|
|
94
|
-
// a result Android ignores calls for opening soft keyboard and here we trigger it manually
|
|
95
|
-
// again after the screen is attached.
|
|
96
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
97
|
-
var view = focusedChild
|
|
98
|
-
if (view != null) {
|
|
99
|
-
while (view is ViewGroup) {
|
|
100
|
-
view = view.focusedChild
|
|
101
|
-
}
|
|
102
|
-
if (view is TextView) {
|
|
103
|
-
val textView = view
|
|
104
|
-
if (textView.showSoftInputOnFocus) {
|
|
105
|
-
textView.addOnAttachStateChangeListener(sShowSoftKeyboardOnAttach)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
77
|
val headerConfig: ScreenStackHeaderConfig?
|
|
113
78
|
get() {
|
|
114
79
|
val child = getChildAt(0)
|
|
@@ -183,6 +148,13 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
183
148
|
fragment?.let { ScreenWindowTraits.setOrientation(this, it.tryGetActivity()) }
|
|
184
149
|
}
|
|
185
150
|
|
|
151
|
+
// Accepts one of 4 accessibility flags
|
|
152
|
+
// developer.android.com/reference/android/view/View#attr_android:importantForAccessibility
|
|
153
|
+
fun changeAccessibilityMode(mode: Int) {
|
|
154
|
+
this.importantForAccessibility = mode
|
|
155
|
+
this.headerConfig?.toolbar?.importantForAccessibility = mode
|
|
156
|
+
}
|
|
157
|
+
|
|
186
158
|
var statusBarStyle: String?
|
|
187
159
|
get() = mStatusBarStyle
|
|
188
160
|
set(statusBarStyle) {
|
|
@@ -229,6 +201,31 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
229
201
|
fragment?.let { ScreenWindowTraits.setColor(this, it.tryGetActivity(), it.tryGetContext()) }
|
|
230
202
|
}
|
|
231
203
|
|
|
204
|
+
var navigationBarColor: Int?
|
|
205
|
+
get() = mNavigationBarColor
|
|
206
|
+
set(navigationBarColor) {
|
|
207
|
+
if (navigationBarColor != null) {
|
|
208
|
+
ScreenWindowTraits.applyDidSetNavigationBarAppearance()
|
|
209
|
+
}
|
|
210
|
+
mNavigationBarColor = navigationBarColor
|
|
211
|
+
fragment?.let { ScreenWindowTraits.setNavigationBarColor(this, it.tryGetActivity()) }
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var isNavigationBarHidden: Boolean?
|
|
215
|
+
get() = mNavigationBarHidden
|
|
216
|
+
set(navigationBarHidden) {
|
|
217
|
+
if (navigationBarHidden != null) {
|
|
218
|
+
ScreenWindowTraits.applyDidSetNavigationBarAppearance()
|
|
219
|
+
}
|
|
220
|
+
mNavigationBarHidden = navigationBarHidden
|
|
221
|
+
fragment?.let {
|
|
222
|
+
ScreenWindowTraits.setNavigationBarHidden(
|
|
223
|
+
this,
|
|
224
|
+
it.tryGetActivity(),
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
232
229
|
var nativeBackButtonDismissalEnabled: Boolean
|
|
233
230
|
get() = mNativeBackButtonDismissalEnabled
|
|
234
231
|
set(enableNativeBackButtonDismissal) {
|
|
@@ -252,20 +249,6 @@ class Screen constructor(context: ReactContext?) : ViewGroup(context) {
|
|
|
252
249
|
}
|
|
253
250
|
|
|
254
251
|
enum class WindowTraits {
|
|
255
|
-
ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
companion object {
|
|
259
|
-
private val sShowSoftKeyboardOnAttach: OnAttachStateChangeListener =
|
|
260
|
-
object : OnAttachStateChangeListener {
|
|
261
|
-
override fun onViewAttachedToWindow(view: View) {
|
|
262
|
-
val inputMethodManager =
|
|
263
|
-
view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
|
264
|
-
inputMethodManager.showSoftInput(view, 0)
|
|
265
|
-
view.removeOnAttachStateChangeListener(this)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
override fun onViewDetachedFromWindow(view: View) {}
|
|
269
|
-
}
|
|
252
|
+
ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED, NAVIGATION_BAR_COLOR, NAVIGATION_BAR_HIDDEN
|
|
270
253
|
}
|
|
271
254
|
}
|
|
@@ -166,7 +166,7 @@ open class ScreenContainer<T : ScreenFragment>(context: Context?) : ViewGroup(co
|
|
|
166
166
|
while (context !is FragmentActivity && context is ContextWrapper) {
|
|
167
167
|
context = context.baseContext
|
|
168
168
|
}
|
|
169
|
-
check(context is FragmentActivity) { "In order to use RNScreens components your app's activity need to extend
|
|
169
|
+
check(context is FragmentActivity) { "In order to use RNScreens components your app's activity need to extend ReactActivity" }
|
|
170
170
|
setFragmentManager(context.supportFragmentManager)
|
|
171
171
|
}
|
|
172
172
|
|
|
@@ -2,6 +2,7 @@ package com.swmansion.rnscreens
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.app.Activity
|
|
5
|
+
import android.content.Context
|
|
5
6
|
import android.os.Bundle
|
|
6
7
|
import android.view.LayoutInflater
|
|
7
8
|
import android.view.View
|
|
@@ -37,6 +38,17 @@ open class ScreenFragment : Fragment {
|
|
|
37
38
|
// due to progress value being already 0.0f
|
|
38
39
|
private var mProgress = -1f
|
|
39
40
|
|
|
41
|
+
// those 2 vars are needed since sometimes the events would be dispatched twice in child containers
|
|
42
|
+
// (should only happen if parent has `NONE` animation) and we don't need too complicated logic.
|
|
43
|
+
// We just check if, after the event was dispatched, its "counter-event" has been also dispatched before sending the same event again.
|
|
44
|
+
// We do it for 'willAppear' -> 'willDisappear' and 'appear' -> 'disappear'
|
|
45
|
+
private var canDispatchWillAppear = true
|
|
46
|
+
private var canDispatchAppear = true
|
|
47
|
+
|
|
48
|
+
// we want to know if we are currently transitioning in order not to fire lifecycle events
|
|
49
|
+
// in nested fragments. See more explanation in dispatchViewAnimationEvent
|
|
50
|
+
private var isTransitioning = false
|
|
51
|
+
|
|
40
52
|
constructor() {
|
|
41
53
|
throw IllegalStateException(
|
|
42
54
|
"Screen fragments should never be restored. Follow instructions from https://github.com/software-mansion/react-native-screens/issues/17#issuecomment-424704067 to properly configure your main activity."
|
|
@@ -61,7 +73,7 @@ open class ScreenFragment : Fragment {
|
|
|
61
73
|
container: ViewGroup?,
|
|
62
74
|
savedInstanceState: Bundle?
|
|
63
75
|
): View? {
|
|
64
|
-
val wrapper = context?.let {
|
|
76
|
+
val wrapper = context?.let { ScreensFrameLayout(it) }
|
|
65
77
|
|
|
66
78
|
val params = FrameLayout.LayoutParams(
|
|
67
79
|
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
|
@@ -71,6 +83,23 @@ open class ScreenFragment : Fragment {
|
|
|
71
83
|
return wrapper
|
|
72
84
|
}
|
|
73
85
|
|
|
86
|
+
private class ScreensFrameLayout(
|
|
87
|
+
context: Context,
|
|
88
|
+
) : FrameLayout(context) {
|
|
89
|
+
/**
|
|
90
|
+
* This method implements a workaround for RN's autoFocus functionality. Because of the way
|
|
91
|
+
* autoFocus is implemented it dismisses soft keyboard in fragment transition
|
|
92
|
+
* due to change of visibility of the view at the start of the transition. Here we override the
|
|
93
|
+
* call to `clearFocus` when the visibility of view is `INVISIBLE` since `clearFocus` triggers the
|
|
94
|
+
* hiding of the keyboard in `ReactEditText.java`.
|
|
95
|
+
*/
|
|
96
|
+
override fun clearFocus() {
|
|
97
|
+
if (visibility != INVISIBLE) {
|
|
98
|
+
super.clearFocus()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
74
103
|
open fun onContainerUpdate() {
|
|
75
104
|
updateWindowTraits()
|
|
76
105
|
}
|
|
@@ -123,33 +152,52 @@ open class ScreenFragment : Fragment {
|
|
|
123
152
|
val childScreenContainers: List<ScreenContainer<*>>
|
|
124
153
|
get() = mChildScreenContainers
|
|
125
154
|
|
|
126
|
-
fun
|
|
155
|
+
private fun canDispatchEvent(event: ScreenLifecycleEvent): Boolean {
|
|
156
|
+
return when (event) {
|
|
157
|
+
ScreenLifecycleEvent.WillAppear -> canDispatchWillAppear
|
|
158
|
+
ScreenLifecycleEvent.Appear -> canDispatchAppear
|
|
159
|
+
ScreenLifecycleEvent.WillDisappear -> !canDispatchWillAppear
|
|
160
|
+
ScreenLifecycleEvent.Disappear -> !canDispatchAppear
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private fun setLastEventDispatched(event: ScreenLifecycleEvent) {
|
|
165
|
+
when (event) {
|
|
166
|
+
ScreenLifecycleEvent.WillAppear -> canDispatchWillAppear = false
|
|
167
|
+
ScreenLifecycleEvent.Appear -> canDispatchAppear = false
|
|
168
|
+
ScreenLifecycleEvent.WillDisappear -> canDispatchWillAppear = true
|
|
169
|
+
ScreenLifecycleEvent.Disappear -> canDispatchAppear = true
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private fun dispatchOnWillAppear() {
|
|
127
174
|
dispatchEvent(ScreenLifecycleEvent.WillAppear, this)
|
|
128
175
|
|
|
129
176
|
dispatchTransitionProgress(0.0f, false)
|
|
130
177
|
}
|
|
131
178
|
|
|
132
|
-
fun dispatchOnAppear() {
|
|
179
|
+
private fun dispatchOnAppear() {
|
|
133
180
|
dispatchEvent(ScreenLifecycleEvent.Appear, this)
|
|
134
181
|
|
|
135
182
|
dispatchTransitionProgress(1.0f, false)
|
|
136
183
|
}
|
|
137
184
|
|
|
138
|
-
|
|
185
|
+
private fun dispatchOnWillDisappear() {
|
|
139
186
|
dispatchEvent(ScreenLifecycleEvent.WillDisappear, this)
|
|
140
187
|
|
|
141
188
|
dispatchTransitionProgress(0.0f, true)
|
|
142
189
|
}
|
|
143
190
|
|
|
144
|
-
|
|
191
|
+
private fun dispatchOnDisappear() {
|
|
145
192
|
dispatchEvent(ScreenLifecycleEvent.Disappear, this)
|
|
146
193
|
|
|
147
194
|
dispatchTransitionProgress(1.0f, true)
|
|
148
195
|
}
|
|
149
196
|
|
|
150
197
|
private fun dispatchEvent(event: ScreenLifecycleEvent, fragment: ScreenFragment) {
|
|
151
|
-
if (fragment is ScreenStackFragment) {
|
|
198
|
+
if (fragment is ScreenStackFragment && fragment.canDispatchEvent(event)) {
|
|
152
199
|
fragment.screen.let {
|
|
200
|
+
fragment.setLastEventDispatched(event)
|
|
153
201
|
val lifecycleEvent: Event<*> = when (event) {
|
|
154
202
|
ScreenLifecycleEvent.WillAppear -> ScreenWillAppearEvent(it.id)
|
|
155
203
|
ScreenLifecycleEvent.Appear -> ScreenAppearEvent(it.id)
|
|
@@ -169,12 +217,7 @@ open class ScreenFragment : Fragment {
|
|
|
169
217
|
for (sc in mChildScreenContainers) {
|
|
170
218
|
if (sc.screenCount > 0) {
|
|
171
219
|
sc.topScreen?.let {
|
|
172
|
-
|
|
173
|
-
// we do not dispatch events in child when it has `none` animation
|
|
174
|
-
// and we are going forward since then they will be dispatched in child via
|
|
175
|
-
// `onCreateAnimation` of ScreenStackFragment
|
|
176
|
-
sc.topScreen?.fragment?.let { fragment -> dispatchEvent(event, fragment) }
|
|
177
|
-
}
|
|
220
|
+
sc.topScreen?.fragment?.let { fragment -> dispatchEvent(event, fragment) }
|
|
178
221
|
}
|
|
179
222
|
}
|
|
180
223
|
}
|
|
@@ -220,31 +263,37 @@ open class ScreenFragment : Fragment {
|
|
|
220
263
|
}
|
|
221
264
|
|
|
222
265
|
fun onViewAnimationStart() {
|
|
223
|
-
|
|
224
|
-
// view. We override Screen#onAnimationStart and an appropriate method of the StackFragment's
|
|
225
|
-
// root view in order to achieve this.
|
|
226
|
-
if (isResumed) {
|
|
227
|
-
// Android dispatches the animation start event for the fragment that is being added first
|
|
228
|
-
// however we want the one being dismissed first to match iOS. It also makes more sense from
|
|
229
|
-
// a navigation point of view to have the disappear event first.
|
|
230
|
-
// Since there are no explicit relationships between the fragment being added / removed the
|
|
231
|
-
// practical way to fix this is delaying dispatching the appear events at the end of the
|
|
232
|
-
// frame.
|
|
233
|
-
UiThreadUtil.runOnUiThread { dispatchOnWillAppear() }
|
|
234
|
-
} else {
|
|
235
|
-
dispatchOnWillDisappear()
|
|
236
|
-
}
|
|
266
|
+
dispatchViewAnimationEvent(false)
|
|
237
267
|
}
|
|
238
268
|
|
|
239
269
|
open fun onViewAnimationEnd() {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
270
|
+
dispatchViewAnimationEvent(true)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private fun dispatchViewAnimationEvent(animationEnd: Boolean) {
|
|
274
|
+
isTransitioning = !animationEnd
|
|
275
|
+
// if parent fragment is transitioning, we do not want the events dispatched from the child,
|
|
276
|
+
// since we subscribe to parent's animation start/end and dispatch events in child from there
|
|
277
|
+
// check for `isTransitioning` should be enough since the child's animation should take only
|
|
278
|
+
// 20ms due to always being `StackAnimation.NONE` when nested stack being pushed
|
|
279
|
+
val parent = parentFragment
|
|
280
|
+
if (parent == null || (parent is ScreenFragment && !parent.isTransitioning)) {
|
|
281
|
+
// onViewAnimationStart/End is triggered from View#onAnimationStart/End method of the fragment's root
|
|
282
|
+
// view. We override an appropriate method of the StackFragment's
|
|
283
|
+
// root view in order to achieve this.
|
|
284
|
+
if (isResumed) {
|
|
285
|
+
// Android dispatches the animation start event for the fragment that is being added first
|
|
286
|
+
// however we want the one being dismissed first to match iOS. It also makes more sense from
|
|
287
|
+
// a navigation point of view to have the disappear event first.
|
|
288
|
+
// Since there are no explicit relationships between the fragment being added / removed the
|
|
289
|
+
// practical way to fix this is delaying dispatching the appear events at the end of the
|
|
290
|
+
// frame.
|
|
291
|
+
UiThreadUtil.runOnUiThread {
|
|
292
|
+
if (animationEnd) dispatchOnAppear() else dispatchOnWillAppear()
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
if (animationEnd) dispatchOnDisappear() else dispatchOnWillDisappear()
|
|
296
|
+
}
|
|
248
297
|
}
|
|
249
298
|
}
|
|
250
299
|
|