(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 | }
--------------------------------------------------------------------------------