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