1import { Platform, Subscription } from 'expo-modules-core';
2import * as ScreenOrientation from 'expo-screen-orientation';
3import React from 'react';
4import { ScrollView, Text, SafeAreaView } from 'react-native';
5
6import ListButton from '../components/ListButton';
7
8interface State {
9  orientation?: ScreenOrientation.Orientation;
10  orientationLock?: ScreenOrientation.OrientationLock;
11}
12
13// See: https://github.com/expo/expo/pull/10229#discussion_r490961694
14// eslint-disable-next-line @typescript-eslint/ban-types
15export default class ScreenOrientationScreen extends React.Component<{}, State> {
16  static navigationOptions = {
17    title: 'ScreenOrientation',
18  };
19
20  readonly state: State = {};
21
22  listener?: Subscription;
23
24  async componentDidMount() {
25    this.listener = ScreenOrientation.addOrientationChangeListener(
26      ({ orientationInfo, orientationLock }) => {
27        this.setState({
28          orientation: orientationInfo.orientation,
29          orientationLock,
30        });
31      }
32    );
33
34    await this.updateCurrentOrientationAndLock();
35  }
36
37  updateCurrentOrientationAndLock = async () => {
38    const [orientation, orientationLock] = await Promise.all([
39      ScreenOrientation.getOrientationAsync(),
40      ScreenOrientation.getOrientationLockAsync(),
41    ]);
42
43    // update state
44    this.setState({
45      orientation,
46      orientationLock,
47    });
48  };
49
50  updateOrientationAsync = async () => {
51    this.setState({
52      orientation: await ScreenOrientation.getOrientationAsync(),
53    });
54  };
55
56  componentWillUnmount() {
57    if (this.listener) {
58      this.listener.remove();
59    }
60  }
61
62  lock = async (orientation: ScreenOrientation.OrientationLock) => {
63    if (Platform.OS === 'web') {
64      // most web browsers require fullscreen in order to change screen orientation
65      await document.documentElement.requestFullscreen();
66    }
67
68    await ScreenOrientation.lockAsync(orientation).catch(console.warn); // on iPhoneX PortraitUpsideDown would be rejected
69
70    if (Platform.OS === 'web') {
71      await document.exitFullscreen();
72    }
73
74    await this.updateCurrentOrientationAndLock();
75  };
76
77  lockPlatformExample = async () => {
78    if (Platform.OS === 'web') {
79      // most web browsers require fullscreen in order to change screen orientation
80      await document.documentElement.requestFullscreen();
81    }
82
83    await ScreenOrientation.lockPlatformAsync({
84      screenOrientationLockWeb: ScreenOrientation.WebOrientationLock.LANDSCAPE,
85      screenOrientationArrayIOS: [
86        ScreenOrientation.Orientation.PORTRAIT_DOWN,
87        ScreenOrientation.Orientation.LANDSCAPE_RIGHT,
88      ],
89      screenOrientationConstantAndroid: 8, // reverse landscape
90    }).catch((e) => alert(e)); // on iPhoneX PortraitUpsideDown would be rejected
91
92    if (Platform.OS === 'web') {
93      await document.exitFullscreen();
94    }
95
96    await this.updateCurrentOrientationAndLock();
97  };
98
99  doesSupport = async () => {
100    const result = await ScreenOrientation.supportsOrientationLockAsync(
101      ScreenOrientation.OrientationLock.PORTRAIT_DOWN
102    ).catch(console.warn);
103    alert(`Orientation.PORTRAIT_DOWN supported: ${JSON.stringify(result)}`);
104  };
105
106  getScreenOrientationLockOptions(): {
107    key: string;
108    value: ScreenOrientation.OrientationLock;
109  }[] {
110    const orientationOptions = [
111      ScreenOrientation.OrientationLock.DEFAULT,
112      ScreenOrientation.OrientationLock.ALL,
113      ScreenOrientation.OrientationLock.PORTRAIT,
114      ScreenOrientation.OrientationLock.PORTRAIT_UP,
115      ScreenOrientation.OrientationLock.PORTRAIT_DOWN,
116      ScreenOrientation.OrientationLock.LANDSCAPE,
117      ScreenOrientation.OrientationLock.LANDSCAPE_LEFT,
118      ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT,
119    ];
120
121    return orientationOptions.map((orientation) => ({
122      key: ScreenOrientation.OrientationLock[orientation],
123      value: orientation,
124    }));
125  }
126
127  render() {
128    const { orientation, orientationLock } = this.state;
129    return (
130      <SafeAreaView>
131        <ScrollView style={{ padding: 10 }}>
132          {orientation !== undefined && (
133            <Text>Orientation: {ScreenOrientation.Orientation[orientation]}</Text>
134          )}
135          {orientationLock !== undefined && (
136            <Text>OrientationLock: {ScreenOrientation.OrientationLock[orientationLock]}</Text>
137          )}
138          {this.getScreenOrientationLockOptions().map((o) => (
139            <ListButton key={o.key} onPress={() => this.lock(o.value)} title={o.key} />
140          ))}
141          <ListButton
142            key="lockPlatformAsync Example"
143            onPress={this.lockPlatformExample}
144            title="Apply a custom native lock"
145          />
146          <ListButton
147            key="doesSupport"
148            onPress={this.doesSupport}
149            title="Check Orientation.PORTRAIT_DOWN support"
150          />
151          <ListButton
152            key="updateCurrentOrientationAndLock"
153            onPress={this.updateCurrentOrientationAndLock}
154            title="Update current orientation and lock"
155          />
156        </ScrollView>
157      </SafeAreaView>
158    );
159  }
160}
161