1import { Audio, AudioMode, InterruptionModeIOS } from 'expo-av';
2import React from 'react';
3import { PixelRatio, Switch, Text, View } from 'react-native';
4
5import Button from '../../components/Button';
6import ListButton from '../../components/ListButton';
7
8interface State {
9  modeToSet: Partial<AudioMode>;
10  setMode: Partial<AudioMode>;
11}
12
13export default class AudioModeSelector extends React.Component<object, State> {
14  readonly state: State = {
15    modeToSet: {
16      interruptionModeIOS: InterruptionModeIOS.MixWithOthers,
17      playsInSilentModeIOS: false,
18      allowsRecordingIOS: false,
19      staysActiveInBackground: false,
20    },
21    setMode: {
22      interruptionModeIOS: InterruptionModeIOS.MixWithOthers,
23      playsInSilentModeIOS: false,
24      allowsRecordingIOS: false,
25      staysActiveInBackground: false,
26    },
27  };
28
29  _applyMode = async () => {
30    try {
31      await Audio.setAudioModeAsync({ ...this.state.modeToSet });
32      const { modeToSet } = this.state;
33      this.setState({ setMode: modeToSet });
34    } catch (error) {
35      alert(error.message);
36    }
37  };
38
39  _modesEqual = (modeA: Partial<AudioMode>, modeB: Partial<AudioMode>) =>
40    modeA.interruptionModeIOS === modeB.interruptionModeIOS &&
41    modeA.playsInSilentModeIOS === modeB.playsInSilentModeIOS &&
42    modeA.allowsRecordingIOS === modeB.allowsRecordingIOS &&
43    modeA.staysActiveInBackground === modeB.staysActiveInBackground;
44
45  _setMode = (interruptionModeIOS: number) => () =>
46    this.setState((state) => ({ modeToSet: { ...state.modeToSet, interruptionModeIOS } }));
47
48  _renderToggle = ({
49    title,
50    disabled,
51    valueName,
52    value,
53  }: {
54    title: string;
55    disabled?: boolean;
56    valueName: keyof AudioMode;
57    value?: boolean;
58  }) => (
59    <View
60      style={{
61        flexDirection: 'row',
62        alignItems: 'center',
63        justifyContent: 'space-between',
64        paddingVertical: 5,
65        borderBottomWidth: 1.0 / PixelRatio.get(),
66        borderBottomColor: '#cccccc',
67      }}>
68      <Text style={{ flex: 1, fontSize: 16 }}>{title}</Text>
69      <Switch
70        disabled={disabled}
71        value={value !== undefined ? value : Boolean(this.state.modeToSet[valueName])}
72        onValueChange={() =>
73          this.setState((state) => ({
74            modeToSet: { ...state.modeToSet, [valueName]: !state.modeToSet[valueName] },
75          }))
76        }
77      />
78    </View>
79  );
80
81  _renderModeSelector = ({
82    title,
83    disabled,
84    value,
85  }: {
86    title: string;
87    disabled?: boolean;
88    value: number;
89  }) => (
90    <ListButton
91      disabled={disabled}
92      title={`${this.state.modeToSet.interruptionModeIOS === value ? '✓ ' : ''}${title}`}
93      onPress={this._setMode(value)}
94    />
95  );
96
97  render() {
98    return (
99      <View style={{ marginTop: 5 }}>
100        {this._renderToggle({
101          title: 'Plays in silent mode',
102          valueName: 'playsInSilentModeIOS',
103        })}
104        {this._renderToggle({
105          title: 'Allows recording',
106          valueName: 'allowsRecordingIOS',
107          disabled: !this.state.modeToSet.playsInSilentModeIOS,
108          value: !this.state.modeToSet.playsInSilentModeIOS ? false : undefined,
109        })}
110        {this._renderToggle({
111          title: 'Stay active in background',
112          valueName: 'staysActiveInBackground',
113          disabled: !this.state.modeToSet.playsInSilentModeIOS,
114          value: !this.state.modeToSet.playsInSilentModeIOS ? false : undefined,
115        })}
116        {this._renderModeSelector({
117          title: 'Mix with others',
118          value: InterruptionModeIOS.MixWithOthers,
119        })}
120        {this._renderModeSelector({
121          title: 'Do not mix',
122          value: InterruptionModeIOS.DoNotMix,
123        })}
124        {this._renderModeSelector({
125          disabled: this.state.modeToSet.playsInSilentModeIOS === false,
126          title: 'Duck others',
127          value: InterruptionModeIOS.DuckOthers,
128        })}
129        <Button
130          title="Apply changes"
131          onPress={this._applyMode}
132          style={{ marginTop: 10 }}
133          disabled={this._modesEqual(this.state.modeToSet, this.state.setMode)}
134        />
135      </View>
136    );
137  }
138}
139