1import { Audio, AudioMode, InterruptionModeAndroid } 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      interruptionModeAndroid: InterruptionModeAndroid.DuckOthers,
17      shouldDuckAndroid: true,
18      playThroughEarpieceAndroid: false,
19      staysActiveInBackground: false,
20    },
21    setMode: {
22      interruptionModeAndroid: InterruptionModeAndroid.DuckOthers,
23      shouldDuckAndroid: true,
24      playThroughEarpieceAndroid: 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.interruptionModeAndroid === modeB.interruptionModeAndroid &&
41    modeA.playThroughEarpieceAndroid === modeB.playThroughEarpieceAndroid &&
42    modeA.shouldDuckAndroid === modeB.shouldDuckAndroid &&
43    modeA.staysActiveInBackground === modeB.staysActiveInBackground;
44
45  _setMode = (interruptionModeAndroid: number) => () =>
46    this.setState((state) => ({ modeToSet: { ...state.modeToSet, interruptionModeAndroid } }));
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.interruptionModeAndroid === value ? '✓ ' : ''}${title}`}
93      onPress={this._setMode(value)}
94    />
95  );
96
97  render() {
98    return (
99      <View style={{ marginTop: 5 }}>
100        {this._renderToggle({
101          title: 'Should be ducked',
102          valueName: 'shouldDuckAndroid',
103        })}
104        {this._renderToggle({
105          title: 'Play through earpiece',
106          valueName: 'playThroughEarpieceAndroid',
107        })}
108        {this._renderToggle({
109          title: 'Stay active in background',
110          valueName: 'staysActiveInBackground',
111        })}
112        {this._renderModeSelector({
113          title: 'Do not mix',
114          value: InterruptionModeAndroid.DoNotMix,
115        })}
116        {this._renderModeSelector({
117          title: 'Duck others',
118          value: InterruptionModeAndroid.DuckOthers,
119        })}
120        <Button
121          title="Apply changes"
122          onPress={this._applyMode}
123          style={{ marginTop: 10 }}
124          disabled={this._modesEqual(this.state.modeToSet, this.state.setMode)}
125        />
126      </View>
127    );
128  }
129}
130