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