react-native-screens 5.0.0-alpha.2 → 5.0.0-alpha.3
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/Package.swift +87 -0
- package/README.md +0 -7
- package/android/src/main/cpp/NativeProxy.cpp +30 -6
- package/android/src/main/cpp/NativeProxy.h +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/common/container/Container.kt +8 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/container/ContainerItem.kt +13 -1
- package/android/src/main/java/com/swmansion/rnscreens/common/text/ReactTextAppearance.kt +62 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearance.kt +20 -0
- package/android/src/main/java/com/swmansion/rnscreens/common/text/TextAppearanceDefaults.kt +65 -0
- package/android/src/main/java/com/swmansion/rnscreens/helpers/PropIconResolver.kt +17 -23
- package/android/src/main/java/com/swmansion/rnscreens/legacy/Screen.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/legacy/ScreenStack.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetBehaviorController.kt +49 -18
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetDimensionsCoordinator.kt +65 -40
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetKeyboardCoordinator.kt +101 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/coordinator/FormSheetNativeDismissCoordinator.kt +0 -6
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetAvailableHeightProvider.kt +44 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialog.kt +35 -3
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/core/FormSheetDialogManager.kt +33 -106
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/interfaces/FormSheetDialogEventEmitter.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/model/FormSheetDetents.kt +61 -15
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetAnimatorFactory.kt +8 -9
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDimmingManager.kt +115 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetDismissalOrigin.kt +22 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentation.kt +173 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationManager.kt +141 -49
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetPresentationState.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/native/presentation/FormSheetStackRegistry.kt +28 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/event/FormSheetDismissEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostEventEmitter.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/formsheet/react/host/FormSheetHostViewManager.kt +2 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderApplicator.kt +369 -58
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderBackPressHandler.kt +11 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt +143 -98
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt +155 -23
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderContentScrimDrawable.kt +36 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderCollapsedTitleGravityMode.kt +6 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfig.kt +170 -167
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigViewManager.kt +385 -12
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationObserver.kt +0 -4
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderConfigurationProviding.kt +32 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderDelegate.kt +0 -7
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/config/StackHeaderInvalidationFlags.kt +7 -2
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuApplicator.kt +27 -280
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuController.kt +448 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuDelegate.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuMapper.kt +28 -20
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuConfig.kt +3 -55
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuGroupMetadata.kt +1 -10
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuItemConfig.kt +1 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/model/StackHeaderToolbarMenuModel.kt +180 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementOptions.kt +57 -6
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementRawUpdate.kt +3 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuElementUpdate.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuIconResolver.kt +4 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/update/StackHeaderToolbarMenuUpdateQueue.kt +23 -9
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackContainer.kt +103 -3
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHost.kt +13 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/host/StackHostViewManager.kt +14 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreen.kt +4 -0
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragment.kt +20 -1
- package/android/src/main/java/com/swmansion/rnscreens/stack/screen/StackScreenFragmentDelegate.kt +12 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/appearance/TabsAppearanceCoordinator.kt +2 -4
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/MenuHelpers.kt +5 -23
- package/android/src/main/java/com/swmansion/rnscreens/tabs/container/TabsContainer.kt +46 -25
- package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHost.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/host/TabsHostA11yCoordinator.kt +23 -2
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreen.kt +17 -0
- package/android/src/main/java/com/swmansion/rnscreens/tabs/screen/TabsScreenFragment.kt +3 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/DimensionUtils.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/utils/StyleUtils.kt +22 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_available_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_large_entire_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_available_space.xml +8 -0
- package/android/src/main/res/base/layout/rns_collapsing_toolbar_medium_entire_space.xml +8 -0
- package/cpp/legacy/RNSScreenRemovalListener.cpp +31 -1
- package/cpp/legacy/RNSScreenRemovalListener.h +18 -3
- package/ios/RNSEnums.h +2 -0
- package/ios/conversion/RNSConversions.h +7 -108
- package/ios/legacy/RNSFullWindowOverlay.mm +1 -1
- package/ios/legacy/RNSModalScreen.mm +1 -1
- package/ios/legacy/RNSScreen.mm +8 -19
- package/ios/legacy/RNSScreenStackHeaderConfig.mm +3 -1
- package/ios/legacy/RNSScreenStackHeaderSubview.mm +1 -1
- package/ios/legacy/RNSScreenWindowTraits.mm +5 -13
- package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.h +1 -3
- package/ios/modals/form-sheet/RNSFormSheetConfigurationApplicator.mm +11 -22
- package/ios/modals/form-sheet/RNSFormSheetContentController.h +1 -1
- package/ios/modals/form-sheet/RNSFormSheetContentController.mm +7 -18
- package/ios/modals/form-sheet/RNSFormSheetHostComponentView.mm +4 -4
- package/ios/modals/form-sheet/RNSFormSheetHostShadowStateProxy.mm +2 -2
- package/ios/modals/form-sheet/RNSFormSheetUpdateFlags.h +3 -0
- package/ios/safe-area/RNSSafeAreaViewComponentView.mm +2 -2
- package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.mm +2 -3
- package/ios/split/RNSSplitAppearanceApplicator.h +4 -4
- package/ios/split/RNSSplitAppearanceApplicator.mm +53 -56
- package/ios/split/RNSSplitHostComponentEventEmitter.h +1 -0
- package/ios/split/RNSSplitHostComponentEventEmitter.mm +1 -1
- package/ios/split/RNSSplitHostComponentView.h +5 -15
- package/ios/split/RNSSplitHostComponentView.mm +55 -6
- package/ios/split/RNSSplitHostController.h +25 -6
- package/ios/split/RNSSplitHostController.mm +24 -92
- package/ios/split/RNSSplitHostProviders.h +67 -0
- package/ios/split/RNSSplitScreenComponentView.h +0 -39
- package/ios/split/RNSSplitScreenComponentView.mm +27 -10
- package/ios/split/RNSSplitScreenController.h +26 -3
- package/ios/split/RNSSplitScreenController.mm +13 -30
- package/ios/split/RNSSplitScreenShadowStateProxy.h +1 -40
- package/ios/split/RNSSplitScreenShadowStateProxy.mm +1 -26
- package/ios/split/conversion/RNSConversions-SplitView.h +56 -0
- package/ios/{conversion → split/conversion}/RNSConversions-SplitView.mm +4 -1
- package/ios/stack/conversion/RNSConversions-Stack.h +32 -0
- package/ios/stack/conversion/RNSConversions-Stack.mm +55 -0
- package/ios/stack/header/RNSStackHeaderAppearanceMapper.h +26 -0
- package/ios/stack/header/RNSStackHeaderAppearanceMapper.mm +88 -0
- package/ios/stack/header/RNSStackHeaderConfigComponentView.h +10 -1
- package/ios/stack/header/RNSStackHeaderConfigComponentView.mm +67 -35
- package/ios/stack/header/RNSStackHeaderConfigDataProviding.h +6 -0
- package/ios/stack/header/RNSStackHeaderConfigShadowStateProxy.mm +1 -1
- package/ios/stack/header/RNSStackHeaderIconResolver.mm +7 -0
- package/ios/stack/header/RNSStackHeaderItemComponentView.h +3 -1
- package/ios/stack/header/RNSStackHeaderItemComponentView.mm +15 -2
- package/ios/stack/header/RNSStackHeaderItemDataProviding.h +2 -0
- package/ios/stack/header/RNSStackHeaderItemShadowStateProxy.mm +1 -1
- package/ios/stack/header/RNSStackHeaderItemSpacerComponentView.mm +2 -1
- package/ios/stack/header/RNSStackHeaderMenuMapper.mm +4 -0
- package/ios/stack/host/RNSStackNavigationBar.h +25 -0
- package/ios/stack/host/RNSStackNavigationBar.mm +80 -0
- package/ios/stack/host/RNSStackNavigationBarCoordinator.h +8 -0
- package/ios/stack/host/RNSStackNavigationBarCoordinator.mm +11 -0
- package/ios/stack/host/RNSStackNavigationController.mm +13 -5
- package/ios/stack/host/RNSStackOperation.h +0 -2
- package/ios/stack/screen/RNSStackScreenComponentView.mm +9 -2
- package/ios/stack/screen/RNSStackScreenController.mm +12 -0
- package/ios/stack/screen/RNSStackScreenHeaderCoordinator.h +8 -0
- package/ios/stack/screen/RNSStackScreenHeaderCoordinator.mm +98 -16
- package/ios/tabs/RNSTabBarAppearanceCoordinator.mm +40 -9
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.h +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryComponentView.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryContentComponentView.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryEventEmitter.mm +1 -1
- package/ios/tabs/bottom-accessory/RNSTabsBottomAccessoryShadowStateProxy.mm +1 -1
- package/ios/tabs/conversion/RNSConversions-Tabs.h +78 -0
- package/ios/{conversion → tabs/conversion}/RNSConversions-Tabs.mm +8 -71
- package/ios/tabs/extensions/RNSTabsHostComponentView+RNSImageLoader.h +1 -1
- package/ios/tabs/host/RNSTabsHostComponentView.h +2 -0
- package/ios/tabs/host/RNSTabsHostComponentView.mm +10 -3
- package/ios/tabs/host/RNSTabsHostEventEmitter.mm +1 -1
- package/ios/tabs/screen/RNSTabsScreenComponentView.mm +20 -5
- package/ios/utils/RNSDefines.h +3 -0
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js +34 -19
- package/lib/commonjs/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js +33 -1
- package/lib/commonjs/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/commonjs/components/stack/host/StackHost.js +9 -6
- package/lib/commonjs/components/stack/host/StackHost.js.map +1 -1
- package/lib/commonjs/components/stack/index.js.map +1 -1
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js +1 -0
- package/lib/commonjs/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/stack/StackHostNativeComponent.js.map +1 -1
- package/lib/commonjs/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.android.js +35 -20
- package/lib/module/components/stack/header/StackHeaderConfig.android.js.map +1 -1
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js +34 -2
- package/lib/module/components/stack/header/StackHeaderConfig.ios.js.map +1 -1
- package/lib/module/components/stack/host/StackHost.js +9 -6
- package/lib/module/components/stack/host/StackHost.js.map +1 -1
- package/lib/module/components/stack/index.js.map +1 -1
- package/lib/module/components/tabs/host/TabsHost.ios.js +1 -0
- package/lib/module/components/tabs/host/TabsHost.ios.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderConfigAndroidNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderConfigIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHeaderItemIOSNativeComponent.js.map +1 -1
- package/lib/module/fabric/stack/StackHostNativeComponent.js.map +1 -1
- package/lib/module/fabric/tabs/TabsHostIOSNativeComponent.js.map +1 -1
- package/lib/typescript/components/modals/form-sheet/FormSheet.types.d.ts +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts +616 -21
- package/lib/typescript/components/stack/header/StackHeaderConfig.android.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts +226 -2
- package/lib/typescript/components/stack/header/StackHeaderConfig.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/header/StackHeaderConfig.types.d.ts +2 -2
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts +2 -0
- package/lib/typescript/components/stack/header/ios/StackHeaderItem.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.d.ts +1 -1
- package/lib/typescript/components/stack/host/StackHost.d.ts.map +1 -1
- package/lib/typescript/components/stack/host/StackHost.types.d.ts +24 -0
- package/lib/typescript/components/stack/host/StackHost.types.d.ts.map +1 -1
- package/lib/typescript/components/stack/index.d.ts +2 -2
- package/lib/typescript/components/stack/index.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.ios.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts +16 -0
- package/lib/typescript/components/tabs/host/TabsHost.ios.types.d.ts.map +1 -1
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts +3 -0
- package/lib/typescript/components/tabs/host/TabsHost.types.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts +52 -0
- package/lib/typescript/fabric/stack/StackHeaderConfigAndroidNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts +25 -1
- package/lib/typescript/fabric/stack/StackHeaderConfigIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts +2 -0
- package/lib/typescript/fabric/stack/StackHeaderItemIOSNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts +3 -1
- package/lib/typescript/fabric/stack/StackHostNativeComponent.d.ts.map +1 -1
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts +1 -0
- package/lib/typescript/fabric/tabs/TabsHostIOSNativeComponent.d.ts.map +1 -1
- package/package.json +8 -7
- package/react-native.config.js +3 -0
- package/src/components/modals/form-sheet/FormSheet.types.ts +1 -1
- package/src/components/stack/header/StackHeaderConfig.android.tsx +48 -23
- package/src/components/stack/header/StackHeaderConfig.android.types.ts +637 -21
- package/src/components/stack/header/StackHeaderConfig.ios.tsx +45 -1
- package/src/components/stack/header/StackHeaderConfig.ios.types.ts +238 -2
- package/src/components/stack/header/StackHeaderConfig.types.ts +2 -2
- package/src/components/stack/header/ios/StackHeaderItem.ios.types.ts +2 -0
- package/src/components/stack/host/StackHost.tsx +4 -2
- package/src/components/stack/host/StackHost.types.ts +26 -0
- package/src/components/stack/index.ts +6 -1
- package/src/components/tabs/host/TabsHost.ios.tsx +1 -0
- package/src/components/tabs/host/TabsHost.ios.types.ts +16 -0
- package/src/components/tabs/host/TabsHost.types.ts +3 -0
- package/src/fabric/stack/StackHeaderConfigAndroidNativeComponent.ts +78 -0
- package/src/fabric/stack/StackHeaderConfigIOSNativeComponent.ts +32 -0
- package/src/fabric/stack/StackHeaderItemIOSNativeComponent.ts +2 -0
- package/src/fabric/stack/StackHostNativeComponent.ts +11 -2
- package/src/fabric/tabs/TabsHostIOSNativeComponent.ts +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingView.kt +0 -83
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewManager.kt +0 -89
- package/android/src/main/java/com/swmansion/rnscreens/modals/dimmingview/DimmingViewPointerEvents.kt +0 -18
- package/android/src/main/java/com/swmansion/rnscreens/stack/header/toolbar/StackHeaderToolbarMenuSelectionController.kt +0 -83
- package/ios/conversion/RNSConversions-Stack.h +0 -32
- package/ios/conversion/RNSConversions-Stack.mm +0 -41
- package/ios/conversion/always_false.h +0 -31
- /package/ios/{conversion → scroll-view-marker/conversion}/RNSConversions-ScrollViewMarker.h +0 -0
package/android/src/main/java/com/swmansion/rnscreens/stack/header/StackHeaderCoordinatorLayout.kt
CHANGED
|
@@ -2,16 +2,18 @@ package com.swmansion.rnscreens.stack.header
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.
|
|
5
|
+
import android.content.res.Configuration
|
|
6
|
+
import android.os.Parcelable
|
|
7
|
+
import android.util.SparseArray
|
|
6
8
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
7
9
|
import android.widget.FrameLayout
|
|
8
|
-
import androidx.activity.OnBackPressedDispatcherOwner
|
|
9
10
|
import androidx.appcompat.view.ContextThemeWrapper
|
|
10
11
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
11
|
-
import com.facebook.react.bridge.ReactContext
|
|
12
12
|
import com.google.android.material.R
|
|
13
13
|
import com.google.android.material.appbar.AppBarLayout
|
|
14
|
-
import com.
|
|
14
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeCoordinator
|
|
15
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeListener
|
|
16
|
+
import com.swmansion.rnscreens.common.colorscheme.ColorSchemeProviding
|
|
15
17
|
import com.swmansion.rnscreens.stack.header.appbar.StackHeaderAppBarLayout
|
|
16
18
|
import com.swmansion.rnscreens.stack.header.appbar.StackHeaderScrollingViewBehavior
|
|
17
19
|
import com.swmansion.rnscreens.stack.header.config.OnHeaderConfigurationAttachListener
|
|
@@ -19,9 +21,6 @@ import com.swmansion.rnscreens.stack.header.config.StackHeaderConfigurationObser
|
|
|
19
21
|
import com.swmansion.rnscreens.stack.header.config.StackHeaderConfigurationProviding
|
|
20
22
|
import com.swmansion.rnscreens.stack.header.config.StackHeaderDelegate
|
|
21
23
|
import com.swmansion.rnscreens.stack.header.config.StackHeaderInvalidationFlags
|
|
22
|
-
import com.swmansion.rnscreens.stack.header.toolbar.StackHeaderToolbarMenuApplicator
|
|
23
|
-
import com.swmansion.rnscreens.stack.header.toolbar.StackHeaderToolbarMenuSelectionController
|
|
24
|
-
import com.swmansion.rnscreens.stack.header.toolbar.update.StackHeaderToolbarMenuElementUpdate
|
|
25
24
|
import com.swmansion.rnscreens.stack.screen.StackScreen
|
|
26
25
|
|
|
27
26
|
/**
|
|
@@ -34,7 +33,9 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
34
33
|
context: Context,
|
|
35
34
|
internal val stackScreen: StackScreen,
|
|
36
35
|
private val canNavigateBack: Boolean,
|
|
37
|
-
|
|
36
|
+
private val backPressHandler: StackHeaderBackPressHandler,
|
|
37
|
+
) : CoordinatorLayout(context),
|
|
38
|
+
ColorSchemeProviding {
|
|
38
39
|
// region Config attach / detach
|
|
39
40
|
|
|
40
41
|
private var currentProvider: StackHeaderConfigurationProviding? = null
|
|
@@ -52,7 +53,10 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
52
53
|
delegate: StackHeaderDelegate?,
|
|
53
54
|
) {
|
|
54
55
|
// Disconnect old config to prevent spurious updates from a detached config.
|
|
55
|
-
currentProvider?.
|
|
56
|
+
currentProvider?.let {
|
|
57
|
+
it.setConfigurationObserver(null)
|
|
58
|
+
it.toolbarMenuController.detach()
|
|
59
|
+
}
|
|
56
60
|
|
|
57
61
|
currentProvider = provider
|
|
58
62
|
currentDelegate = delegate
|
|
@@ -72,34 +76,6 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
72
76
|
private val configObserver =
|
|
73
77
|
object : StackHeaderConfigurationObserver {
|
|
74
78
|
override fun onConfigChanged(config: StackHeaderConfigurationProviding) = processUpdate(config)
|
|
75
|
-
|
|
76
|
-
override fun onMenuElementsUpdated(updates: List<StackHeaderToolbarMenuElementUpdate>) {
|
|
77
|
-
val toolbar = appBarLayout?.toolbar
|
|
78
|
-
if (toolbar == null) {
|
|
79
|
-
Log.w(
|
|
80
|
-
TAG,
|
|
81
|
-
"[RNScreens] Dropping ${updates.size} resolved toolbar menu update(s): " +
|
|
82
|
-
"the header toolbar is not currently attached (header hidden or detached).",
|
|
83
|
-
)
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
// Apply every element first, collecting the groups whose selection changed,
|
|
87
|
-
// then emit a single coalesced event per affected group.
|
|
88
|
-
val affectedGroups = LinkedHashSet<String>()
|
|
89
|
-
for (update in updates) {
|
|
90
|
-
StackHeaderToolbarMenuApplicator.updateToolbarMenuElement(
|
|
91
|
-
toolbar,
|
|
92
|
-
selectionController.forwardIdMap,
|
|
93
|
-
update.id,
|
|
94
|
-
update.options,
|
|
95
|
-
)
|
|
96
|
-
val checked = update.options.checked
|
|
97
|
-
if (checked != null) {
|
|
98
|
-
selectionController.applyGroupItemStateChange(toolbar, update.id, checked)?.let(affectedGroups::add)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
affectedGroups.forEach { groupId -> emitGroupSelection(toolbar, groupId) }
|
|
102
|
-
}
|
|
103
79
|
}
|
|
104
80
|
|
|
105
81
|
// endregion
|
|
@@ -107,7 +83,8 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
107
83
|
// region Layout callbacks
|
|
108
84
|
|
|
109
85
|
private val appBarOffsetListener =
|
|
110
|
-
AppBarLayout.OnOffsetChangedListener {
|
|
86
|
+
AppBarLayout.OnOffsetChangedListener { appBar, verticalOffset ->
|
|
87
|
+
evaluateCollapseState(appBar, verticalOffset)
|
|
111
88
|
onMaybeHeaderLayoutChanged()
|
|
112
89
|
}
|
|
113
90
|
|
|
@@ -133,6 +110,20 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
133
110
|
StackHeaderFrameSynchronizer.sync(appBar, provider, delegate)
|
|
134
111
|
}
|
|
135
112
|
|
|
113
|
+
// Tracks whether the app bar is currently scrolled to its fully collapsed offset. Used to work
|
|
114
|
+
// around a Material offset bug when the title/subtitle changes at runtime — see
|
|
115
|
+
// StackHeaderApplicator.applyTitleAndSubtitle. This should be equivalent to Material's
|
|
116
|
+
// `collapsingTitleHelper.getExpansionFraction() == 1f` condition.
|
|
117
|
+
private var isAppBarFullyCollapsed = false
|
|
118
|
+
|
|
119
|
+
private fun evaluateCollapseState(
|
|
120
|
+
appBar: AppBarLayout,
|
|
121
|
+
verticalOffset: Int,
|
|
122
|
+
) {
|
|
123
|
+
val totalScrollRange = appBar.totalScrollRange
|
|
124
|
+
isAppBarFullyCollapsed = totalScrollRange > 0 && -verticalOffset >= totalScrollRange
|
|
125
|
+
}
|
|
126
|
+
|
|
136
127
|
// endregion
|
|
137
128
|
|
|
138
129
|
// region Header updates
|
|
@@ -145,19 +136,18 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
145
136
|
|
|
146
137
|
private val applicator = StackHeaderApplicator(wrappedContext)
|
|
147
138
|
|
|
148
|
-
private val selectionController = StackHeaderToolbarMenuSelectionController()
|
|
149
|
-
|
|
150
139
|
private var appBarLayout: StackHeaderAppBarLayout? = null
|
|
151
140
|
|
|
152
141
|
private val onNavigationIconClick: () -> Unit = {
|
|
153
|
-
|
|
154
|
-
(stackScreen.context as? ReactContext)?.currentActivity
|
|
155
|
-
as? OnBackPressedDispatcherOwner
|
|
156
|
-
activity?.onBackPressedDispatcher?.onBackPressed()
|
|
142
|
+
backPressHandler.handleHeaderBackButtonPress(stackScreen)
|
|
157
143
|
}
|
|
158
144
|
|
|
159
|
-
private fun processUpdate(
|
|
160
|
-
|
|
145
|
+
private fun processUpdate(
|
|
146
|
+
provider: StackHeaderConfigurationProviding,
|
|
147
|
+
forcedFlags: StackHeaderInvalidationFlags = StackHeaderInvalidationFlags.NONE,
|
|
148
|
+
) {
|
|
149
|
+
val effectiveFlags = provider.invalidationFlags or forcedFlags
|
|
150
|
+
val needsRebuild = effectiveFlags.needsRebuild
|
|
161
151
|
if (needsRebuild) {
|
|
162
152
|
resetHeader()
|
|
163
153
|
if (provider.hidden) {
|
|
@@ -179,22 +169,42 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
179
169
|
|
|
180
170
|
val appBar = appBarLayout
|
|
181
171
|
if (appBar != null) {
|
|
182
|
-
if (needsRebuild ||
|
|
183
|
-
applicator.
|
|
172
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.TITLE)) {
|
|
173
|
+
applicator.applyTitleAndSubtitle(appBar, provider, isAppBarFullyCollapsed)
|
|
184
174
|
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.TITLE)
|
|
185
175
|
}
|
|
186
176
|
|
|
187
|
-
if (needsRebuild ||
|
|
177
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.TITLE_APPEARANCE)) {
|
|
178
|
+
applicator.applyTitleAndSubtitleAppearance(appBar, provider)
|
|
179
|
+
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.TITLE_APPEARANCE)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.TITLE_POSITIONING)) {
|
|
183
|
+
applicator.applyTitlePositioning(appBar, provider)
|
|
184
|
+
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.TITLE_POSITIONING)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.CONTENT_INSETS)) {
|
|
188
|
+
applicator.applyContentInsets(appBar, provider)
|
|
189
|
+
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.CONTENT_INSETS)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.BACK_BUTTON)) {
|
|
188
193
|
applicator.applyBackButton(appBar.toolbar, provider, canNavigateBack, onNavigationIconClick)
|
|
189
194
|
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.BACK_BUTTON)
|
|
190
195
|
}
|
|
191
196
|
|
|
192
|
-
if (needsRebuild ||
|
|
197
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.SCROLL_FLAGS)) {
|
|
193
198
|
applicator.applyScrollFlags(appBar, provider)
|
|
194
199
|
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.SCROLL_FLAGS)
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
if (needsRebuild ||
|
|
202
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.BACKGROUND_COLORS)) {
|
|
203
|
+
applicator.applyBackgroundColors(appBar, provider)
|
|
204
|
+
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.BACKGROUND_COLORS)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.LIFT_ON_SCROLL)) {
|
|
198
208
|
// Lift-on-scroll is disabled in transparent mode: there is no content
|
|
199
209
|
// scrolling behavior installed and the app bar overlays the content.
|
|
200
210
|
applicator.applyLiftOnScroll(
|
|
@@ -205,44 +215,15 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
205
215
|
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.LIFT_ON_SCROLL)
|
|
206
216
|
}
|
|
207
217
|
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
StackHeaderToolbarMenuApplicator.generateToolbarMenuItemMappings(
|
|
211
|
-
provider.toolbarMenu,
|
|
212
|
-
)
|
|
213
|
-
val forwardGroupIdMap =
|
|
214
|
-
StackHeaderToolbarMenuApplicator.generateToolbarMenuGroupMappings(
|
|
215
|
-
provider.toolbarMenu,
|
|
216
|
-
)
|
|
217
|
-
val groupMetadata =
|
|
218
|
-
StackHeaderToolbarMenuApplicator.computeGroupMetadata(
|
|
219
|
-
provider.toolbarMenu,
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
StackHeaderToolbarMenuApplicator.validateRadioInitialSelection(provider.toolbarMenu)
|
|
223
|
-
|
|
224
|
-
selectionController.setMenuMaps(forwardIdMap, groupMetadata)
|
|
225
|
-
|
|
226
|
-
StackHeaderToolbarMenuApplicator.rebuildToolbarMenu(
|
|
227
|
-
appBar.toolbar,
|
|
228
|
-
provider.toolbarMenu,
|
|
229
|
-
forwardIdMap,
|
|
230
|
-
reverseIdMap,
|
|
231
|
-
forwardGroupIdMap,
|
|
232
|
-
groupDividerEnabled = provider.toolbarMenuGroupDividerEnabled,
|
|
233
|
-
onItemClicked = { id, menuItem ->
|
|
234
|
-
if (menuItem.isCheckable) {
|
|
235
|
-
selectionController.applyGroupItemStateChange(appBar.toolbar, id)?.let { groupId ->
|
|
236
|
-
emitGroupSelection(appBar.toolbar, groupId)
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
currentDelegate?.onMenuItemClicked(id)
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
)
|
|
243
|
-
|
|
218
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.TOOLBAR_MENU)) {
|
|
219
|
+
provider.toolbarMenuController.attach(appBar.toolbar)
|
|
244
220
|
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.TOOLBAR_MENU)
|
|
245
221
|
}
|
|
222
|
+
|
|
223
|
+
if (needsRebuild || effectiveFlags.containsAny(StackHeaderInvalidationFlags.OVERFLOW_ICON)) {
|
|
224
|
+
applicator.applyOverflowIcon(appBar.toolbar, provider)
|
|
225
|
+
provider.clearInvalidationFlags(StackHeaderInvalidationFlags.OVERFLOW_ICON)
|
|
226
|
+
}
|
|
246
227
|
}
|
|
247
228
|
|
|
248
229
|
onMaybeHeaderLayoutChanged()
|
|
@@ -250,13 +231,61 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
250
231
|
|
|
251
232
|
// endregion
|
|
252
233
|
|
|
253
|
-
// region
|
|
234
|
+
// region Color scheme
|
|
254
235
|
|
|
255
|
-
private
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
)
|
|
259
|
-
|
|
236
|
+
private val colorSchemeCoordinator = ColorSchemeCoordinator()
|
|
237
|
+
|
|
238
|
+
// Night mode the header visuals were last applied against. Unlike the coordinator's
|
|
239
|
+
// internal dedupe (reset on every setup()), this survives detach/reattach, skipping
|
|
240
|
+
// redundant full re-applies e.g. on tab switches.
|
|
241
|
+
private var appliedUiNightMode: Int =
|
|
242
|
+
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
|
243
|
+
|
|
244
|
+
override fun getResolvedUiNightMode() = colorSchemeCoordinator.getResolvedUiNightMode()
|
|
245
|
+
|
|
246
|
+
override fun addColorSchemeListener(listener: ColorSchemeListener) = colorSchemeCoordinator.addColorSchemeListener(listener)
|
|
247
|
+
|
|
248
|
+
override fun removeColorSchemeListener(listener: ColorSchemeListener) = colorSchemeCoordinator.removeColorSchemeListener(listener)
|
|
249
|
+
|
|
250
|
+
// No onConfigurationChanged override is needed: this view never sets its own colorScheme,
|
|
251
|
+
// so resolution always delegates to the parent provider, which does handle system changes.
|
|
252
|
+
|
|
253
|
+
override fun onAttachedToWindow() {
|
|
254
|
+
super.onAttachedToWindow()
|
|
255
|
+
colorSchemeCoordinator.setup(this) { applyUiNightMode(it) }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override fun onDetachedFromWindow() {
|
|
259
|
+
colorSchemeCoordinator.teardown()
|
|
260
|
+
super.onDetachedFromWindow()
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private fun applyUiNightMode(uiNightMode: Int) {
|
|
264
|
+
wrappedContext.setTheme(
|
|
265
|
+
when (uiNightMode) {
|
|
266
|
+
Configuration.UI_MODE_NIGHT_YES -> R.style.Theme_Material3Expressive_Dark_NoActionBar
|
|
267
|
+
Configuration.UI_MODE_NIGHT_NO -> R.style.Theme_Material3Expressive_Light_NoActionBar
|
|
268
|
+
else -> R.style.Theme_Material3Expressive_DayNight_NoActionBar
|
|
269
|
+
},
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if (uiNightMode == appliedUiNightMode) {
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
appliedUiNightMode = uiNightMode
|
|
277
|
+
currentProvider?.let {
|
|
278
|
+
// A rebuild is forced because MaterialToolbar snapshots its theme at construction:
|
|
279
|
+
// ripples, the overflow popup and menu item views resolve from that frozen copy, so
|
|
280
|
+
// only view recreation refreshes them.
|
|
281
|
+
val wasFullyCollapsed = isAppBarFullyCollapsed
|
|
282
|
+
processUpdate(it, forcedFlags = StackHeaderInvalidationFlags.STRUCTURE)
|
|
283
|
+
// The rebuilt app bar starts expanded; restore the fully-collapsed resting state
|
|
284
|
+
// so a scrolled-down screen doesn't jump. Fractional offsets reset.
|
|
285
|
+
if (wasFullyCollapsed) {
|
|
286
|
+
appBarLayout?.setExpanded(false, false)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
260
289
|
}
|
|
261
290
|
|
|
262
291
|
// endregion
|
|
@@ -269,7 +298,9 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
269
298
|
removeView(it)
|
|
270
299
|
}
|
|
271
300
|
appBarLayout = null
|
|
272
|
-
|
|
301
|
+
// A rebuilt header starts fully expanded; drop any stale collapsed state from the old one.
|
|
302
|
+
isAppBarFullyCollapsed = false
|
|
303
|
+
currentProvider?.toolbarMenuController?.detach()
|
|
273
304
|
}
|
|
274
305
|
|
|
275
306
|
private fun removeHeader() {
|
|
@@ -306,6 +337,22 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
306
337
|
|
|
307
338
|
// endregion
|
|
308
339
|
|
|
340
|
+
// region Instance state
|
|
341
|
+
|
|
342
|
+
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
|
|
343
|
+
// Do nothing. This view is the root of a fragment-managed, react-owned hierarchy that
|
|
344
|
+
// React Native keeps alive, so there is no need to serialize/deserialize native view
|
|
345
|
+
// state. View ids in this subtree are react tags, which are not stable identities -
|
|
346
|
+
// restoring state by id can apply state saved by one view type to a different one,
|
|
347
|
+
// crashing e.g. in CompoundButton (see #4523).
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
|
|
351
|
+
// Ignore restoring instance state too, as we are not saving anything anyways.
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// endregion
|
|
355
|
+
|
|
309
356
|
// region Init
|
|
310
357
|
|
|
311
358
|
internal var stackScreenWrapper: FrameLayout
|
|
@@ -334,6 +381,8 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
334
381
|
// region Teardown
|
|
335
382
|
|
|
336
383
|
internal fun tearDown() {
|
|
384
|
+
colorSchemeCoordinator.teardown()
|
|
385
|
+
|
|
337
386
|
removeHeader()
|
|
338
387
|
|
|
339
388
|
stackScreenWrapper.removeView(stackScreen)
|
|
@@ -346,8 +395,4 @@ internal class StackHeaderCoordinatorLayout(
|
|
|
346
395
|
}
|
|
347
396
|
|
|
348
397
|
// endregion
|
|
349
|
-
|
|
350
|
-
companion object {
|
|
351
|
-
private const val TAG = "StackHeaderCoordinatorLayout"
|
|
352
|
-
}
|
|
353
398
|
}
|
package/android/src/main/java/com/swmansion/rnscreens/stack/header/appbar/StackHeaderAppBarLayout.kt
CHANGED
|
@@ -2,14 +2,20 @@ package com.swmansion.rnscreens.stack.header.appbar
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.graphics.drawable.PaintDrawable
|
|
7
|
+
import android.view.LayoutInflater
|
|
5
8
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
|
6
9
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
|
7
|
-
import
|
|
10
|
+
import android.view.WindowInsets
|
|
11
|
+
import android.widget.TextView
|
|
8
12
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
9
|
-
import
|
|
13
|
+
import androidx.core.view.WindowInsetsCompat
|
|
14
|
+
import androidx.core.view.children
|
|
10
15
|
import com.google.android.material.appbar.AppBarLayout
|
|
11
16
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
|
12
17
|
import com.google.android.material.appbar.MaterialToolbar
|
|
18
|
+
import com.swmansion.rnscreens.stack.header.config.StackHeaderCollapsedTitleGravityMode
|
|
13
19
|
import com.swmansion.rnscreens.stack.header.config.StackHeaderType
|
|
14
20
|
import com.swmansion.rnscreens.utils.resolveDimensionAttr
|
|
15
21
|
|
|
@@ -18,6 +24,9 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
18
24
|
) : AppBarLayout(context) {
|
|
19
25
|
abstract val toolbar: MaterialToolbar
|
|
20
26
|
|
|
27
|
+
internal abstract val defaultContentInsetStart: Int
|
|
28
|
+
internal abstract val defaultContentInsetEnd: Int
|
|
29
|
+
|
|
21
30
|
init {
|
|
22
31
|
layoutParams =
|
|
23
32
|
CoordinatorLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
|
|
@@ -38,11 +47,66 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
38
47
|
layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT)
|
|
39
48
|
}
|
|
40
49
|
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
override val defaultContentInsetStart: Int = toolbar.contentInsetStart
|
|
51
|
+
override val defaultContentInsetEnd: Int = toolbar.contentInsetEnd
|
|
52
|
+
|
|
53
|
+
// Setting text size and typeface separately (Toolbar exposes only a whole text appearance)
|
|
54
|
+
// needs the title/subtitle TextViews, and Toolbar has no getter for them. Material locates
|
|
55
|
+
// them by matching the current text (internal ToolbarUtils.getTitleTextView); we attempt
|
|
56
|
+
// the same, but since we don't have current text, we use distinct placeholders - they force
|
|
57
|
+
// both views into existence and tell them apart — and clear the text only after capturing
|
|
58
|
+
// the references. Toolbar keeps both instances for its lifetime (empty text only detaches
|
|
59
|
+
// them), so the references stay valid.
|
|
60
|
+
internal val titleTextView: TextView
|
|
61
|
+
internal val subtitleTextView: TextView
|
|
62
|
+
|
|
63
|
+
// Keeps the status bar strip in sync with the bar's effective color through
|
|
64
|
+
// the lift animation (same per-frame mixed color Material applies to the
|
|
65
|
+
// background). Detached when an explicit statusBarScrimColor is set.
|
|
66
|
+
private var statusBarScrimSyncEnabled = false
|
|
67
|
+
private val statusBarScrimSync =
|
|
68
|
+
object : LiftOnScrollProgressListener() {
|
|
69
|
+
override fun onUpdate(
|
|
70
|
+
elevation: Float,
|
|
71
|
+
backgroundColor: Int,
|
|
72
|
+
progress: Float,
|
|
73
|
+
) {
|
|
74
|
+
statusBarForeground?.setTint(backgroundColor)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
43
77
|
|
|
44
78
|
init {
|
|
45
79
|
addView(toolbar)
|
|
80
|
+
|
|
81
|
+
// PaintDrawable is not color-extractable, so AppBarLayout's built-in
|
|
82
|
+
// colorSurface-keyed strip tint sync can never activate and fight the
|
|
83
|
+
// listener above (or an explicit scrim color) — regardless of theme.
|
|
84
|
+
statusBarForeground = PaintDrawable().apply { setTint(Color.TRANSPARENT) }
|
|
85
|
+
|
|
86
|
+
toolbar.title = TITLE_PLACEHOLDER
|
|
87
|
+
toolbar.subtitle = SUBTITLE_PLACEHOLDER
|
|
88
|
+
titleTextView = toolbar.findTextViewWithText(TITLE_PLACEHOLDER)
|
|
89
|
+
subtitleTextView = toolbar.findTextViewWithText(SUBTITLE_PLACEHOLDER)
|
|
90
|
+
toolbar.title = ""
|
|
91
|
+
toolbar.subtitle = ""
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
internal fun setStatusBarScrimSyncEnabled(enabled: Boolean) {
|
|
95
|
+
if (statusBarScrimSyncEnabled == enabled) return
|
|
96
|
+
statusBarScrimSyncEnabled = enabled
|
|
97
|
+
if (enabled) {
|
|
98
|
+
addLiftOnScrollProgressListener(statusBarScrimSync)
|
|
99
|
+
} else {
|
|
100
|
+
removeLiftOnScrollProgressListener(statusBarScrimSync)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private fun MaterialToolbar.findTextViewWithText(text: String): TextView =
|
|
105
|
+
children.filterIsInstance<TextView>().first { it.text?.toString() == text }
|
|
106
|
+
|
|
107
|
+
private companion object {
|
|
108
|
+
private const val TITLE_PLACEHOLDER = "rns_title"
|
|
109
|
+
private const val SUBTITLE_PLACEHOLDER = "rns_subtitle"
|
|
46
110
|
}
|
|
47
111
|
}
|
|
48
112
|
|
|
@@ -50,6 +114,7 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
50
114
|
internal class Collapsing(
|
|
51
115
|
context: Context,
|
|
52
116
|
val type: StackHeaderType,
|
|
117
|
+
collapsedTitleGravityMode: StackHeaderCollapsedTitleGravityMode,
|
|
53
118
|
) : StackHeaderAppBarLayout(context) {
|
|
54
119
|
override val toolbar =
|
|
55
120
|
MaterialToolbar(context).apply {
|
|
@@ -64,25 +129,21 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
64
129
|
}
|
|
65
130
|
}
|
|
66
131
|
|
|
132
|
+
override val defaultContentInsetStart: Int = toolbar.contentInsetStart
|
|
133
|
+
override val defaultContentInsetEnd: Int = toolbar.contentInsetEnd
|
|
134
|
+
|
|
135
|
+
// collapsedTitleGravityMode is a construction-time-only attr (no public setter), so both
|
|
136
|
+
// gravity modes are inflated from XML — differing only in that attribute — to stay 1:1.
|
|
67
137
|
internal val collapsingToolbarLayout: CollapsingToolbarLayout =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
CollapsingToolbarLayout(context, null, styleAttr).apply {
|
|
78
|
-
layoutParams =
|
|
79
|
-
LayoutParams(
|
|
80
|
-
MATCH_PARENT,
|
|
81
|
-
resolveDimensionAttr(context, sizeAttr),
|
|
82
|
-
)
|
|
83
|
-
addView(toolbar)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
138
|
+
(
|
|
139
|
+
LayoutInflater
|
|
140
|
+
.from(context)
|
|
141
|
+
.inflate(layoutResFor(type, collapsedTitleGravityMode), this, false)
|
|
142
|
+
as CollapsingToolbarLayout
|
|
143
|
+
).apply { addView(toolbar) }
|
|
144
|
+
|
|
145
|
+
private var trackedTopInset = 0
|
|
146
|
+
private var currentVerticalOffset = 0
|
|
86
147
|
|
|
87
148
|
init {
|
|
88
149
|
require(
|
|
@@ -92,6 +153,75 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
92
153
|
"[RNScreens] Collapsing StackHeaderAppBarLayout must be MEDIUM or LARGE type."
|
|
93
154
|
}
|
|
94
155
|
addView(collapsingToolbarLayout)
|
|
156
|
+
|
|
157
|
+
addOnOffsetChangedListener { _, verticalOffset ->
|
|
158
|
+
currentVerticalOffset = verticalOffset
|
|
159
|
+
updateContentScrimExclusion()
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Observed here because both AppBarLayout and CollapsingToolbarLayout
|
|
164
|
+
// install their own OnApplyWindowInsetsListener in their constructors —
|
|
165
|
+
// setting ours would silently replace Material's. Reads the same inset
|
|
166
|
+
// CTL uses for its status bar scrim bounds.
|
|
167
|
+
override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {
|
|
168
|
+
@Suppress("DEPRECATION")
|
|
169
|
+
trackedTopInset =
|
|
170
|
+
if (fitsSystemWindows) {
|
|
171
|
+
WindowInsetsCompat.toWindowInsetsCompat(insets, this).systemWindowInsetTop
|
|
172
|
+
} else {
|
|
173
|
+
0
|
|
174
|
+
}
|
|
175
|
+
updateContentScrimExclusion()
|
|
176
|
+
return super.dispatchApplyWindowInsets(insets)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* While a status bar scrim is installed, the content scrim skips the
|
|
181
|
+
* status-bar strip that the status bar scrim paints alone. Otherwise,
|
|
182
|
+
* the two scrims stack there while fading (they share the same partial
|
|
183
|
+
* alpha), showing a darker band during the transition. The strip spans
|
|
184
|
+
* `[-verticalOffset, -verticalOffset + topInset]` in CTL coordinates,
|
|
185
|
+
* matching the status bar scrim bounds set in
|
|
186
|
+
* `CollapsingToolbarLayout.draw`.
|
|
187
|
+
*/
|
|
188
|
+
internal fun updateContentScrimExclusion() {
|
|
189
|
+
val contentScrim =
|
|
190
|
+
collapsingToolbarLayout.contentScrim
|
|
191
|
+
check(contentScrim is StackHeaderContentScrimDrawable) {
|
|
192
|
+
"[RNScreens] Unexpected contentScrim class."
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
contentScrim.exclusionBottom =
|
|
196
|
+
if (collapsingToolbarLayout.statusBarScrim != null && trackedTopInset > 0) {
|
|
197
|
+
trackedTopInset - currentVerticalOffset
|
|
198
|
+
} else {
|
|
199
|
+
0
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private companion object {
|
|
204
|
+
fun layoutResFor(
|
|
205
|
+
type: StackHeaderType,
|
|
206
|
+
mode: StackHeaderCollapsedTitleGravityMode,
|
|
207
|
+
): Int =
|
|
208
|
+
when (type) {
|
|
209
|
+
StackHeaderType.MEDIUM ->
|
|
210
|
+
when (mode) {
|
|
211
|
+
StackHeaderCollapsedTitleGravityMode.ENTIRE_SPACE ->
|
|
212
|
+
com.swmansion.rnscreens.R.layout.rns_collapsing_toolbar_medium_entire_space
|
|
213
|
+
StackHeaderCollapsedTitleGravityMode.AVAILABLE_SPACE ->
|
|
214
|
+
com.swmansion.rnscreens.R.layout.rns_collapsing_toolbar_medium_available_space
|
|
215
|
+
}
|
|
216
|
+
StackHeaderType.LARGE ->
|
|
217
|
+
when (mode) {
|
|
218
|
+
StackHeaderCollapsedTitleGravityMode.ENTIRE_SPACE ->
|
|
219
|
+
com.swmansion.rnscreens.R.layout.rns_collapsing_toolbar_large_entire_space
|
|
220
|
+
StackHeaderCollapsedTitleGravityMode.AVAILABLE_SPACE ->
|
|
221
|
+
com.swmansion.rnscreens.R.layout.rns_collapsing_toolbar_large_available_space
|
|
222
|
+
}
|
|
223
|
+
else -> error("[RNScreens] Invalid header mode.")
|
|
224
|
+
}
|
|
95
225
|
}
|
|
96
226
|
}
|
|
97
227
|
|
|
@@ -99,10 +229,12 @@ internal sealed class StackHeaderAppBarLayout(
|
|
|
99
229
|
fun create(
|
|
100
230
|
context: Context,
|
|
101
231
|
type: StackHeaderType,
|
|
232
|
+
collapsedTitleGravityMode: StackHeaderCollapsedTitleGravityMode,
|
|
102
233
|
): StackHeaderAppBarLayout =
|
|
103
234
|
when (type) {
|
|
104
235
|
StackHeaderType.SMALL -> Small(context)
|
|
105
|
-
StackHeaderType.MEDIUM, StackHeaderType.LARGE ->
|
|
236
|
+
StackHeaderType.MEDIUM, StackHeaderType.LARGE ->
|
|
237
|
+
Collapsing(context, type, collapsedTitleGravityMode)
|
|
106
238
|
}
|
|
107
239
|
}
|
|
108
240
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package com.swmansion.rnscreens.stack.header.appbar
|
|
2
|
+
|
|
3
|
+
import android.graphics.Canvas
|
|
4
|
+
import android.graphics.drawable.ColorDrawable
|
|
5
|
+
import androidx.core.graphics.withClip
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Content scrim that can skip the status-bar strip painted by the
|
|
9
|
+
* CollapsingToolbarLayout status bar scrim. Both scrims share the same alpha
|
|
10
|
+
* while fading, so wherever they overlap the strip composites darker than the
|
|
11
|
+
* rest of the scrim during the transition.
|
|
12
|
+
*/
|
|
13
|
+
internal class StackHeaderContentScrimDrawable(
|
|
14
|
+
color: Int,
|
|
15
|
+
) : ColorDrawable(color) {
|
|
16
|
+
// Bottom of the skipped band in CollapsingToolbarLayout coordinates; the
|
|
17
|
+
// visible top edge of the scrim always aligns with the strip top. Values
|
|
18
|
+
// <= bounds.top disable the exclusion.
|
|
19
|
+
internal var exclusionBottom: Int = 0
|
|
20
|
+
set(value) {
|
|
21
|
+
if (field != value) {
|
|
22
|
+
field = value
|
|
23
|
+
invalidateSelf()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override fun draw(canvas: Canvas) {
|
|
28
|
+
if (exclusionBottom <= bounds.top) {
|
|
29
|
+
super.draw(canvas)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
canvas.withClip(bounds.left, exclusionBottom, bounds.right, bounds.bottom) {
|
|
33
|
+
super.draw(canvas)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|