From c1373d62acaf2c21e7a20aede50bd305ac6fb2c9 Mon Sep 17 00:00:00 2001 From: Kacper Kafara Date: Wed, 19 Jun 2024 13:11:06 +0200 Subject: [PATCH] chore(Android): update spotless & ktlint (#2189) ## Description Encountered some errors (seems they were looking for old CMake version (3.10.2)) & decided to bump these deps as they are long overdue. ## Changes Ktlint: 0.43 -> 1.1.1 spotless 6.11 -> 6.22 ## Checklist - [ ] Ensured that CI passes --- android/build.gradle | 2 +- android/spotless.gradle | 2 +- .../rnscreens/FabricEnabledViewGroup.kt | 21 +- .../swmansion/rnscreens/CustomSearchView.kt | 7 +- .../com/swmansion/rnscreens/CustomToolbar.kt | 5 +- .../rnscreens/FragmentBackPressOverrider.kt | 4 +- .../swmansion/rnscreens/LifecycleHelper.kt | 35 +-- .../swmansion/rnscreens/RNScreensPackage.kt | 34 ++- .../java/com/swmansion/rnscreens/Screen.kt | 97 ++++--- .../swmansion/rnscreens/ScreenContainer.kt | 82 +++--- .../rnscreens/ScreenContainerViewManager.kt | 24 +- .../rnscreens/ScreenEventDispatcher.kt | 12 +- .../com/swmansion/rnscreens/ScreenFragment.kt | 83 ++++-- .../rnscreens/ScreenFragmentWrapper.kt | 9 +- .../com/swmansion/rnscreens/ScreenStack.kt | 67 +++-- .../rnscreens/ScreenStackFragment.kt | 96 ++++--- .../rnscreens/ScreenStackFragmentWrapper.kt | 4 + .../rnscreens/ScreenStackHeaderConfig.kt | 110 +++++--- .../ScreenStackHeaderConfigViewManager.kt | 163 +++++++++--- .../rnscreens/ScreenStackHeaderSubview.kt | 23 +- .../ScreenStackHeaderSubviewManager.kt | 26 +- .../rnscreens/ScreenStackViewManager.kt | 27 +- .../swmansion/rnscreens/ScreenViewManager.kt | 244 ++++++++++++------ .../swmansion/rnscreens/ScreenWindowTraits.kt | 83 ++++-- .../com/swmansion/rnscreens/ScreensModule.kt | 23 +- .../swmansion/rnscreens/ScreensShadowNode.kt | 4 +- .../swmansion/rnscreens/SearchBarManager.kt | 149 +++++++---- .../com/swmansion/rnscreens/SearchBarView.kt | 51 ++-- .../rnscreens/SearchViewFormatter.kt | 9 +- .../rnscreens/events/HeaderAttachedEvent.kt | 5 +- .../events/HeaderBackButtonClickedEvent.kt | 5 +- .../rnscreens/events/HeaderDetachedEvent.kt | 5 +- .../events/HeaderHeightChangeEvent.kt | 10 +- .../rnscreens/events/ScreenAppearEvent.kt | 5 +- .../rnscreens/events/ScreenDisappearEvent.kt | 5 +- .../rnscreens/events/ScreenDismissedEvent.kt | 12 +- .../events/ScreenTransitionProgressEvent.kt | 13 +- .../rnscreens/events/ScreenWillAppearEvent.kt | 5 +- .../events/ScreenWillDisappearEvent.kt | 5 +- .../rnscreens/events/SearchBarBlurEvent.kt | 5 +- .../events/SearchBarChangeTextEvent.kt | 7 +- .../rnscreens/events/SearchBarCloseEvent.kt | 5 +- .../rnscreens/events/SearchBarFocusEvent.kt | 5 +- .../rnscreens/events/SearchBarOpenEvent.kt | 5 +- .../events/SearchBarSearchButtonPressEvent.kt | 13 +- .../events/StackFinishTransitioningEvent.kt | 5 +- .../swmansion/rnscreens/utils/DeviceUtils.kt | 6 +- .../utils/ScreenDummyLayoutHelper.kt | 105 +++++--- .../rnscreens/FabricEnabledViewGroup.kt | 11 +- 49 files changed, 1170 insertions(+), 563 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index b2a4b88a15..fbc634305c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -18,7 +18,7 @@ buildscript { dependencies { classpath('com.android.tools.build:gradle:4.2.2') classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', rnsDefaultKotlinVersion)}" - classpath "com.diffplug.spotless:spotless-plugin-gradle:6.11.0" + classpath "com.diffplug.spotless:spotless-plugin-gradle:6.25.0" } } diff --git a/android/spotless.gradle b/android/spotless.gradle index b8bed0830d..be623081b8 100644 --- a/android/spotless.gradle +++ b/android/spotless.gradle @@ -4,7 +4,7 @@ apply plugin: 'com.diffplug.spotless' spotless { kotlin { target 'src/**/*.kt' - ktlint("0.43.0") + ktlint("1.3.0") trimTrailingWhitespace() indentWithSpaces() endWithNewline() diff --git a/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt b/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt index 2912a138d7..cefa92896b 100644 --- a/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +++ b/android/src/fabric/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt @@ -9,22 +9,31 @@ import com.facebook.react.uimanager.FabricViewStateManager import com.facebook.react.uimanager.PixelUtil import kotlin.math.abs -abstract class FabricEnabledViewGroup constructor(context: ReactContext?) : ViewGroup(context), FabricViewStateManager.HasFabricViewStateManager { +abstract class FabricEnabledViewGroup constructor( + context: ReactContext?, +) : ViewGroup(context), + FabricViewStateManager.HasFabricViewStateManager { private val mFabricViewStateManager: FabricViewStateManager = FabricViewStateManager() private var lastSetWidth = 0f private var lastSetHeight = 0f - override fun getFabricViewStateManager(): FabricViewStateManager { - return mFabricViewStateManager - } + override fun getFabricViewStateManager(): FabricViewStateManager = mFabricViewStateManager - protected fun updateScreenSizeFabric(width: Int, height: Int, headerHeight: Double) { + protected fun updateScreenSizeFabric( + width: Int, + height: Int, + headerHeight: Double, + ) { updateState(width, height, headerHeight) } @UiThread - fun updateState(width: Int, height: Int, headerHeight: Double) { + fun updateState( + width: Int, + height: Int, + headerHeight: Double, + ) { val realWidth: Float = PixelUtil.toDIPFromPixel(width.toFloat()) val realHeight: Float = PixelUtil.toDIPFromPixel(height.toFloat()) diff --git a/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt b/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt index 89f7c7c7bb..f05e1f232c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +++ b/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt @@ -7,14 +7,17 @@ import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated. -class CustomSearchView(context: Context, fragment: Fragment) : SearchView(context) { +class CustomSearchView( + context: Context, + fragment: Fragment, +) : SearchView(context) { /* CustomSearchView uses some variables from SearchView. They are listed below with links to documentation isIconified - https://developer.android.com/reference/android/widget/SearchView#setIconified(boolean) maxWidth - https://developer.android.com/reference/android/widget/SearchView#setMaxWidth(int) setOnSearchClickListener - https://developer.android.com/reference/android/widget/SearchView#setOnSearchClickListener(android.view.View.OnClickListener) setOnCloseListener - https://developer.android.com/reference/android/widget/SearchView#setOnCloseListener(android.widget.SearchView.OnCloseListener) - */ + */ private var onCloseListener: OnCloseListener? = null private var onSearchClickedListener: OnClickListener? = null diff --git a/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt b/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt index c774078399..7168a4c6fd 100644 --- a/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +++ b/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt @@ -6,4 +6,7 @@ import androidx.appcompat.widget.Toolbar // This class is used to store config closer to search bar @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated. -open class CustomToolbar(context: Context, val config: ScreenStackHeaderConfig) : Toolbar(context) +open class CustomToolbar( + context: Context, + val config: ScreenStackHeaderConfig, +) : Toolbar(context) diff --git a/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt b/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt index 3f62fa3f36..541749e762 100644 --- a/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +++ b/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt @@ -5,7 +5,7 @@ import androidx.fragment.app.Fragment class FragmentBackPressOverrider( private val fragment: Fragment, - private val onBackPressedCallback: OnBackPressedCallback + private val onBackPressedCallback: OnBackPressedCallback, ) { private var isCallbackAdded: Boolean = false var overrideBackAction: Boolean = true @@ -14,7 +14,7 @@ class FragmentBackPressOverrider( if (!isCallbackAdded && overrideBackAction) { fragment.activity?.onBackPressedDispatcher?.addCallback( fragment, - onBackPressedCallback + onBackPressedCallback, ) isCallbackAdded = true } diff --git a/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.kt b/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.kt index 41bfc802df..cc65f51a4b 100644 --- a/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.kt +++ b/android/src/main/java/com/swmansion/rnscreens/LifecycleHelper.kt @@ -7,22 +7,23 @@ import androidx.lifecycle.LifecycleObserver class LifecycleHelper { private val mViewToLifecycleMap: MutableMap = HashMap() - private val mRegisterOnLayoutChange: View.OnLayoutChangeListener = object : View.OnLayoutChangeListener { - override fun onLayoutChange( - view: View, - i: Int, - i1: Int, - i2: Int, - i3: Int, - i4: Int, - i5: Int, - i6: Int, - i7: Int - ) { - registerViewWithLifecycleOwner(view) - view.removeOnLayoutChangeListener(this) + private val mRegisterOnLayoutChange: View.OnLayoutChangeListener = + object : View.OnLayoutChangeListener { + override fun onLayoutChange( + view: View, + i: Int, + i1: Int, + i2: Int, + i3: Int, + i4: Int, + i5: Int, + i6: Int, + i7: Int, + ) { + registerViewWithLifecycleOwner(view) + view.removeOnLayoutChangeListener(this) + } } - } private fun registerViewWithLifecycleOwner(view: View) { val parent = findNearestScreenFragmentAncestor(view) @@ -54,7 +55,9 @@ class LifecycleHelper { } return if (parent != null) { (parent as Screen).fragment - } else null + } else { + null + } } } } diff --git a/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt b/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt index c0c3749ac7..e91379ba4c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +++ b/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt @@ -9,18 +9,16 @@ import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uimanager.ViewManager import com.swmansion.rnscreens.utils.ScreenDummyLayoutHelper - @ReactModuleList( nativeModules = [ - ScreensModule::class - ] + ScreensModule::class, + ], ) class RNScreensPackage : TurboReactPackage() { // We just retain it here. This object helps us tackle jumping content when using native header. // See: https://github.com/software-mansion/react-native-screens/pull/2169 private var screenDummyLayoutHelper: ScreenDummyLayoutHelper? = null - override fun createViewManagers(reactContext: ReactApplicationContext): List> { // This is the earliest we lay our hands on react context. // Moreover this is called before FabricUIManger has finished initializing, not to mention @@ -36,13 +34,13 @@ class RNScreensPackage : TurboReactPackage() { ScreenStackViewManager(), ScreenStackHeaderConfigViewManager(), ScreenStackHeaderSubviewManager(), - SearchBarManager() + SearchBarManager(), ) } override fun getModule( s: String, - reactApplicationContext: ReactApplicationContext + reactApplicationContext: ReactApplicationContext, ): NativeModule? { when (s) { ScreensModule.NAME -> return ScreensModule(reactApplicationContext) @@ -50,22 +48,22 @@ class RNScreensPackage : TurboReactPackage() { return null } - override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { - return ReactModuleInfoProvider { + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = + ReactModuleInfoProvider { val moduleInfos: MutableMap = HashMap() val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - moduleInfos[ScreensModule.NAME] = ReactModuleInfo( - ScreensModule.NAME, - ScreensModule.NAME, - false, // canOverrideExistingModule - false, // needsEagerInit - true, // hasConstants - false, // isCxxModule - isTurboModule - ) + moduleInfos[ScreensModule.NAME] = + ReactModuleInfo( + ScreensModule.NAME, + ScreensModule.NAME, + false, // canOverrideExistingModule + false, // needsEagerInit + true, // hasConstants + false, // isCxxModule + isTurboModule, + ) moduleInfos } - } companion object { const val TAG = "RNScreensPackage" diff --git a/android/src/main/java/com/swmansion/rnscreens/Screen.kt b/android/src/main/java/com/swmansion/rnscreens/Screen.kt index 9a3b2c8394..5d867e75de 100644 --- a/android/src/main/java/com/swmansion/rnscreens/Screen.kt +++ b/android/src/main/java/com/swmansion/rnscreens/Screen.kt @@ -19,7 +19,9 @@ import com.facebook.react.uimanager.UIManagerModule import com.swmansion.rnscreens.events.HeaderHeightChangeEvent @SuppressLint("ViewConstructor") // Only we construct this view, it is never inflated. -class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { +class Screen( + context: ReactContext?, +) : FabricEnabledViewGroup(context) { val fragment: Fragment? get() = fragmentWrapper?.fragment @@ -60,14 +62,20 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { // ignore restoring instance state too as we are not saving anything anyways. } - override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + override fun onLayout( + changed: Boolean, + l: Int, + t: Int, + r: Int, + b: Int, + ) { if (container is ScreenStack && changed) { val width = r - l val height = b - t val headerHeight = calculateHeaderHeight() val totalHeight = - headerHeight.first + headerHeight.second // action bar height + status bar height + headerHeight.first + headerHeight.second // action bar height + status bar height if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { updateScreenSizeFabric(width, height, totalHeight) } else { @@ -78,7 +86,10 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { } } - private fun updateScreenSizePaper(width: Int, height: Int) { + private fun updateScreenSizePaper( + width: Int, + height: Int, + ) { val reactContext = context as ReactContext reactContext.runOnNativeModulesQueueThread( object : GuardedRunnable(reactContext.exceptionHandler) { @@ -87,7 +98,8 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { .getNativeModule(UIManagerModule::class.java) ?.updateNodeSize(id, width, height) } - }) + }, + ) } val headerConfig: ScreenStackHeaderConfig? @@ -109,7 +121,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { } super.setLayerType( if (transitioning && !isWebViewInScreen) LAYER_TYPE_HARDWARE else LAYER_TYPE_NONE, - null + null, ) } @@ -127,7 +139,10 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { return false } - override fun setLayerType(layerType: Int, paint: Paint?) { + override fun setLayerType( + layerType: Int, + paint: Paint?, + ) { // ignore - layer type is controlled by `transitioning` prop } @@ -145,16 +160,17 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { return } ScreenWindowTraits.applyDidSetOrientation() - this.screenOrientation = when (screenOrientation) { - "all" -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR - "portrait" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT - "portrait_up" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - "portrait_down" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT - "landscape" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - "landscape_left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE - "landscape_right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - } + this.screenOrientation = + when (screenOrientation) { + "all" -> ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR + "portrait" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + "portrait_up" -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + "portrait_down" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT + "landscape" -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + "landscape_left" -> ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE + "landscape_right" -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + else -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + } fragmentWrapper?.let { ScreenWindowTraits.setOrientation(this, it.tryGetActivity()) } } @@ -176,7 +192,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { ScreenWindowTraits.setStyle( this, it.tryGetActivity(), - it.tryGetContext() + it.tryGetContext(), ) } } @@ -200,7 +216,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { ScreenWindowTraits.setTranslucent( this, it.tryGetActivity(), - it.tryGetContext() + it.tryGetContext(), ) } } @@ -215,7 +231,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { ScreenWindowTraits.setColor( this, it.tryGetActivity(), - it.tryGetContext() + it.tryGetContext(), ) } } @@ -229,7 +245,7 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { fragmentWrapper?.let { ScreenWindowTraits.setNavigationBarColor( this, - it.tryGetActivity() + it.tryGetActivity(), ) } } @@ -272,12 +288,14 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { // Check if it's possible to get an attribute from theme context and assign a value from it. // Otherwise, the default value will be returned. val actionBarHeight = - TypedValue.complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics) + TypedValue + .complexToDimensionPixelSize(actionBarTv.data, resources.displayMetrics) .takeIf { resolvedActionBarSize && headerConfig?.isHeaderHidden != true && headerConfig?.isHeaderTranslucent != true } ?.let { PixelUtil.toDIPFromPixel(it.toFloat()).toDouble() } ?: 0.0 val statusBarHeight = - context.resources.getIdentifier("status_bar_height", "dimen", "android") + context.resources + .getIdentifier("status_bar_height", "dimen", "android") // Count only status bar when action bar is visible and status bar is not hidden .takeIf { it > 0 && isStatusBarHidden != true && actionBarHeight > 0 } ?.let { (context.resources::getDimensionPixelSize)(it) } @@ -290,27 +308,48 @@ class Screen(context: ReactContext?) : FabricEnabledViewGroup(context) { private fun notifyHeaderHeightChange(headerHeight: Double) { val screenContext = context as ReactContext val surfaceId = UIManagerHelper.getSurfaceId(screenContext) - UIManagerHelper.getEventDispatcherForReactTag(screenContext, id) + UIManagerHelper + .getEventDispatcherForReactTag(screenContext, id) ?.dispatchEvent(HeaderHeightChangeEvent(surfaceId, id, headerHeight)) } enum class StackPresentation { - PUSH, MODAL, TRANSPARENT_MODAL + PUSH, + MODAL, + TRANSPARENT_MODAL, } enum class StackAnimation { - DEFAULT, NONE, FADE, SLIDE_FROM_BOTTOM, SLIDE_FROM_RIGHT, SLIDE_FROM_LEFT, FADE_FROM_BOTTOM, IOS + DEFAULT, + NONE, + FADE, + SLIDE_FROM_BOTTOM, + SLIDE_FROM_RIGHT, + SLIDE_FROM_LEFT, + FADE_FROM_BOTTOM, + IOS, } enum class ReplaceAnimation { - PUSH, POP + PUSH, + POP, } enum class ActivityState { - INACTIVE, TRANSITIONING_OR_BELOW_TOP, ON_TOP + INACTIVE, + TRANSITIONING_OR_BELOW_TOP, + ON_TOP, } enum class WindowTraits { - ORIENTATION, COLOR, STYLE, TRANSLUCENT, HIDDEN, ANIMATED, NAVIGATION_BAR_COLOR, NAVIGATION_BAR_TRANSLUCENT, NAVIGATION_BAR_HIDDEN + ORIENTATION, + COLOR, + STYLE, + TRANSLUCENT, + HIDDEN, + ANIMATED, + NAVIGATION_BAR_COLOR, + NAVIGATION_BAR_TRANSLUCENT, + NAVIGATION_BAR_HIDDEN, } } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt index 4d3a3e85f5..cf44d7dd35 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt @@ -19,27 +19,37 @@ import com.facebook.react.uimanager.UIManagerHelper import com.swmansion.rnscreens.Screen.ActivityState import com.swmansion.rnscreens.events.ScreenDismissedEvent -open class ScreenContainer(context: Context?) : ViewGroup(context) { +open class ScreenContainer( + context: Context?, +) : ViewGroup(context) { @JvmField protected val screenWrappers = ArrayList() + @JvmField protected var fragmentManager: FragmentManager? = null private var isAttached = false private var needsUpdate = false private var isLayoutEnqueued = false - private val layoutCallback: ChoreographerCompat.FrameCallback = object : ChoreographerCompat.FrameCallback() { - override fun doFrame(frameTimeNanos: Long) { - isLayoutEnqueued = false - measure( - MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) - ) - layout(left, top, right, bottom) + private val layoutCallback: ChoreographerCompat.FrameCallback = + object : ChoreographerCompat.FrameCallback() { + override fun doFrame(frameTimeNanos: Long) { + isLayoutEnqueued = false + measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), + ) + layout(left, top, right, bottom) + } } - } private var parentScreenWrapper: ScreenFragmentWrapper? = null - override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + override fun onLayout( + changed: Boolean, + l: Int, + t: Int, + r: Int, + b: Int, + ) { var i = 0 val size = childCount while (i < size) { @@ -69,9 +79,11 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { isLayoutEnqueued = true // we use NATIVE_ANIMATED_MODULE choreographer queue because it allows us to catch the current // looper loop instead of enqueueing the update in the next loop causing a one frame delay. - ReactChoreographer.getInstance() + ReactChoreographer + .getInstance() .postFrameCallback( - ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, layoutCallback + ReactChoreographer.CallbackType.NATIVE_ANIMATED_MODULE, + layoutCallback, ) } } @@ -85,7 +97,10 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { protected open fun adapt(screen: Screen): ScreenFragmentWrapper = ScreenFragment(screen) - fun addScreen(screen: Screen, index: Int) { + fun addScreen( + screen: Screen, + index: Int, + ) { val fragment = adapt(screen) screen.fragmentWrapper = fragment screenWrappers.add(index, fragment) @@ -173,7 +188,7 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { parentScreenWrapper = fragmentWrapper fragmentWrapper.addChildScreenContainer(this) setFragmentManager(fragmentWrapper.fragment.childFragmentManager) - } + }, ) { "Parent Screen does not have its Fragment attached" } } else { // we expect top level view to be of type ReactRootView, this isn't really necessary but in @@ -186,13 +201,15 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { } } - protected fun createTransaction(): FragmentTransaction { - return requireNotNull(fragmentManager) { "fragment manager is null when creating transaction" } + protected fun createTransaction(): FragmentTransaction = + requireNotNull(fragmentManager) { "fragment manager is null when creating transaction" } .beginTransaction() .setReorderingAllowed(true) - } - private fun attachScreen(transaction: FragmentTransaction, fragment: Fragment) { + private fun attachScreen( + transaction: FragmentTransaction, + fragment: Fragment, + ) { transaction.add(id, fragment) } @@ -228,15 +245,16 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { } } - private fun detachScreen(transaction: FragmentTransaction, fragment: Fragment) { + private fun detachScreen( + transaction: FragmentTransaction, + fragment: Fragment, + ) { transaction.remove(fragment) } - private fun getActivityState(screenFragmentWrapper: ScreenFragmentWrapper): ActivityState? = - screenFragmentWrapper.screen.activityState + private fun getActivityState(screenFragmentWrapper: ScreenFragmentWrapper): ActivityState? = screenFragmentWrapper.screen.activityState - open fun hasScreen(screenFragmentWrapper: ScreenFragmentWrapper?): Boolean = - screenWrappers.contains(screenFragmentWrapper) + open fun hasScreen(screenFragmentWrapper: ScreenFragmentWrapper?): Boolean = screenWrappers.contains(screenFragmentWrapper) override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -295,7 +313,10 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { } } - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + override fun onMeasure( + widthMeasureSpec: Int, + heightMeasureSpec: Int, + ) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) for (i in 0 until childCount) { getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec) @@ -342,11 +363,12 @@ open class ScreenContainer(context: Context?) : ViewGroup(context) { open fun onUpdate() { createTransaction().let { // detach screens that are no longer active - val orphaned: MutableSet = HashSet( - requireNotNull(fragmentManager) { - "fragment manager is null when performing update in ScreenContainer" - }.fragments - ) + val orphaned: MutableSet = + HashSet( + requireNotNull(fragmentManager) { + "fragment manager is null when performing update in ScreenContainer" + }.fragments, + ) for (fragmentWrapper in screenWrappers) { if (getActivityState(fragmentWrapper) === ActivityState.INACTIVE && fragmentWrapper.fragment.isAdded diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt index b7758771ed..6e82cb62cc 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenContainerViewManager.kt @@ -11,27 +11,34 @@ import com.facebook.react.viewmanagers.RNSScreenContainerManagerDelegate import com.facebook.react.viewmanagers.RNSScreenContainerManagerInterface @ReactModule(name = ScreenContainerViewManager.REACT_CLASS) -class ScreenContainerViewManager : ViewGroupManager(), RNSScreenContainerManagerInterface { +class ScreenContainerViewManager : + ViewGroupManager(), + RNSScreenContainerManagerInterface { private val delegate: ViewManagerDelegate init { delegate = RNSScreenContainerManagerDelegate(this) } - protected override fun getDelegate(): ViewManagerDelegate { - return delegate - } + protected override fun getDelegate(): ViewManagerDelegate = delegate override fun getName(): String = REACT_CLASS override fun createViewInstance(reactContext: ThemedReactContext): ScreenContainer = ScreenContainer(reactContext) - override fun addView(parent: ScreenContainer, child: View, index: Int) { + override fun addView( + parent: ScreenContainer, + child: View, + index: Int, + ) { require(child is Screen) { "Attempt attach child that is not of type RNScreens" } parent.addScreen(child, index) } - override fun removeViewAt(parent: ScreenContainer, index: Int) { + override fun removeViewAt( + parent: ScreenContainer, + index: Int, + ) { parent.removeScreenAt(index) } @@ -41,7 +48,10 @@ class ScreenContainerViewManager : ViewGroupManager(), RNSScree override fun getChildCount(parent: ScreenContainer): Int = parent.screenCount - override fun getChildAt(parent: ScreenContainer, index: Int): View = parent.getScreenAt(index) + override fun getChildAt( + parent: ScreenContainer, + index: Int, + ): View = parent.getScreenAt(index) override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreensShadowNode(context) diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt index 1c42b3e0eb..67970ea617 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenEventDispatcher.kt @@ -2,12 +2,16 @@ package com.swmansion.rnscreens interface ScreenEventDispatcher { fun canDispatchLifecycleEvent(event: ScreenFragment.ScreenLifecycleEvent): Boolean + fun updateLastEventDispatched(event: ScreenFragment.ScreenLifecycleEvent) /** * Dispatches given screen lifecycle event to JS using screen from given fragment `fragmentWrapper` */ - fun dispatchLifecycleEvent(event: ScreenFragment.ScreenLifecycleEvent, fragmentWrapper: ScreenFragmentWrapper) + fun dispatchLifecycleEvent( + event: ScreenFragment.ScreenLifecycleEvent, + fragmentWrapper: ScreenFragmentWrapper, + ) /** * Dispatches given screen lifecycle event from all non-empty child containers to JS @@ -15,7 +19,11 @@ interface ScreenEventDispatcher { fun dispatchLifecycleEventInChildContainers(event: ScreenFragment.ScreenLifecycleEvent) fun dispatchHeaderBackButtonClickedEvent() - fun dispatchTransitionProgressEvent(alpha: Float, closing: Boolean) + + fun dispatchTransitionProgressEvent( + alpha: Float, + closing: Boolean, + ) // Concrete dispatchers } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt index 3915dd6c1e..cf260fbe06 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt @@ -25,9 +25,14 @@ import com.swmansion.rnscreens.events.ScreenWillDisappearEvent import kotlin.math.max import kotlin.math.min -open class ScreenFragment : Fragment, ScreenFragmentWrapper { +open class ScreenFragment : + Fragment, + ScreenFragmentWrapper { enum class ScreenLifecycleEvent { - DID_APPEAR, WILL_APPEAR, DID_DISAPPEAR, WILL_DISAPPEAR + DID_APPEAR, + WILL_APPEAR, + DID_DISAPPEAR, + WILL_DISAPPEAR, } override val fragment: Fragment @@ -40,6 +45,7 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { override val childScreenContainers: MutableList = ArrayList() private var shouldUpdateOnResume = false + // if we don't set it, it will be 0.0f at the beginning so the progress will not be sent // due to progress value being already 0.0f private var transitionProgress = -1f @@ -57,7 +63,7 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { constructor() { throw IllegalStateException( - "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." + "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.", ) } @@ -77,14 +83,17 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { - screen.layoutParams = FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT - ) - val wrapper = context?.let { ScreensFrameLayout(it) }?.apply { - addView(recycleView(screen)) - } + screen.layoutParams = + FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + ) + val wrapper = + context?.let { ScreensFrameLayout(it) }?.apply { + addView(recycleView(screen)) + } return wrapper } @@ -154,12 +163,13 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { return null } - override fun canDispatchLifecycleEvent(event: ScreenLifecycleEvent): Boolean = when (event) { - ScreenLifecycleEvent.WILL_APPEAR -> canDispatchWillAppear - ScreenLifecycleEvent.DID_APPEAR -> canDispatchAppear - ScreenLifecycleEvent.WILL_DISAPPEAR -> !canDispatchWillAppear - ScreenLifecycleEvent.DID_DISAPPEAR -> !canDispatchAppear - } + override fun canDispatchLifecycleEvent(event: ScreenLifecycleEvent): Boolean = + when (event) { + ScreenLifecycleEvent.WILL_APPEAR -> canDispatchWillAppear + ScreenLifecycleEvent.DID_APPEAR -> canDispatchAppear + ScreenLifecycleEvent.WILL_DISAPPEAR -> !canDispatchWillAppear + ScreenLifecycleEvent.DID_DISAPPEAR -> !canDispatchAppear + } override fun updateLastEventDispatched(event: ScreenLifecycleEvent) { when (event) { @@ -190,18 +200,22 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { dispatchTransitionProgressEvent(1.0f, true) } - override fun dispatchLifecycleEvent(event: ScreenLifecycleEvent, fragmentWrapper: ScreenFragmentWrapper) { + override fun dispatchLifecycleEvent( + event: ScreenLifecycleEvent, + fragmentWrapper: ScreenFragmentWrapper, + ) { val fragment = fragmentWrapper.fragment if (fragment is ScreenStackFragment && fragment.canDispatchLifecycleEvent(event)) { fragment.screen.let { fragmentWrapper.updateLastEventDispatched(event) val surfaceId = UIManagerHelper.getSurfaceId(it) - val lifecycleEvent: Event<*> = when (event) { - ScreenLifecycleEvent.WILL_APPEAR -> ScreenWillAppearEvent(surfaceId, it.id) - ScreenLifecycleEvent.DID_APPEAR -> ScreenAppearEvent(surfaceId, it.id) - ScreenLifecycleEvent.WILL_DISAPPEAR -> ScreenWillDisappearEvent(surfaceId, it.id) - ScreenLifecycleEvent.DID_DISAPPEAR -> ScreenDisappearEvent(surfaceId, it.id) - } + val lifecycleEvent: Event<*> = + when (event) { + ScreenLifecycleEvent.WILL_APPEAR -> ScreenWillAppearEvent(surfaceId, it.id) + ScreenLifecycleEvent.DID_APPEAR -> ScreenAppearEvent(surfaceId, it.id) + ScreenLifecycleEvent.WILL_DISAPPEAR -> ScreenWillDisappearEvent(surfaceId, it.id) + ScreenLifecycleEvent.DID_DISAPPEAR -> ScreenDisappearEvent(surfaceId, it.id) + } val screenContext = screen.context as ReactContext val eventDispatcher: EventDispatcher? = UIManagerHelper.getEventDispatcherForReactTag(screenContext, screen.id) @@ -225,7 +239,10 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { ?.dispatchEvent(HeaderBackButtonClickedEvent(surfaceId, screen.id)) } - override fun dispatchTransitionProgressEvent(alpha: Float, closing: Boolean) { + override fun dispatchTransitionProgressEvent( + alpha: Float, + closing: Boolean, + ) { if (this is ScreenStackFragment) { if (transitionProgress != alpha) { transitionProgress = max(0.0f, min(1.0f, alpha)) @@ -238,8 +255,12 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { ?.dispatchEvent( ScreenTransitionProgressEvent( UIManagerHelper.getSurfaceId(screenContext), - screen.id, transitionProgress, closing, goingForward, coalescingKey - ) + screen.id, + transitionProgress, + closing, + goingForward, + coalescingKey, + ), ) } } @@ -328,7 +349,15 @@ open class ScreenFragment : Fragment, ScreenFragmentWrapper { - progress is 1 -> key 2 - progress is between 0 and 1 -> key 3 */ - return (if (progress == 0.0f) 1 else if (progress == 1.0f) 2 else 3).toShort() + return ( + if (progress == 0.0f) { + 1 + } else if (progress == 1.0f) { + 2 + } else { + 3 + } + ).toShort() } } } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt index 4d507149b9..615b5b5d5c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenFragmentWrapper.kt @@ -3,20 +3,27 @@ package com.swmansion.rnscreens import android.app.Activity import com.facebook.react.bridge.ReactContext -interface ScreenFragmentWrapper : FragmentHolder, ScreenEventDispatcher { +interface ScreenFragmentWrapper : + FragmentHolder, + ScreenEventDispatcher { var screen: Screen // Communication with container val childScreenContainers: List + fun addChildScreenContainer(container: ScreenContainer) + fun removeChildScreenContainer(container: ScreenContainer) + fun onContainerUpdate() // Animation phase callbacks fun onViewAnimationStart() + fun onViewAnimationEnd() // Helpers fun tryGetActivity(): Activity? + fun tryGetContext(): ReactContext? } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt index 458465ce9f..28d9a15fc2 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt @@ -12,7 +12,9 @@ import java.util.Collections import kotlin.collections.ArrayList import kotlin.collections.HashSet -class ScreenStack(context: Context?) : ScreenContainer(context) { +class ScreenStack( + context: Context?, +) : ScreenContainer(context) { private val stack = ArrayList() private val dismissedWrappers: MutableSet = HashSet() private val drawingOpPool: MutableList = ArrayList() @@ -142,11 +144,21 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_enter_in, R.anim.rns_default_enter_out) StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20) StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out) - StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left) - StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right) - StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations( - R.anim.rns_slide_in_from_bottom, R.anim.rns_no_animation_medium - ) + StackAnimation.SLIDE_FROM_RIGHT -> + it.setCustomAnimations( + R.anim.rns_slide_in_from_right, + R.anim.rns_slide_out_to_left, + ) + StackAnimation.SLIDE_FROM_LEFT -> + it.setCustomAnimations( + R.anim.rns_slide_in_from_left, + R.anim.rns_slide_out_to_right, + ) + StackAnimation.SLIDE_FROM_BOTTOM -> + it.setCustomAnimations( + R.anim.rns_slide_in_from_bottom, + R.anim.rns_no_animation_medium, + ) StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350) StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_right_ios, R.anim.rns_slide_out_to_left_ios) } @@ -155,11 +167,21 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { StackAnimation.DEFAULT -> it.setCustomAnimations(R.anim.rns_default_exit_in, R.anim.rns_default_exit_out) StackAnimation.NONE -> it.setCustomAnimations(R.anim.rns_no_animation_20, R.anim.rns_no_animation_20) StackAnimation.FADE -> it.setCustomAnimations(R.anim.rns_fade_in, R.anim.rns_fade_out) - StackAnimation.SLIDE_FROM_RIGHT -> it.setCustomAnimations(R.anim.rns_slide_in_from_left, R.anim.rns_slide_out_to_right) - StackAnimation.SLIDE_FROM_LEFT -> it.setCustomAnimations(R.anim.rns_slide_in_from_right, R.anim.rns_slide_out_to_left) - StackAnimation.SLIDE_FROM_BOTTOM -> it.setCustomAnimations( - R.anim.rns_no_animation_medium, R.anim.rns_slide_out_to_bottom - ) + StackAnimation.SLIDE_FROM_RIGHT -> + it.setCustomAnimations( + R.anim.rns_slide_in_from_left, + R.anim.rns_slide_out_to_right, + ) + StackAnimation.SLIDE_FROM_LEFT -> + it.setCustomAnimations( + R.anim.rns_slide_in_from_right, + R.anim.rns_slide_out_to_left, + ) + StackAnimation.SLIDE_FROM_BOTTOM -> + it.setCustomAnimations( + R.anim.rns_no_animation_medium, + R.anim.rns_slide_out_to_bottom, + ) StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom) StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_left_ios, R.anim.rns_slide_out_to_right_ios) } @@ -170,7 +192,8 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { goingForward = shouldUseOpenAnimation if (shouldUseOpenAnimation && - newTop != null && needsDrawReordering(newTop) && + newTop != null && + needsDrawReordering(newTop) && visibleBottom == null ) { // When using an open animation in which two screens overlap (eg. fade_from_bottom or @@ -208,9 +231,12 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { for (fragmentWrapper in screenWrappers) { // ignore all screens beneath the visible bottom if (beneathVisibleBottom) { - beneathVisibleBottom = if (fragmentWrapper === visibleBottom) { - false - } else continue + beneathVisibleBottom = + if (fragmentWrapper === visibleBottom) { + false + } else { + continue + } } // when first visible screen found, make all screens after that visible it.add(id, fragmentWrapper.fragment).runOnCommit { top?.screen?.bringToFront() } @@ -298,13 +324,17 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { drawAndRelease() } - override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean { + override fun drawChild( + canvas: Canvas, + child: View, + drawingTime: Long, + ): Boolean { drawingOps.add( obtainDrawingOp().apply { this.canvas = canvas this.child = child this.drawingTime = drawingTime - } + }, ) return true } @@ -315,8 +345,7 @@ class ScreenStack(context: Context?) : ScreenContainer(context) { super.drawChild(op.canvas!!, op.child, op.drawingTime) } - private fun obtainDrawingOp(): DrawingOp = - if (drawingOpPool.isEmpty()) DrawingOp() else drawingOpPool.removeLast() + private fun obtainDrawingOp(): DrawingOp = if (drawingOpPool.isEmpty()) DrawingOp() else drawingOpPool.removeLast() private inner class DrawingOp { var canvas: Canvas? = null diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt index 320bc876a3..0865396b42 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt @@ -21,7 +21,9 @@ import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior import com.swmansion.rnscreens.utils.DeviceUtils -class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { +class ScreenStackFragment : + ScreenFragment, + ScreenStackFragmentWrapper { private var appBarLayout: AppBarLayout? = null private var toolbar: Toolbar? = null private var isToolbarShadowHidden = false @@ -37,7 +39,7 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { constructor() { throw IllegalStateException( - "ScreenStack 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." + "ScreenStack 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.", ) } @@ -54,9 +56,12 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { override fun setToolbar(toolbar: Toolbar) { appBarLayout?.addView(toolbar) - toolbar.layoutParams = AppBarLayout.LayoutParams( - AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.WRAP_CONTENT - ).apply { scrollFlags = 0 } + toolbar.layoutParams = + AppBarLayout + .LayoutParams( + AppBarLayout.LayoutParams.MATCH_PARENT, + AppBarLayout.LayoutParams.WRAP_CONTENT, + ).apply { scrollFlags = 0 } this.toolbar = toolbar } @@ -102,27 +107,33 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View? { val view: ScreensCoordinatorLayout? = context?.let { ScreensCoordinatorLayout(it, this) } - screen.layoutParams = CoordinatorLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT - ).apply { behavior = if (isToolbarTranslucent) null else ScrollingViewBehavior() } + screen.layoutParams = + CoordinatorLayout + .LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT, + ).apply { behavior = if (isToolbarTranslucent) null else ScrollingViewBehavior() } view?.addView(recycleView(screen)) - appBarLayout = context?.let { AppBarLayout(it) }?.apply { - // By default AppBarLayout will have a background color set but since we cover the whole layout - // with toolbar (that can be semi-transparent) the bar layout background color does not pay a - // role. On top of that it breaks screens animations when alfa offscreen compositing is off - // (which is the default) - setBackgroundColor(Color.TRANSPARENT) - layoutParams = AppBarLayout.LayoutParams( - AppBarLayout.LayoutParams.MATCH_PARENT, AppBarLayout.LayoutParams.WRAP_CONTENT - ) - } + appBarLayout = + context?.let { AppBarLayout(it) }?.apply { + // By default AppBarLayout will have a background color set but since we cover the whole layout + // with toolbar (that can be semi-transparent) the bar layout background color does not pay a + // role. On top of that it breaks screens animations when alfa offscreen compositing is off + // (which is the default) + setBackgroundColor(Color.TRANSPARENT) + layoutParams = + AppBarLayout.LayoutParams( + AppBarLayout.LayoutParams.MATCH_PARENT, + AppBarLayout.LayoutParams.WRAP_CONTENT, + ) + } view?.addView(appBarLayout) if (isToolbarShadowHidden) { @@ -134,8 +145,9 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { } override fun onStop() { - if (DeviceUtils.isPlatformAndroidTV(context)) + if (DeviceUtils.isPlatformAndroidTV(context)) { lastFocusedChild = findLastFocusedChild() + } super.onStop() } @@ -145,7 +157,10 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { return super.onPrepareOptionsMenu(menu) } - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + override fun onCreateOptionsMenu( + menu: Menu, + inflater: MenuInflater, + ) { updateToolbarMenu(menu) return super.onCreateOptionsMenu(menu, inflater) } @@ -215,7 +230,7 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { private class ScreensCoordinatorLayout( context: Context, - private val mFragment: ScreenFragment + private val mFragment: ScreenFragment, ) : CoordinatorLayout(context) { private val mAnimationListener: Animation.AnimationListener = object : Animation.AnimationListener { @@ -243,20 +258,22 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { val fakeAnimation = ScreensAnimation(mFragment).apply { duration = animation.duration } if (animation is AnimationSet && !mFragment.isRemoving) { - animation.apply { - addAnimation(fakeAnimation) - setAnimationListener(mAnimationListener) - }.also { - super.startAnimation(it) - } + animation + .apply { + addAnimation(fakeAnimation) + setAnimationListener(mAnimationListener) + }.also { + super.startAnimation(it) + } } else { - AnimationSet(true).apply { - addAnimation(animation) - addAnimation(fakeAnimation) - setAnimationListener(mAnimationListener) - }.also { - super.startAnimation(it) - } + AnimationSet(true) + .apply { + addAnimation(animation) + addAnimation(fakeAnimation) + setAnimationListener(mAnimationListener) + }.also { + super.startAnimation(it) + } } } @@ -274,8 +291,13 @@ class ScreenStackFragment : ScreenFragment, ScreenStackFragmentWrapper { } } - private class ScreensAnimation(private val mFragment: ScreenFragment) : Animation() { - override fun applyTransformation(interpolatedTime: Float, t: Transformation) { + private class ScreensAnimation( + private val mFragment: ScreenFragment, + ) : Animation() { + override fun applyTransformation( + interpolatedTime: Float, + t: Transformation, + ) { super.applyTransformation(interpolatedTime, t) // interpolated time should be the progress of the current transition mFragment.dispatchTransitionProgressEvent(interpolatedTime, !mFragment.isResumed) diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt index ec9a71ab55..e9f4e74a4b 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragmentWrapper.kt @@ -5,11 +5,15 @@ import androidx.appcompat.widget.Toolbar interface ScreenStackFragmentWrapper : ScreenFragmentWrapper { // Toolbar management fun removeToolbar() + fun setToolbar(toolbar: Toolbar) + fun setToolbarShadowHidden(hidden: Boolean) + fun setToolbarTranslucent(translucent: Boolean) // Navigation fun canNavigateBack(): Boolean + fun dismiss() } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt index a806a01736..9cea3fe971 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt @@ -23,7 +23,9 @@ import com.facebook.react.views.text.ReactTypefaceUtils import com.swmansion.rnscreens.events.HeaderAttachedEvent import com.swmansion.rnscreens.events.HeaderDetachedEvent -class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { +class ScreenStackHeaderConfig( + context: Context, +) : ViewGroup(context) { private val configSubviews = ArrayList(3) val toolbar: CustomToolbar var isHeaderHidden = false // named this way to avoid conflict with platform's isHidden @@ -45,29 +47,36 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { private var isAttachedToWindow = false private val defaultStartInset: Int private val defaultStartInsetWithNavigation: Int - private val backClickListener = OnClickListener { - screenFragment?.let { - val stack = screenStack - if (stack != null && stack.rootScreen == it.screen) { - val parentFragment = it.parentFragment - if (parentFragment is ScreenStackFragment) { - if (parentFragment.screen.nativeBackButtonDismissalEnabled) { - parentFragment.dismiss() - } else { - parentFragment.dispatchHeaderBackButtonClickedEvent() + private val backClickListener = + OnClickListener { + screenFragment?.let { + val stack = screenStack + if (stack != null && stack.rootScreen == it.screen) { + val parentFragment = it.parentFragment + if (parentFragment is ScreenStackFragment) { + if (parentFragment.screen.nativeBackButtonDismissalEnabled) { + parentFragment.dismiss() + } else { + parentFragment.dispatchHeaderBackButtonClickedEvent() + } } - } - } else { - if (it.screen.nativeBackButtonDismissalEnabled) { - it.dismiss() } else { - it.dispatchHeaderBackButtonClickedEvent() + if (it.screen.nativeBackButtonDismissalEnabled) { + it.dismiss() + } else { + it.dispatchHeaderBackButtonClickedEvent() + } } } } - } - override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + override fun onLayout( + changed: Boolean, + l: Int, + t: Int, + r: Int, + b: Int, + ) { // no-op } @@ -79,18 +88,21 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { super.onAttachedToWindow() isAttachedToWindow = true val surfaceId = UIManagerHelper.getSurfaceId(this) - UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id) + UIManagerHelper + .getEventDispatcherForReactTag(context as ReactContext, id) ?.dispatchEvent(HeaderAttachedEvent(surfaceId, id)) // we want to save the top inset before the status bar can be hidden, which would resolve in // inset being 0 if (headerTopInset == null) { - headerTopInset = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) - rootWindowInsets.getInsets(WindowInsets.Type.systemBars()).top - else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - rootWindowInsets.systemWindowInsetTop - else - // Hacky fallback for old android. Before Marshmallow, the status bar height was always 25 - (25 * resources.displayMetrics.density).toInt() + headerTopInset = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + rootWindowInsets.getInsets(WindowInsets.Type.systemBars()).top + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + rootWindowInsets.systemWindowInsetTop + } else { + // Hacky fallback for old android. Before Marshmallow, the status bar height was always 25 + (25 * resources.displayMetrics.density).toInt() + } } onUpdate() } @@ -99,7 +111,8 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { super.onDetachedFromWindow() isAttachedToWindow = false val surfaceId = UIManagerHelper.getSurfaceId(this) - UIManagerHelper.getEventDispatcherForReactTag(context as ReactContext, id) + UIManagerHelper + .getEventDispatcherForReactTag(context as ReactContext, id) ?.dispatchEvent(HeaderDetachedEvent(surfaceId, id)) } @@ -144,11 +157,12 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { // because sometimes we don't have the Fragment and Activity available then yet, e.g. on the // first setting of props. Similar thing is done for Screens of ScreenContainers, but in // `onContainerUpdate` of their Fragment - val reactContext = if (context is ReactContext) { - context as ReactContext - } else { - it.fragmentWrapper?.tryGetContext() - } + val reactContext = + if (context is ReactContext) { + context as ReactContext + } else { + it.fragmentWrapper?.tryGetContext() + } ScreenWindowTraits.trySetWindowTraits(it, activity, reactContext) } @@ -187,7 +201,7 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { // hide back button actionBar.setDisplayHomeAsUpEnabled( - screenFragment?.canNavigateBack() == true && !isBackButtonHidden + screenFragment?.canNavigateBack() == true && !isBackButtonHidden, ) // when setSupportActionBar is called a toolbar wrapper gets initialized that overwrites @@ -217,9 +231,14 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { if (titleTextView != null) { if (titleFontFamily != null || titleFontWeight > 0) { - val titleTypeface = ReactTypefaceUtils.applyStyles( - null, 0, titleFontWeight, titleFontFamily, context.assets - ) + val titleTypeface = + ReactTypefaceUtils.applyStyles( + null, + 0, + titleFontWeight, + titleFontFamily, + context.assets, + ) titleTextView.typeface = titleTypeface } if (titleFontSize > 0) { @@ -250,10 +269,11 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { if (type === ScreenStackHeaderSubview.Type.BACK) { // we special case BACK button header config type as we don't add it as a view into toolbar // but instead just copy the drawable from imageview that's added as a first child to it. - val firstChild = view.getChildAt(0) as? ImageView - ?: throw JSApplicationIllegalArgumentException( - "Back button header config view should have Image as first child" - ) + val firstChild = + view.getChildAt(0) as? ImageView + ?: throw JSApplicationIllegalArgumentException( + "Back button header config view should have Image as first child", + ) actionBar.setHomeAsUpIndicator(firstChild.drawable) i++ continue @@ -304,7 +324,10 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { maybeUpdate() } - fun addConfigSubview(child: ScreenStackHeaderSubview, index: Int) { + fun addConfigSubview( + child: ScreenStackHeaderSubview, + index: Int, + ) { configSubviews.add(index, child) maybeUpdate() } @@ -365,7 +388,10 @@ class ScreenStackHeaderConfig(context: Context) : ViewGroup(context) { this.direction = direction } - private class DebugMenuToolbar(context: Context, config: ScreenStackHeaderConfig) : CustomToolbar(context, config) { + private class DebugMenuToolbar( + context: Context, + config: ScreenStackHeaderConfig, + ) : CustomToolbar(context, config) { override fun showOverflowMenu(): Boolean { (context.applicationContext as ReactApplication) .reactNativeHost diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt index 02fe8dc2d5..735d6f5dcc 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt @@ -16,7 +16,9 @@ import com.swmansion.rnscreens.events.HeaderDetachedEvent import javax.annotation.Nonnull @ReactModule(name = ScreenStackHeaderConfigViewManager.REACT_CLASS) -class ScreenStackHeaderConfigViewManager : ViewGroupManager(), RNSScreenStackHeaderConfigManagerInterface { +class ScreenStackHeaderConfigViewManager : + ViewGroupManager(), + RNSScreenStackHeaderConfigManagerInterface { private val delegate: ViewManagerDelegate init { @@ -27,16 +29,22 @@ class ScreenStackHeaderConfigViewManager : ViewGroupManager? { - return MapBuilder.of( + override fun getExportedCustomDirectEventTypeConstants(): Map? = + MapBuilder.of( HeaderAttachedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onAttached"), HeaderDetachedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDetached"), ) - } protected override fun getDelegate(): ViewManagerDelegate = delegate @@ -154,55 +206,94 @@ class ScreenStackHeaderConfigViewManager : ViewGroupManager(), RNSScreenStackHeaderSubviewManagerInterface { +class ScreenStackHeaderSubviewManager : + ViewGroupManager(), + RNSScreenStackHeaderSubviewManagerInterface { private val delegate: ViewManagerDelegate init { @@ -22,15 +24,19 @@ class ScreenStackHeaderSubviewManager : ViewGroupManager ScreenStackHeaderSubview.Type.LEFT - "center" -> ScreenStackHeaderSubview.Type.CENTER - "right" -> ScreenStackHeaderSubview.Type.RIGHT - "back" -> ScreenStackHeaderSubview.Type.BACK - "searchBar" -> ScreenStackHeaderSubview.Type.SEARCH_BAR - else -> throw JSApplicationIllegalArgumentException("Unknown type $type") - } + override fun setType( + view: ScreenStackHeaderSubview, + type: String?, + ) { + view.type = + when (type) { + "left" -> ScreenStackHeaderSubview.Type.LEFT + "center" -> ScreenStackHeaderSubview.Type.CENTER + "right" -> ScreenStackHeaderSubview.Type.RIGHT + "back" -> ScreenStackHeaderSubview.Type.BACK + "searchBar" -> ScreenStackHeaderSubview.Type.SEARCH_BAR + else -> throw JSApplicationIllegalArgumentException("Unknown type $type") + } } protected override fun getDelegate(): ViewManagerDelegate = delegate diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt index cbbf0d5209..c3dd2c880d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackViewManager.kt @@ -13,7 +13,9 @@ import com.facebook.react.viewmanagers.RNSScreenStackManagerInterface import com.swmansion.rnscreens.events.StackFinishTransitioningEvent @ReactModule(name = ScreenStackViewManager.REACT_CLASS) -class ScreenStackViewManager : ViewGroupManager(), RNSScreenStackManagerInterface { +class ScreenStackViewManager : + ViewGroupManager(), + RNSScreenStackManagerInterface { private val delegate: ViewManagerDelegate init { @@ -24,12 +26,19 @@ class ScreenStackViewManager : ViewGroupManager(), RNSScreenStackMa override fun createViewInstance(reactContext: ThemedReactContext) = ScreenStack(reactContext) - override fun addView(parent: ScreenStack, child: View, index: Int) { + override fun addView( + parent: ScreenStack, + child: View, + index: Int, + ) { require(child is Screen) { "Attempt attach child that is not of type RNScreen" } parent.addScreen(child, index) } - override fun removeViewAt(parent: ScreenStack, index: Int) { + override fun removeViewAt( + parent: ScreenStack, + index: Int, + ) { prepareOutTransition(parent.getScreenAt(index)) parent.removeScreenAt(index) } @@ -57,7 +66,10 @@ class ScreenStackViewManager : ViewGroupManager(), RNSScreenStackMa override fun getChildCount(parent: ScreenStack) = parent.screenCount - override fun getChildAt(parent: ScreenStack, index: Int): View = parent.getScreenAt(index) + override fun getChildAt( + parent: ScreenStack, + index: Int, + ): View = parent.getScreenAt(index) override fun createShadowNodeInstance(context: ReactApplicationContext): LayoutShadowNode = ScreensShadowNode(context) @@ -65,9 +77,10 @@ class ScreenStackViewManager : ViewGroupManager(), RNSScreenStackMa protected override fun getDelegate(): ViewManagerDelegate = delegate - override fun getExportedCustomDirectEventTypeConstants(): MutableMap = mutableMapOf( - StackFinishTransitioningEvent.EVENT_NAME to mutableMapOf("registrationName" to "onFinishTransitioning") - ) + override fun getExportedCustomDirectEventTypeConstants(): MutableMap = + mutableMapOf( + StackFinishTransitioningEvent.EVENT_NAME to mutableMapOf("registrationName" to "onFinishTransitioning"), + ) companion object { const val REACT_CLASS = "RNSScreenStack" diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt index bd6282e196..62cf547c19 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt @@ -22,7 +22,9 @@ import com.swmansion.rnscreens.events.ScreenWillAppearEvent import com.swmansion.rnscreens.events.ScreenWillDisappearEvent @ReactModule(name = ScreenViewManager.REACT_CLASS) -open class ScreenViewManager : ViewGroupManager(), RNSScreenManagerInterface { +open class ScreenViewManager : + ViewGroupManager(), + RNSScreenManagerInterface { private val delegate: ViewManagerDelegate init { @@ -33,14 +35,17 @@ open class ScreenViewManager : ViewGroupManager(), RNSScreenManagerInter override fun createViewInstance(reactContext: ThemedReactContext) = Screen(reactContext) - override fun setActivityState(view: Screen, activityState: Float) { + override fun setActivityState( + view: Screen, + activityState: Float, + ) { setActivityState(view, activityState.toInt()) } override fun updateState( view: Screen, props: ReactStylesDiffMap?, - stateWrapper: StateWrapper? + stateWrapper: StateWrapper?, ): Any? { if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // fabricViewStateManager should never be null in Fabric. The null check is only for Paper's empty impl. @@ -50,7 +55,10 @@ open class ScreenViewManager : ViewGroupManager(), RNSScreenManagerInter } @ReactProp(name = "activityState") - fun setActivityState(view: Screen, activityState: Int) { + fun setActivityState( + view: Screen, + activityState: Int, + ) { if (activityState == -1) { // Null will be provided when activityState is set as an animated value and we change // it from JS to be a plain value (non animated). @@ -66,137 +74,219 @@ open class ScreenViewManager : ViewGroupManager(), RNSScreenManagerInter } @ReactProp(name = "stackPresentation") - override fun setStackPresentation(view: Screen, presentation: String?) { - view.stackPresentation = when (presentation) { - "push" -> Screen.StackPresentation.PUSH - "modal", "containedModal", "fullScreenModal", "formSheet" -> - Screen.StackPresentation.MODAL - "transparentModal", "containedTransparentModal" -> - Screen.StackPresentation.TRANSPARENT_MODAL - else -> throw JSApplicationIllegalArgumentException("Unknown presentation type $presentation") - } + override fun setStackPresentation( + view: Screen, + presentation: String?, + ) { + view.stackPresentation = + when (presentation) { + "push" -> Screen.StackPresentation.PUSH + "modal", "containedModal", "fullScreenModal", "formSheet" -> + Screen.StackPresentation.MODAL + "transparentModal", "containedTransparentModal" -> + Screen.StackPresentation.TRANSPARENT_MODAL + else -> throw JSApplicationIllegalArgumentException("Unknown presentation type $presentation") + } } @ReactProp(name = "stackAnimation") - override fun setStackAnimation(view: Screen, animation: String?) { - view.stackAnimation = when (animation) { - null, "default", "flip", "simple_push" -> Screen.StackAnimation.DEFAULT - "none" -> Screen.StackAnimation.NONE - "fade" -> Screen.StackAnimation.FADE - "slide_from_right" -> Screen.StackAnimation.SLIDE_FROM_RIGHT - "slide_from_left" -> Screen.StackAnimation.SLIDE_FROM_LEFT - "slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM - "fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM - "ios" -> Screen.StackAnimation.IOS - else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation") - } + override fun setStackAnimation( + view: Screen, + animation: String?, + ) { + view.stackAnimation = + when (animation) { + null, "default", "flip", "simple_push" -> Screen.StackAnimation.DEFAULT + "none" -> Screen.StackAnimation.NONE + "fade" -> Screen.StackAnimation.FADE + "slide_from_right" -> Screen.StackAnimation.SLIDE_FROM_RIGHT + "slide_from_left" -> Screen.StackAnimation.SLIDE_FROM_LEFT + "slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM + "fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM + "ios" -> Screen.StackAnimation.IOS + else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation") + } } @ReactProp(name = "gestureEnabled", defaultBoolean = true) - override fun setGestureEnabled(view: Screen, gestureEnabled: Boolean) { + override fun setGestureEnabled( + view: Screen, + gestureEnabled: Boolean, + ) { view.isGestureEnabled = gestureEnabled } @ReactProp(name = "replaceAnimation") - override fun setReplaceAnimation(view: Screen, animation: String?) { - view.replaceAnimation = when (animation) { - null, "pop" -> Screen.ReplaceAnimation.POP - "push" -> Screen.ReplaceAnimation.PUSH - else -> throw JSApplicationIllegalArgumentException("Unknown replace animation type $animation") - } + override fun setReplaceAnimation( + view: Screen, + animation: String?, + ) { + view.replaceAnimation = + when (animation) { + null, "pop" -> Screen.ReplaceAnimation.POP + "push" -> Screen.ReplaceAnimation.PUSH + else -> throw JSApplicationIllegalArgumentException("Unknown replace animation type $animation") + } } @ReactProp(name = "screenOrientation") - override fun setScreenOrientation(view: Screen, screenOrientation: String?) { + override fun setScreenOrientation( + view: Screen, + screenOrientation: String?, + ) { view.setScreenOrientation(screenOrientation) } @ReactProp(name = "statusBarAnimation") - override fun setStatusBarAnimation(view: Screen, statusBarAnimation: String?) { + override fun setStatusBarAnimation( + view: Screen, + statusBarAnimation: String?, + ) { val animated = statusBarAnimation != null && "none" != statusBarAnimation view.isStatusBarAnimated = animated } @ReactProp(name = "statusBarColor", customType = "Color") - override fun setStatusBarColor(view: Screen, statusBarColor: Int?) { + override fun setStatusBarColor( + view: Screen, + statusBarColor: Int?, + ) { view.statusBarColor = statusBarColor } @ReactProp(name = "statusBarStyle") - override fun setStatusBarStyle(view: Screen, statusBarStyle: String?) { + override fun setStatusBarStyle( + view: Screen, + statusBarStyle: String?, + ) { view.statusBarStyle = statusBarStyle } @ReactProp(name = "statusBarTranslucent") - override fun setStatusBarTranslucent(view: Screen, statusBarTranslucent: Boolean) { + override fun setStatusBarTranslucent( + view: Screen, + statusBarTranslucent: Boolean, + ) { view.isStatusBarTranslucent = statusBarTranslucent } @ReactProp(name = "statusBarHidden") - override fun setStatusBarHidden(view: Screen, statusBarHidden: Boolean) { + override fun setStatusBarHidden( + view: Screen, + statusBarHidden: Boolean, + ) { view.isStatusBarHidden = statusBarHidden } @ReactProp(name = "navigationBarColor", customType = "Color") - override fun setNavigationBarColor(view: Screen, navigationBarColor: Int?) { + override fun setNavigationBarColor( + view: Screen, + navigationBarColor: Int?, + ) { view.navigationBarColor = navigationBarColor } @ReactProp(name = "navigationBarTranslucent") - override fun setNavigationBarTranslucent(view: Screen, navigationBarTranslucent: Boolean) { + override fun setNavigationBarTranslucent( + view: Screen, + navigationBarTranslucent: Boolean, + ) { view.isNavigationBarTranslucent = navigationBarTranslucent } @ReactProp(name = "navigationBarHidden") - override fun setNavigationBarHidden(view: Screen, navigationBarHidden: Boolean) { + override fun setNavigationBarHidden( + view: Screen, + navigationBarHidden: Boolean, + ) { view.isNavigationBarHidden = navigationBarHidden } @ReactProp(name = "nativeBackButtonDismissalEnabled") override fun setNativeBackButtonDismissalEnabled( view: Screen, - nativeBackButtonDismissalEnabled: Boolean + nativeBackButtonDismissalEnabled: Boolean, ) { view.nativeBackButtonDismissalEnabled = nativeBackButtonDismissalEnabled } // these props are not available on Android, however we must override their setters - override fun setFullScreenSwipeEnabled(view: Screen?, value: Boolean) = Unit - - override fun setTransitionDuration(view: Screen?, value: Int) = Unit - - override fun setHideKeyboardOnSwipe(view: Screen?, value: Boolean) = Unit - - override fun setCustomAnimationOnSwipe(view: Screen?, value: Boolean) = Unit - - override fun setGestureResponseDistance(view: Screen?, value: ReadableMap?) = Unit - - override fun setHomeIndicatorHidden(view: Screen?, value: Boolean) = Unit - - override fun setPreventNativeDismiss(view: Screen?, value: Boolean) = Unit - - override fun setSwipeDirection(view: Screen?, value: String?) = Unit - - override fun setSheetAllowedDetents(view: Screen, value: String?) = Unit - - override fun setSheetLargestUndimmedDetent(view: Screen, value: String?) = Unit - - override fun setSheetGrabberVisible(view: Screen?, value: Boolean) = Unit - - override fun setSheetCornerRadius(view: Screen?, value: Float) = Unit - - override fun setSheetExpandsWhenScrolledToEdge(view: Screen?, value: Boolean) = Unit + override fun setFullScreenSwipeEnabled( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setTransitionDuration( + view: Screen?, + value: Int, + ) = Unit + + override fun setHideKeyboardOnSwipe( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setCustomAnimationOnSwipe( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setGestureResponseDistance( + view: Screen?, + value: ReadableMap?, + ) = Unit + + override fun setHomeIndicatorHidden( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setPreventNativeDismiss( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setSwipeDirection( + view: Screen?, + value: String?, + ) = Unit + + override fun setSheetAllowedDetents( + view: Screen, + value: String?, + ) = Unit - override fun getExportedCustomDirectEventTypeConstants(): MutableMap = mutableMapOf( - ScreenDismissedEvent.EVENT_NAME to MapBuilder.of("registrationName", "onDismissed"), - ScreenWillAppearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onWillAppear"), - ScreenAppearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onAppear"), - ScreenWillDisappearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onWillDisappear"), - ScreenDisappearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onDisappear"), - HeaderHeightChangeEvent.EVENT_NAME to MapBuilder.of("registrationName", "onHeaderHeightChange"), - HeaderBackButtonClickedEvent.EVENT_NAME to MapBuilder.of("registrationName", "onHeaderBackButtonClicked"), - ScreenTransitionProgressEvent.EVENT_NAME to MapBuilder.of("registrationName", "onTransitionProgress") - ) + override fun setSheetLargestUndimmedDetent( + view: Screen, + value: String?, + ) = Unit + + override fun setSheetGrabberVisible( + view: Screen?, + value: Boolean, + ) = Unit + + override fun setSheetCornerRadius( + view: Screen?, + value: Float, + ) = Unit + + override fun setSheetExpandsWhenScrolledToEdge( + view: Screen?, + value: Boolean, + ) = Unit + + override fun getExportedCustomDirectEventTypeConstants(): MutableMap = + mutableMapOf( + ScreenDismissedEvent.EVENT_NAME to MapBuilder.of("registrationName", "onDismissed"), + ScreenWillAppearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onWillAppear"), + ScreenAppearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onAppear"), + ScreenWillDisappearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onWillDisappear"), + ScreenDisappearEvent.EVENT_NAME to MapBuilder.of("registrationName", "onDisappear"), + HeaderHeightChangeEvent.EVENT_NAME to MapBuilder.of("registrationName", "onHeaderHeightChange"), + HeaderBackButtonClickedEvent.EVENT_NAME to MapBuilder.of("registrationName", "onHeaderBackButtonClicked"), + ScreenTransitionProgressEvent.EVENT_NAME to MapBuilder.of("registrationName", "onTransitionProgress"), + ) protected override fun getDelegate(): ViewManagerDelegate = delegate diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt b/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt index ce1764e2a5..edcfa7d5f2 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt @@ -2,7 +2,6 @@ package com.swmansion.rnscreens import android.animation.ArgbEvaluator import android.animation.ValueAnimator -import android.annotation.SuppressLint import android.annotation.TargetApi import android.app.Activity import android.content.pm.ActivityInfo @@ -39,7 +38,10 @@ object ScreenWindowTraits { didSetNavigationBarAppearance = true } - internal fun setOrientation(screen: Screen, activity: Activity?) { + internal fun setOrientation( + screen: Screen, + activity: Activity?, + ) { if (activity == null) { return } @@ -48,7 +50,11 @@ object ScreenWindowTraits { activity.requestedOrientation = orientation } - internal fun setColor(screen: Screen, activity: Activity?, context: ReactContext?) { + internal fun setColor( + screen: Screen, + activity: Activity?, + context: ReactContext?, + ) { if (activity == null || context == null) { return } @@ -76,10 +82,15 @@ object ScreenWindowTraits { } colorAnimation.start() } - }) + }, + ) } - internal fun setStyle(screen: Screen, activity: Activity?, context: ReactContext?) { + internal fun setStyle( + screen: Screen, + activity: Activity?, + context: ReactContext?, + ) { if (activity == null || context == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { return } @@ -98,7 +109,7 @@ object ScreenWindowTraits { internal fun setTranslucent( screen: Screen, activity: Activity?, - context: ReactContext? + context: ReactContext?, ) { if (activity == null || context == null) { return @@ -128,16 +139,15 @@ object ScreenWindowTraits { windowInsets.left, 0, windowInsets.right, - windowInsets.bottom - ) - ) - .build() + windowInsets.bottom, + ), + ).build() } else { defaultInsets.replaceSystemWindowInsets( defaultInsets.systemWindowInsetLeft, 0, defaultInsets.systemWindowInsetRight, - defaultInsets.systemWindowInsetBottom + defaultInsets.systemWindowInsetBottom, ) } } @@ -146,10 +156,14 @@ object ScreenWindowTraits { } ViewCompat.requestApplyInsets(decorView) } - }) + }, + ) } - internal fun setHidden(screen: Screen, activity: Activity?) { + internal fun setHidden( + screen: Screen, + activity: Activity?, + ) { if (activity == null) { return } @@ -169,7 +183,10 @@ object ScreenWindowTraits { // Methods concerning navigationBar management were taken from `react-native-navigation`'s repo: // https://github.com/wix/react-native-navigation/blob/9bb70d81700692141a2c505c081c2d86c7f9c66e/lib/android/app/src/main/java/com/reactnativenavigation/utils/SystemUiUtils.kt - internal fun setNavigationBarColor(screen: Screen, activity: Activity?) { + internal fun setNavigationBarColor( + screen: Screen, + activity: Activity?, + ) { if (activity == null) { return } @@ -186,7 +203,10 @@ object ScreenWindowTraits { window.navigationBarColor = color } - internal fun setNavigationBarTranslucent(screen: Screen, activity: Activity?) { + internal fun setNavigationBarTranslucent( + screen: Screen, + activity: Activity?, + ) { if (activity == null) { return } @@ -200,7 +220,10 @@ object ScreenWindowTraits { WindowCompat.setDecorFitsSystemWindows(window, !translucent) } - internal fun setNavigationBarHidden(screen: Screen, activity: Activity?) { + internal fun setNavigationBarHidden( + screen: Screen, + activity: Activity?, + ) { if (activity == null) { return } @@ -219,12 +242,16 @@ object ScreenWindowTraits { } else { WindowInsetsControllerCompat( window, - window.decorView + window.decorView, ).show(WindowInsetsCompat.Type.navigationBars()) } } - internal fun trySetWindowTraits(screen: Screen, activity: Activity?, context: ReactContext?) { + internal fun trySetWindowTraits( + screen: Screen, + activity: Activity?, + context: ReactContext?, + ) { if (didSetOrientation) { setOrientation(screen, activity) } @@ -241,7 +268,10 @@ object ScreenWindowTraits { } } - private fun findScreenForTrait(screen: Screen, trait: WindowTraits): Screen? { + private fun findScreenForTrait( + screen: Screen, + trait: WindowTraits, + ): Screen? { val childWithTrait = childScreenWithTraitSet(screen, trait) if (childWithTrait != null) { return childWithTrait @@ -255,7 +285,10 @@ object ScreenWindowTraits { } } - private fun findParentWithTraitSet(screen: Screen, trait: WindowTraits): Screen? { + private fun findParentWithTraitSet( + screen: Screen, + trait: WindowTraits, + ): Screen? { var parent: ViewParent? = screen.container while (parent != null) { if (parent is Screen) { @@ -270,7 +303,7 @@ object ScreenWindowTraits { private fun childScreenWithTraitSet( screen: Screen?, - trait: WindowTraits + trait: WindowTraits, ): Screen? { screen?.fragmentWrapper?.let { for (sc in it.childScreenContainers) { @@ -288,8 +321,11 @@ object ScreenWindowTraits { return null } - private fun checkTraitForScreen(screen: Screen, trait: WindowTraits): Boolean { - return when (trait) { + private fun checkTraitForScreen( + screen: Screen, + trait: WindowTraits, + ): Boolean = + when (trait) { WindowTraits.ORIENTATION -> screen.screenOrientation != null WindowTraits.COLOR -> screen.statusBarColor != null WindowTraits.STYLE -> screen.statusBarStyle != null @@ -300,7 +336,6 @@ object ScreenWindowTraits { WindowTraits.NAVIGATION_BAR_TRANSLUCENT -> screen.isNavigationBarTranslucent != null WindowTraits.NAVIGATION_BAR_HIDDEN -> screen.isNavigationBarHidden != null } - } private fun isColorLight(color: Int): Boolean { val darkness: Double = diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt b/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt index e2f5332001..047a14acc8 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreensModule.kt @@ -10,9 +10,9 @@ import com.swmansion.rnscreens.events.ScreenTransitionProgressEvent import java.util.concurrent.atomic.AtomicBoolean @ReactModule(name = ScreensModule.NAME) -class ScreensModule(private val reactContext: ReactApplicationContext) - : NativeScreensModuleSpec(reactContext) -{ +class ScreensModule( + private val reactContext: ReactApplicationContext, +) : NativeScreensModuleSpec(reactContext) { private var topScreenId: Int = -1 private val isActiveTransition = AtomicBoolean(false) @@ -64,25 +64,32 @@ class ScreensModule(private val reactContext: ReactApplicationContext) if (topScreenId == -1) { return } - val progressFloat = progress.toFloat(); + val progressFloat = progress.toFloat() val coalescingKey = ScreenFragment.getCoalescingKey(progressFloat) UIManagerHelper .getEventDispatcherForReactTag(reactContext, topScreenId) ?.dispatchEvent( ScreenTransitionProgressEvent( UIManagerHelper.getSurfaceId(reactContext), - topScreenId, progressFloat, true, true, coalescingKey - ) + topScreenId, + progressFloat, + true, + true, + coalescingKey, + ), ) } @DoNotStrip - private fun finishTransition(reactTag: Int?, canceled: Boolean) { + private fun finishTransition( + reactTag: Int?, + canceled: Boolean, + ) { UiThreadUtil.assertOnUiThread() if (!isActiveTransition.get() || reactTag == null) { Log.e( "[RNScreens]", - "Unable to call `finishTransition` method before transition start." + "Unable to call `finishTransition` method before transition start.", ) return } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt b/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt index 6d853fe0af..72ec827a62 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt +++ b/android/src/main/java/com/swmansion/rnscreens/ScreensShadowNode.kt @@ -6,7 +6,9 @@ import com.facebook.react.uimanager.NativeViewHierarchyManager import com.facebook.react.uimanager.NativeViewHierarchyOptimizer import com.facebook.react.uimanager.UIManagerModule -internal class ScreensShadowNode(private var context: ReactContext) : LayoutShadowNode() { +internal class ScreensShadowNode( + private var context: ReactContext, +) : LayoutShadowNode() { override fun onBeforeLayout(nativeViewHierarchyOptimizer: NativeViewHierarchyOptimizer) { super.onBeforeLayout(nativeViewHierarchyOptimizer) (context.getNativeModule(UIManagerModule::class.java))?.addUIBlock { nativeViewHierarchyManager: NativeViewHierarchyManager -> diff --git a/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt b/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt index 8c674cec3d..2d14ccbdbc 100644 --- a/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +++ b/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt @@ -18,24 +18,20 @@ import com.swmansion.rnscreens.events.SearchBarOpenEvent import com.swmansion.rnscreens.events.SearchBarSearchButtonPressEvent @ReactModule(name = SearchBarManager.REACT_CLASS) -class SearchBarManager : ViewGroupManager(), RNSSearchBarManagerInterface { +class SearchBarManager : + ViewGroupManager(), + RNSSearchBarManagerInterface { private val delegate: ViewManagerDelegate init { delegate = RNSSearchBarManagerDelegate(this) } - protected override fun getDelegate(): ViewManagerDelegate { - return delegate - } + protected override fun getDelegate(): ViewManagerDelegate = delegate - override fun getName(): String { - return REACT_CLASS - } + override fun getName(): String = REACT_CLASS - override fun createViewInstance(context: ThemedReactContext): SearchBarView { - return SearchBarView(context) - } + override fun createViewInstance(context: ThemedReactContext): SearchBarView = SearchBarView(context) override fun onAfterUpdateTransaction(view: SearchBarView) { super.onAfterUpdateTransaction(view) @@ -43,75 +39,107 @@ class SearchBarManager : ViewGroupManager(), RNSSearchBarManagerI } @ReactProp(name = "autoCapitalize") - override fun setAutoCapitalize(view: SearchBarView, autoCapitalize: String?) { - view.autoCapitalize = when (autoCapitalize) { - null, "none" -> SearchBarView.SearchBarAutoCapitalize.NONE - "words" -> SearchBarView.SearchBarAutoCapitalize.WORDS - "sentences" -> SearchBarView.SearchBarAutoCapitalize.SENTENCES - "characters" -> SearchBarView.SearchBarAutoCapitalize.CHARACTERS - else -> throw JSApplicationIllegalArgumentException( - "Forbidden auto capitalize value passed" - ) - } + override fun setAutoCapitalize( + view: SearchBarView, + autoCapitalize: String?, + ) { + view.autoCapitalize = + when (autoCapitalize) { + null, "none" -> SearchBarView.SearchBarAutoCapitalize.NONE + "words" -> SearchBarView.SearchBarAutoCapitalize.WORDS + "sentences" -> SearchBarView.SearchBarAutoCapitalize.SENTENCES + "characters" -> SearchBarView.SearchBarAutoCapitalize.CHARACTERS + else -> throw JSApplicationIllegalArgumentException( + "Forbidden auto capitalize value passed", + ) + } } @ReactProp(name = "autoFocus") - fun setAutoFocus(view: SearchBarView, autoFocus: Boolean?) { + fun setAutoFocus( + view: SearchBarView, + autoFocus: Boolean?, + ) { view.autoFocus = autoFocus ?: false } @ReactProp(name = "barTintColor", customType = "Color") - override fun setBarTintColor(view: SearchBarView, color: Int?) { + override fun setBarTintColor( + view: SearchBarView, + color: Int?, + ) { view.tintColor = color } @ReactProp(name = "disableBackButtonOverride") - override fun setDisableBackButtonOverride(view: SearchBarView, disableBackButtonOverride: Boolean) { + override fun setDisableBackButtonOverride( + view: SearchBarView, + disableBackButtonOverride: Boolean, + ) { view.shouldOverrideBackButton = disableBackButtonOverride != true } @ReactProp(name = "inputType") - override fun setInputType(view: SearchBarView, inputType: String?) { - view.inputType = when (inputType) { - null, "text" -> SearchBarView.SearchBarInputTypes.TEXT - "phone" -> SearchBarView.SearchBarInputTypes.PHONE - "number" -> SearchBarView.SearchBarInputTypes.NUMBER - "email" -> SearchBarView.SearchBarInputTypes.EMAIL - else -> throw JSApplicationIllegalArgumentException( - "Forbidden input type value" - ) - } + override fun setInputType( + view: SearchBarView, + inputType: String?, + ) { + view.inputType = + when (inputType) { + null, "text" -> SearchBarView.SearchBarInputTypes.TEXT + "phone" -> SearchBarView.SearchBarInputTypes.PHONE + "number" -> SearchBarView.SearchBarInputTypes.NUMBER + "email" -> SearchBarView.SearchBarInputTypes.EMAIL + else -> throw JSApplicationIllegalArgumentException( + "Forbidden input type value", + ) + } } @ReactProp(name = "placeholder") - override fun setPlaceholder(view: SearchBarView, placeholder: String?) { + override fun setPlaceholder( + view: SearchBarView, + placeholder: String?, + ) { if (placeholder != null) { view.placeholder = placeholder } } @ReactProp(name = "textColor", customType = "Color") - override fun setTextColor(view: SearchBarView, color: Int?) { + override fun setTextColor( + view: SearchBarView, + color: Int?, + ) { view.textColor = color } @ReactProp(name = "headerIconColor", customType = "Color") - override fun setHeaderIconColor(view: SearchBarView, color: Int?) { + override fun setHeaderIconColor( + view: SearchBarView, + color: Int?, + ) { view.headerIconColor = color } @ReactProp(name = "hintTextColor", customType = "Color") - override fun setHintTextColor(view: SearchBarView, color: Int?) { + override fun setHintTextColor( + view: SearchBarView, + color: Int?, + ) { view.hintTextColor = color } @ReactProp(name = "shouldShowHintSearchIcon") - override fun setShouldShowHintSearchIcon(view: SearchBarView, shouldShowHintSearchIcon: Boolean) { + override fun setShouldShowHintSearchIcon( + view: SearchBarView, + shouldShowHintSearchIcon: Boolean, + ) { view.shouldShowHintSearchIcon = shouldShowHintSearchIcon ?: true } - override fun getExportedCustomDirectEventTypeConstants(): Map? { - return MapBuilder.of( + override fun getExportedCustomDirectEventTypeConstants(): Map? = + MapBuilder.of( SearchBarBlurEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSearchBlur"), SearchBarChangeTextEvent.EVENT_NAME, @@ -125,7 +153,6 @@ class SearchBarManager : ViewGroupManager(), RNSSearchBarManagerI SearchBarSearchButtonPressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSearchButtonPress"), ) - } companion object { const val REACT_CLASS = "RNSSearchBar" @@ -149,11 +176,17 @@ class SearchBarManager : ViewGroupManager(), RNSSearchBarManagerI view?.handleClearTextJsRequest() } - override fun toggleCancelButton(view: SearchBarView?, flag: Boolean) { + override fun toggleCancelButton( + view: SearchBarView?, + flag: Boolean, + ) { view?.handleToggleCancelButtonJsRequest(flag) } - override fun setText(view: SearchBarView?, text: String?) { + override fun setText( + view: SearchBarView?, + text: String?, + ) { view?.handleSetTextJsRequest(text) } @@ -163,27 +196,45 @@ class SearchBarManager : ViewGroupManager(), RNSSearchBarManagerI // iOS only - override fun setPlacement(view: SearchBarView, placeholder: String?) { + override fun setPlacement( + view: SearchBarView, + placeholder: String?, + ) { logNotAvailable("setPlacement") } - override fun setHideWhenScrolling(view: SearchBarView?, value: Boolean) { + override fun setHideWhenScrolling( + view: SearchBarView?, + value: Boolean, + ) { logNotAvailable("hideWhenScrolling") } - override fun setObscureBackground(view: SearchBarView?, value: Boolean) { + override fun setObscureBackground( + view: SearchBarView?, + value: Boolean, + ) { logNotAvailable("hideNavigationBar") } - override fun setHideNavigationBar(view: SearchBarView?, value: Boolean) { + override fun setHideNavigationBar( + view: SearchBarView?, + value: Boolean, + ) { logNotAvailable("hideNavigationBar") } - override fun setCancelButtonText(view: SearchBarView?, value: String?) { + override fun setCancelButtonText( + view: SearchBarView?, + value: String?, + ) { logNotAvailable("cancelButtonText") } - override fun setTintColor(view: SearchBarView?, value: Int?) { + override fun setTintColor( + view: SearchBarView?, + value: Int?, + ) { logNotAvailable("tintColor") } } diff --git a/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt b/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt index 097ff078b7..dd666ba099 100644 --- a/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +++ b/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt @@ -16,7 +16,9 @@ import com.swmansion.rnscreens.events.SearchBarOpenEvent import com.swmansion.rnscreens.events.SearchBarSearchButtonPressEvent @SuppressLint("ViewConstructor") -class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext) { +class SearchBarView( + reactContext: ReactContext?, +) : ReactViewGroup(reactContext) { var inputType: SearchBarInputTypes = SearchBarInputTypes.TEXT var autoCapitalize: SearchBarAutoCapitalize = SearchBarAutoCapitalize.NONE var textColor: Int? = null @@ -71,8 +73,10 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext) super.onAttachedToWindow() screenStackFragment?.onSearchViewCreate = { newSearchView -> - if (searchViewFormatter == null) searchViewFormatter = - SearchViewFormatter(newSearchView) + if (searchViewFormatter == null) { + searchViewFormatter = + SearchViewFormatter(newSearchView) + } setSearchViewProps() if (autoFocus) { screenStackFragment?.searchView?.focus() @@ -81,17 +85,19 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext) } private fun setSearchViewListeners(searchView: SearchView) { - searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextChange(newText: String?): Boolean { - handleTextChange(newText) - return true - } + searchView.setOnQueryTextListener( + object : SearchView.OnQueryTextListener { + override fun onQueryTextChange(newText: String?): Boolean { + handleTextChange(newText) + return true + } - override fun onQueryTextSubmit(query: String?): Boolean { - handleTextSubmit(query) - return true - } - }) + override fun onQueryTextSubmit(query: String?): Boolean { + handleTextSubmit(query) + return true + } + }, + ) searchView.setOnQueryTextFocusChangeListener { _, hasFocus -> handleFocusChange(hasFocus) } @@ -157,15 +163,19 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext) private fun setToolbarElementsVisibility(visibility: Int) { for (i in 0..(headerConfig?.configSubviewsCount?.minus(1) ?: 0)) { val subview = headerConfig?.getConfigSubview(i) - if (subview?.type != ScreenStackHeaderSubview.Type.SEARCH_BAR) + if (subview?.type != ScreenStackHeaderSubview.Type.SEARCH_BAR) { subview?.visibility = visibility + } } } private val surfaceId = UIManagerHelper.getSurfaceId(this) enum class SearchBarAutoCapitalize { - NONE, WORDS, SENTENCES, CHARACTERS + NONE, + WORDS, + SENTENCES, + CHARACTERS, } enum class SearchBarInputTypes { @@ -179,17 +189,14 @@ class SearchBarView(reactContext: ReactContext?) : ReactViewGroup(reactContext) } }, PHONE { - override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = - InputType.TYPE_CLASS_PHONE + override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_CLASS_PHONE }, NUMBER { - override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = - InputType.TYPE_CLASS_NUMBER + override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_CLASS_NUMBER }, EMAIL { - override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = - InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS - }; + override fun toAndroidInputType(capitalize: SearchBarAutoCapitalize) = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + }, ; abstract fun toAndroidInputType(capitalize: SearchBarAutoCapitalize): Int } diff --git a/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt b/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt index 68102c323b..037d16510d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +++ b/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt @@ -7,7 +7,9 @@ import android.widget.ImageView import androidx.appcompat.R import androidx.appcompat.widget.SearchView -class SearchViewFormatter(var searchView: SearchView) { +class SearchViewFormatter( + var searchView: SearchView, +) { private var defaultTextColor: Int? = null private var defaultTintBackground: Drawable? = null @@ -57,7 +59,10 @@ class SearchViewFormatter(var searchView: SearchView) { } } - fun setPlaceholder(placeholder: String, shouldShowHintSearchIcon: Boolean) { + fun setPlaceholder( + placeholder: String, + shouldShowHintSearchIcon: Boolean, + ) { if (shouldShowHintSearchIcon) { searchView.queryHint = placeholder } else { diff --git a/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt index 777a95f406..39b2e3f8dc 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/HeaderAttachedEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class HeaderAttachedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class HeaderAttachedEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt index d7fa04f9ae..0bee5d717e 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/HeaderBackButtonClickedEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class HeaderBackButtonClickedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class HeaderBackButtonClickedEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt index f4286e9c20..39576873b8 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/HeaderDetachedEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class HeaderDetachedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class HeaderDetachedEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt index 37aefab15a..40ed2583d1 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/HeaderHeightChangeEvent.kt @@ -7,17 +7,17 @@ import com.facebook.react.uimanager.events.Event class HeaderHeightChangeEvent( surfaceId: Int, viewId: Int, - private val headerHeight: Double + private val headerHeight: Double, ) : Event(surfaceId, viewId) { - override fun getEventName() = EVENT_NAME // As the same header height could appear twice, use header height as a coalescing key. override fun getCoalescingKey(): Short = headerHeight.toInt().toShort() - override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putDouble("headerHeight", headerHeight) - } + override fun getEventData(): WritableMap? = + Arguments.createMap().apply { + putDouble("headerHeight", headerHeight) + } companion object { const val EVENT_NAME = "topHeaderHeightChange" diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt index ae3978886a..e1e3a9fa7c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenAppearEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class ScreenAppearEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class ScreenAppearEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt index dcdd514416..5fafa4f987 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenDisappearEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class ScreenDisappearEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class ScreenDisappearEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt index 515a91a8db..22b22c5b1c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenDismissedEvent.kt @@ -4,15 +4,19 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class ScreenDismissedEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class ScreenDismissedEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. override fun getCoalescingKey(): Short = 0 - override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putInt("dismissCount", 1) - } + override fun getEventData(): WritableMap? = + Arguments.createMap().apply { + putInt("dismissCount", 1) + } companion object { const val EVENT_NAME = "topDismissed" diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt index f414595722..1f1d3d27bd 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenTransitionProgressEvent.kt @@ -10,17 +10,18 @@ class ScreenTransitionProgressEvent( private val progress: Float, private val isClosing: Boolean, private val isGoingForward: Boolean, - private val coalescingKey: Short + private val coalescingKey: Short, ) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME override fun getCoalescingKey(): Short = coalescingKey - override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putDouble("progress", progress.toDouble()) - putInt("closing", if (isClosing) 1 else 0) - putInt("goingForward", if (isGoingForward) 1 else 0) - } + override fun getEventData(): WritableMap? = + Arguments.createMap().apply { + putDouble("progress", progress.toDouble()) + putInt("closing", if (isClosing) 1 else 0) + putInt("goingForward", if (isGoingForward) 1 else 0) + } companion object { const val EVENT_NAME = "topTransitionProgress" diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt index 2c537606e0..e121004e3d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillAppearEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class ScreenWillAppearEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class ScreenWillAppearEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt index 0b220a5c75..e81991bf9d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/ScreenWillDisappearEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class ScreenWillDisappearEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class ScreenWillDisappearEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt index 2a73f90e30..9929aa7d21 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarBlurEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class SearchBarBlurEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class SearchBarBlurEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt index 155c8bc12b..d1c1e07068 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarChangeTextEvent.kt @@ -14,9 +14,10 @@ class SearchBarChangeTextEvent( // All events for a given view can be coalesced. override fun getCoalescingKey(): Short = 0 - override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putString("text", text) - } + override fun getEventData(): WritableMap? = + Arguments.createMap().apply { + putString("text", text) + } companion object { const val EVENT_NAME = "topChangeText" diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt index 9e0aa7318c..7ace372655 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarCloseEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class SearchBarCloseEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class SearchBarCloseEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt index fda5180fb7..2b155ec660 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarFocusEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class SearchBarFocusEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class SearchBarFocusEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt index 10f2f478b7..6a227c8d6d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarOpenEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class SearchBarOpenEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class SearchBarOpenEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt index 7ec2d096ea..bb4d1cb04c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/SearchBarSearchButtonPressEvent.kt @@ -4,15 +4,20 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class SearchBarSearchButtonPressEvent(surfaceId: Int, viewId: Int, private val text: String?) : Event(surfaceId, viewId) { +class SearchBarSearchButtonPressEvent( + surfaceId: Int, + viewId: Int, + private val text: String?, +) : Event(surfaceId, viewId) { override fun getEventName(): String = EVENT_NAME // All events for a given view can be coalesced. override fun getCoalescingKey(): Short = 0 - override fun getEventData(): WritableMap? = Arguments.createMap().apply { - putString("text", text) - } + override fun getEventData(): WritableMap? = + Arguments.createMap().apply { + putString("text", text) + } companion object { const val EVENT_NAME = "topSearchButtonPress" diff --git a/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt b/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt index 95c0c1bdc7..af096272fd 100644 --- a/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt +++ b/android/src/main/java/com/swmansion/rnscreens/events/StackFinishTransitioningEvent.kt @@ -4,7 +4,10 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.WritableMap import com.facebook.react.uimanager.events.Event -class StackFinishTransitioningEvent(surfaceId: Int, viewId: Int) : Event(surfaceId, viewId) { +class StackFinishTransitioningEvent( + surfaceId: Int, + viewId: Int, +) : Event(surfaceId, viewId) { override fun getEventName() = EVENT_NAME // All events for a given view can be coalesced. diff --git a/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt b/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt index 57ddaaa5d3..5d259287f4 100644 --- a/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt +++ b/android/src/main/java/com/swmansion/rnscreens/utils/DeviceUtils.kt @@ -4,9 +4,5 @@ import android.content.Context import android.content.pm.PackageManager object DeviceUtils { - - fun isPlatformAndroidTV(context: Context?): Boolean { - return context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true - } - + fun isPlatformAndroidTV(context: Context?): Boolean = context?.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true } diff --git a/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt b/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt index 908cae9854..aabf4338af 100644 --- a/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt +++ b/android/src/main/java/com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper.kt @@ -17,7 +17,9 @@ import java.lang.ref.WeakReference * See https://github.com/software-mansion/react-native-screens/pull/2169 * for more detailed description of the issue this code solves. */ -internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { +internal class ScreenDummyLayoutHelper( + reactContext: ReactApplicationContext, +) { // The state required to compute header dimensions. We want this on instance rather than on class // for context access & being tied to instance lifetime. private lateinit var coordinatorLayout: CoordinatorLayout @@ -33,7 +35,8 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { // We do not want to be responsible for the context lifecycle. If it's null, we're fine. // This same context is being passed down to our view components so it is destroyed // only if our views also are. - private var reactContextRef: WeakReference = WeakReference(reactContext) + private var reactContextRef: WeakReference = + WeakReference(reactContext) init { @@ -46,7 +49,7 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { Log.w(TAG, "Failed to load $LIBRARY_NAME") } - WEAK_INSTANCE = WeakReference(this) + weakInstance = WeakReference(this) ensureDummyLayoutWithHeader(reactContext) } @@ -66,20 +69,25 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { coordinatorLayout = CoordinatorLayout(contextWithTheme) - appBarLayout = AppBarLayout(contextWithTheme).apply { - layoutParams = CoordinatorLayout.LayoutParams( - CoordinatorLayout.LayoutParams.MATCH_PARENT, - CoordinatorLayout.LayoutParams.WRAP_CONTENT, - ) - } - - toolbar = Toolbar(contextWithTheme).apply { - title = DEFAULT_HEADER_TITLE - layoutParams = AppBarLayout.LayoutParams( - AppBarLayout.LayoutParams.MATCH_PARENT, - AppBarLayout.LayoutParams.WRAP_CONTENT - ).apply { scrollFlags = 0 } - } + appBarLayout = + AppBarLayout(contextWithTheme).apply { + layoutParams = + CoordinatorLayout.LayoutParams( + CoordinatorLayout.LayoutParams.MATCH_PARENT, + CoordinatorLayout.LayoutParams.WRAP_CONTENT, + ) + } + + toolbar = + Toolbar(contextWithTheme).apply { + title = DEFAULT_HEADER_TITLE + layoutParams = + AppBarLayout + .LayoutParams( + AppBarLayout.LayoutParams.MATCH_PARENT, + AppBarLayout.LayoutParams.WRAP_CONTENT, + ).apply { scrollFlags = 0 } + } // We know the title text view will be there, cause we've just set title. defaultFontSize = ScreenStackHeaderConfig.findTitleTextViewInToolbar(toolbar)!!.textSize @@ -87,12 +95,14 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { appBarLayout.addView(toolbar) - dummyContentView = View(contextWithTheme).apply { - layoutParams = CoordinatorLayout.LayoutParams( - CoordinatorLayout.LayoutParams.MATCH_PARENT, - CoordinatorLayout.LayoutParams.MATCH_PARENT - ) - } + dummyContentView = + View(contextWithTheme).apply { + layoutParams = + CoordinatorLayout.LayoutParams( + CoordinatorLayout.LayoutParams.MATCH_PARENT, + CoordinatorLayout.LayoutParams.MATCH_PARENT, + ) + } coordinatorLayout.apply { addView(appBarLayout) @@ -107,9 +117,15 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { * @param fontSize font size value as passed from JS * @return header height in dp as consumed by Yoga */ - private fun computeDummyLayout(fontSize: Int, isTitleEmpty: Boolean): Float { + private fun computeDummyLayout( + fontSize: Int, + isTitleEmpty: Boolean, + ): Float { if (!::coordinatorLayout.isInitialized) { - Log.e(TAG, "[RNScreens] Attempt to access dummy view hierarchy before it is initialized") + Log.e( + TAG, + "[RNScreens] Attempt to access dummy view hierarchy before it is initialized", + ) return 0.0f } @@ -124,8 +140,10 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { val decorViewWidth = topLevelDecorView.width val decorViewHeight = topLevelDecorView.height - val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(decorViewWidth, View.MeasureSpec.EXACTLY) - val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(decorViewHeight, View.MeasureSpec.EXACTLY) + val widthMeasureSpec = + View.MeasureSpec.makeMeasureSpec(decorViewWidth, View.MeasureSpec.EXACTLY) + val heightMeasureSpec = + View.MeasureSpec.makeMeasureSpec(decorViewHeight, View.MeasureSpec.EXACTLY) if (isTitleEmpty) { toolbar.title = "" @@ -136,7 +154,8 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { } val textView = ScreenStackHeaderConfig.findTitleTextViewInToolbar(toolbar) - textView?.textSize = if (fontSize != FONT_SIZE_UNSET) fontSize.toFloat() else defaultFontSize + textView?.textSize = + if (fontSize != FONT_SIZE_UNSET) fontSize.toFloat() else defaultFontSize coordinatorLayout.measure(widthMeasureSpec, heightMeasureSpec) @@ -149,13 +168,15 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { return headerHeight } - private fun requireReactContext(): ReactApplicationContext { - return requireNotNull(reactContextRef.get()) { "[RNScreens] Attempt to require missing react context" } - } + private fun requireReactContext(): ReactApplicationContext = + requireNotNull(reactContextRef.get()) { + "[RNScreens] Attempt to require missing react context" + } - private fun requireActivity(): Activity { - return requireNotNull(requireReactContext().currentActivity) { "[RNScreens] Attempt to use context detached from activity" } - } + private fun requireActivity(): Activity = + requireNotNull(requireReactContext().currentActivity) { + "[RNScreens] Attempt to use context detached from activity" + } companion object { const val TAG = "ScreenDummyLayoutHelper" @@ -169,18 +190,22 @@ internal class ScreenDummyLayoutHelper(reactContext: ReactApplicationContext) { // We access this field from C++ layer, through `getInstance` method. // We don't care what instance we get access to as long as it has initialized // dummy view hierarchy. - private var WEAK_INSTANCE = WeakReference(null) + private var weakInstance = WeakReference(null) @JvmStatic - fun getInstance(): ScreenDummyLayoutHelper? { - return WEAK_INSTANCE.get() - } + fun getInstance(): ScreenDummyLayoutHelper? = weakInstance.get() } } -private data class CacheKey(val fontSize: Int, val isTitleEmpty: Boolean) +private data class CacheKey( + val fontSize: Int, + val isTitleEmpty: Boolean, +) -private class CacheEntry(val cacheKey: CacheKey, val headerHeight: Float) { +private class CacheEntry( + val cacheKey: CacheKey, + val headerHeight: Float, +) { fun hasKey(key: CacheKey) = cacheKey.fontSize != Int.MIN_VALUE && cacheKey == key companion object { diff --git a/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt b/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt index 917c18d159..9eaab72e03 100644 --- a/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt +++ b/android/src/paper/java/com/swmansion/rnscreens/FabricEnabledViewGroup.kt @@ -4,11 +4,16 @@ import android.view.ViewGroup import com.facebook.react.bridge.ReactContext import com.facebook.react.uimanager.FabricViewStateManager -abstract class FabricEnabledViewGroup constructor(context: ReactContext?) : ViewGroup(context) { - +abstract class FabricEnabledViewGroup constructor( + context: ReactContext?, +) : ViewGroup(context) { val fabricViewStateManager get() = null as FabricViewStateManager? - protected fun updateScreenSizeFabric(width: Int, height: Int, headerHeight: Double) { + protected fun updateScreenSizeFabric( + width: Int, + height: Int, + headerHeight: Double, + ) { // do nothing } }