├── .nvmrc ├── .watchmanconfig ├── example ├── .watchmanconfig ├── windows │ ├── RNOrientationManagerExample │ │ ├── .gitignore │ │ ├── pch.cpp │ │ ├── App.idl │ │ ├── Assets │ │ │ ├── StoreLogo.png │ │ │ ├── SplashScreen.scale-200.png │ │ │ ├── LockScreenLogo.scale-200.png │ │ │ ├── Square44x44Logo.scale-200.png │ │ │ ├── Wide310x150Logo.scale-200.png │ │ │ ├── Square150x150Logo.scale-200.png │ │ │ └── Square44x44Logo.targetsize-24_altform-unplated.png │ │ ├── MainPage.idl │ │ ├── AutolinkedNativeModules.g.props │ │ ├── AutolinkedNativeModules.g.h │ │ ├── MainPage.h │ │ ├── ReactPackageProvider.cpp │ │ ├── App.xaml │ │ ├── MainPage.cpp │ │ ├── ReactPackageProvider.h │ │ ├── PropertySheet.props │ │ ├── MainPage.xaml │ │ ├── pch.h │ │ ├── App.h │ │ ├── AutolinkedNativeModules.g.targets │ │ ├── AutolinkedNativeModules.g.cpp │ │ ├── Package.appxmanifest │ │ ├── RNOrientationManagerExample.vcxproj.filters │ │ ├── App.cpp │ │ └── RNOrientationManagerExample.vcxproj │ ├── NuGet.Config │ ├── .gitignore │ └── ExperimentalFeatures.props ├── jest.config.js ├── .bundle │ └── config ├── ios │ ├── File.swift │ ├── OrientationManagerExample │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.mm │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ ├── OrientationManagerExample-Bridging-Header.h │ ├── OrientationManagerExample.xcworkspace │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── contents.xcworkspacedata │ ├── .xcode.env │ ├── OrientationManagerExampleTests │ │ ├── Info.plist │ │ └── OrientationManagerExampleTests.m │ ├── Podfile │ └── OrientationManagerExample.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── OrientationManagerExample.xcscheme ├── app.json ├── android │ ├── app │ │ ├── debug.keystore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ ├── colors.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── orientationmanagerexample │ │ │ │ │ ├── MainActivity.java │ │ │ │ │ └── MainApplication.java │ │ │ ├── debug │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── orientationmanagerexample │ │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── release │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── orientationmanagerexample │ │ │ │ └── ReactNativeFlipper.java │ │ ├── proguard-rules.pro │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ ├── gradlew.bat │ └── gradlew ├── Gemfile ├── index.js ├── src │ ├── types │ │ ├── OrientationLock.ts │ │ ├── RootStackScreenProps.ts │ │ └── RootStackParamList.ts │ ├── components │ │ ├── OrientationLockedListItem.tsx │ │ ├── screens │ │ │ ├── OrientationObserverScreen.tsx │ │ │ ├── LandscapeOnlyWithLeavingOnOrientationChangeScreen.tsx │ │ │ ├── PortraitOnlyThatNavigatesToLandscapeOnlyScreen.tsx │ │ │ ├── OrientationLockedScreen.tsx │ │ │ └── HomeScreen.tsx │ │ ├── ListItem.tsx │ │ └── navigators │ │ │ └── RootStackNavigator.tsx │ ├── App.tsx │ └── Color.ts ├── babel.config.js ├── react-native.config.js ├── package.json ├── README.md ├── metro.config.js └── Gemfile.lock ├── windows ├── RNOrientationManager │ ├── pch.cpp │ ├── RNOrientationManager.def │ ├── ReactPackageProvider.idl │ ├── ReactPackageProvider.cpp │ ├── ReactPackageProvider.h │ ├── pch.h │ ├── PropertySheet.props │ ├── RNOrientationManager.vcxproj.filters │ ├── RNOrientationManager.h │ ├── RNOrientationManager.vcxproj │ └── RNOrientationManager.cpp ├── NuGet.Config ├── .gitignore └── ExperimentalFeatures.props ├── .gitattributes ├── tsconfig.build.json ├── babel.config.js ├── android ├── src │ └── main │ │ ├── AndroidManifestNew.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── kroosx4v │ │ └── orientationmanager │ │ ├── InterfaceOrientation.java │ │ ├── DeviceOrientation.java │ │ ├── OrientationManagerPackage.java │ │ ├── OrientationManagerActivityLifecycleCallbacks.java │ │ └── DeviceOrientationListener.java ├── gradle.properties └── build.gradle ├── src ├── src │ ├── OrientationManagerModule.ts │ ├── getUniqueId.ts │ ├── types │ │ ├── DeviceOrientationEffect.ts │ │ ├── DeviceOrientationChangeListenerCallback.ts │ │ ├── InterfaceOrientationEffect.ts │ │ ├── InterfaceOrientationValue.ts │ │ ├── InterfaceOrientationChangeListenerCallback.ts │ │ ├── DeviceOrientationChangedNativeEvent.ts │ │ ├── DeviceOrientationValue.ts │ │ ├── InterfaceOrientationChangedNativeEvent.ts │ │ └── Orientations.ts │ ├── hooks │ │ ├── useDeviceOrientation.ts │ │ ├── useInterfaceOrientation.ts │ │ ├── useDeviceOrientationEffect.ts │ │ ├── useInterfaceOrientationEffect.ts │ │ └── useInterfaceOrientationWhenFocusedEffect.ts │ ├── lockFunctions.ts │ ├── InterfaceOrientation.ts │ ├── DeviceOrientation.ts │ ├── components │ │ └── OrientationLocker.ts │ └── manager.ts └── index.ts ├── ios └── RNOrientationManager.h ├── .yarnrc.yml ├── tsconfig.json ├── .yarn └── patches │ ├── @react-navigation-stack-npm-6.3.17-4ef7cbe143.patch │ └── react-native-screens-npm-3.24.0-0887b5e74d.patch ├── LICENSE ├── .gitignore ├── RNOrientationManager.podspec ├── react-native-orientation-manager.code-workspace └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | v16 -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /windows/RNOrientationManager/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/.gitignore: -------------------------------------------------------------------------------- 1 | /Bundle 2 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /example/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "react-native", 3 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "exclude": ["example"] 4 | } -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["module:metro-react-native-babel-preset"], 3 | }; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/App.idl: -------------------------------------------------------------------------------- 1 | namespace RNOrientationManagerExample 2 | { 3 | } 4 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // OrientationManagerExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OrientationManagerExample", 3 | "displayName": "Orientation Manager Example" 4 | } -------------------------------------------------------------------------------- /android/src/main/AndroidManifestNew.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/debug.keystore -------------------------------------------------------------------------------- /src/src/OrientationManagerModule.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules } from "react-native"; 2 | 3 | export default NativeModules.OrientationManagerModule; -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | OrientationManagerExample 3 | 4 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #FEF2F2 3 | #F03861 4 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /windows/RNOrientationManager/RNOrientationManager.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE 3 | DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/src/getUniqueId.ts: -------------------------------------------------------------------------------- 1 | let uniqueIdCounter: number = Number.MIN_SAFE_INTEGER; 2 | 3 | function getUniqueId(): number 4 | { 5 | return ++uniqueIdCounter; 6 | } 7 | 8 | export default getUniqueId; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/StoreLogo.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | gem 'cocoapods', '~> 1.12' 7 | gem 'activesupport', '~> 7.0', '<= 7.0.8' -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | RNOrientationManager_kotlinVersion=1.7.0 2 | RNOrientationManager_minSdkVersion=21 3 | RNOrientationManager_targetSdkVersion=33 4 | RNOrientationManager_compileSdkVersion=33 5 | RNOrientationManager_ndkversion=23.1.7779620 -------------------------------------------------------------------------------- /src/src/types/DeviceOrientationEffect.ts: -------------------------------------------------------------------------------- 1 | import type DeviceOrientation from "../DeviceOrientation"; 2 | 3 | type DeviceOrientationEffect = (deviceOrientation: DeviceOrientation) => ReturnType; 4 | 5 | export default DeviceOrientationEffect; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KroosX4V/react-native-orientation-manager/HEAD/example/windows/RNOrientationManagerExample/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /src/src/types/DeviceOrientationChangeListenerCallback.ts: -------------------------------------------------------------------------------- 1 | import type DeviceOrientation from "../DeviceOrientation"; 2 | 3 | type DeviceOrientationChangeListenerCallback = (deviceOrientation: DeviceOrientation) => void; 4 | 5 | export default DeviceOrientationChangeListenerCallback; -------------------------------------------------------------------------------- /src/src/types/InterfaceOrientationEffect.ts: -------------------------------------------------------------------------------- 1 | import type InterfaceOrientation from "../InterfaceOrientation"; 2 | 3 | type InterfaceOrientationEffect = (interfaceOrientation: InterfaceOrientation) => ReturnType; 4 | 5 | export default InterfaceOrientationEffect; -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import "react-native-gesture-handler"; 2 | import "react-native-orientation-manager"; 3 | import { AppRegistry } from "react-native"; 4 | import App from "./src/App"; 5 | import { name as appName } from "./app.json"; 6 | 7 | AppRegistry.registerComponent(appName, () => App); -------------------------------------------------------------------------------- /src/src/types/InterfaceOrientationValue.ts: -------------------------------------------------------------------------------- 1 | const enum InterfaceOrientationValue 2 | { 3 | Unknown = 0, 4 | Portrait = 1, 5 | PortraitUpsideDown = 2, 6 | LandscapeLeft = 3, 7 | LandscapeRight = 4, 8 | } 9 | 10 | export default InterfaceOrientationValue; -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/ReactPackageProvider.idl: -------------------------------------------------------------------------------- 1 | namespace RNOrientationManager 2 | { 3 | [webhosthidden] 4 | [default_interface] 5 | runtimeclass ReactPackageProvider : Microsoft.ReactNative.IReactPackageProvider 6 | { 7 | ReactPackageProvider(); 8 | }; 9 | } -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/MainPage.idl: -------------------------------------------------------------------------------- 1 | #include "NamespaceRedirect.h" 2 | 3 | namespace RNOrientationManagerExample 4 | { 5 | [default_interface] 6 | runtimeclass MainPage : XAML_NAMESPACE.Controls.Page 7 | { 8 | MainPage(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/src/types/InterfaceOrientationChangeListenerCallback.ts: -------------------------------------------------------------------------------- 1 | import type InterfaceOrientation from "../InterfaceOrientation"; 2 | 3 | type InterfaceOrientationChangeListenerCallback = (interfaceOrientation: InterfaceOrientation) => void; 4 | 5 | export default InterfaceOrientationChangeListenerCallback; -------------------------------------------------------------------------------- /src/src/types/DeviceOrientationChangedNativeEvent.ts: -------------------------------------------------------------------------------- 1 | import type DeviceOrientationValue from "./DeviceOrientationValue"; 2 | 3 | interface DeviceOrientationChangedNativeEvent 4 | { 5 | deviceOrientationValue: DeviceOrientationValue; 6 | } 7 | 8 | export default DeviceOrientationChangedNativeEvent; -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'OrientationManagerExample' 2 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | includeBuild('../../node_modules/@react-native/gradle-plugin') 5 | -------------------------------------------------------------------------------- /ios/RNOrientationManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | @interface RNOrientationManager : RCTEventEmitter 7 | + (UIInterfaceOrientationMask)getSupportedInterfaceOrientations; 8 | @end 9 | -------------------------------------------------------------------------------- /src/src/types/DeviceOrientationValue.ts: -------------------------------------------------------------------------------- 1 | const enum DeviceOrientationValue 2 | { 3 | Unknown = 0, 4 | Portrait = 1, 5 | PortraitUpsideDown = 2, 6 | LandscapeLeft = 3, 7 | LandscapeRight = 4, 8 | FaceUp = 5, 9 | FaceDown = 6, 10 | } 11 | 12 | export default DeviceOrientationValue; -------------------------------------------------------------------------------- /src/src/types/InterfaceOrientationChangedNativeEvent.ts: -------------------------------------------------------------------------------- 1 | import type InterfaceOrientationValue from "./InterfaceOrientationValue"; 2 | 3 | interface InterfaceOrientationChangedNativeEvent 4 | { 5 | interfaceOrientationValue: InterfaceOrientationValue; 6 | } 7 | 8 | export default InterfaceOrientationChangedNativeEvent; -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/src/types/OrientationLock.ts: -------------------------------------------------------------------------------- 1 | const enum OrientationLock 2 | { 3 | PORTRAIT = 0, 4 | PORTRAIT_UPSIDE_DOWN = 1, 5 | LANDSCAPE_LEFT = 2, 6 | LANDSCAPE_RIGHT = 3, 7 | LANDSCAPE = 4, 8 | ALL_ORIENTATIONS_BUT_UPSIDE_DOWN = 5, 9 | UNLOCK_ALL_ORIENTATIONS = 6, 10 | } 11 | 12 | export default OrientationLock; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/AutolinkedNativeModules.g.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/src/types/Orientations.ts: -------------------------------------------------------------------------------- 1 | import type DeviceOrientation from "../DeviceOrientation"; 2 | import type InterfaceOrientation from "../InterfaceOrientation"; 3 | 4 | interface Orientations 5 | { 6 | readonly interfaceOrientation: InterfaceOrientation; 7 | readonly deviceOrientation: DeviceOrientation; 8 | } 9 | 10 | export default Orientations; -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | nmHoistingLimits: workspaces 3 | 4 | plugins: 5 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 6 | spec: "@yarnpkg/plugin-interactive-tools" 7 | - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs 8 | spec: "@yarnpkg/plugin-workspace-tools" 9 | 10 | yarnPath: .yarn/releases/yarn-3.6.1.cjs 11 | -------------------------------------------------------------------------------- /example/windows/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/src/types/RootStackScreenProps.ts: -------------------------------------------------------------------------------- 1 | import type RootStackParamList from "./RootStackParamList"; 2 | import type { NativeStackScreenProps } from "@react-navigation/native-stack"; 3 | import type { StackScreenProps } from "@react-navigation/stack"; 4 | 5 | type RootStackScreenProps = NativeStackScreenProps | StackScreenProps; 6 | 7 | export default RootStackScreenProps; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/AutolinkedNativeModules.g.h: -------------------------------------------------------------------------------- 1 | // AutolinkedNativeModules.g.h contents generated by "react-native autolink-windows" 2 | // clang-format off 3 | #pragma once 4 | 5 | namespace winrt::Microsoft::ReactNative 6 | { 7 | 8 | void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /android/src/main/java/com/kroosx4v/orientationmanager/InterfaceOrientation.java: -------------------------------------------------------------------------------- 1 | package com.kroosx4v.orientationmanager; 2 | 3 | enum InterfaceOrientation 4 | { 5 | UNKNOWN("0"), 6 | PORTRAIT("1"), 7 | PORTRAIT_UPSIDE_DOWN("2"), 8 | LANDSCAPE_LEFT("3"), 9 | LANDSCAPE_RIGHT("4"); 10 | 11 | public final String value; 12 | 13 | InterfaceOrientation(String value) 14 | { 15 | this.value = value; 16 | } 17 | } -------------------------------------------------------------------------------- /android/src/main/java/com/kroosx4v/orientationmanager/DeviceOrientation.java: -------------------------------------------------------------------------------- 1 | package com.kroosx4v.orientationmanager; 2 | 3 | enum DeviceOrientation 4 | { 5 | UNKNOWN("0"), 6 | PORTRAIT("1"), 7 | PORTRAIT_UPSIDE_DOWN("2"), 8 | LANDSCAPE_LEFT("3"), 9 | LANDSCAPE_RIGHT("4"), 10 | FACE_UP("5"), 11 | FACE_DOWN("6"); 12 | 13 | public final String value; 14 | 15 | DeviceOrientation(String value) 16 | { 17 | this.value = value; 18 | } 19 | } -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/src/types/RootStackParamList.ts: -------------------------------------------------------------------------------- 1 | import type OrientationLock from "./OrientationLock"; 2 | 3 | type RootStackParamList = { 4 | Home: undefined; 5 | OrientationObserver: undefined; 6 | OrientationLocked: { 7 | lock: OrientationLock; 8 | title: string; 9 | text: string; 10 | }; 11 | LandscapeOnlyWithLeavingOnOrientationChange: undefined; 12 | PortraitOnlyThatNavigatesToLandscapeOnly: undefined; 13 | }; 14 | 15 | export default RootStackParamList; -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const pak = require("../package.json"); 3 | 4 | module.exports = { 5 | presets: ["module:metro-react-native-babel-preset"], 6 | plugins: [ 7 | [ 8 | "module-resolver", 9 | { 10 | extensions: [".tsx", ".ts", ".js", ".json"], 11 | alias: { 12 | [pak.name]: path.join(__dirname, "..", pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | }; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/MainPage.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MainPage.g.h" 3 | #include 4 | 5 | namespace winrt::RNOrientationManagerExample::implementation 6 | { 7 | struct MainPage : MainPageT 8 | { 9 | MainPage(); 10 | }; 11 | } 12 | 13 | namespace winrt::RNOrientationManagerExample::factory_implementation 14 | { 15 | struct MainPage : MainPageT 16 | { 17 | }; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const pak = require("../package.json"); 3 | 4 | module.exports = { 5 | dependencies: { 6 | [pak.name]: { 7 | root: path.join(__dirname, ".."), 8 | }, 9 | "react-native": { 10 | root: path.join(__dirname, "..", "node_modules", "react-native"), 11 | }, 12 | "react-native-windows": { 13 | root: path.join(__dirname, "..", "node_modules", "react-native-windows"), 14 | }, 15 | }, 16 | }; -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ReactPackageProvider.h" 3 | #include "NativeModules.h" 4 | 5 | using namespace winrt::Microsoft::ReactNative; 6 | 7 | namespace winrt::RNOrientationManagerExample::implementation 8 | { 9 | 10 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept 11 | { 12 | AddAttributedModules(packageBuilder, true); 13 | } 14 | 15 | } // namespace winrt::RNOrientationManagerExample::implementation 16 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/MainPage.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "MainPage.h" 3 | #if __has_include("MainPage.g.cpp") 4 | #include "MainPage.g.cpp" 5 | #endif 6 | 7 | #include "App.h" 8 | 9 | using namespace winrt; 10 | using namespace xaml; 11 | 12 | namespace winrt::RNOrientationManagerExample::implementation 13 | { 14 | MainPage::MainPage() 15 | { 16 | InitializeComponent(); 17 | auto app = Application::Current().as(); 18 | ReactRootView().ReactNativeHost(app->Host()); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/ReactPackageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "ReactPackageProvider.h" 3 | #if __has_include("ReactPackageProvider.g.cpp") 4 | #include "ReactPackageProvider.g.cpp" 5 | #endif 6 | 7 | #include "RNOrientationManager.h" 8 | 9 | using namespace winrt::Microsoft::ReactNative; 10 | 11 | namespace winrt::RNOrientationManager::implementation 12 | { 13 | void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept 14 | { 15 | AddAttributedModules(packageBuilder); 16 | } 17 | } -------------------------------------------------------------------------------- /windows/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "winrt/Microsoft.ReactNative.h" 4 | 5 | namespace winrt::RNOrientationManagerExample::implementation 6 | { 7 | struct ReactPackageProvider : winrt::implements 8 | { 9 | public: // IReactPackageProvider 10 | void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; 11 | }; 12 | } // namespace winrt::RNOrientationManagerExample::implementation 13 | 14 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/ReactPackageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ReactPackageProvider.g.h" 3 | 4 | using namespace winrt::Microsoft::ReactNative; 5 | 6 | namespace winrt::RNOrientationManager::implementation 7 | { 8 | struct ReactPackageProvider : ReactPackageProviderT 9 | { 10 | ReactPackageProvider() = default; 11 | 12 | void CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept; 13 | }; 14 | } 15 | 16 | namespace winrt::RNOrientationManager::factory_implementation 17 | { 18 | struct ReactPackageProvider : ReactPackageProviderT {}; 19 | } -------------------------------------------------------------------------------- /windows/RNOrientationManager/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #if __has_include() 11 | #include 12 | #endif 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace winrt::Windows::Foundation; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/PropertySheet.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/src/hooks/useDeviceOrientation.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type DeviceOrientation from "../DeviceOrientation"; 3 | import Orientations, { addDeviceOrientationChangeListener } from "../manager"; 4 | 5 | function useDeviceOrientation(): DeviceOrientation 6 | { 7 | const [deviceOrientation, setDeviceOrientation] = React.useState(Orientations.deviceOrientation); 8 | 9 | React.useEffect( 10 | () => { 11 | return addDeviceOrientationChangeListener((deviceOrientation: DeviceOrientation) => { 12 | setDeviceOrientation(deviceOrientation); 13 | }); 14 | }, 15 | [], 16 | ); 17 | 18 | return deviceOrientation; 19 | } 20 | 21 | export default useDeviceOrientation; -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "33.0.0" 6 | minSdkVersion = 21 7 | compileSdkVersion = 33 8 | targetSdkVersion = 33 9 | 10 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. 11 | ndkVersion = "23.1.7779620" 12 | REACT_NATIVE_NODE_MODULES_DIR = file("../../node_modules/react-native") 13 | } 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | dependencies { 19 | classpath("com.android.tools.build:gradle") 20 | classpath("com.facebook.react:react-native-gradle-plugin") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /example/src/components/OrientationLockedListItem.tsx: -------------------------------------------------------------------------------- 1 | import type RootStackParamList from "../types/RootStackParamList"; 2 | import ListItem from "./ListItem"; 3 | 4 | interface OrientationLockedListItemProps 5 | { 6 | name: Parameters[0]["name"]; 7 | lock: RootStackParamList["OrientationLocked"]["lock"]; 8 | text: RootStackParamList["OrientationLocked"]["text"]; 9 | guard?: Parameters[0]["guard"]; 10 | } 11 | 12 | function OrientationLockedListItem({ name, lock, text, guard }: OrientationLockedListItemProps): React.JSX.Element 13 | { 14 | return ( 15 | 16 | ); 17 | } 18 | 19 | export default OrientationLockedListItem; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "paths": { 5 | "react-native-orientation-manager": ["./src/index"] 6 | }, 7 | "allowUnreachableCode": false, 8 | "allowUnusedLabels": false, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "jsx": "react-native", 12 | "lib": ["esnext"], 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "noImplicitReturns": true, 16 | "noImplicitUseStrict": false, 17 | "noStrictGenericChecks": false, 18 | "noUncheckedIndexedAccess": true, 19 | "resolveJsonModule": true, 20 | "skipLibCheck": true, 21 | "strict": true, 22 | "target": "esnext" 23 | } 24 | } -------------------------------------------------------------------------------- /src/src/hooks/useInterfaceOrientation.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type InterfaceOrientation from "../InterfaceOrientation"; 3 | import Orientations, { addInterfaceOrientationChangeListener } from "../manager"; 4 | 5 | function useInterfaceOrientation(): InterfaceOrientation 6 | { 7 | const [interfaceOrientation, setInterfaceOrientation] = React.useState(Orientations.interfaceOrientation); 8 | 9 | React.useEffect( 10 | () => { 11 | return addInterfaceOrientationChangeListener((interfaceOrientation: InterfaceOrientation) => { 12 | setInterfaceOrientation(interfaceOrientation); 13 | }); 14 | }, 15 | [], 16 | ); 17 | 18 | return interfaceOrientation; 19 | } 20 | 21 | export default useInterfaceOrientation; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /example/android/app/src/release/java/com/orientationmanagerexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.orientationmanagerexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.react.ReactInstanceManager; 11 | 12 | /** 13 | * Class responsible of loading Flipper inside your React Native application. This is the release 14 | * flavor of it so it's empty as we don't want to load Flipper. 15 | */ 16 | public class ReactNativeFlipper { 17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 18 | // Do nothing as we don't want to initialize Flipper on Release. 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NOMINMAX 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | using namespace winrt::Windows::Foundation; 25 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/RNOrientationManager.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/App.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "App.xaml.g.h" 4 | 5 | #include 6 | 7 | namespace activation = winrt::Windows::ApplicationModel::Activation; 8 | 9 | namespace winrt::RNOrientationManagerExample::implementation 10 | { 11 | struct App : AppT 12 | { 13 | App() noexcept; 14 | void OnLaunched(activation::LaunchActivatedEventArgs const&); 15 | void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs const &e); 16 | void OnSuspending(IInspectable const&, Windows::ApplicationModel::SuspendingEventArgs const&); 17 | void OnNavigationFailed(IInspectable const&, xaml::Navigation::NavigationFailedEventArgs const&); 18 | private: 19 | using super = AppT; 20 | }; 21 | } // namespace winrt::RNOrientationManagerExample::implementation 22 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/AutolinkedNativeModules.g.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {9a1ff53c-8b9a-4c51-9937-aefb0b8f3e62} 8 | 9 | 10 | 11 | {d638f49e-29d2-4699-ac52-facd82fa7138} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from "./src/manager"; 2 | export * from "./src/manager"; 3 | 4 | export * from "./src/lockFunctions"; 5 | 6 | export { default as InterfaceOrientation } from "./src/InterfaceOrientation"; 7 | export { default as DeviceOrientation } from "./src/DeviceOrientation"; 8 | 9 | export { default as useInterfaceOrientation } from "./src/hooks/useInterfaceOrientation"; 10 | export { default as useInterfaceOrientationEffect } from "./src/hooks/useInterfaceOrientationEffect"; 11 | export { default as useInterfaceOrientationWhenFocusedEffect } from "./src/hooks/useInterfaceOrientationWhenFocusedEffect"; 12 | export { default as useDeviceOrientation } from "./src/hooks/useDeviceOrientation"; 13 | export { default as useDeviceOrientationEffect } from "./src/hooks/useDeviceOrientationEffect"; 14 | 15 | export { default as OrientationLocker } from "./src/components/OrientationLocker"; -------------------------------------------------------------------------------- /.yarn/patches/@react-navigation-stack-npm-6.3.17-4ef7cbe143.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/views/Stack/Card.tsx b/src/views/Stack/Card.tsx 2 | index 64a551ef46ca7d7ff8941997dd116fecbda70295..fb7fc1f2ace898803198aa6dcdb6a64c920af9cc 100644 3 | --- a/src/views/Stack/Card.tsx 4 | +++ b/src/views/Stack/Card.tsx 5 | @@ -517,7 +517,7 @@ export default class Card extends React.Component { 6 | // Make sure that this view isn't removed. If this view is removed, our style with animated value won't apply 7 | collapsable={false} 8 | /> 9 | - 10 | + 11 | {overlayEnabled ? ( 12 | 13 | {overlay({ style: overlayStyle })} 14 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/AutolinkedNativeModules.g.cpp: -------------------------------------------------------------------------------- 1 | // AutolinkedNativeModules.g.cpp contents generated by "react-native autolink-windows" 2 | // clang-format off 3 | #include "pch.h" 4 | #include "AutolinkedNativeModules.g.h" 5 | 6 | // Includes from react-native-orientation-manager 7 | #include 8 | 9 | // Includes from react-native-screens 10 | #include 11 | 12 | namespace winrt::Microsoft::ReactNative 13 | { 14 | 15 | void RegisterAutolinkedNativeModulePackages(winrt::Windows::Foundation::Collections::IVector const& packageProviders) 16 | { 17 | // IReactPackageProviders from react-native-orientation-manager 18 | packageProviders.Append(winrt::RNOrientationManager::ReactPackageProvider()); 19 | // IReactPackageProviders from react-native-screens 20 | packageProviders.Append(winrt::RNScreens::ReactPackageProvider()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /android/src/main/java/com/kroosx4v/orientationmanager/OrientationManagerPackage.java: -------------------------------------------------------------------------------- 1 | package com.kroosx4v.orientationmanager; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.facebook.react.ReactPackage; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class OrientationManagerPackage implements ReactPackage 13 | { 14 | @NonNull 15 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) 16 | { 17 | List modules = new ArrayList<>(); 18 | modules.add(new OrientationManagerModule(reactContext)); 19 | 20 | return modules; 21 | } 22 | 23 | @NonNull 24 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) 25 | { 26 | return Collections.emptyList(); 27 | } 28 | } -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-orientation-manager-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "react-native start", 7 | "android": "react-native run-android", 8 | "ios": "react-native run-ios", 9 | "windows": "react-native run-windows" 10 | }, 11 | "dependencies": { 12 | "@react-navigation/native": "6.1.7", 13 | "@react-navigation/native-stack": "^6.9.13", 14 | "@react-navigation/stack": "6.3.17", 15 | "react-native-gesture-handler": "2.12.0", 16 | "react-native-safe-area-context": "4.7.0", 17 | "react-native-screens": "3.24.0" 18 | }, 19 | "devDependencies": { 20 | "@babel/core": "^7.20.0", 21 | "@babel/preset-env": "^7.20.0", 22 | "@babel/runtime": "^7.20.0", 23 | "@react-native/metro-config": "^0.72.11", 24 | "babel-plugin-module-resolver": "^5.0.0", 25 | "metro-react-native-babel-preset": "0.76.8" 26 | }, 27 | "engines": { 28 | "node": ">=16" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 KroosX4V 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Instructions 2 | - Ensure that you download the entire repository, not just the `example` folder, as the app relies on the module files located at the directory level above. 3 | 4 | # Steps to Run the App: 5 | 6 | ## Step 1: Installing Dependencies 7 | 8 | Install dependencies by running the following commands from the _root_ of the repository: 9 | 10 | ```bash 11 | yarn 12 | ``` 13 | ```bash 14 | cd example/ios 15 | ``` 16 | ```bash 17 | bundle install 18 | ``` 19 | ```bash 20 | bundle exec pod install 21 | ``` 22 | 23 | ## Step 2: Start the Metro Server 24 | 25 | To start Metro, run the following command from the _root_ of the repository: 26 | 27 | ```bash 28 | yarn example start 29 | ``` 30 | 31 | ## Step 3: Start the App 32 | 33 | Let the Metro server run in its _own_ terminal. Open a _new_ terminal from the _root_ of the repository. Run the following command to start the _Android_, _iOS_, or _Windows_ app: 34 | 35 | ### For Android: 36 | 37 | ```bash 38 | yarn example android 39 | ``` 40 | 41 | ### For iOS: 42 | 43 | ```bash 44 | yarn example ios 45 | ``` 46 | 47 | ### For Windows: 48 | 49 | ```bash 50 | yarn example windows 51 | ``` -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/src/hooks/useDeviceOrientationEffect.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Orientations, { addDeviceOrientationChangeListener } from "../manager"; 3 | import type DeviceOrientationEffect from "../types/DeviceOrientationEffect"; 4 | import type DeviceOrientation from "../DeviceOrientation"; 5 | 6 | function useDeviceOrientationEffect(effect: DeviceOrientationEffect, deps: React.DependencyList = []): void 7 | { 8 | React.useEffect( 9 | () => { 10 | let cleanupCallback = effect(Orientations.deviceOrientation); 11 | 12 | const deviceOrientationChangeListenerRemover = addDeviceOrientationChangeListener((deviceOrientation: DeviceOrientation) => { 13 | if (typeof cleanupCallback === "function") cleanupCallback(); 14 | cleanupCallback = effect(deviceOrientation); 15 | }); 16 | 17 | return () => { 18 | deviceOrientationChangeListenerRemover(); 19 | if (typeof cleanupCallback === "function") cleanupCallback(); 20 | }; 21 | }, 22 | deps, 23 | ); 24 | } 25 | 26 | export default useDeviceOrientationEffect; -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import { NavigationContainer } from "@react-navigation/native"; 4 | import { SafeAreaProvider } from "react-native-safe-area-context"; 5 | import { GestureHandlerRootView } from "react-native-gesture-handler"; 6 | import { RootStackNavigator } from "./components/navigators/RootStackNavigator"; 7 | import Color from "./Color"; 8 | 9 | function App(): React.JSX.Element 10 | { 11 | return ( 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | 24 | const style = StyleSheet.create({ 25 | backgroundMaskView: { 26 | flex: 1, 27 | backgroundColor: Color.Primary, 28 | }, 29 | gestureHandlerRootView: { 30 | flex: 1, 31 | }, 32 | }); 33 | 34 | export default App; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | 32 | # Android/IJ 33 | # 34 | .classpath 35 | .cxx 36 | .gradle 37 | .idea 38 | .project 39 | .settings 40 | local.properties 41 | android.iml 42 | 43 | # Cocoapods 44 | # 45 | /example/ios/Pods 46 | 47 | # Ruby 48 | /example/vendor/ 49 | 50 | /example/ios/.xcode.env.local 51 | 52 | # node.js 53 | # 54 | node_modules/ 55 | npm-debug.log 56 | yarn-debug.log 57 | yarn-error.log 58 | 59 | # BUCK 60 | buck-out/ 61 | \.buckd/ 62 | android/app/libs 63 | android/keystores/debug.keystore 64 | 65 | # Yarn 66 | .pnp.* 67 | .yarn/* 68 | !.yarn/patches 69 | !.yarn/plugins 70 | !.yarn/releases 71 | !.yarn/sdks 72 | !.yarn/versions 73 | yarn-error.log 74 | 75 | # Expo 76 | .expo/ 77 | 78 | # generated by bob 79 | lib/ 80 | 81 | # Windows 82 | msbuild.binlog -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import 3 | #import "RNOrientationManager.h" 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"OrientationManagerExample"; 10 | 11 | // You can add your custom initial props in the dictionary below. 12 | // They will be passed down to the ViewController used by React Native. 13 | self.initialProps = @{}; 14 | 15 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 16 | } 17 | 18 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 19 | { 20 | #if DEBUG 21 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 22 | #else 23 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 24 | #endif 25 | } 26 | 27 | - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 28 | { 29 | return [RNOrientationManager getSupportedInterfaceOrientations]; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /android/src/main/java/com/kroosx4v/orientationmanager/OrientationManagerActivityLifecycleCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.kroosx4v.orientationmanager; 2 | 3 | import android.app.Activity; 4 | import android.app.Application.ActivityLifecycleCallbacks; 5 | import android.os.Bundle; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | public class OrientationManagerActivityLifecycleCallbacks implements ActivityLifecycleCallbacks 10 | { 11 | public void onActivityResumed(@NonNull Activity activity) 12 | { 13 | OrientationManagerModule.enableOrientationListener(); 14 | } 15 | 16 | public void onActivityPaused(@NonNull Activity activity) 17 | { 18 | OrientationManagerModule.disableOrientationListener(); 19 | } 20 | 21 | public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} 22 | public void onActivityStarted(@NonNull Activity activity) {} 23 | public void onActivityStopped(@NonNull Activity activity) {} 24 | public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {} 25 | public void onActivityDestroyed(@NonNull Activity activity) {} 26 | } -------------------------------------------------------------------------------- /src/src/hooks/useInterfaceOrientationEffect.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type InterfaceOrientationEffect from "../types/InterfaceOrientationEffect"; 3 | import Orientations, { addInterfaceOrientationChangeListener } from "../manager"; 4 | import type InterfaceOrientation from "../InterfaceOrientation"; 5 | 6 | function useInterfaceOrientationEffect(effect: InterfaceOrientationEffect, deps: React.DependencyList = []): void 7 | { 8 | React.useEffect( 9 | () => { 10 | let cleanupCallback = effect(Orientations.interfaceOrientation); 11 | 12 | const interfaceOrientationChangeListenerRemover = addInterfaceOrientationChangeListener((interfaceOrientation: InterfaceOrientation) => { 13 | if (typeof cleanupCallback === "function") cleanupCallback(); 14 | cleanupCallback = effect(interfaceOrientation); 15 | }); 16 | 17 | return () => { 18 | interfaceOrientationChangeListenerRemover(); 19 | if (typeof cleanupCallback === "function") cleanupCallback(); 20 | }; 21 | }, 22 | deps, 23 | ); 24 | } 25 | 26 | export default useInterfaceOrientationEffect; -------------------------------------------------------------------------------- /.yarn/patches/react-native-screens-npm-3.24.0-0887b5e74d.patch: -------------------------------------------------------------------------------- 1 | diff --git a/windows/RNScreens/RNScreens.vcxproj b/windows/RNScreens/RNScreens.vcxproj 2 | index 679da2844a0d7a876f7d003e0da8e6725233dca8..8e3d195877de6f9ad37fa2b717087aa67c804768 100644 3 | --- a/windows/RNScreens/RNScreens.vcxproj 4 | +++ b/windows/RNScreens/RNScreens.vcxproj 5 | @@ -14,7 +14,7 @@ 6 | Windows Store 7 | 10.0 8 | 10.0.18362.0 9 | - 10.0.16299.0 10 | + 10.0.17763.0 11 | 12 | 13 | 14 | @@ -56,6 +56,7 @@ 15 | 16 | 17 | DynamicLibrary 18 | + v142 19 | Unicode 20 | false 21 | 22 | -------------------------------------------------------------------------------- /example/src/components/screens/OrientationObserverScreen.tsx: -------------------------------------------------------------------------------- 1 | import { View, StyleSheet, Text, StatusBar } from "react-native"; 2 | import Color from "../../Color"; 3 | import { useInterfaceOrientation, useDeviceOrientation } from "react-native-orientation-manager"; 4 | 5 | function OrientationObserverScreen(): React.JSX.Element 6 | { 7 | const interfaceOrientation = useInterfaceOrientation(); 8 | const deviceOrientation = useDeviceOrientation(); 9 | 10 | return ( 11 | 12 | 13 | 14 | Interface orientation: {interfaceOrientation.getName()} 15 | 16 | 17 | Device orientation: {deviceOrientation.getName()} 18 | 19 | 20 | ); 21 | } 22 | 23 | const style = StyleSheet.create({ 24 | container: { 25 | flex: 1, 26 | backgroundColor: Color.Tertiary, 27 | justifyContent: "center", 28 | alignItems: "center", 29 | }, 30 | text: { 31 | color: Color.TertiaryText, 32 | fontSize: 18, 33 | }, 34 | }); 35 | 36 | export default OrientationObserverScreen; -------------------------------------------------------------------------------- /example/src/Color.ts: -------------------------------------------------------------------------------- 1 | const enum Color 2 | { 3 | Amber = "#E8B844", 4 | GoldenYellow = "#F5D97E", 5 | Pink = "#F03861", 6 | Beige = "#FEF2F2", 7 | } 8 | 9 | const enum AppColor 10 | { 11 | Primary = Color.Beige, 12 | PrimaryAdjacent = Color.Pink, 13 | PrimaryText = "#0D153D", 14 | 15 | Secondary = Color.Amber, 16 | SecondaryAdjacent = Color.GoldenYellow, 17 | 18 | Tertiary = "#0D153D", 19 | } 20 | 21 | enum ComponentColor 22 | { 23 | Primary = AppColor.Primary, 24 | PrimaryAdjacent = AppColor.PrimaryAdjacent, 25 | PrimaryText = AppColor.PrimaryText, 26 | PrimaryAdjacentText = "white", 27 | PrimaryAccent1 = AppColor.Secondary, 28 | PrimaryAccent1Adjacent = AppColor.SecondaryAdjacent, 29 | PrimaryAccent2 = AppColor.Tertiary, 30 | PrimaryAccent1Text = "black", 31 | PrimaryAccent2Text = AppColor.SecondaryAdjacent, 32 | 33 | Secondary = AppColor.Secondary, 34 | SecondaryAdjacent = AppColor.SecondaryAdjacent, 35 | SecondaryText = "#FAF2DB", 36 | SecondaryAdjacentText = "black", 37 | SecondaryAccent = AppColor.Tertiary, 38 | 39 | Tertiary = AppColor.Tertiary, 40 | TertiaryText = AppColor.SecondaryAdjacent, 41 | TertiaryAccent = AppColor.PrimaryAdjacent, 42 | } 43 | 44 | export default ComponentColor; -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | 4 | #OS junk files 5 | [Tt]humbs.db 6 | *.DS_Store 7 | 8 | #Visual Studio files 9 | *.[Oo]bj 10 | *.user 11 | *.aps 12 | *.pch 13 | *.vspscc 14 | *.vssscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.[Cc]ache 23 | *.ilk 24 | *.log 25 | *.lib 26 | *.sbr 27 | *.sdf 28 | *.opensdf 29 | *.opendb 30 | *.unsuccessfulbuild 31 | ipch/ 32 | [Oo]bj/ 33 | [Bb]in 34 | [Dd]ebug*/ 35 | [Rr]elease*/ 36 | Ankh.NoLoad 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opendb 43 | *.opensdf 44 | *.sdf 45 | *.cachefile 46 | *.VC.db 47 | *.VC.VC.opendb 48 | 49 | #MonoDevelop 50 | *.pidb 51 | *.userprefs 52 | 53 | #Tooling 54 | _ReSharper*/ 55 | *.resharper 56 | [Tt]est[Rr]esult* 57 | *.sass-cache 58 | 59 | #Project files 60 | [Bb]uild/ 61 | 62 | #Subversion files 63 | .svn 64 | 65 | # Office Temp Files 66 | ~$* 67 | 68 | # vim Temp Files 69 | *~ 70 | 71 | #NuGet 72 | packages/ 73 | *.nupkg 74 | 75 | #ncrunch 76 | *ncrunch* 77 | *crunch*.local.xml 78 | 79 | # visual studio database projects 80 | *.dbmdl 81 | 82 | #Test files 83 | *.testsettings 84 | 85 | #Other files 86 | *.DotSettings 87 | .vs/ 88 | *project.lock.json 89 | 90 | #Files generated by the VS build 91 | **/Generated Files/** 92 | 93 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | *AppPackages* 2 | *BundleArtifacts* 3 | 4 | #OS junk files 5 | [Tt]humbs.db 6 | *.DS_Store 7 | 8 | #Visual Studio files 9 | *.[Oo]bj 10 | *.user 11 | *.aps 12 | *.pch 13 | *.vspscc 14 | *.vssscc 15 | *_i.c 16 | *_p.c 17 | *.ncb 18 | *.suo 19 | *.tlb 20 | *.tlh 21 | *.bak 22 | *.[Cc]ache 23 | *.ilk 24 | *.log 25 | *.lib 26 | *.sbr 27 | *.sdf 28 | *.opensdf 29 | *.opendb 30 | *.unsuccessfulbuild 31 | ipch/ 32 | [Oo]bj/ 33 | [Bb]in 34 | [Dd]ebug*/ 35 | [Rr]elease*/ 36 | Ankh.NoLoad 37 | 38 | # Visual C++ cache files 39 | ipch/ 40 | *.aps 41 | *.ncb 42 | *.opendb 43 | *.opensdf 44 | *.sdf 45 | *.cachefile 46 | *.VC.db 47 | *.VC.VC.opendb 48 | 49 | #MonoDevelop 50 | *.pidb 51 | *.userprefs 52 | 53 | #Tooling 54 | _ReSharper*/ 55 | *.resharper 56 | [Tt]est[Rr]esult* 57 | *.sass-cache 58 | 59 | #Project files 60 | [Bb]uild/ 61 | 62 | #Subversion files 63 | .svn 64 | 65 | # Office Temp Files 66 | ~$* 67 | 68 | # vim Temp Files 69 | *~ 70 | 71 | #NuGet 72 | packages/ 73 | *.nupkg 74 | 75 | #ncrunch 76 | *ncrunch* 77 | *crunch*.local.xml 78 | 79 | # visual studio database projects 80 | *.dbmdl 81 | 82 | #Test files 83 | *.testsettings 84 | 85 | #Other files 86 | *.DotSettings 87 | .vs/ 88 | *project.lock.json 89 | 90 | #Files generated by the VS build 91 | **/Generated Files/** 92 | 93 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/orientationmanagerexample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.orientationmanagerexample; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate; 7 | import android.os.Bundle; 8 | 9 | public class MainActivity extends ReactActivity { 10 | 11 | /** 12 | * Returns the name of the main component registered from JavaScript. This is used to schedule 13 | * rendering of the component. 14 | */ 15 | @Override 16 | protected String getMainComponentName() { 17 | return "OrientationManagerExample"; 18 | } 19 | 20 | /** 21 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link 22 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React 23 | * (aka React 18) with two boolean flags. 24 | */ 25 | @Override 26 | protected ReactActivityDelegate createReactActivityDelegate() { 27 | return new DefaultReactActivityDelegate( 28 | this, 29 | getMainComponentName(), 30 | // If you opted-in for the New Architecture, we enable the Fabric Renderer. 31 | DefaultNewArchitectureEntryPoint.getFabricEnabled()); 32 | } 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(null); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/windows/ExperimentalFeatures.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 17 | false 18 | 19 | 25 | false 26 | 27 | 34 | false 35 | 36 | true 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /windows/ExperimentalFeatures.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 17 | false 18 | 19 | 25 | false 26 | 27 | 34 | false 35 | 36 | true 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /example/src/components/screens/LandscapeOnlyWithLeavingOnOrientationChangeScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Text, StatusBar } from "react-native"; 3 | import Color from "../../Color"; 4 | import { useInterfaceOrientationWhenFocusedEffect, type InterfaceOrientation } from "react-native-orientation-manager"; 5 | import type RootStackScreenProps from "../../types/RootStackScreenProps"; 6 | 7 | function LandscapeOnlyWithLeavingOnOrientationChangeScreen({ navigation }: RootStackScreenProps<"LandscapeOnlyWithLeavingOnOrientationChange">): React.JSX.Element 8 | { 9 | useInterfaceOrientationWhenFocusedEffect((interfaceOrientation: InterfaceOrientation) => { 10 | if (!interfaceOrientation.isLandscape()) navigation.goBack(); 11 | }); 12 | 13 | return ( 14 | 15 | 16 | 17 | This is a landscape-only screen that leaves when orientation changes to portrait. 18 | 19 | 20 | Try to change orientation to portrait. 21 | 22 | 23 | ); 24 | } 25 | 26 | const style = StyleSheet.create({ 27 | container: { 28 | flex: 1, 29 | backgroundColor: Color.Tertiary, 30 | justifyContent: "center", 31 | alignItems: "center", 32 | }, 33 | text: { 34 | color: Color.TertiaryText, 35 | fontSize: 18, 36 | textAlign: "center", 37 | }, 38 | }); 39 | 40 | export default LandscapeOnlyWithLeavingOnOrientationChangeScreen; -------------------------------------------------------------------------------- /src/src/lockFunctions.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from "react-native"; 2 | import OrientationManagerModule from "./OrientationManagerModule"; 3 | 4 | async function lockToPortrait(): Promise 5 | { 6 | await OrientationManagerModule.lockToPortrait(); 7 | } 8 | 9 | async function lockToPortraitUpsideDown(): Promise 10 | { 11 | await OrientationManagerModule.lockToPortraitUpsideDown(); 12 | } 13 | 14 | async function lockToLandscapeLeft(): Promise 15 | { 16 | await OrientationManagerModule.lockToLandscapeLeft(); 17 | } 18 | 19 | async function lockToLandscapeRight(): Promise 20 | { 21 | await OrientationManagerModule.lockToLandscapeRight(); 22 | } 23 | 24 | async function lockToLandscape(): Promise 25 | { 26 | await OrientationManagerModule.lockToLandscape(); 27 | } 28 | 29 | async function lockToAllOrientationsButUpsideDown(): Promise 30 | { 31 | if (Platform.OS === "android") throw new Error("lockToAllOrientationsButUpsideDown() is not supported on Android"); 32 | await OrientationManagerModule.lockToAllOrientationsButUpsideDown(); 33 | } 34 | 35 | async function unlockAllOrientations(): Promise 36 | { 37 | await OrientationManagerModule.unlockAllOrientations(); 38 | } 39 | 40 | async function resetInterfaceOrientationSetting(): Promise 41 | { 42 | await OrientationManagerModule.resetInterfaceOrientationSetting(); 43 | } 44 | 45 | export { 46 | lockToPortrait, 47 | lockToPortraitUpsideDown, 48 | lockToLandscapeLeft, 49 | lockToLandscapeRight, 50 | lockToLandscape, 51 | lockToAllOrientationsButUpsideDown, 52 | unlockAllOrientations, 53 | resetInterfaceOrientationSetting, 54 | }; -------------------------------------------------------------------------------- /example/src/components/ListItem.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigation } from "@react-navigation/native"; 2 | import Color from "../Color"; 3 | import type RootStackParamList from "../types/RootStackParamList"; 4 | import type RootStackScreenProps from "../types/RootStackScreenProps"; 5 | import { TouchableWithoutFeedback, Text, View, StyleSheet } from "react-native"; 6 | 7 | type ListItemProps = 8 | { 9 | name: string; 10 | guard?: () => boolean; 11 | } 12 | & 13 | { 14 | [K in keyof RootStackParamList]: 15 | { screen: K } & (RootStackParamList[K] extends undefined ? { params?: RootStackParamList[K] } : { params: RootStackParamList[K] }) 16 | ; 17 | }[keyof RootStackParamList] 18 | ; 19 | 20 | function ListItem({ name, screen, params, guard }: ListItemProps): React.JSX.Element 21 | { 22 | const navigation: RootStackScreenProps<"Home">["navigation"] = useNavigation(); 23 | 24 | return ( 25 | (guard ? guard() : true) && navigation.push(screen, params)}> 26 | 27 | {name} 28 | 29 | 30 | ); 31 | } 32 | 33 | const style = StyleSheet.create({ 34 | container: { 35 | width: "100%", 36 | minHeight: 50, 37 | justifyContent: "center", 38 | paddingLeft: 15, 39 | borderStyle: "solid", 40 | borderBottomWidth: StyleSheet.hairlineWidth, 41 | borderColor: "#0D153D60", 42 | }, 43 | text: { 44 | fontSize: 16, 45 | color: Color.PrimaryText, 46 | }, 47 | }); 48 | 49 | export default ListItem; -------------------------------------------------------------------------------- /example/src/components/screens/PortraitOnlyThatNavigatesToLandscapeOnlyScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Text, StatusBar } from "react-native"; 3 | import Color from "../../Color"; 4 | import type RootStackScreenProps from "../../types/RootStackScreenProps"; 5 | import { useInterfaceOrientationWhenFocusedEffect, type InterfaceOrientation } from "react-native-orientation-manager"; 6 | 7 | function PortraitOnlyThatNavigatesToLandscapeOnlyScreen({ navigation }: RootStackScreenProps<"PortraitOnlyThatNavigatesToLandscapeOnly">): React.JSX.Element 8 | { 9 | useInterfaceOrientationWhenFocusedEffect((interfaceOrientation: InterfaceOrientation) => { 10 | if (interfaceOrientation.isLandscape()) navigation.push("LandscapeOnlyWithLeavingOnOrientationChange"); 11 | }); 12 | 13 | return ( 14 | 15 | 16 | 17 | This is a portrait-only screen that navigates to a landscape-only screen when orientation changes to landscape 18 | which goes back when orientation changes to portrait. 19 | 20 | 21 | ); 22 | } 23 | 24 | const style = StyleSheet.create({ 25 | container: { 26 | flex: 1, 27 | backgroundColor: Color.Secondary, 28 | justifyContent: "center", 29 | alignItems: "center", 30 | paddingHorizontal: 20, 31 | }, 32 | text: { 33 | color: Color.SecondaryText, 34 | fontSize: 18, 35 | textAlign: "center", 36 | }, 37 | }); 38 | 39 | export default PortraitOnlyThatNavigatesToLandscapeOnlyScreen; -------------------------------------------------------------------------------- /src/src/hooks/useInterfaceOrientationWhenFocusedEffect.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type InterfaceOrientation from "../InterfaceOrientation"; 3 | import Orientations, { addInterfaceOrientationChangeListener } from "../manager"; 4 | import type InterfaceOrientationEffect from "../types/InterfaceOrientationEffect"; 5 | 6 | let useFocusEffect: ((effect: () => ReturnType) => void) | undefined = undefined; 7 | 8 | try 9 | { 10 | useFocusEffect = require("@react-navigation/native").useFocusEffect; 11 | } 12 | catch {} 13 | 14 | function useInterfaceOrientationWhenFocusedEffect(effect: InterfaceOrientationEffect, deps: React.DependencyList = []): void 15 | { 16 | if (!useFocusEffect) throw new Error("'@react-navigation/native' >= 5x package is required to use useInterfaceOrientationWhenFocusedEffect()"); 17 | 18 | const effectRef = React.useRef(effect); 19 | effectRef.current = effect; 20 | 21 | useFocusEffect(React.useCallback( 22 | () => { 23 | let cleanupCallback = effectRef.current(Orientations.interfaceOrientation); 24 | 25 | const interfaceOrientationChangeListenerRemover = addInterfaceOrientationChangeListener((interfaceOrientation: InterfaceOrientation) => { 26 | if (typeof cleanupCallback === "function") cleanupCallback(); 27 | cleanupCallback = effectRef.current(interfaceOrientation); 28 | }); 29 | 30 | return () => { 31 | interfaceOrientationChangeListenerRemover(); 32 | if (typeof cleanupCallback === "function") cleanupCallback(); 33 | }; 34 | }, 35 | deps, 36 | )); 37 | } 38 | 39 | export default useInterfaceOrientationWhenFocusedEffect; -------------------------------------------------------------------------------- /RNOrientationManager.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' 5 | 6 | Pod::Spec.new do |s| 7 | s.name = "RNOrientationManager" 8 | s.version = package["version"] 9 | s.summary = package["description"] 10 | s.homepage = package["homepage"] 11 | s.license = package["license"] 12 | s.authors = package["author"] 13 | 14 | s.platforms = { :ios => "11.0" } 15 | s.source = { :git => "https://github.com/KroosX4V/react-native-orientation-manager.git", :tag => "#{s.version}" } 16 | 17 | s.source_files = "ios/**/*.{h,m,mm}" 18 | 19 | # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. 20 | # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. 21 | if respond_to?(:install_modules_dependencies, true) 22 | install_modules_dependencies(s) 23 | else 24 | s.dependency "React-Core" 25 | 26 | # Don't install the dependencies when we run `pod install` in the old architecture. 27 | if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then 28 | s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" 29 | s.pod_target_xcconfig = { 30 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", 31 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", 32 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" 33 | } 34 | s.dependency "React-Codegen" 35 | s.dependency "RCT-Folly" 36 | s.dependency "RCTRequired" 37 | s.dependency "RCTTypeSafety" 38 | s.dependency "ReactCommon/turbomodule/core" 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | OrientationManagerExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const exclusionList = require("metro-config/src/defaults/exclusionList"); 5 | 6 | const root = path.resolve(__dirname, ".."); 7 | const rnwPath = fs.realpathSync(path.resolve(require.resolve("react-native-windows/package.json"), "..")); 8 | 9 | /** 10 | * Metro configuration 11 | * https://facebook.github.io/metro/docs/configuration 12 | * 13 | * @type {import('metro-config').MetroConfig} 14 | */ 15 | const config = { 16 | watchFolders: [root], 17 | resolver: { 18 | blockList: exclusionList([ 19 | // This stops "react-native run-windows" from causing the metro server to crash if its already running 20 | new RegExp(`${path.resolve(__dirname, "windows").replace(/[/\\]/g, "/")}.*`), 21 | // This prevents "react-native run-windows" from hitting: EBUSY: resource busy or locked, open msbuild.ProjectImports.zip or other files produced by msbuild 22 | new RegExp(`${rnwPath}/build/.*`), 23 | new RegExp(`${rnwPath}/target/.*`), 24 | /.*\.ProjectImports\.zip/, 25 | ]), 26 | extraNodeModules: { 27 | "@react-navigation/native": path.join(__dirname, "node_modules", "@react-navigation", "native"), 28 | }, 29 | }, 30 | transformer: { 31 | getTransformOptions: async () => ({ 32 | transform: { 33 | experimentalImportSupport: false, 34 | inlineRequires: true, 35 | }, 36 | }), 37 | // This fixes the 'missing-asset-registry-path` error (see https://github.com/microsoft/react-native-windows/issues/11437) 38 | assetRegistryPath: "react-native/Libraries/Image/AssetRegistry", 39 | }, 40 | }; 41 | 42 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.182.0 29 | 30 | # Use this property to specify which architecture you want to build. 31 | # You can also override it from the CLI using 32 | # ./gradlew -PreactNativeArchitectures=x86_64 33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 34 | 35 | # Use this property to enable support to the new architecture. 36 | # This will allow you to use TurboModules and the Fabric render in 37 | # your application. You should enable this flag either if you want 38 | # to write custom TurboModules/Fabric components OR use libraries that 39 | # are providing them. 40 | newArchEnabled=false 41 | 42 | # Use this property to enable or disable the Hermes JS engine. 43 | # If set to false, you will be using JSC instead. 44 | hermesEnabled=true 45 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | RNOrientationManagerExample 18 | KroosX4V 19 | Assets\StoreLogo.png 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/RNOrientationManagerExample.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Assets 25 | 26 | 27 | Assets 28 | 29 | 30 | Assets 31 | 32 | 33 | Assets 34 | 35 | 36 | Assets 37 | 38 | 39 | Assets 40 | 41 | 42 | Assets 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {e48dc53e-40b1-40cb-970a-f89935452892} 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExampleTests/OrientationManagerExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface OrientationManagerExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation OrientationManagerExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. 12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded 13 | # 14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` 15 | # ```js 16 | # module.exports = { 17 | # dependencies: { 18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), 19 | # ``` 20 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled 21 | 22 | linkage = ENV['USE_FRAMEWORKS'] 23 | if linkage != nil 24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 25 | use_frameworks! :linkage => linkage.to_sym 26 | end 27 | 28 | target 'OrientationManagerExample' do 29 | config = use_native_modules! 30 | 31 | # Flags change depending on the env values. 32 | flags = get_default_flags() 33 | 34 | use_react_native!( 35 | :path => config[:reactNativePath], 36 | # Hermes is now enabled by default. Disable by setting this flag to false. 37 | :hermes_enabled => flags[:hermes_enabled], 38 | :fabric_enabled => flags[:fabric_enabled], 39 | # Enables Flipper. 40 | # 41 | # Note that if you have use_frameworks! enabled, Flipper will not work and 42 | # you should disable the next line. 43 | :flipper_configuration => flipper_config, 44 | # An absolute path to your application root. 45 | :app_path => "#{Pod::Config.instance.installation_root}/.." 46 | ) 47 | 48 | target 'OrientationManagerExampleTests' do 49 | inherit! :complete 50 | # Pods for testing 51 | end 52 | 53 | post_install do |installer| 54 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 55 | react_native_post_install( 56 | installer, 57 | config[:reactNativePath], 58 | :mac_catalyst_enabled => false 59 | ) 60 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/orientationmanagerexample/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.orientationmanagerexample; 2 | 3 | import android.app.Application; 4 | import com.facebook.react.PackageList; 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; 9 | import com.facebook.react.defaults.DefaultReactNativeHost; 10 | import com.facebook.soloader.SoLoader; 11 | import java.util.List; 12 | import com.kroosx4v.orientationmanager.OrientationManagerActivityLifecycleCallbacks; 13 | 14 | public class MainApplication extends Application implements ReactApplication 15 | { 16 | private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() 19 | { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() 25 | { 26 | @SuppressWarnings("UnnecessaryLocalVariable") 27 | List packages = new PackageList(this).getPackages(); 28 | 29 | // Packages that cannot be autolinked yet can be added manually here, for example: 30 | // packages.add(new MyReactNativePackage()); 31 | 32 | return packages; 33 | } 34 | 35 | @Override 36 | protected String getJSMainModuleName() 37 | { 38 | return "index"; 39 | } 40 | 41 | @Override 42 | protected boolean isNewArchEnabled() 43 | { 44 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 45 | } 46 | 47 | @Override 48 | protected Boolean isHermesEnabled() 49 | { 50 | return BuildConfig.IS_HERMES_ENABLED; 51 | } 52 | }; 53 | 54 | @Override 55 | public ReactNativeHost getReactNativeHost() 56 | { 57 | return mReactNativeHost; 58 | } 59 | 60 | @Override 61 | public void onCreate() 62 | { 63 | super.onCreate(); 64 | SoLoader.init(this, /* native exopackage */false); 65 | 66 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) 67 | { 68 | // If you opted-in for the New Architecture, we load the native entry point for this app. 69 | DefaultNewArchitectureEntryPoint.load(); 70 | } 71 | 72 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 73 | registerActivityLifecycleCallbacks(new OrientationManagerActivityLifecycleCallbacks()); 74 | } 75 | } -------------------------------------------------------------------------------- /src/src/InterfaceOrientation.ts: -------------------------------------------------------------------------------- 1 | import InterfaceOrientationValue from "./types/InterfaceOrientationValue"; 2 | 3 | class InterfaceOrientation 4 | { 5 | private readonly interfaceOrientationValue: InterfaceOrientationValue; 6 | 7 | constructor (interfaceOrientationValue: InterfaceOrientationValue) 8 | { 9 | this.interfaceOrientationValue = interfaceOrientationValue; 10 | } 11 | 12 | public isUnknown(): boolean 13 | { 14 | return this.interfaceOrientationValue === InterfaceOrientationValue.Unknown; 15 | } 16 | 17 | public isPortrait(): boolean 18 | { 19 | return this.interfaceOrientationValue === InterfaceOrientationValue.Portrait; 20 | } 21 | 22 | public isPortraitUpsideDown(): boolean 23 | { 24 | return this.interfaceOrientationValue === InterfaceOrientationValue.PortraitUpsideDown; 25 | } 26 | 27 | public isLandscapeLeft(): boolean 28 | { 29 | return this.interfaceOrientationValue === InterfaceOrientationValue.LandscapeLeft; 30 | } 31 | 32 | public isLandscapeRight(): boolean 33 | { 34 | return this.interfaceOrientationValue === InterfaceOrientationValue.LandscapeRight; 35 | } 36 | 37 | public isEitherPortrait(): boolean 38 | { 39 | return this.isPortrait() || this.isPortraitUpsideDown(); 40 | } 41 | 42 | public isLandscape(): boolean 43 | { 44 | return this.isLandscapeLeft() || this.isLandscapeRight(); 45 | } 46 | 47 | public getName(): string 48 | { 49 | switch (this.interfaceOrientationValue) 50 | { 51 | case InterfaceOrientationValue.Unknown: 52 | return "Unknown"; 53 | 54 | case InterfaceOrientationValue.Portrait: 55 | return "Portrait"; 56 | 57 | case InterfaceOrientationValue.PortraitUpsideDown: 58 | return "Portrait Upside Down"; 59 | 60 | case InterfaceOrientationValue.LandscapeLeft: 61 | return "Landscape Left"; 62 | 63 | case InterfaceOrientationValue.LandscapeRight: 64 | return "Landscape Right"; 65 | 66 | default: 67 | throw new Error("Could not get name of interface orientation"); 68 | } 69 | } 70 | 71 | public equals(interfaceOrientation: InterfaceOrientation): boolean 72 | { 73 | if (!(interfaceOrientation instanceof InterfaceOrientation)) throw new Error("Value compared to must be an instance of InterfaceOrientation"); 74 | return this.interfaceOrientationValue === interfaceOrientation.interfaceOrientationValue; 75 | } 76 | } 77 | 78 | export default InterfaceOrientation; -------------------------------------------------------------------------------- /example/src/components/navigators/RootStackNavigator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { StyleSheet } from "react-native"; 3 | import { createNativeStackNavigator } from "@react-navigation/native-stack"; 4 | import { createStackNavigator } from "@react-navigation/stack"; 5 | import { Platform } from "react-native"; 6 | import Color from "../../Color"; 7 | import type RootStackParamList from "../../types/RootStackParamList"; 8 | import HomeScreen from "../screens/HomeScreen"; 9 | import OrientationObserverScreen from "../screens/OrientationObserverScreen"; 10 | import OrientationLockedScreen from "../screens/OrientationLockedScreen"; 11 | import LandscapeOnlyWithLeavingOnOrientationChangeScreen from "../screens/LandscapeOnlyWithLeavingOnOrientationChangeScreen"; 12 | import PortraitOnlyThatNavigatesToLandscapeOnlyScreen from "../screens/PortraitOnlyThatNavigatesToLandscapeOnlyScreen"; 13 | 14 | const RootStack = (Platform.OS === "windows" ? createStackNavigator : createNativeStackNavigator)(); 15 | 16 | export function RootStackNavigator(): React.JSX.Element 17 | { 18 | return ( 19 | 25 | 26 | 27 | 28 | 33 | 42 | 43 | ); 44 | } 45 | 46 | const style = StyleSheet.create({ 47 | header: { 48 | backgroundColor: Color.PrimaryAdjacent, 49 | borderBottomWidth: 0, 50 | }, 51 | portraitOnlyThatNavigatesToLandscapeOnlyHeader: { 52 | backgroundColor: Color.SecondaryAdjacent, 53 | borderBottomWidth: 0, 54 | }, 55 | }); -------------------------------------------------------------------------------- /react-native-orientation-manager.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.exclude": { 9 | "node_modules": true, 10 | "example/node_modules": true, 11 | } 12 | }, 13 | "tasks": { 14 | "version": "2.0.0", 15 | "tasks": [ 16 | { 17 | "label": "Start: Android - Emulator", 18 | "dependsOn": ["Metro", "Emulator: Android", "Delayed Android Installation Initializer"], 19 | "problemMatcher": [], 20 | }, 21 | { 22 | "label": "Start: Android - Physical Device", 23 | "dependsOn": ["Metro", "Install App: Android"], 24 | "problemMatcher": [], 25 | }, 26 | { 27 | "label": "Start: iOS - Emulator", 28 | "dependsOn": ["Metro", "Install App: iOS"], 29 | "problemMatcher": [], 30 | }, 31 | { 32 | "label": "Start: Windows", 33 | "dependsOn": ["Metro", "Install App: Windows"], 34 | "problemMatcher": [], 35 | }, 36 | { 37 | "label": "Metro", 38 | "type": "shell", 39 | "command": "yarn example start", 40 | "presentation": { 41 | "panel": "dedicated", 42 | "clear": true 43 | }, 44 | "problemMatcher": [], 45 | }, 46 | { 47 | "label": "Install App: Android", 48 | "type": "shell", 49 | "command": "yarn example android", 50 | "presentation": { 51 | "panel": "dedicated", 52 | "clear": true 53 | }, 54 | "problemMatcher": [], 55 | }, 56 | { 57 | "label": "Install App: iOS", 58 | "type": "shell", 59 | "command": "yarn example ios", 60 | "presentation": { 61 | "panel": "dedicated", 62 | "clear": true 63 | }, 64 | "problemMatcher": [], 65 | }, 66 | { 67 | "label": "Install App: Windows", 68 | "type": "shell", 69 | "command": "yarn example windows", 70 | "presentation": { 71 | "panel": "dedicated", 72 | "clear": true 73 | }, 74 | "problemMatcher": [], 75 | }, 76 | { 77 | "label": "Emulator: Android", 78 | "type": "shell", 79 | "command": "emulator @Pixel_3a_API_33_x86_64", 80 | "presentation": { 81 | "panel": "dedicated", 82 | "clear": true 83 | }, 84 | "problemMatcher": [], 85 | }, 86 | { 87 | "label": "Delayed Android Installation Initializer", 88 | "dependsOrder": "sequence", 89 | "dependsOn": ["Delayed Android Installation", "Install App: Android"], 90 | "problemMatcher": [] 91 | }, 92 | { 93 | "label": "Delayed Android Installation", 94 | "type": "shell", 95 | "command": "echo \"Installing Android app in 15 seconds.\" && timeout /T 15 /nobreak >nul 2>&1", 96 | "presentation": { 97 | "panel": "dedicated", 98 | "close": true, 99 | }, 100 | "problemMatcher": [] 101 | } 102 | ] 103 | }, 104 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:7.2.1" 9 | } 10 | } 11 | 12 | def isNewArchitectureEnabled() { 13 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" 14 | } 15 | 16 | apply plugin: "com.android.library" 17 | 18 | 19 | def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } 20 | 21 | if (isNewArchitectureEnabled()) { 22 | apply plugin: "com.facebook.react" 23 | } 24 | 25 | def getExtOrDefault(name) { 26 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["RNOrientationManager_" + name] 27 | } 28 | 29 | def getExtOrIntegerDefault(name) { 30 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["RNOrientationManager_" + name]).toInteger() 31 | } 32 | 33 | def supportsNamespace() { 34 | def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') 35 | def major = parsed[0].toInteger() 36 | def minor = parsed[1].toInteger() 37 | 38 | // Namespace support was added in 7.3.0 39 | if (major == 7 && minor >= 3) { 40 | return true 41 | } 42 | 43 | return major >= 8 44 | } 45 | 46 | android { 47 | if (supportsNamespace()) { 48 | namespace "com.kroosx4v.orientationmanager" 49 | 50 | sourceSets { 51 | main { 52 | manifest.srcFile "src/main/AndroidManifestNew.xml" 53 | } 54 | } 55 | } 56 | 57 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") 58 | 59 | defaultConfig { 60 | minSdkVersion getExtOrIntegerDefault("minSdkVersion") 61 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") 62 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 63 | } 64 | buildTypes { 65 | release { 66 | minifyEnabled false 67 | } 68 | } 69 | 70 | lintOptions { 71 | disable "GradleCompatible" 72 | } 73 | 74 | compileOptions { 75 | sourceCompatibility JavaVersion.VERSION_1_8 76 | targetCompatibility JavaVersion.VERSION_1_8 77 | } 78 | 79 | } 80 | 81 | repositories { 82 | mavenCentral() 83 | google() 84 | } 85 | 86 | 87 | dependencies { 88 | // For < 0.71, this will be from the local maven repo 89 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin 90 | //noinspection GradleDynamicVersion 91 | implementation "com.facebook.react:react-native:+" 92 | } 93 | 94 | if (isNewArchitectureEnabled()) { 95 | react { 96 | jsRootDir = file("../src/") 97 | libraryName = "RNOrientationManager" 98 | codegenJavaPackageName = "com.kroosx4v.orientationmanager" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.6) 5 | rexml 6 | activesupport (7.0.8) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 1.6, < 2) 9 | minitest (>= 5.1) 10 | tzinfo (~> 2.0) 11 | addressable (2.8.5) 12 | public_suffix (>= 2.0.2, < 6.0) 13 | algoliasearch (1.27.5) 14 | httpclient (~> 2.8, >= 2.8.3) 15 | json (>= 1.5.1) 16 | atomos (0.1.3) 17 | claide (1.1.0) 18 | cocoapods (1.13.0) 19 | addressable (~> 2.8) 20 | claide (>= 1.0.2, < 2.0) 21 | cocoapods-core (= 1.13.0) 22 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 23 | cocoapods-downloader (>= 1.6.0, < 2.0) 24 | cocoapods-plugins (>= 1.0.0, < 2.0) 25 | cocoapods-search (>= 1.0.0, < 2.0) 26 | cocoapods-trunk (>= 1.6.0, < 2.0) 27 | cocoapods-try (>= 1.1.0, < 2.0) 28 | colored2 (~> 3.1) 29 | escape (~> 0.0.4) 30 | fourflusher (>= 2.3.0, < 3.0) 31 | gh_inspector (~> 1.0) 32 | molinillo (~> 0.8.0) 33 | nap (~> 1.0) 34 | ruby-macho (>= 2.3.0, < 3.0) 35 | xcodeproj (>= 1.23.0, < 2.0) 36 | cocoapods-core (1.13.0) 37 | activesupport (>= 5.0, < 8) 38 | addressable (~> 2.8) 39 | algoliasearch (~> 1.0) 40 | concurrent-ruby (~> 1.1) 41 | fuzzy_match (~> 2.0.4) 42 | nap (~> 1.0) 43 | netrc (~> 0.11) 44 | public_suffix (~> 4.0) 45 | typhoeus (~> 1.0) 46 | cocoapods-deintegrate (1.0.5) 47 | cocoapods-downloader (1.6.3) 48 | cocoapods-plugins (1.0.0) 49 | nap 50 | cocoapods-search (1.0.1) 51 | cocoapods-trunk (1.6.0) 52 | nap (>= 0.8, < 2.0) 53 | netrc (~> 0.11) 54 | cocoapods-try (1.2.0) 55 | colored2 (3.1.2) 56 | concurrent-ruby (1.2.2) 57 | escape (0.0.4) 58 | ethon (0.16.0) 59 | ffi (>= 1.15.0) 60 | ffi (1.16.3) 61 | fourflusher (2.3.1) 62 | fuzzy_match (2.0.4) 63 | gh_inspector (1.1.3) 64 | httpclient (2.8.3) 65 | i18n (1.14.1) 66 | concurrent-ruby (~> 1.0) 67 | json (2.6.3) 68 | minitest (5.20.0) 69 | molinillo (0.8.0) 70 | nanaimo (0.3.0) 71 | nap (1.1.0) 72 | netrc (0.11.0) 73 | public_suffix (4.0.7) 74 | rexml (3.2.6) 75 | ruby-macho (2.5.1) 76 | typhoeus (1.4.0) 77 | ethon (>= 0.9.0) 78 | tzinfo (2.0.6) 79 | concurrent-ruby (~> 1.0) 80 | xcodeproj (1.23.0) 81 | CFPropertyList (>= 2.3.3, < 4.0) 82 | atomos (~> 0.1.3) 83 | claide (>= 1.0.2, < 2.0) 84 | colored2 (~> 3.1) 85 | nanaimo (~> 0.3.0) 86 | rexml (~> 3.2.4) 87 | 88 | PLATFORMS 89 | ruby 90 | 91 | DEPENDENCIES 92 | activesupport (~> 7.0, <= 7.0.8) 93 | cocoapods (~> 1.12) 94 | 95 | RUBY VERSION 96 | ruby 2.7.6p219 97 | 98 | BUNDLED WITH 99 | 2.1.4 100 | -------------------------------------------------------------------------------- /example/src/components/screens/OrientationLockedScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { View, StyleSheet, Text, StatusBar } from "react-native"; 3 | import Color from "../../Color"; 4 | import { useFocusEffect } from "@react-navigation/native"; 5 | import { 6 | lockToPortrait, 7 | lockToPortraitUpsideDown, 8 | lockToLandscapeLeft, 9 | lockToLandscapeRight, 10 | lockToLandscape, 11 | lockToAllOrientationsButUpsideDown, 12 | unlockAllOrientations, 13 | resetInterfaceOrientationSetting, 14 | } from "react-native-orientation-manager"; 15 | import RootStackScreenProps from "../../types/RootStackScreenProps"; 16 | import OrientationLock from "../../types/OrientationLock"; 17 | 18 | function OrientationLockedScreen({ navigation, route: { params: { lock, title, text } } }: RootStackScreenProps<"OrientationLocked">): React.JSX.Element 19 | { 20 | React.useEffect( 21 | () => { 22 | navigation.setOptions({ title }); 23 | }, 24 | [], 25 | ); 26 | 27 | const lockFunction = React.useMemo<(...args: []) => any>( 28 | () => { 29 | switch (lock) 30 | { 31 | case OrientationLock.PORTRAIT: 32 | return lockToPortrait; 33 | 34 | case OrientationLock.PORTRAIT_UPSIDE_DOWN: 35 | return lockToPortraitUpsideDown; 36 | 37 | case OrientationLock.LANDSCAPE_LEFT: 38 | return lockToLandscapeLeft; 39 | 40 | case OrientationLock.LANDSCAPE_RIGHT: 41 | return lockToLandscapeRight; 42 | 43 | case OrientationLock.LANDSCAPE: 44 | return lockToLandscape; 45 | 46 | case OrientationLock.ALL_ORIENTATIONS_BUT_UPSIDE_DOWN: 47 | return lockToAllOrientationsButUpsideDown; 48 | 49 | case OrientationLock.UNLOCK_ALL_ORIENTATIONS: 50 | return unlockAllOrientations; 51 | 52 | default: 53 | throw new Error("Unable to determine lock function"); 54 | } 55 | }, 56 | [lock], 57 | ); 58 | 59 | useFocusEffect(React.useCallback( 60 | () => (lockFunction(), resetInterfaceOrientationSetting), 61 | [], 62 | )); 63 | 64 | return ( 65 | 66 | 67 | 68 | {text} 69 | 70 | 71 | ); 72 | } 73 | 74 | const style = StyleSheet.create({ 75 | container: { 76 | flex: 1, 77 | backgroundColor: Color.Tertiary, 78 | justifyContent: "center", 79 | alignItems: "center", 80 | paddingHorizontal: 20, 81 | }, 82 | text: { 83 | color: Color.TertiaryText, 84 | fontSize: 18, 85 | textAlign: "center", 86 | }, 87 | }); 88 | 89 | export default OrientationLockedScreen; -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/src/DeviceOrientation.ts: -------------------------------------------------------------------------------- 1 | import DeviceOrientationValue from "./types/DeviceOrientationValue"; 2 | 3 | class DeviceOrientation 4 | { 5 | private readonly deviceOrientationValue: DeviceOrientationValue; 6 | 7 | constructor (deviceOrientationValue: DeviceOrientationValue) 8 | { 9 | this.deviceOrientationValue = deviceOrientationValue; 10 | } 11 | 12 | public isUnknown(): boolean 13 | { 14 | return this.deviceOrientationValue === DeviceOrientationValue.Unknown; 15 | } 16 | 17 | public isPortrait(): boolean 18 | { 19 | return this.deviceOrientationValue === DeviceOrientationValue.Portrait; 20 | } 21 | 22 | public isPortraitUpsideDown(): boolean 23 | { 24 | return this.deviceOrientationValue === DeviceOrientationValue.PortraitUpsideDown; 25 | } 26 | 27 | public isLandscapeLeft(): boolean 28 | { 29 | return this.deviceOrientationValue === DeviceOrientationValue.LandscapeLeft; 30 | } 31 | 32 | public isLandscapeRight(): boolean 33 | { 34 | return this.deviceOrientationValue === DeviceOrientationValue.LandscapeRight; 35 | } 36 | 37 | public isFaceUp(): boolean 38 | { 39 | return this.deviceOrientationValue === DeviceOrientationValue.FaceDown; 40 | } 41 | 42 | public isFaceDown(): boolean 43 | { 44 | return this.deviceOrientationValue === DeviceOrientationValue.FaceDown; 45 | } 46 | 47 | public isEitherPortrait(): boolean 48 | { 49 | return this.isPortrait() || this.isPortraitUpsideDown(); 50 | } 51 | 52 | public isLandscape(): boolean 53 | { 54 | return this.isLandscapeLeft() || this.isLandscapeRight(); 55 | } 56 | 57 | public isFace(): boolean 58 | { 59 | return this.isFaceUp() || this.isFaceDown(); 60 | } 61 | 62 | public getName(): string 63 | { 64 | switch (this.deviceOrientationValue) 65 | { 66 | case DeviceOrientationValue.Unknown: 67 | return "Unknown"; 68 | 69 | case DeviceOrientationValue.Portrait: 70 | return "Portrait"; 71 | 72 | case DeviceOrientationValue.PortraitUpsideDown: 73 | return "Portrait Upside Down"; 74 | 75 | case DeviceOrientationValue.LandscapeLeft: 76 | return "Landscape Left"; 77 | 78 | case DeviceOrientationValue.LandscapeRight: 79 | return "Landscape Right"; 80 | 81 | case DeviceOrientationValue.FaceUp: 82 | return "Face Up"; 83 | 84 | case DeviceOrientationValue.FaceDown: 85 | return "Face Down"; 86 | 87 | default: 88 | throw new Error("Could not get name of device orientation"); 89 | } 90 | } 91 | 92 | public equals(deviceOrientation: DeviceOrientation): boolean 93 | { 94 | if (!(deviceOrientation instanceof DeviceOrientation)) throw new Error("Value compared to must be an instance of DeviceOrientation"); 95 | return this.deviceOrientationValue === deviceOrientation.deviceOrientationValue; 96 | } 97 | } 98 | 99 | export default DeviceOrientation; -------------------------------------------------------------------------------- /example/src/components/screens/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import { Alert, Platform, ScrollView, StatusBar, StyleSheet } from "react-native"; 2 | import Color from "../../Color"; 3 | import ListItem from "../ListItem"; 4 | import Orientations from "react-native-orientation-manager"; 5 | import OrientationLock from "../../types/OrientationLock"; 6 | import OrientationLockedListItem from "../OrientationLockedListItem"; 7 | 8 | function HomeScreen(): React.JSX.Element 9 | { 10 | return ( 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | { 28 | if (Platform.OS === "android") 29 | { 30 | Alert.alert("Not Supported", "This lock type is not supported on Android."); 31 | return false; 32 | } 33 | 34 | return true; 35 | }} 36 | /> 37 | 42 | { 46 | if (Orientations.interfaceOrientation.isLandscape()) return true; 47 | 48 | Alert.alert("Landscape Only", "You have to be in landscape mode to enter this screen."); 49 | return false; 50 | }} 51 | /> 52 | 53 | 54 | ); 55 | } 56 | 57 | const style = StyleSheet.create({ 58 | container: { 59 | flex: 1, 60 | backgroundColor: Color.Primary, 61 | }, 62 | }); 63 | 64 | export default HomeScreen; -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/App.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include "App.h" 4 | 5 | #include "AutolinkedNativeModules.g.h" 6 | #include "ReactPackageProvider.h" 7 | 8 | using namespace winrt; 9 | using namespace xaml; 10 | using namespace xaml::Controls; 11 | using namespace xaml::Navigation; 12 | 13 | using namespace Windows::ApplicationModel; 14 | namespace winrt::RNOrientationManagerExample::implementation 15 | { 16 | ///

17 | /// Initializes the singleton application object. This is the first line of 18 | /// authored code executed, and as such is the logical equivalent of main() or 19 | /// WinMain(). 20 | /// 21 | App::App() noexcept 22 | { 23 | #if BUNDLE 24 | JavaScriptBundleFile(L"index.windows"); 25 | InstanceSettings().UseWebDebugger(false); 26 | InstanceSettings().UseFastRefresh(false); 27 | #else 28 | JavaScriptBundleFile(L"index"); 29 | InstanceSettings().UseWebDebugger(false); 30 | InstanceSettings().UseFastRefresh(true); 31 | #endif 32 | 33 | #if _DEBUG 34 | InstanceSettings().UseDeveloperSupport(true); 35 | #else 36 | InstanceSettings().UseDeveloperSupport(false); 37 | #endif 38 | 39 | RegisterAutolinkedNativeModulePackages(PackageProviders()); // Includes any autolinked modules 40 | 41 | PackageProviders().Append(make()); // Includes all modules in this project 42 | 43 | InitializeComponent(); 44 | } 45 | 46 | /// 47 | /// Invoked when the application is launched normally by the end user. Other entry points 48 | /// will be used such as when the application is launched to open a specific file. 49 | /// 50 | /// Details about the launch request and process. 51 | void App::OnLaunched(activation::LaunchActivatedEventArgs const& e) 52 | { 53 | super::OnLaunched(e); 54 | 55 | Frame rootFrame = Window::Current().Content().as(); 56 | rootFrame.Navigate(xaml_typename(), box_value(e.Arguments())); 57 | } 58 | 59 | /// 60 | /// Invoked when the application is activated by some means other than normal launching. 61 | /// 62 | void App::OnActivated(Activation::IActivatedEventArgs const &e) { 63 | auto preActivationContent = Window::Current().Content(); 64 | super::OnActivated(e); 65 | if (!preActivationContent && Window::Current()) { 66 | Frame rootFrame = Window::Current().Content().as(); 67 | rootFrame.Navigate(xaml_typename(), nullptr); 68 | } 69 | } 70 | 71 | /// 72 | /// Invoked when application execution is being suspended. Application state is saved 73 | /// without knowing whether the application will be terminated or resumed with the contents 74 | /// of memory still intact. 75 | /// 76 | /// The source of the suspend request. 77 | /// Details about the suspend request. 78 | void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] SuspendingEventArgs const& e) 79 | { 80 | // Save application state and stop any background activity 81 | } 82 | 83 | /// 84 | /// Invoked when Navigation to a certain page fails 85 | /// 86 | /// The Frame which failed navigation 87 | /// Details about the navigation failure 88 | void App::OnNavigationFailed(IInspectable const&, NavigationFailedEventArgs const& e) 89 | { 90 | throw hresult_error(E_FAIL, hstring(L"Failed to load Page ") + e.SourcePageType().Name); 91 | } 92 | 93 | } // namespace winrt::RNOrientationManagerExample::implementation 94 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/orientationmanagerexample/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.orientationmanagerexample; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 21 | import com.facebook.react.ReactInstanceEventListener; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | /** 28 | * Class responsible of loading Flipper inside your React Native application. This is the debug 29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup. 30 | */ 31 | public class ReactNativeFlipper { 32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 33 | if (FlipperUtils.shouldEnableFlipper(context)) { 34 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 35 | 36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 37 | client.addPlugin(new DatabasesFlipperPlugin(context)); 38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 39 | client.addPlugin(CrashReporterPlugin.getInstance()); 40 | 41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 42 | NetworkingModule.setCustomClientBuilder( 43 | new NetworkingModule.CustomClientBuilder() { 44 | @Override 45 | public void apply(OkHttpClient.Builder builder) { 46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 47 | } 48 | }); 49 | client.addPlugin(networkFlipperPlugin); 50 | client.start(); 51 | 52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 53 | // Hence we run if after all native modules have been initialized 54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 55 | if (reactContext == null) { 56 | reactInstanceManager.addReactInstanceEventListener( 57 | new ReactInstanceEventListener() { 58 | @Override 59 | public void onReactContextInitialized(ReactContext reactContext) { 60 | reactInstanceManager.removeReactInstanceEventListener(this); 61 | reactContext.runOnNativeModulesQueueThread( 62 | new Runnable() { 63 | @Override 64 | public void run() { 65 | client.addPlugin(new FrescoFlipperPlugin()); 66 | } 67 | }); 68 | } 69 | }); 70 | } else { 71 | client.addPlugin(new FrescoFlipperPlugin()); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample.xcodeproj/xcshareddata/xcschemes/OrientationManagerExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/OrientationManagerExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/src/components/OrientationLocker.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | lockToPortrait, 4 | lockToPortraitUpsideDown, 5 | lockToLandscapeLeft, 6 | lockToLandscapeRight, 7 | lockToLandscape, 8 | lockToAllOrientationsButUpsideDown, 9 | unlockAllOrientations, 10 | resetInterfaceOrientationSetting 11 | } from "../lockFunctions"; 12 | import { Platform } from "react-native"; 13 | 14 | interface OrientationLockerProps 15 | { 16 | lock: "Portrait" | "PortraitUpsideDown" | "LandscapeLeft" | "LandscapeRight" | "Landscape" | "AllButUpsideDown" | "All"; 17 | } 18 | 19 | interface StackEntry 20 | { 21 | lockFunction: typeof lockToPortrait; 22 | } 23 | 24 | class OrientationLocker extends React.PureComponent 25 | { 26 | private static lockStack: StackEntry[] = []; 27 | private static lockUpdateImmediateId: NodeJS.Immediate | null = null; 28 | 29 | private stackEntry: StackEntry | null = null; 30 | 31 | render(): React.ReactNode 32 | { 33 | return null; 34 | } 35 | 36 | public componentDidMount(): void 37 | { 38 | this.stackEntry = this.createStackEntry(); 39 | 40 | OrientationLocker.lockStack.push(this.stackEntry); 41 | OrientationLocker.updateLock(); 42 | } 43 | 44 | public componentDidUpdate(): void 45 | { 46 | const stackEntryIndex = OrientationLocker.lockStack.indexOf(this.stackEntry!); 47 | OrientationLocker.lockStack[stackEntryIndex] = this.stackEntry = this.createStackEntry(); 48 | if (stackEntryIndex === OrientationLocker.lockStack.length - 1) OrientationLocker.updateLock(); 49 | } 50 | 51 | public componentWillUnmount(): void 52 | { 53 | const stackEntryIndex = OrientationLocker.lockStack.indexOf(this.stackEntry!); 54 | OrientationLocker.lockStack.splice(stackEntryIndex, 1); 55 | if (stackEntryIndex === OrientationLocker.lockStack.length) OrientationLocker.updateLock(); 56 | } 57 | 58 | private static updateLock(): void 59 | { 60 | if (OrientationLocker.lockUpdateImmediateId !== null) 61 | { 62 | clearImmediate(OrientationLocker.lockUpdateImmediateId); 63 | OrientationLocker.lockUpdateImmediateId = null; 64 | } 65 | 66 | OrientationLocker.lockUpdateImmediateId = setImmediate(() => { 67 | OrientationLocker.lockUpdateImmediateId = null; 68 | 69 | if (OrientationLocker.lockStack.length) OrientationLocker.lockStack[OrientationLocker.lockStack.length - 1]!.lockFunction(); 70 | else resetInterfaceOrientationSetting(); 71 | }); 72 | } 73 | 74 | private createStackEntry(): StackEntry 75 | { 76 | let lockFunction: StackEntry["lockFunction"]; 77 | 78 | switch (this.props.lock) 79 | { 80 | case "Portrait": 81 | 82 | lockFunction = lockToPortrait; 83 | break; 84 | 85 | case "PortraitUpsideDown": 86 | 87 | lockFunction = lockToPortraitUpsideDown; 88 | break; 89 | 90 | case "LandscapeLeft": 91 | 92 | lockFunction = lockToLandscapeLeft; 93 | break; 94 | 95 | case "LandscapeRight": 96 | 97 | lockFunction = lockToLandscapeRight; 98 | break; 99 | 100 | case "Landscape": 101 | 102 | lockFunction = lockToLandscape; 103 | break; 104 | 105 | case "AllButUpsideDown": 106 | 107 | if (Platform.OS === "android") 108 | { 109 | console.error(`"${this.props.lock}" is not supported on Android. Assumed "All" instead`); 110 | } 111 | else 112 | { 113 | lockFunction = lockToAllOrientationsButUpsideDown; 114 | break; 115 | } 116 | 117 | case "All": 118 | 119 | lockFunction = unlockAllOrientations; 120 | break; 121 | 122 | default: 123 | throw new Error("Unknown lock type"); 124 | } 125 | 126 | return { lockFunction }; 127 | } 128 | } 129 | 130 | export default OrientationLocker; -------------------------------------------------------------------------------- /src/src/manager.ts: -------------------------------------------------------------------------------- 1 | import OrientationManagerModule from "./OrientationManagerModule"; 2 | import { NativeEventEmitter, Platform } from "react-native"; 3 | import getUniqueId from "./getUniqueId"; 4 | import InterfaceOrientation from "./InterfaceOrientation"; 5 | import DeviceOrientation from "./DeviceOrientation"; 6 | import type IOrientations from "./types/Orientations"; 7 | import type InterfaceOrientationChangeListenerCallback from "./types/InterfaceOrientationChangeListenerCallback"; 8 | import type DeviceOrientationChangeListenerCallback from "./types/DeviceOrientationChangeListenerCallback"; 9 | import type InterfaceOrientationChangedNativeEvent from "./types/InterfaceOrientationChangedNativeEvent"; 10 | import type DeviceOrientationChangedNativeEvent from "./types/DeviceOrientationChangedNativeEvent"; 11 | 12 | type ListenerRemover = () => void; 13 | 14 | let addInterfaceOrientationChangeListener: (callback: InterfaceOrientationChangeListenerCallback) => ListenerRemover; 15 | let addDeviceOrientationChangeListener: (callback: DeviceOrientationChangeListenerCallback) => ListenerRemover; 16 | let Orientations: IOrientations; 17 | { 18 | let currentInterfaceOrientation: InterfaceOrientation; 19 | let currentDeviceOrientation: DeviceOrientation; 20 | { 21 | const { initialInterfaceOrientationValue, initialDeviceOrientationValue } = OrientationManagerModule.getConstants(); 22 | 23 | currentInterfaceOrientation = new InterfaceOrientation( 24 | Platform.OS === "android" ? Number(initialInterfaceOrientationValue) : initialInterfaceOrientationValue, 25 | ); 26 | 27 | currentDeviceOrientation = new DeviceOrientation(Platform.OS === "android" ? Number(initialDeviceOrientationValue) : initialDeviceOrientationValue); 28 | } 29 | 30 | const interfaceOrientationChangeListeners: Record = {}; 31 | const deviceOrientationChangeListeners: Record = {}; 32 | 33 | { 34 | const nativeEventEmitter = new NativeEventEmitter(OrientationManagerModule); 35 | 36 | nativeEventEmitter.addListener( 37 | "interfaceOrientationChanged", 38 | ({ interfaceOrientationValue }: InterfaceOrientationChangedNativeEvent) => { 39 | const interfaceOrientation = new InterfaceOrientation(Platform.OS === "android" ? Number(interfaceOrientationValue) : interfaceOrientationValue); 40 | currentInterfaceOrientation = interfaceOrientation; 41 | 42 | for (const listenerCallbackId in interfaceOrientationChangeListeners) 43 | { 44 | interfaceOrientationChangeListeners[listenerCallbackId]!(interfaceOrientation); 45 | } 46 | }, 47 | ); 48 | 49 | nativeEventEmitter.addListener( 50 | "deviceOrientationChanged", 51 | ({ deviceOrientationValue }: DeviceOrientationChangedNativeEvent) => { 52 | const deviceOrientation = new DeviceOrientation(Platform.OS === "android" ? Number(deviceOrientationValue) : deviceOrientationValue); 53 | currentDeviceOrientation = deviceOrientation; 54 | 55 | for (const listenerCallbackId in deviceOrientationChangeListeners) deviceOrientationChangeListeners[listenerCallbackId]!(deviceOrientation); 56 | }, 57 | ); 58 | } 59 | 60 | Orientations = Object.freeze({ 61 | get interfaceOrientation(): InterfaceOrientation { 62 | return currentInterfaceOrientation; 63 | }, 64 | get deviceOrientation(): DeviceOrientation { 65 | return currentDeviceOrientation; 66 | }, 67 | }); 68 | 69 | addInterfaceOrientationChangeListener = (callback: InterfaceOrientationChangeListenerCallback): ListenerRemover => { 70 | const id = getUniqueId(); 71 | interfaceOrientationChangeListeners[id] = callback; 72 | 73 | return (): void => { 74 | delete interfaceOrientationChangeListeners[id]; 75 | }; 76 | }; 77 | 78 | addDeviceOrientationChangeListener = (callback: DeviceOrientationChangeListenerCallback): ListenerRemover => { 79 | const id = getUniqueId(); 80 | deviceOrientationChangeListeners[id] = callback; 81 | 82 | return (): void => { 83 | delete deviceOrientationChangeListeners[id]; 84 | }; 85 | }; 86 | } 87 | 88 | export default Orientations; 89 | export { addInterfaceOrientationChangeListener, addDeviceOrientationChangeListener }; -------------------------------------------------------------------------------- /windows/RNOrientationManager/RNOrientationManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pch.h" 4 | #include 5 | #include "NativeModules.h" 6 | #include 7 | #include 8 | #include "winrt/Windows.Devices.Sensors.h" 9 | #include 10 | #include 11 | 12 | using namespace winrt::Windows::Devices::Sensors; 13 | using namespace winrt::Windows::Graphics::Display; 14 | using namespace winrt::Windows::UI::ViewManagement; 15 | 16 | namespace RNOrientationManager 17 | { 18 | enum class InterfaceOrientation : int 19 | { 20 | Unknown = 0, 21 | Portrait = 1, 22 | PortraitUpsideDown = 2, 23 | LandscapeLeft = 3, 24 | LandscapeRight = 4, 25 | }; 26 | 27 | enum class DeviceOrientation : int 28 | { 29 | Unknown = 0, 30 | Portrait = 1, 31 | PortraitUpsideDown = 2, 32 | LandscapeLeft = 3, 33 | LandscapeRight = 4, 34 | FaceUp = 5, 35 | FaceDown = 6, 36 | }; 37 | 38 | REACT_MODULE(Module, L"OrientationManagerModule"); 39 | struct Module : public std::enable_shared_from_this 40 | { 41 | REACT_INIT(Initialize) 42 | void Initialize(React::ReactContext const& reactContext) noexcept; 43 | 44 | REACT_CONSTANT_PROVIDER(GetConstants) 45 | void GetConstants(React::ReactConstantProvider& provider) noexcept; 46 | 47 | REACT_EVENT(InterfaceOrientationChanged, L"interfaceOrientationChanged"); 48 | std::function InterfaceOrientationChanged; 49 | 50 | REACT_EVENT(DeviceOrientationChanged, L"deviceOrientationChanged"); 51 | std::function DeviceOrientationChanged; 52 | 53 | REACT_METHOD(AddListener, L"addListener"); 54 | void AddListener(std::string) noexcept; 55 | 56 | REACT_METHOD(RemoveListeners, L"removeListeners"); 57 | void RemoveListeners(int) noexcept; 58 | 59 | REACT_METHOD(LockToPortrait, L"lockToPortrait"); 60 | void LockToPortrait(React::ReactPromise&& promise) noexcept; 61 | 62 | REACT_METHOD(LockToPortraitUpsideDown, L"lockToPortraitUpsideDown"); 63 | void LockToPortraitUpsideDown(React::ReactPromise&& promise) noexcept; 64 | 65 | REACT_METHOD(LockToLandscapeLeft, L"lockToLandscapeLeft"); 66 | void LockToLandscapeLeft(React::ReactPromise&& promise) noexcept; 67 | 68 | REACT_METHOD(LockToLandscapeRight, L"lockToLandscapeRight"); 69 | void LockToLandscapeRight(React::ReactPromise&& promise) noexcept; 70 | 71 | REACT_METHOD(LockToLandscape, L"lockToLandscape"); 72 | void LockToLandscape(React::ReactPromise&& promise) noexcept; 73 | 74 | REACT_METHOD(LockToAllOrientationsButUpsideDown, L"lockToAllOrientationsButUpsideDown"); 75 | void LockToAllOrientationsButUpsideDown(React::ReactPromise&& promise) noexcept; 76 | 77 | REACT_METHOD(UnlockAllOrientations, L"unlockAllOrientations"); 78 | void UnlockAllOrientations(React::ReactPromise&& promise) noexcept; 79 | 80 | REACT_METHOD(ResetInterfaceOrientationSetting, L"resetInterfaceOrientationSetting"); 81 | void ResetInterfaceOrientationSetting(React::ReactPromise&& promise) noexcept; 82 | 83 | private: 84 | 85 | React::ReactContext m_reactContext; 86 | 87 | DisplayInformation m_displayInformation{ nullptr }; 88 | UIViewSettings m_uiViewSettings{ nullptr }; 89 | SimpleOrientationSensor m_simpleOrientationSensor{ nullptr }; 90 | 91 | InterfaceOrientation m_lastInterfaceOrientation = InterfaceOrientation::Unknown; 92 | DeviceOrientation m_lastDeviceOrientation = DeviceOrientation::Unknown; 93 | 94 | std::mutex m_checkingForDeviceOrientationChangeMutex; 95 | std::unique_ptr m_defaultDisplayOrientations{ nullptr }; 96 | 97 | void SetAutoRotationPreferences(React::ReactPromise&& promise, DisplayOrientations displayOrientations); 98 | void SendInterfaceOrientationChangedIfOccurred(); 99 | 100 | void SendDeviceOrientationChangedIfOccurred(); 101 | void SendDeviceOrientationChangedIfOccurred(DeviceOrientation deviceOrientation); 102 | 103 | void OnSimpleOrientationChanged(SimpleOrientationSensor const&, SimpleOrientationSensorOrientationChangedEventArgs const&); 104 | void OnDisplayOrientationChanged(DisplayInformation const&, IInspectable const&); 105 | 106 | InterfaceOrientation GetInterfaceOrientation(); 107 | DeviceOrientation GetDeviceOrientation(); 108 | 109 | void UIDispatch(std::function&& func, bool waitIfPosted = false); 110 | 111 | static InterfaceOrientation DisplayOrientationToInterfaceOrientation(DisplayOrientations displayOrientation); 112 | static DeviceOrientation SimpleOrientationToDeviceOrientation(SimpleOrientation simpleOrientation); 113 | }; 114 | } -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "com.facebook.react" 3 | 4 | /** 5 | * This is the configuration block to customize your React Native Android app. 6 | * By default you don't need to apply any configuration, just uncomment the lines you need. 7 | */ 8 | react { 9 | /* Folders */ 10 | // The root of your project, i.e. where "package.json" lives. Default is '..' 11 | // root = file("../") 12 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native 13 | reactNativeDir = file("../../../node_modules/react-native") 14 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen 15 | codegenDir = file("../../../node_modules/@react-native/codegen") 16 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js 17 | cliFile = file("../../../node_modules/react-native/cli.js") 18 | 19 | /* Variants */ 20 | // The list of variants to that are debuggable. For those we're going to 21 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'. 22 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. 23 | // debuggableVariants = ["liteDebug", "prodDebug"] 24 | 25 | /* Bundling */ 26 | // A list containing the node command and its flags. Default is just 'node'. 27 | // nodeExecutableAndArgs = ["node"] 28 | // 29 | // The command to run when bundling. By default is 'bundle' 30 | // bundleCommand = "ram-bundle" 31 | // 32 | // The path to the CLI configuration file. Default is empty. 33 | // bundleConfig = file(../rn-cli.config.js) 34 | // 35 | // The name of the generated asset file containing your JS bundle 36 | // bundleAssetName = "MyApplication.android.bundle" 37 | // 38 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' 39 | // entryFile = file("../js/MyApplication.android.js") 40 | // 41 | // A list of extra flags to pass to the 'bundle' commands. 42 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle 43 | // extraPackagerArgs = [] 44 | 45 | /* Hermes Commands */ 46 | // The hermes compiler command to run. By default it is 'hermesc' 47 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" 48 | // 49 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" 50 | // hermesFlags = ["-O", "-output-source-map"] 51 | } 52 | 53 | /** 54 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. 55 | */ 56 | def enableProguardInReleaseBuilds = false 57 | 58 | /** 59 | * The preferred build flavor of JavaScriptCore (JSC) 60 | * 61 | * For example, to use the international variant, you can use: 62 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 63 | * 64 | * The international variant includes ICU i18n library and necessary data 65 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 66 | * give correct results when using with locales other than en-US. Note that 67 | * this variant is about 6MiB larger per architecture than default. 68 | */ 69 | def jscFlavor = 'org.webkit:android-jsc:+' 70 | 71 | android { 72 | ndkVersion rootProject.ext.ndkVersion 73 | 74 | compileSdkVersion rootProject.ext.compileSdkVersion 75 | 76 | namespace "com.orientationmanagerexample" 77 | defaultConfig { 78 | applicationId "com.orientationmanagerexample" 79 | minSdkVersion rootProject.ext.minSdkVersion 80 | targetSdkVersion rootProject.ext.targetSdkVersion 81 | versionCode 1 82 | versionName "1.0" 83 | } 84 | signingConfigs { 85 | debug { 86 | storeFile file('debug.keystore') 87 | storePassword 'android' 88 | keyAlias 'androiddebugkey' 89 | keyPassword 'android' 90 | } 91 | } 92 | buildTypes { 93 | debug { 94 | signingConfig signingConfigs.debug 95 | } 96 | release { 97 | // Caution! In production, you need to generate your own keystore file. 98 | // see https://reactnative.dev/docs/signed-apk-android. 99 | signingConfig signingConfigs.debug 100 | minifyEnabled enableProguardInReleaseBuilds 101 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 102 | } 103 | } 104 | } 105 | 106 | dependencies { 107 | // The version of react-native is set by the React Native Gradle Plugin 108 | implementation("com.facebook.react:react-android") 109 | 110 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") 111 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 112 | exclude group:'com.squareup.okhttp3', module:'okhttp' 113 | } 114 | 115 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") 116 | if (hermesEnabled.toBoolean()) { 117 | implementation("com.facebook.react:hermes-android") 118 | } else { 119 | implementation jscFlavor 120 | } 121 | } 122 | 123 | apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 124 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-orientation-manager", 3 | "version": "1.1.2", 4 | "description": "A React Native module to retrieve interface/device orientation, listen to orientation changes, and lock screen to a specific orientation.", 5 | "main": "lib/commonjs/index", 6 | "module": "lib/module/index", 7 | "types": "lib/typescript/src/index.d.ts", 8 | "react-native": "src/index", 9 | "source": "src/index", 10 | "files": [ 11 | "src", 12 | "lib", 13 | "android", 14 | "ios", 15 | "windows", 16 | "cpp", 17 | "*.podspec", 18 | "!ios/build", 19 | "!android/build", 20 | "!android/gradle", 21 | "!android/gradlew", 22 | "!android/gradlew.bat", 23 | "!android/local.properties", 24 | "!**/__tests__", 25 | "!**/__fixtures__", 26 | "!**/__mocks__", 27 | "!**/.*" 28 | ], 29 | "scripts": { 30 | "example": "yarn workspace react-native-orientation-manager-example", 31 | "test": "jest", 32 | "typecheck": "tsc --noEmit", 33 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 34 | "prepare": "bob build", 35 | "release": "release-it" 36 | }, 37 | "keywords": [ 38 | "react-native", 39 | "ios", 40 | "android", 41 | "windows", 42 | "orientation" 43 | ], 44 | "repository": "https://github.com/KroosX4V/react-native-orientation-manager", 45 | "author": "KroosX4V ", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/KroosX4V/react-native-orientation-manager/issues" 49 | }, 50 | "homepage": "https://github.com/KroosX4V/react-native-orientation-manager#readme", 51 | "publishConfig": { 52 | "registry": "https://registry.npmjs.org/" 53 | }, 54 | "devDependencies": { 55 | "@commitlint/config-conventional": "^17.0.2", 56 | "@react-native/eslint-config": "^0.72.2", 57 | "@release-it/conventional-changelog": "^5.0.0", 58 | "@types/jest": "^28.1.2", 59 | "@types/react": "18.2.25", 60 | "@types/react-native": "0.72.3", 61 | "commitlint": "^17.0.2", 62 | "eslint": "^8.4.1", 63 | "eslint-config-prettier": "^8.5.0", 64 | "eslint-plugin-prettier": "^4.0.0", 65 | "jest": "^28.1.1", 66 | "prettier": "^2.0.5", 67 | "react": "18.2.0", 68 | "react-native": "0.72.5", 69 | "react-native-builder-bob": "^0.20.0", 70 | "react-native-windows": "0.72.10", 71 | "release-it": "^15.0.0", 72 | "typescript": "^5.0.2" 73 | }, 74 | "peerDependencies": { 75 | "@react-navigation/native": ">= 5.0.0", 76 | "react": "*", 77 | "react-native": "*" 78 | }, 79 | "peerDependenciesMeta": { 80 | "@react-navigation/native": { 81 | "optional": true 82 | } 83 | }, 84 | "resolutions": { 85 | "@react-navigation/stack@6.3.17": "patch:@react-navigation/stack@npm%3A6.3.17#./.yarn/patches/@react-navigation-stack-npm-6.3.17-4ef7cbe143.patch", 86 | "react-native-screens@3.24.0": "patch:react-native-screens@npm%3A3.24.0#./.yarn/patches/react-native-screens-npm-3.24.0-0887b5e74d.patch" 87 | }, 88 | "workspaces": [ 89 | "example" 90 | ], 91 | "packageManager": "yarn@3.6.1", 92 | "engines": { 93 | "node": ">= 16.0.0" 94 | }, 95 | "jest": { 96 | "preset": "react-native", 97 | "modulePathIgnorePatterns": [ 98 | "/example/node_modules", 99 | "/lib/" 100 | ] 101 | }, 102 | "commitlint": { 103 | "extends": [ 104 | "@commitlint/config-conventional" 105 | ] 106 | }, 107 | "release-it": { 108 | "git": { 109 | "commitMessage": "chore: release ${version}", 110 | "tagName": "v${version}" 111 | }, 112 | "npm": { 113 | "publish": true 114 | }, 115 | "github": { 116 | "release": true 117 | }, 118 | "plugins": { 119 | "@release-it/conventional-changelog": { 120 | "preset": "angular" 121 | } 122 | } 123 | }, 124 | "eslintConfig": { 125 | "root": true, 126 | "extends": [ 127 | "@react-native", 128 | "prettier" 129 | ], 130 | "rules": { 131 | "prettier/prettier": [ 132 | "error", 133 | { 134 | "quoteProps": "consistent", 135 | "singleQuote": false, 136 | "tabWidth": 4, 137 | "trailingComma": "es5", 138 | "useTabs": false 139 | } 140 | ] 141 | } 142 | }, 143 | "eslintIgnore": [ 144 | "node_modules/", 145 | "lib/" 146 | ], 147 | "prettier": { 148 | "quoteProps": "consistent", 149 | "singleQuote": false, 150 | "tabWidth": 4, 151 | "trailingComma": "es5", 152 | "useTabs": false 153 | }, 154 | "react-native-builder-bob": { 155 | "source": "src", 156 | "output": "lib", 157 | "targets": [ 158 | "commonjs", 159 | "module", 160 | [ 161 | "typescript", 162 | { 163 | "project": "tsconfig.build.json" 164 | } 165 | ] 166 | ] 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /android/src/main/java/com/kroosx4v/orientationmanager/DeviceOrientationListener.java: -------------------------------------------------------------------------------- 1 | package com.kroosx4v.orientationmanager; 2 | 3 | import android.content.Context; 4 | import android.hardware.Sensor; 5 | import android.hardware.SensorEvent; 6 | import android.hardware.SensorEventListener; 7 | import android.hardware.SensorManager; 8 | import android.os.Handler; 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | abstract class DeviceOrientationListener 13 | { 14 | private boolean mEnabled = false; 15 | 16 | @NonNull 17 | private final SensorManager mSensorManager; 18 | 19 | @Nullable 20 | private final Sensor mAccelerometer; 21 | 22 | @Nullable 23 | private DeviceOrientationEventListener mOrientationListener; 24 | 25 | @Nullable 26 | private Handler mHandler; 27 | 28 | @NonNull 29 | private DeviceOrientation mLastReportedOrientation = DeviceOrientation.UNKNOWN; 30 | 31 | @NonNull 32 | private final DeviceOrientation[] mLastThreeOrientations = new DeviceOrientation[] { 33 | DeviceOrientation.UNKNOWN, 34 | DeviceOrientation.UNKNOWN, 35 | DeviceOrientation.UNKNOWN, 36 | }; 37 | 38 | private boolean mLastOrientationRepeated = false; 39 | 40 | public DeviceOrientationListener(@NonNull Context context, @NonNull Handler handler) 41 | { 42 | mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); 43 | mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); 44 | 45 | if (mAccelerometer != null) 46 | { 47 | mOrientationListener = new DeviceOrientationEventListener(); 48 | mHandler = handler; 49 | } 50 | } 51 | 52 | public void enable() 53 | { 54 | synchronized (this) 55 | { 56 | if (mEnabled || mAccelerometer == null) return; 57 | 58 | mSensorManager.registerListener(mOrientationListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL, mHandler); 59 | mEnabled = true; 60 | } 61 | } 62 | 63 | public void disable() 64 | { 65 | synchronized (this) 66 | { 67 | if (!mEnabled) return; 68 | 69 | mSensorManager.unregisterListener(mOrientationListener); 70 | mEnabled = false; 71 | } 72 | } 73 | 74 | @Override 75 | protected void finalize() 76 | { 77 | disable(); 78 | } 79 | 80 | private class DeviceOrientationEventListener implements SensorEventListener 81 | { 82 | public void onSensorChanged(SensorEvent event) 83 | { 84 | double gForce; 85 | DeviceOrientation orientation = DeviceOrientation.UNKNOWN; 86 | { 87 | final float x = event.values[0]; 88 | final float y = event.values[1]; 89 | final float z = event.values[2]; 90 | 91 | final float gForceZ = z / SensorManager.GRAVITY_EARTH; 92 | 93 | { 94 | final float gForceX = x / SensorManager.GRAVITY_EARTH; 95 | final float gForceY = y / SensorManager.GRAVITY_EARTH; 96 | 97 | gForce = Math.sqrt(gForceX * gForceX + gForceY * gForceY + gForceZ * gForceZ); 98 | if (gForce > 1.5) return; 99 | } 100 | 101 | if ((x * x + y * y) * 4 >= z * z) 102 | { 103 | int orientationInDegrees = normalizeDegree(90 - Math.round((float)Math.atan2(y, -x) * 57.29577957855f)); 104 | 105 | if (orientationInDegrees >= 350 || orientationInDegrees <= 10) orientation = DeviceOrientation.PORTRAIT; 106 | else if (orientationInDegrees >= 80 && orientationInDegrees <= 100) orientation = DeviceOrientation.LANDSCAPE_RIGHT; 107 | else if (orientationInDegrees >= 170 && orientationInDegrees <= 190) orientation = DeviceOrientation.PORTRAIT_UPSIDE_DOWN; 108 | else if (orientationInDegrees >= 260 && orientationInDegrees <= 280) orientation = DeviceOrientation.LANDSCAPE_LEFT; 109 | } 110 | 111 | if (orientation == DeviceOrientation.UNKNOWN) 112 | { 113 | int inclination = normalizeDegree((int)Math.round(Math.toDegrees(Math.acos(gForceZ)))); 114 | if (inclination <= 25 || inclination >= 155) orientation = z > 0 ? DeviceOrientation.FACE_UP : DeviceOrientation.FACE_DOWN; 115 | } 116 | } 117 | 118 | orientation = decideNewOrientation(orientation, gForce); 119 | 120 | if (orientation != mLastReportedOrientation) 121 | { 122 | mLastReportedOrientation = orientation; 123 | onOrientationChanged(); 124 | } 125 | } 126 | 127 | public void onAccuracyChanged(Sensor sensor, int accuracy) {} 128 | } 129 | 130 | public DeviceOrientation getLastOrientation() 131 | { 132 | return mLastReportedOrientation; 133 | } 134 | 135 | abstract protected void onOrientationChanged(); 136 | 137 | private DeviceOrientation decideNewOrientation(DeviceOrientation calculatedOrientation, double gForce) 138 | { 139 | if (calculatedOrientation == mLastThreeOrientations[mLastThreeOrientations.length - 1] && !mLastOrientationRepeated) 140 | { 141 | mLastOrientationRepeated = true; 142 | return mLastReportedOrientation; 143 | } 144 | 145 | mLastOrientationRepeated = false; 146 | 147 | for (int i = 1; i < mLastThreeOrientations.length; ++i) mLastThreeOrientations[i - 1] = mLastThreeOrientations[i]; 148 | mLastThreeOrientations[mLastThreeOrientations.length - 1] = calculatedOrientation; 149 | 150 | for (DeviceOrientation orientationToStabilize : new DeviceOrientation[] { DeviceOrientation.FACE_UP, DeviceOrientation.FACE_DOWN }) 151 | { 152 | if (calculatedOrientation != orientationToStabilize) continue; 153 | 154 | second: 155 | for (DeviceOrientation orientationToStabilize2 : new DeviceOrientation[] { DeviceOrientation.PORTRAIT, DeviceOrientation.PORTRAIT_UPSIDE_DOWN }) 156 | { 157 | boolean orientationPresent = false; 158 | 159 | for (DeviceOrientation orientation : mLastThreeOrientations) 160 | { 161 | if (orientation != orientationToStabilize && orientation != orientationToStabilize2 && orientation != DeviceOrientation.UNKNOWN) 162 | { 163 | continue second; 164 | } 165 | 166 | if (!orientationPresent && orientation == orientationToStabilize2) orientationPresent = true; 167 | } 168 | 169 | if (orientationPresent) return orientationToStabilize2; 170 | } 171 | } 172 | 173 | if (calculatedOrientation == DeviceOrientation.UNKNOWN && gForce <= 1.15) 174 | { 175 | DeviceOrientation onlyOrientation = mLastThreeOrientations[0]; 176 | 177 | for (int i = 1; i < mLastThreeOrientations.length - 1; ++i) 178 | { 179 | if (mLastThreeOrientations[i] == DeviceOrientation.UNKNOWN) continue; 180 | 181 | if (onlyOrientation == DeviceOrientation.UNKNOWN) 182 | { 183 | onlyOrientation = mLastThreeOrientations[i]; 184 | } 185 | else if (onlyOrientation != mLastThreeOrientations[i]) 186 | { 187 | onlyOrientation = DeviceOrientation.UNKNOWN; 188 | break; 189 | } 190 | } 191 | 192 | if (onlyOrientation != DeviceOrientation.UNKNOWN) return onlyOrientation; 193 | } 194 | 195 | return calculatedOrientation; 196 | } 197 | 198 | private int normalizeDegree(int degree) 199 | { 200 | if (degree >= 360) 201 | { 202 | degree %= 360; 203 | } 204 | else if (degree < 0) 205 | { 206 | degree = degree % 360; 207 | if (degree < 0) degree += 360; 208 | } 209 | 210 | return degree; 211 | } 212 | } -------------------------------------------------------------------------------- /example/windows/RNOrientationManagerExample/RNOrientationManagerExample.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | true 8 | true 9 | {b0082212-a15d-4132-a08d-8e0e7086e89b} 10 | RNOrientationManagerExample 11 | RNOrientationManagerExample 12 | en-US 13 | 17.0 14 | true 15 | Windows Store 16 | 10.0 17 | 18 | 19 | $([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ 20 | 21 | 22 | 23 | 24 | 25 | Debug 26 | ARM64 27 | 28 | 29 | Debug 30 | Win32 31 | 32 | 33 | Debug 34 | x64 35 | 36 | 37 | Release 38 | ARM64 39 | 40 | 41 | Release 42 | Win32 43 | 44 | 45 | Release 46 | x64 47 | 48 | 49 | 50 | Application 51 | Unicode 52 | 53 | 54 | true 55 | true 56 | 57 | 58 | false 59 | true 60 | false 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Use 77 | pch.h 78 | $(IntDir)pch.pch 79 | Level4 80 | %(AdditionalOptions) /bigobj 81 | 4453;28204 82 | 83 | 84 | 85 | 86 | _DEBUG;%(PreprocessorDefinitions) 87 | 88 | 89 | 90 | 91 | NDEBUG;%(PreprocessorDefinitions) 92 | 93 | 94 | 95 | 96 | MainPage.xaml 97 | Code 98 | 99 | 100 | 101 | 102 | 103 | App.xaml 104 | 105 | 106 | 107 | 108 | Designer 109 | 110 | 111 | 112 | 113 | Designer 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | MainPage.xaml 128 | Code 129 | 130 | 131 | 132 | 133 | Create 134 | 135 | 136 | App.xaml 137 | 138 | 139 | 140 | 141 | 142 | App.xaml 143 | 144 | 145 | MainPage.xaml 146 | Code 147 | 148 | 149 | 150 | 151 | 152 | false 153 | 154 | 155 | 156 | 157 | Designer 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | This project references targets in your node_modules\react-native-windows folder that are missing. The missing file is {0}. 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/RNOrientationManager.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | true 8 | true 9 | {9a1ff53c-8b9a-4c51-9937-aefb0b8f3e62} 10 | RNOrientationManager 11 | RNOrientationManager 12 | en-US 13 | 17.0 14 | true 15 | Windows Store 16 | 10.0 17 | 18 | 19 | $([MSBuild]::GetDirectoryNameOfFileAbove($(SolutionDir), 'node_modules\react-native-windows\package.json'))\node_modules\react-native-windows\ 20 | 21 | 22 | 23 | 10.0.19041.0 24 | 10.0.17763.0 25 | 26 | 27 | 28 | 29 | Debug 30 | ARM64 31 | 32 | 33 | Debug 34 | Win32 35 | 36 | 37 | Debug 38 | x64 39 | 40 | 41 | Release 42 | ARM64 43 | 44 | 45 | Release 46 | Win32 47 | 48 | 49 | Release 50 | x64 51 | 52 | 53 | 54 | DynamicLibrary 55 | Unicode 56 | false 57 | 58 | 59 | true 60 | true 61 | 62 | 63 | false 64 | true 65 | false 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Use 82 | pch.h 83 | $(IntDir)pch.pch 84 | Level4 85 | %(AdditionalOptions) /bigobj 86 | 4453;28204 87 | _WINRT_DLL;%(PreprocessorDefinitions) 88 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) 89 | 90 | 91 | 92 | %(AdditionalOptions) /noattributename 93 | 94 | 95 | Console 96 | true 97 | RNOrientationManager.def 98 | 99 | 100 | 101 | 102 | _DEBUG;%(PreprocessorDefinitions) 103 | 104 | 105 | 106 | 107 | NDEBUG;%(PreprocessorDefinitions) 108 | 109 | 110 | 111 | 112 | ReactPackageProvider.idl 113 | 114 | 115 | 116 | 117 | 118 | 119 | Create 120 | 121 | 122 | ReactPackageProvider.idl 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | This project references targets in your node_modules\react-native-windows folder that are missing. The missing file is {0}. 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /windows/RNOrientationManager/RNOrientationManager.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | #include "RNOrientationManager.h" 3 | 4 | namespace RNOrientationManager 5 | { 6 | void Module::Initialize(React::ReactContext const& reactContext) noexcept 7 | { 8 | m_reactContext = reactContext; 9 | 10 | Module::UIDispatch( 11 | [&] { 12 | m_displayInformation = DisplayInformation::GetForCurrentView(); 13 | m_uiViewSettings = UIViewSettings::GetForCurrentView(); 14 | 15 | m_lastInterfaceOrientation = Module::GetInterfaceOrientation(); 16 | 17 | m_defaultDisplayOrientations = std::unique_ptr( 18 | new DisplayOrientations{ m_displayInformation.AutoRotationPreferences() } 19 | ); 20 | }, 21 | true 22 | ); 23 | 24 | m_simpleOrientationSensor = SimpleOrientationSensor::GetDefault(); 25 | m_lastDeviceOrientation = Module::GetDeviceOrientation(); 26 | } 27 | 28 | void Module::GetConstants(React::ReactConstantProvider& provider) noexcept 29 | { 30 | provider.Add(L"initialInterfaceOrientationValue", static_cast(m_lastInterfaceOrientation)); 31 | provider.Add(L"initialDeviceOrientationValue", static_cast(m_lastDeviceOrientation)); 32 | } 33 | 34 | void Module::AddListener(std::string eventName) noexcept 35 | { 36 | if (eventName == "interfaceOrientationChanged") 37 | { 38 | Module::UIDispatch([&] { 39 | m_displayInformation.OrientationChanged({ this, &Module::OnDisplayOrientationChanged }); 40 | Module::SendInterfaceOrientationChangedIfOccurred(); 41 | }); 42 | } 43 | else if (m_simpleOrientationSensor) 44 | { 45 | m_simpleOrientationSensor.OrientationChanged({ this, &Module::OnSimpleOrientationChanged }); 46 | Module::SendDeviceOrientationChangedIfOccurred(); 47 | } 48 | } 49 | 50 | void Module::RemoveListeners(int) noexcept {} 51 | 52 | void Module::LockToPortrait(React::ReactPromise&& promise) noexcept 53 | { 54 | Module::SetAutoRotationPreferences(std::move(promise), DisplayOrientations::Portrait); 55 | } 56 | 57 | void Module::LockToPortraitUpsideDown(React::ReactPromise&& promise) noexcept 58 | { 59 | Module::SetAutoRotationPreferences(std::move(promise), DisplayOrientations::PortraitFlipped); 60 | } 61 | 62 | void Module::LockToLandscapeLeft(React::ReactPromise && promise) noexcept 63 | { 64 | Module::SetAutoRotationPreferences(std::move(promise), DisplayOrientations::Landscape); 65 | } 66 | 67 | void Module::LockToLandscapeRight(React::ReactPromise && promise) noexcept 68 | { 69 | Module::SetAutoRotationPreferences(std::move(promise), DisplayOrientations::LandscapeFlipped); 70 | } 71 | 72 | void Module::LockToLandscape(React::ReactPromise && promise) noexcept 73 | { 74 | Module::SetAutoRotationPreferences(std::move(promise), DisplayOrientations::Landscape | DisplayOrientations::LandscapeFlipped); 75 | } 76 | 77 | void Module::LockToAllOrientationsButUpsideDown(React::ReactPromise&& promise) noexcept 78 | { 79 | Module::SetAutoRotationPreferences( 80 | std::move(promise), 81 | DisplayOrientations::Portrait | DisplayOrientations::Landscape | DisplayOrientations::LandscapeFlipped 82 | ); 83 | } 84 | 85 | void Module::UnlockAllOrientations(React::ReactPromise&& promise) noexcept 86 | { 87 | Module::SetAutoRotationPreferences( 88 | std::move(promise), 89 | DisplayOrientations::Portrait | DisplayOrientations::PortraitFlipped | DisplayOrientations::Landscape 90 | | DisplayOrientations::LandscapeFlipped 91 | ); 92 | } 93 | 94 | void Module::ResetInterfaceOrientationSetting(React::ReactPromise&& promise) noexcept 95 | { 96 | Module::SetAutoRotationPreferences(std::move(promise), *m_defaultDisplayOrientations); 97 | } 98 | 99 | void Module::SetAutoRotationPreferences(React::ReactPromise&& promise, DisplayOrientations displayOrientations) 100 | { 101 | Module::UIDispatch([&, promise = std::move(promise)]() noexcept { 102 | try 103 | { 104 | if 105 | ( 106 | m_uiViewSettings.UserInteractionMode() == UserInteractionMode::Touch 107 | && m_displayInformation.AutoRotationPreferences() != displayOrientations 108 | ) DisplayInformation::AutoRotationPreferences(displayOrientations); 109 | 110 | promise.Resolve(); 111 | } 112 | catch (...) 113 | { 114 | promise.Reject("Unknown error"); 115 | } 116 | }); 117 | } 118 | 119 | void Module::SendInterfaceOrientationChangedIfOccurred() 120 | { 121 | Module::UIDispatch([&] { 122 | InterfaceOrientation interfaceOrientation = Module::GetInterfaceOrientation(); 123 | if (interfaceOrientation == m_lastInterfaceOrientation) return; 124 | 125 | m_lastInterfaceOrientation = interfaceOrientation; 126 | 127 | InterfaceOrientationChanged(React::JSValueObject{ 128 | { "interfaceOrientationValue", static_cast(interfaceOrientation) } 129 | }); 130 | }); 131 | } 132 | 133 | void Module::SendDeviceOrientationChangedIfOccurred() 134 | { 135 | Module::SendDeviceOrientationChangedIfOccurred(Module::GetDeviceOrientation()); 136 | } 137 | 138 | void Module::SendDeviceOrientationChangedIfOccurred(DeviceOrientation deviceOrientation) 139 | { 140 | std::lock_guard lock{ m_checkingForDeviceOrientationChangeMutex }; 141 | 142 | if (deviceOrientation == m_lastDeviceOrientation) return; 143 | m_lastDeviceOrientation = deviceOrientation; 144 | 145 | DeviceOrientationChanged(React::JSValueObject{ 146 | { "deviceOrientationValue", static_cast(deviceOrientation) } 147 | }); 148 | } 149 | 150 | void Module::OnSimpleOrientationChanged(SimpleOrientationSensor const&, SimpleOrientationSensorOrientationChangedEventArgs const& args) 151 | { 152 | Module::SendDeviceOrientationChangedIfOccurred(Module::SimpleOrientationToDeviceOrientation(args.Orientation())); 153 | } 154 | 155 | void Module::OnDisplayOrientationChanged(DisplayInformation const&, IInspectable const&) 156 | { 157 | Module::SendInterfaceOrientationChangedIfOccurred(); 158 | } 159 | 160 | InterfaceOrientation Module::GetInterfaceOrientation() 161 | { 162 | return Module::DisplayOrientationToInterfaceOrientation(m_displayInformation.CurrentOrientation()); 163 | } 164 | 165 | DeviceOrientation Module::GetDeviceOrientation() 166 | { 167 | return m_simpleOrientationSensor 168 | ? 169 | Module::SimpleOrientationToDeviceOrientation(m_simpleOrientationSensor.GetCurrentOrientation()) 170 | : 171 | DeviceOrientation::Unknown 172 | ; 173 | } 174 | 175 | void Module::UIDispatch(std::function&& func, bool waitIfPosted) 176 | { 177 | if (m_reactContext.UIDispatcher().HasThreadAccess()) 178 | { 179 | func(); 180 | } 181 | else if (waitIfPosted) 182 | { 183 | std::promise promise; 184 | 185 | m_reactContext.UIDispatcher().Post([&]() noexcept { 186 | func(); 187 | promise.set_value(); 188 | }); 189 | 190 | promise.get_future().wait(); 191 | } 192 | else 193 | { 194 | m_reactContext.UIDispatcher().Post([func = std::move(func), weakThis = weak_from_this()] { 195 | std::shared_ptr sharedThis = weakThis.lock(); 196 | if (sharedThis) func(); 197 | }); 198 | } 199 | } 200 | 201 | InterfaceOrientation Module::DisplayOrientationToInterfaceOrientation(DisplayOrientations displayOrientation) 202 | { 203 | switch (displayOrientation) 204 | { 205 | case DisplayOrientations::Portrait: 206 | return InterfaceOrientation::Portrait; 207 | 208 | case DisplayOrientations::PortraitFlipped: 209 | return InterfaceOrientation::PortraitUpsideDown; 210 | 211 | case DisplayOrientations::Landscape: 212 | return InterfaceOrientation::LandscapeLeft; 213 | 214 | case DisplayOrientations::LandscapeFlipped: 215 | return InterfaceOrientation::LandscapeRight; 216 | 217 | default: 218 | return InterfaceOrientation::Unknown; 219 | } 220 | } 221 | 222 | DeviceOrientation Module::SimpleOrientationToDeviceOrientation(SimpleOrientation simpleOrientation) 223 | { 224 | switch (simpleOrientation) 225 | { 226 | case SimpleOrientation::NotRotated: 227 | return DeviceOrientation::LandscapeLeft; 228 | 229 | case SimpleOrientation::Rotated90DegreesCounterclockwise: 230 | return DeviceOrientation::PortraitUpsideDown; 231 | 232 | case SimpleOrientation::Rotated180DegreesCounterclockwise: 233 | return DeviceOrientation::LandscapeRight; 234 | 235 | case SimpleOrientation::Rotated270DegreesCounterclockwise: 236 | return DeviceOrientation::Portrait; 237 | 238 | case SimpleOrientation::Faceup: 239 | return DeviceOrientation::FaceUp; 240 | 241 | case SimpleOrientation::Facedown: 242 | return DeviceOrientation::FaceDown; 243 | 244 | default: 245 | return DeviceOrientation::Unknown; 246 | } 247 | } 248 | } --------------------------------------------------------------------------------