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