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