This change adds new event that gets dispatched from native stack when screen transitioning is finished.
The new event is used by react-navigation to update the library internal state that the triggered action has been finished. Previously we were relying solely on onAppear and onDisappear events, however those does not get triggered when new iOS 13 modals are used or when transparent modals are displayed. This is because with transparent modals the view below is still visible. We therefore needed another way of notifying the library that screen transition have finished despite the fact that disappear event couldn't be triggered.
As a part of this change I also refactored invalid ref cycle-break code on iOS which was ought to remove reference cycle between view and view controller. This code have been moved to viewWillDisappear callback.
Also on Android part small refactoring has been done and we removed the necessity to keep mActiveScreens array which was occasionally getting out of sync with the list of active fragments.
The library would crash when the `mTopScreen` instance variable is null. This change checkes if it null before calling the `getScreen()` method. It also marks the method as having a nullable return value. The only place this method is used is already setup to handle `null` responses.
We currently cannot take any adventage of saving and restoring fragment state because view hierarchy of the unmounted fragment is retained by RN core anyways. Skipping sabe/restore will allow us to avoid unnecessary serializiation/deserialization of the view hierarchy but will also help circumvent some bugs which comes from the side effects of restoring. One of the bugs have been reported in #162 and in that particular case restoring result in InputText change event being triggered with stale value. This was happening in the situation when we were going back to a screen with InputText component.
This change fixes the problem with missing text selection modals when text input component is rendered under screen container. The reason turned out to be a problem with Android TextView implementation which expect the component to be attached to window at the moment when layout happens (see Editor.java#prepareCursorControllers and TextView.java$makeNewLayout). Apparently, within screens container layout will happen before the component of a given screen are attached (because attaching is managed by the fragment manager which delays the moment at which the screen is attached). As a result the textView component does not get a chance to fetch proper window layout params such that it can tell whether it should support selection mode or not. We workaround this issue by providing window layout params directly from Screen class which is used instead of the DecorWindow in case TextView is not yet attached (it searches for the top-most parent when not attached).
This change fixes a problem with native stack on Android where we'd display a sw back button on screens that are nested stack navigatodespite the fact those screens were the initial screens in the whole container stack. In #306 we introuced a change that would allow for displaying sw back in nested stacks, however that change did not handle the case where screen is a root screen of a container that is nested and placed as an initial screen in another container.
This change allows for root screens of nested stack navigators on Android to display back button in the navigation bar. Navigating back is still possible using hw back because of the way hw back is handled by nested fragment containers. However, despite hw back functioning properly, before this change we would not allow for the soft back button to be rendered in the header. This change adds this possibility to keep software back consistent with the hw back button functionality. This behavior can still be disabled/adjusted using hideBackButton property of the header config component.
This change removes ScreenStackHeaderTitleView component and code that used to handle title view scaling. There were multiple issues related to scaling toolbar title views and it is not a priority at the moment to us to work on addressing those. The most frequent usecase is to put a fixed-dimensions view (e.g. logo or text) which can be now handled w/o this extra code.
We believe that it might be better to manage the logic of blocking interactions from JS with setNativeProps and pointerEvents.
The current logic leads to some issues we're facing within React Navigation. There might be a state when more that one screen can be active. E.g. in modals we want to have two screens visible on the stack and have enabled interaction on the bottom one.
New code should not give any issues, because even though we need to handle activation of each screen from JS and this will just introduce an additional native call for enabling / disabling pointer events.
On iOS, we're not using React Native Screens in React Navigation for now and we'd like to focus on it later because right now we're not observing that crucial performance issues.
When you have 2 screens in a stack with the bottom one with gestureEnabled=false using the back gesture causes the screen to become unresponsive. This seems to be caused by setting interactivePopGestureRecognizer.enabled = NO as soon as the gesture starts. This causes the gesture to cancel immediately and leaves the screen in an unresponsive state.
<Stack><Screen gestureEnabled={false} /><Screen /></Stack>
To fix this instead of using interactivePopGestureRecognizer.enabled we can leverage the existing delegate that we have in RNScreenStack. In gestureRecognizerShouldBegin we can check if the top screen has gestures enabled.
To make this simpler I moved the gestureEnabled config to Screen instead of HeaderConfig. I think it also makes more sense conceptually since the gesture is tied to the screen and not the header. It also simplifies the android code a bit.
This is a breaking change.
Update
This now only moves the config to screen since a separate fix was merged for the bug.
This change removes the behavior that's been added for an unclear reason. We may consider reverting this one, however I was unable to reproduce a problem when setting header title view layout measurements was necessary. To the contrary it was causing issues in the case when the title header view updates. In such a case we would never update width/height so the updated view might be cropped. In addition we are changing the way we schedule native layout updates. Previously we'd use handler.post but it was causing one frame delay so this change migrates us to use choreographer.
This change fixes the issue with auto focusing text input fields inside native stack. Due to the fact we perform hide and show transactions in order to control back stack the hiding part would propagate visibility change event down the view hierarchy. As a result views would receive that visibility event and blur themselves resulting in textinput not getting the focus when the fragment mounts. It turns out however that for the back stack to function properly we don't need to run hide and show operation within the transaction because show is idempotent. So we change our back stack handling transaction to only call show.
This change fixes crash caused when nested stack is detached due to the parent stack being closed. As a result we may end up in a situation when fragment manager is not yet set despite onDetach callback being call. When fragment manager is not set we can also ignore the operations we should've performed in on detach. This change adds a null check before we call into fragment manager in on detach callback.
This change fixes the issue when left item added to native stack header on Android would by default be shifted from the screen edge by some amount. This turned out to be the default config of the native toolbar which applies some padding on the left side. WIn this change we reset that padding to always be 0 to let the position be specified from react. Note that the setting we reset does not influence the position of the native back navigation button as Android does not apply the padding in case navigation back icon is rendered.
Code to reproduce and test: https://snack.expo.io/@angelikaserwa/humiliated-bagel.
Switch to the Settings tab, then go back to the Home tab and press the details button. Nothing happens. It was because after re-attaching we were using a destroyed FragmentManager that was cached inside the ScreenContainer class.
Then, when we go back from Details to Home screen, using a hardware back button, an exception occured: `The specified child already has a parent. You must call removeView() on the child's parent first`.
I fixed this by calling `removeAllViews()` when detaching container from window and forcing an update on re-attach.
This change prevents toolbar from updating when there is a transition happening to the view. This fixes the issue where header subviews would disappear when going back from a given screen. The root cause was that the config view manager would trigger subviews removal and re-layout itself. As aresult if we had a custom back button that back button would get removed and the title would move into its place while the screen animation is running. In order to prevent that we hook into View Manager's onDropViewInstance to notify header config that it is about to be destroyed. This in turn prevents any updates to be made to the toolbar config.
This change fixes the problem when header update would get triggered while the header config subviews are not yet fully unmounted. The root cause of this problem is kind of a mystery, I encouneter a crash when going back using back button from a screen that have custom back button image rendered. Turned out that after implementing removeAll schema the problem no longer occurs. I didn't have enough time to investigate it further but supporting removeAll schema is an improvement regardless so going with this solution for now.
For customizing back button image we use platform native functionality that is: `setBackIndicatorImage` on iOS and `setHomeAsUpIndicator` on Android.
The reason we don't do that just by setting left item is that we get a couple of things for free such as handling RTL properly, working accessibility features, handling prop for hiding back button and a couple more.
Unfortunately there are some downsides to that approach too. We need to install the back button as an Image component from the JS side, and the extract the image payload on the native side to set it with the navigator. This is specifically problematic in DEV mode where images are loaded asynchronously over HTTP from the packager. In order for that to work we had to employ a few hacks (more comments on that in the code).
Appear event is used by react-navigation to properly dispatch focus. It is important that appear is dispatched after dismissed event. The reverse order of actions would result in getting react-navigation stack in a weird state.
It is relatively streightforward to implement onAppear event on iOS where we hook into didAppear callback from UIViewController. It gets dispatched in the right moment, that is when the transition is fully over.
On Android however it is much more tricky. There is no standard way to be notified from the fragment level that fragment transition finished. One way that is frequently recommended is to override Fragment.onCreateAnimation. However, this only works when custom transitions are provided (e.g. if we set the transition to use fade animation). As we want the platform native transition to be run by default we had to look for other ways. The current approach relies on fragment container's callbacks startViewTransition and endViewTransition, with the latter being triggered once the animation is over. We also need to take into account that a good starting point for the transition is when we call commit on fragment transaction. We use these two methods to determine if the fragment is instantiated (onCreate) within a running transaction and if so we schedule event dispatch at the moment when endViewTransition is called.
Another change this commit introduces on the Android side is that we no longer rely on show/hide for replacing fragments on stack and we now use add/remove transaction methods. Due to this change we had to make our fragments reusable and make onCreateView indempotent.
This change fixes the problem when there are changes being made to the stack while the hosting activity is in paused state (e.g. an activity-based dialog from google play services). In such a case it is not allowed to do any changes which can affect fragment manager's state (e.g. updating backstack). For other changes we use `commitAllowingStatLoss` to indicate we are not interested in fragment manager handling their restoration and hence we can perform most operations. Unfortunately installing back handler is not allowed without state change and we need to wait until the fragment host is resumed before we install it.
This change fixes the issue of handling transparent status bar. In such a case the navbar should became slightly bigger in order to fill the status bar space that now belongs to the window. In order for that to happen we use `setFitsSystemWindows` on main screen layout, on app bar layout and on toolbar (the toolbar is the view that gets bigger in the end).
Note that this currently only work if we use Android's native mechanism for changingthe status bar behavior and not using `StatusBar` module from RN. It is because `StausBar` module actually strips top insets from the event that gets delivered to the views. For the time being you can try the following to change status bar:
```
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
```
Note that we are also disabling app bar's scroll behavior. We wasn't utilising this yet, and it cause some weird errors on hot reload because scrolling behavior actually sets `firsSystemWindows` flag on the screen view which ended up consuming insets before appbar would get them.
This change fixes two issues related to stack nesting on Android.
First one being the fact that root and nested fragments were relying on the same fragment manager as opposed to using nested fragment manager structure (via getChildFragmentManager). This resulted in an unprodictable behavior when displaying the top screen. This commit changes this behavior by correctly returning child fragment manager or root fragment manager depending on the containers hierarchy.
Second issue was related to back stack handling and resulted in always the root fragment manager interacting. Instead we want the bottommost active stack to handle back presses. This has been addressed by adding `setPrimaryNavigationFragment` when creating back stack entries.
When some of config props change or new header items are added we need to perform a header update. This diff adds logic to trigger updating header props when that happens.
There was an issue with back stack listener being triggered after reload caused by the fact we weren't cleaning up stack manager's back stack on reload. As a result after reload listener would get triggered with and empty stack first and only then with a back stack with 1 item. Consequence of the first trigger was that we'd call dismiss and move back from the top screen which wasn't a desirable behavior.
This is a similar fix to the one already merged in #215 but for Android this time. We change default screen container to layout its direct children without using flexbox. This is to follow the same pattern as with native stack container.
Before this change we'd rely on new androidx OnBackPressedCallback mechanism. It turns out not to work well in a few cases. The biggest problem with it was that when registered it'd never allow for the hw back button press to fallback to a system default behaviour and instead would always "still" that event. After several attempts of trying to make it work I decided to revert back to a FragmentManager's default solution.
This change adds an ability to use FragmentManager's back stack API. There are also a few problems with it we need to workaround though. One of the problem is the fact that we can not modify back stack history. What we do because of that is that we try to keep at most one item on the back stack and reset it each time our native stack updates. In order for that to work we create a fake transaction that hides and shows the same screen that displays on top. Thanks to that when hw back is pressed the built in transaction rollback logic does nothing to the UI and allows us to handle back navigation using back stack change listener.
A few bugs fixed with android native stack in this commit:
- fixed a problem with views not resizing when keyboard appears, the bug was due to onMeasure forwardin getHeight which was the old height of the container instead of the changed one
- fixed a problem with back button behavior not being consistent. Now back button is controlled by 'gestureEnabled' to emulate iOS behavior where there is no hw back button but you can swipe back instead.
- added compatibility for "contained" modal styles - for now we always render "containted" fragments on Android, plan to add a way to render in dialogs in the future
* Let UINavController control subcontroller view's frames.
This PR changes the way we've been handling yoga <> NavController layout interactions. Now we ignore frame updates coming from react for the main Screen view to allow NavController take the controll. In order to keep yoga working we now use `setSize` to pass the dimensions of the view back to yoga such that it can properly calculate layout of the views under Screen component.
* Header resizing fixes for Android.
In this change we use CoordinatorLayout as a stack screen container to handle rendering of toolbar and screen content. Thanks to this approach we can support collapsable bars in the future. Instead of relying on RN to layout screen container when renered under ScreenStack we rely on Android native layout to measure and position screen content and then use UIManager.setNodeSize to communicate that back to react.
I was getting the following compilation errors when I pulled in this repo.
```
error: unmappable character for encoding ASCII
```
The root cause seems to be the character below was not from the ASCII set.
Add suport to gradle 4.10.1 or high!
The new version of android studio 3.3 recommendete to update gradle project to 4.10.1
> To take advantage of the latest features, improvements, and security fixes, we strongly recommend that you update the Android Gradle plugin to version 3.3.0 and Gradle to version 4.10.1. [Release notes ](https://developer.android.com/studio/releases/gradle-plugin)
>Android plugin 3.2.0 and higher now support building the Android App Bundle—a new upload format that defers APK generation and signing to compatible app stores, such as Google Play. With app bundles, you no longer have to build, sign, and manage multiple APKs, and users get smaller, more optimized downloads. [Learn more](https://developer.android.com/guide/app-bundle/?utm_source=android-studio)
but if the upgrade to the new Android gradle warning come up, becouse was obsoleted
> WARNING: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'.
> It will be removed at the end of 2019.
> For more information, [see ](https://d.android.com/r/tools/task-configuration-avoidance.)
> To determine what is calling variant.getJavaCompile(), use -Pandroid.debug.obsoleteApi=true on the command line to display a stack trace.
Changelog:
----------
[Android] [Deprecated] - fix warinings obsolete to update to gradle 4.10.1 or high
Test Plan:
----------
change gradle-wrapper.proprerties:
`- distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip`
`+ distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip`
and in build.gradle
` - classpath 'com.android.tools.build:gradle:3.2.0'`
`+ classpath 'com.android.tools.build:gradle:3.3.0'`
for warnnings starts, use this change and fix the problem! =)
This fixes crash on Expo client which is wrapping Activity prior to passing it as a context to the root view.
After my recent change in the logic on how we access main activity we know extract the reference to it using `getContext` from the root view. Previously we were using `getTopLevelActivity` which wasn't working well in the cases where other non-react-native activities were transitioning in or out. The new approach however turned out not to be the best as for example expo client does not pass activity instance as a context directly to the root view. Instead the activity class is wrapped in ContextThemeWrapper ([see it here](41458d1de9/android/expoview/src/main/java/versioned/host/exp/exponent/ReactUnthemedRootView.java (L13))).
We now try to unwrap the context if it is not a fragment activity using `getBaseContext`
This fixes https://github.com/expo/expo/issues/3191
This change makes us reach to fragment manager only when screen container is attached to window (and therefore we are sure that the current activity is react one). The problem reported in #33 was due to the fact we'd do that when container is instantiated which does not necessarily mean that react activity is in foreground. We didn't need to actually access fragment manager while not in foreground so this change removes fragmentManager as a member and we get it directly from context when needed.
Supposedly fixes#33
This change adds an ability for screen container on Android to apply the correct mechanism for transparent layer blending. This is specifically important as screens are usually a complex views that may displays many layers and while transitioning often opacity is used to animate these. When we detect screen transitioning we (a) turn on offscreen alpha compositing (which makes the opacity being applied for the whole screen layer at once instead of making all the children semi-transparent) and also (b) turn on hardware layer that makes offscreen compositing render to GPU (which is both faster and consumes only GPU memory).
In addition to that change we also need to disable wrapping Screen's children with View, as in such a case opacity is applied on the underlying View instead of a Screen. That workaround has been added because of a bug in Animated library and fixed in RN 0.57+