From a54e5ccac47338729e6099c75c0e6fba05e6896e Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 12:55:35 +0100 Subject: [PATCH 01/13] fix(android): example app manifest typo --- example/android/app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 4fe5d08..e189252 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" - android:supportsRtl="true">> + android:supportsRtl="true"> Date: Sun, 16 Mar 2025 12:56:41 +0100 Subject: [PATCH 02/13] feat(android): create ConfigurationChangedBroadcastReceiver --- .../ConfigurationChangedBroadcastReceiver.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt diff --git a/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt new file mode 100644 index 0000000..b7fbefb --- /dev/null +++ b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt @@ -0,0 +1,36 @@ +package com.orientationdirector.implementation + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build +import com.facebook.react.bridge.ReactApplicationContext + +class ConfigurationChangedBroadcastReceiver internal constructor(private val context: ReactApplicationContext) : + BroadcastReceiver() { + + private var onReceiveCallback: ((intent: Intent?) -> Unit)? = null + + override fun onReceive(context: Context?, intent: Intent?) { + this.onReceiveCallback?.invoke(intent) + } + + fun setOnReceiveCallback(callback: (intent: Intent?) -> Unit) { + onReceiveCallback = callback + } + + fun register() { + val filter = IntentFilter("${context.packageName}.CONFIGURATION_CHANGED") + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED) + } else { + context.registerReceiver(this, filter) + } + } + + fun unregister() { + context.unregisterReceiver(this) + } +} From bf99c414ac631641b172443be318395970be9bbb Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 12:57:29 +0100 Subject: [PATCH 03/13] feat(android): hook broadcast receiver into implementation --- .../OrientationDirectorModuleImpl.kt | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index d121fe7..4ee50ab 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -15,6 +15,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re ) ) private var mLifecycleListener = LifecycleListener() + private var mBroadcastReceiver = ConfigurationChangedBroadcastReceiver(context) private var initialSupportedInterfaceOrientations = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED private var lastInterfaceOrientation = Orientation.UNKNOWN @@ -31,24 +32,31 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re mAutoRotationObserver.enable() + mBroadcastReceiver.setOnReceiveCallback { + // TODO: IMPLEMENT LOGIC TO COMPUTE INTERFACE PROPERLY + } + context.addLifecycleEventListener(mLifecycleListener) mLifecycleListener.setOnHostResumeCallback { if (!didComputeInitialDeviceOrientation || areOrientationSensorsEnabled) { mOrientationSensorsEventListener.enable() } mAutoRotationObserver.enable() + mBroadcastReceiver.register() } mLifecycleListener.setOnHostPauseCallback { if (initialized && areOrientationSensorsEnabled) { mOrientationSensorsEventListener.disable() mAutoRotationObserver.disable() } + mBroadcastReceiver.unregister() } mLifecycleListener.setOnHostDestroyCallback { if (areOrientationSensorsEnabled) { mOrientationSensorsEventListener.disable() mAutoRotationObserver.disable() } + mBroadcastReceiver.unregister() } initialSupportedInterfaceOrientations = @@ -89,15 +97,16 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re return } - val lastInterfaceOrientationIsAlreadyInLandscape = lastInterfaceOrientation == Orientation.LANDSCAPE_RIGHT - || lastInterfaceOrientation == Orientation.LANDSCAPE_LEFT - if (lastInterfaceOrientationIsAlreadyInLandscape) { - updateLastInterfaceOrientationTo(lastInterfaceOrientation) - return; - } + val lastInterfaceOrientationIsAlreadyInLandscape = + lastInterfaceOrientation == Orientation.LANDSCAPE_RIGHT + || lastInterfaceOrientation == Orientation.LANDSCAPE_LEFT + if (lastInterfaceOrientationIsAlreadyInLandscape) { + updateLastInterfaceOrientationTo(lastInterfaceOrientation) + return; + } val systemDefaultLandscapeOrientation = Orientation.LANDSCAPE_RIGHT - updateLastInterfaceOrientationTo(systemDefaultLandscapeOrientation) + updateLastInterfaceOrientationTo(systemDefaultLandscapeOrientation) } fun unlock() { @@ -165,7 +174,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re return } - val supportsLandscape = mUtils.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + val supportsLandscape = + mUtils.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; if (isLocked && !supportsLandscape) { return } @@ -191,8 +201,9 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re * Instead, we check that its value is either LANDSCAPE_RIGHT or LANDSCAPE_LEFT, otherwise we * exit */ - val newInterfaceOrientationIsNotLandscape = newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT - && newInterfaceOrientation != Orientation.LANDSCAPE_LEFT; + val newInterfaceOrientationIsNotLandscape = + newInterfaceOrientation != Orientation.LANDSCAPE_RIGHT + && newInterfaceOrientation != Orientation.LANDSCAPE_LEFT; if (supportsLandscape && newInterfaceOrientationIsNotLandscape) { return } From bb0b86a4b459857e3f9541b3a25eae4cd0273f18 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 13:15:13 +0100 Subject: [PATCH 04/13] chore(android): properly comment the ConfigurationChangedBroadcastReceiver --- .../ConfigurationChangedBroadcastReceiver.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt index b7fbefb..131aa96 100644 --- a/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt +++ b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt @@ -7,6 +7,13 @@ import android.content.IntentFilter import android.os.Build import com.facebook.react.bridge.ReactApplicationContext +/** + * This custom broadcast receiver is needed to properly update the interface orientation when + * the user has disabled the automatic rotation. + * + * It listens for an explicit intent that the MainActivity can send in the onConfigurationChanged + * method and calls a custom callback that is set in the main implementation init + */ class ConfigurationChangedBroadcastReceiver internal constructor(private val context: ReactApplicationContext) : BroadcastReceiver() { @@ -20,6 +27,11 @@ class ConfigurationChangedBroadcastReceiver internal constructor(private val con onReceiveCallback = callback } + /** + * This method registers the receiver by checking the api we are currently running with. + * With the latest changes in Android 14, we need to explicitly set the `Context.RECEIVER_NOT_EXPORTED` + * flag. + */ fun register() { val filter = IntentFilter("${context.packageName}.CONFIGURATION_CHANGED") From fdf63f9612dbad616718801c485163e2916a02f8 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 13:15:47 +0100 Subject: [PATCH 05/13] feat(android): implement the logic to properly update interface on manual rotation --- .../implementation/OrientationDirectorModuleImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt index 4ee50ab..1e4dc3b 100644 --- a/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt +++ b/android/src/main/java/com/orientationdirector/implementation/OrientationDirectorModuleImpl.kt @@ -33,7 +33,7 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re mAutoRotationObserver.enable() mBroadcastReceiver.setOnReceiveCallback { - // TODO: IMPLEMENT LOGIC TO COMPUTE INTERFACE PROPERLY + adaptInterfaceTo(lastDeviceOrientation, false) } context.addLifecycleEventListener(mLifecycleListener) @@ -169,8 +169,8 @@ class OrientationDirectorModuleImpl internal constructor(private val context: Re } } - private fun adaptInterfaceTo(deviceOrientation: Orientation) { - if (!mAutoRotationObserver.getLastAutoRotationStatus()) { + private fun adaptInterfaceTo(deviceOrientation: Orientation, checkLastAutoRotationStatus: Boolean = true) { + if (checkLastAutoRotationStatus && !mAutoRotationObserver.getLastAutoRotationStatus()) { return } From b20883145b0ec230653682b640d8bd8ac48f46e1 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 13:25:32 +0100 Subject: [PATCH 06/13] refactor(android): improve custom action setup --- .../implementation/ConfigurationChangedBroadcastReceiver.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt index 131aa96..1b6061e 100644 --- a/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt +++ b/android/src/main/java/com/orientationdirector/implementation/ConfigurationChangedBroadcastReceiver.kt @@ -33,7 +33,7 @@ class ConfigurationChangedBroadcastReceiver internal constructor(private val con * flag. */ fun register() { - val filter = IntentFilter("${context.packageName}.CONFIGURATION_CHANGED") + val filter = IntentFilter("${context.packageName}.$CUSTOM_INTENT_ACTION") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { context.registerReceiver(this, filter, Context.RECEIVER_NOT_EXPORTED) @@ -45,4 +45,8 @@ class ConfigurationChangedBroadcastReceiver internal constructor(private val con fun unregister() { context.unregisterReceiver(this) } + + companion object { + const val CUSTOM_INTENT_ACTION = "CONFIGURATION_CHANGED" + } } From 33d05431dbbbe73c09ca3056653b4ceb84688a19 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 13:36:11 +0100 Subject: [PATCH 07/13] chore: update README --- README.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5d528c5..76499ce 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,40 @@ This way, Expo will handle the native setup for you during `prebuild`. ## Setup +### Android + +This library uses a custom broadcast receiver to handle the manual orientation changes: when the user disables the +autorotation feature and the system prompts the user to rotate the device, the library will listen to the broadcast +sent by the MainActivity and update the interface orientation accordingly. + +To allow the library to listen to the broadcast, you need to override the `onConfigurationChanged` method in your +MainActivity file, as shown below: + +```kotlin +override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + val orientationDirectorCustomAction = + "${packageName}.${ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION}" + + val intent = + Intent(orientationDirectorCustomAction).apply { + putExtra("newConfig", newConfig) + setPackage(packageName) + } + + this.sendBroadcast(intent) +} +``` + +Nothing else is required for Android. + +### iOS + To properly handle interface orientation changes in iOS, you need to update your AppDelegate file. Since React Native 0.77, the AppDelegate has been migrated to Swift, so see the instructions below for both Swift and Objective-C. -### Objective-C +#### Objective-C In your AppDelegate.h file, import "OrientationDirector.h" and implement supportedInterfaceOrientationsForWindow method as follows: @@ -82,7 +112,7 @@ In your AppDelegate.h file, import "OrientationDirector.h" and implement support } ``` -### Swift +#### Swift You need to create a [bridging header](https://developer.apple.com/documentation/swift/importing-objective-c-into-swift#Import-Code-Within-an-App-Target) to import the library, as shown below: @@ -101,8 +131,6 @@ override func application(_ application: UIApplication, supportedInterfaceOrient If you need help, you can check the example project. -There is no need to do anything in Android, it works out of the box. - ## Usage This library exports a class called: [RNOrientationDirector](https://github.com/gladiuscode/react-native-orientation-director/blob/main/src/RNOrientationDirector.ts) that exposes the following methods: From f9f418b16b4062a046d9ddf6961b9ba7eaff1ea5 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 16 Mar 2025 13:36:33 +0100 Subject: [PATCH 08/13] feat(example): update the example app with onConfigurationChanged override --- .../MainActivity.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/example/android/app/src/main/java/com/orientationdirectorexample/MainActivity.kt b/example/android/app/src/main/java/com/orientationdirectorexample/MainActivity.kt index 32db482..bc85498 100644 --- a/example/android/app/src/main/java/com/orientationdirectorexample/MainActivity.kt +++ b/example/android/app/src/main/java/com/orientationdirectorexample/MainActivity.kt @@ -1,10 +1,13 @@ package com.orientationdirectorexample +import android.content.Intent +import android.content.res.Configuration import android.os.Bundle import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.orientationdirector.implementation.ConfigurationChangedBroadcastReceiver class MainActivity : ReactActivity() { @@ -19,9 +22,24 @@ class MainActivity : ReactActivity() { * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] */ override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(null) } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + val orientationDirectorCustomAction = + "${packageName}.${ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION}" + + val intent = + Intent(orientationDirectorCustomAction).apply { + putExtra("newConfig", newConfig) + setPackage(packageName) + } + + this.sendBroadcast(intent) + } } From d4a13bfa46a4612502ea86fb99a32c474bc0e75c Mon Sep 17 00:00:00 2001 From: gladius Date: Fri, 21 Mar 2025 23:44:34 +0100 Subject: [PATCH 09/13] feat(plugin): create withRNOrientationMainActivity - wip --- plugin/src/index.ts | 2 + plugin/src/withRNOrientationMainActivity.ts | 86 +++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 plugin/src/withRNOrientationMainActivity.ts diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 58a112d..dc179ef 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -7,6 +7,7 @@ import { import { withAppBridgingHeaderMod } from './custom-mod/withBridgingHeader'; import { withRNOrientationAppDelegate } from './withRNOrientationAppDelegate'; import { withRNOrientationBridgingHeader } from './withRNOrientationBridgingHeader'; +import { withRNOrientationMainActivity } from './withRNOrientationMainActivity'; /** * So, expo config plugin are awesome and the documentation is well written, but I still needed to look around to see @@ -22,6 +23,7 @@ const withRNOrientationDirector: ConfigPlugin = (config) => { return withPlugins(config, [ withRNOrientationAppDelegate, withRNOrientationBridgingHeader, + withRNOrientationMainActivity, withAppBridgingHeaderMod, ]); }; diff --git a/plugin/src/withRNOrientationMainActivity.ts b/plugin/src/withRNOrientationMainActivity.ts new file mode 100644 index 0000000..7b26c7c --- /dev/null +++ b/plugin/src/withRNOrientationMainActivity.ts @@ -0,0 +1,86 @@ +import { + type ConfigPlugin, + type ExportedConfigWithProps, + withMainActivity, +} from '@expo/config-plugins'; +import { type ApplicationProjectFile } from '@expo/config-plugins/build/android/Paths'; +import { mergeContents } from '@expo/config-plugins/build/utils/generateCode'; + +export const withRNOrientationMainActivity: ConfigPlugin = (config) => { + return withMainActivity(config, readMainActivityFileAndUpdateContents); +}; + +async function readMainActivityFileAndUpdateContents( + config: ExportedConfigWithProps +): Promise> { + const { modResults: mainActivityFile } = config; + + const worker = getCompatibleFileUpdater(mainActivityFile.language); + mainActivityFile.contents = worker(mainActivityFile.contents); + + return config; +} + +function getCompatibleFileUpdater( + language: ApplicationProjectFile['language'] +): (originalContents: string) => string { + switch (language) { + case 'java': + return javaFileUpdater; + case 'kt': + return ktFileUpdater; + default: + throw new Error( + `Cannot add React Native Orientation Director code to MainActivity of language "${language}"` + ); + } +} + +export function ktFileUpdater(originalContents: string): string { + const libraryImportCodeBlock = + 'import com.orientationdirector.implementation.ConfigurationChangedBroadcastReceiver\n'; + const rightBeforeClassDeclaration = /class MainActivity\s+\w+/g; + + const importMergeResults = mergeContents({ + tag: '@react-native-orientation-director/library-import', + src: originalContents, + newSrc: libraryImportCodeBlock, + anchor: rightBeforeClassDeclaration, + offset: 0, + comment: '// React Native Orientation Director', + }); + + const onConfigurationChangedCodeBlock = `override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + val orientationDirectorCustomAction = + "\${packageName}.\${ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION}" + + val intent = + Intent(orientationDirectorCustomAction).apply { + putExtra("newConfig", newConfig) + setPackage(packageName) + } + + this.sendBroadcast(intent) +}\n`; + + const rightBeforeLastClosingBrace = /super\.onCreate/g; + const pasteInTheListJustAfterTheClosingBracket = 2; + + const implementationMergeResults = mergeContents({ + tag: '@react-native-orientation-director/supportedInterfaceOrientationsFor-implementation', + src: importMergeResults.contents, + newSrc: onConfigurationChangedCodeBlock, + anchor: rightBeforeLastClosingBrace, + offset: pasteInTheListJustAfterTheClosingBracket, + comment: '// React Native Orientation Director', + }); + + return implementationMergeResults.contents; +} + +export function javaFileUpdater(originalContents: string): string { + // TODO: Implement java file update + return originalContents; +} From a7f71daa03baad6d3940cb5224ba22ef590768c3 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 23 Mar 2025 13:55:52 +0100 Subject: [PATCH 10/13] chore(test): create main activity kotlin fixture --- plugin/__tests__/fixtures/MainActivity.kt | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 plugin/__tests__/fixtures/MainActivity.kt diff --git a/plugin/__tests__/fixtures/MainActivity.kt b/plugin/__tests__/fixtures/MainActivity.kt new file mode 100644 index 0000000..a69c044 --- /dev/null +++ b/plugin/__tests__/fixtures/MainActivity.kt @@ -0,0 +1,30 @@ +package com.orientationdirectorexample + +import android.content.Intent +import android.content.res.Configuration +import android.os.Bundle +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +class MainActivity : ReactActivity() { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "OrientationDirectorExample" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(null) + } + +} From 18924b34b22065182a0d7798e0b4e0e7f6f4df82 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 23 Mar 2025 13:56:25 +0100 Subject: [PATCH 11/13] feat(test): create withRNOrientationMainActivity kotlin --- ...withRNOrientationMainActivity.spec.ts.snap | 57 +++++++++++++++++++ .../withRNOrientationMainActivity.spec.ts | 22 +++++++ plugin/src/withRNOrientationMainActivity.ts | 25 ++++---- 3 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 plugin/__tests__/__snapshots__/withRNOrientationMainActivity.spec.ts.snap create mode 100644 plugin/__tests__/withRNOrientationMainActivity.spec.ts diff --git a/plugin/__tests__/__snapshots__/withRNOrientationMainActivity.spec.ts.snap b/plugin/__tests__/__snapshots__/withRNOrientationMainActivity.spec.ts.snap new file mode 100644 index 0000000..e94f791 --- /dev/null +++ b/plugin/__tests__/__snapshots__/withRNOrientationMainActivity.spec.ts.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`withRNOrientationMainActivity updates the MainActivity.kt with both import and method implementation 1`] = ` +"package com.orientationdirectorexample + +import android.content.Intent +import android.content.res.Configuration +import android.os.Bundle +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate + +// React Native Orientation Director @generated begin @react-native-orientation-director/library-import - expo prebuild (DO NOT MODIFY) sync-dd77fee7fe624fed474053ea60c3105920a01a6a +import com.orientationdirector.implementation.ConfigurationChangedBroadcastReceiver + +// React Native Orientation Director @generated end @react-native-orientation-director/library-import +class MainActivity : ReactActivity() { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "OrientationDirectorExample" + + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(null) + } +// React Native Orientation Director @generated begin @react-native-orientation-director/supportedInterfaceOrientationsFor-implementation - expo prebuild (DO NOT MODIFY) sync-7a5cdf10057b2ddf1bcf4593bf408862cbed5473 + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + + val orientationDirectorCustomAction = + packageName + "." + ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION + + val intent = + Intent(orientationDirectorCustomAction).apply { + putExtra("newConfig", newConfig) + setPackage(packageName) + } + + this.sendBroadcast(intent) + } + +// React Native Orientation Director @generated end @react-native-orientation-director/supportedInterfaceOrientationsFor-implementation + +} +" +`; diff --git a/plugin/__tests__/withRNOrientationMainActivity.spec.ts b/plugin/__tests__/withRNOrientationMainActivity.spec.ts new file mode 100644 index 0000000..f8a29ce --- /dev/null +++ b/plugin/__tests__/withRNOrientationMainActivity.spec.ts @@ -0,0 +1,22 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; + +import { ktFileUpdater } from '../src/withRNOrientationMainActivity'; + +describe('withRNOrientationMainActivity', function () { + beforeEach(function () { + jest.resetAllMocks(); + }); + + it('updates the MainActivity.kt with both import and method implementation', async function () { + const mainActivityPath = path.join(__dirname, './fixtures/MainActivity.kt'); + const mainActivity = await fs.promises.readFile(mainActivityPath, 'utf-8'); + + const result = ktFileUpdater(mainActivity); + expect(result).toMatchSnapshot(); + }); + + it('updates the MainActivity.java with both the import the method implementation', async function () { + // TODO: Implement java test + }); +}); diff --git a/plugin/src/withRNOrientationMainActivity.ts b/plugin/src/withRNOrientationMainActivity.ts index 7b26c7c..21e9179 100644 --- a/plugin/src/withRNOrientationMainActivity.ts +++ b/plugin/src/withRNOrientationMainActivity.ts @@ -39,7 +39,7 @@ function getCompatibleFileUpdater( export function ktFileUpdater(originalContents: string): string { const libraryImportCodeBlock = 'import com.orientationdirector.implementation.ConfigurationChangedBroadcastReceiver\n'; - const rightBeforeClassDeclaration = /class MainActivity\s+\w+/g; + const rightBeforeClassDeclaration = /class MainActivity/g; const importMergeResults = mergeContents({ tag: '@react-native-orientation-director/library-import', @@ -50,20 +50,21 @@ export function ktFileUpdater(originalContents: string): string { comment: '// React Native Orientation Director', }); - const onConfigurationChangedCodeBlock = `override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) + const onConfigurationChangedCodeBlock = ` + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) - val orientationDirectorCustomAction = - "\${packageName}.\${ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION}" + val orientationDirectorCustomAction = + packageName + "." + ConfigurationChangedBroadcastReceiver.CUSTOM_INTENT_ACTION - val intent = - Intent(orientationDirectorCustomAction).apply { - putExtra("newConfig", newConfig) - setPackage(packageName) - } + val intent = + Intent(orientationDirectorCustomAction).apply { + putExtra("newConfig", newConfig) + setPackage(packageName) + } - this.sendBroadcast(intent) -}\n`; + this.sendBroadcast(intent) + }\n`; const rightBeforeLastClosingBrace = /super\.onCreate/g; const pasteInTheListJustAfterTheClosingBracket = 2; From 19936a3d936224082afb224a238b6bae4ab9a196 Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 23 Mar 2025 14:01:20 +0100 Subject: [PATCH 12/13] chore: remove expo plugin java support --- plugin/__tests__/withRNOrientationMainActivity.spec.ts | 4 ---- plugin/src/withRNOrientationMainActivity.ts | 7 ------- 2 files changed, 11 deletions(-) diff --git a/plugin/__tests__/withRNOrientationMainActivity.spec.ts b/plugin/__tests__/withRNOrientationMainActivity.spec.ts index f8a29ce..1e46f47 100644 --- a/plugin/__tests__/withRNOrientationMainActivity.spec.ts +++ b/plugin/__tests__/withRNOrientationMainActivity.spec.ts @@ -15,8 +15,4 @@ describe('withRNOrientationMainActivity', function () { const result = ktFileUpdater(mainActivity); expect(result).toMatchSnapshot(); }); - - it('updates the MainActivity.java with both the import the method implementation', async function () { - // TODO: Implement java test - }); }); diff --git a/plugin/src/withRNOrientationMainActivity.ts b/plugin/src/withRNOrientationMainActivity.ts index 21e9179..9c03cd6 100644 --- a/plugin/src/withRNOrientationMainActivity.ts +++ b/plugin/src/withRNOrientationMainActivity.ts @@ -25,8 +25,6 @@ function getCompatibleFileUpdater( language: ApplicationProjectFile['language'] ): (originalContents: string) => string { switch (language) { - case 'java': - return javaFileUpdater; case 'kt': return ktFileUpdater; default: @@ -80,8 +78,3 @@ export function ktFileUpdater(originalContents: string): string { return implementationMergeResults.contents; } - -export function javaFileUpdater(originalContents: string): string { - // TODO: Implement java file update - return originalContents; -} From 691c02be02960f3052f419d4cd7e1b45a0fab5cd Mon Sep 17 00:00:00 2001 From: gladius Date: Sun, 23 Mar 2025 14:21:46 +0100 Subject: [PATCH 13/13] chore: update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 76499ce..a722fe4 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ Then, you need to add the plugin to your app.json file: This way, Expo will handle the native setup for you during `prebuild`. +> Note: only SDK 50 and above are supported, the plugin is configured to handle only the kotlin template. + ## Setup ### Android