您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

AudioRoutePickerDialog.js 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // @flow
  2. import _ from 'lodash';
  3. import React, { Component } from 'react';
  4. import { NativeModules, Text, TouchableHighlight, View } from 'react-native';
  5. import { hideDialog, BottomSheet } from '../../../base/dialog';
  6. import { translate } from '../../../base/i18n';
  7. import { Icon } from '../../../base/font-icons';
  8. import { connect } from '../../../base/redux';
  9. import { ColorPalette } from '../../../base/styles';
  10. import styles from './styles';
  11. /**
  12. * Type definition for a single entry in the device list.
  13. */
  14. type Device = {
  15. /**
  16. * Name of the icon which will be rendered on the right.
  17. */
  18. iconName: string,
  19. /**
  20. * True if the element is selected (will be highlighted in blue),
  21. * false otherwise.
  22. */
  23. selected: boolean,
  24. /**
  25. * Text which will be rendered in the row.
  26. */
  27. text: string,
  28. /**
  29. * Device type.
  30. */
  31. type: string
  32. };
  33. /**
  34. * {@code AudioRoutePickerDialog}'s React {@code Component} prop types.
  35. */
  36. type Props = {
  37. /**
  38. * Used for hiding the dialog when the selection was completed.
  39. */
  40. dispatch: Function,
  41. /**
  42. * Invoked to obtain translated strings.
  43. */
  44. t: Function
  45. };
  46. /**
  47. * {@code AudioRoutePickerDialog}'s React {@code Component} state types.
  48. */
  49. type State = {
  50. /**
  51. * Array of available devices.
  52. */
  53. devices: Array<Device>
  54. };
  55. const { AudioMode } = NativeModules;
  56. /**
  57. * Maps each device type to a display name and icon.
  58. */
  59. const deviceInfoMap = {
  60. BLUETOOTH: {
  61. iconName: 'bluetooth',
  62. text: 'audioDevices.bluetooth',
  63. type: 'BLUETOOTH'
  64. },
  65. EARPIECE: {
  66. iconName: 'phone-talk',
  67. text: 'audioDevices.phone',
  68. type: 'EARPIECE'
  69. },
  70. HEADPHONES: {
  71. iconName: 'headset',
  72. text: 'audioDevices.headphones',
  73. type: 'HEADPHONES'
  74. },
  75. SPEAKER: {
  76. iconName: 'volume',
  77. text: 'audioDevices.speaker',
  78. type: 'SPEAKER'
  79. }
  80. };
  81. /**
  82. * The exported React {@code Component}. {@code AudioRoutePickerDialog} is
  83. * exported only if the {@code AudioMode} module has the capability to get / set
  84. * audio devices.
  85. */
  86. let AudioRoutePickerDialog_;
  87. /**
  88. * Implements a React {@code Component} which prompts the user when a password
  89. * is required to join a conference.
  90. */
  91. class AudioRoutePickerDialog extends Component<Props, State> {
  92. state = {
  93. /**
  94. * Available audio devices, it will be set in
  95. * {@link #componentDidMount()}.
  96. */
  97. devices: []
  98. };
  99. /**
  100. * Initializes a new {@code PasswordRequiredPrompt} instance.
  101. *
  102. * @param {Props} props - The read-only React {@code Component} props with
  103. * which the new instance is to be initialized.
  104. */
  105. constructor(props: Props) {
  106. super(props);
  107. // Bind event handlers so they are only bound once per instance.
  108. this._onCancel = this._onCancel.bind(this);
  109. }
  110. /**
  111. * Initializes the device list by querying {@code AudioMode}.
  112. *
  113. * @inheritdoc
  114. */
  115. componentDidMount() {
  116. AudioMode.getAudioDevices().then(({ devices, selected }) => {
  117. const audioDevices = [];
  118. if (devices) {
  119. for (const device of devices) {
  120. if (deviceInfoMap[device]) {
  121. const info = Object.assign({}, deviceInfoMap[device]);
  122. info.selected = device === selected;
  123. info.text = this.props.t(info.text);
  124. audioDevices.push(info);
  125. }
  126. }
  127. }
  128. if (audioDevices) {
  129. // Make sure devices is alphabetically sorted.
  130. this.setState({
  131. devices: _.sortBy(audioDevices, 'text')
  132. });
  133. }
  134. });
  135. }
  136. /**
  137. * Dispatches a redux action to hide this sheet.
  138. *
  139. * @returns {void}
  140. */
  141. _hide() {
  142. this.props.dispatch(hideDialog(AudioRoutePickerDialog_));
  143. }
  144. _onCancel: () => void;
  145. /**
  146. * Cancels the dialog by hiding it.
  147. *
  148. * @private
  149. * @returns {void}
  150. */
  151. _onCancel() {
  152. this._hide();
  153. }
  154. _onSelectDeviceFn: (Device) => Function;
  155. /**
  156. * Builds and returns a function which handles the selection of a device
  157. * on the sheet. The selected device will be used by {@code AudioMode}.
  158. *
  159. * @param {Device} device - Object representing the selected device.
  160. * @private
  161. * @returns {Function}
  162. */
  163. _onSelectDeviceFn(device: Device) {
  164. return () => {
  165. this._hide();
  166. AudioMode.setAudioDevice(device.type);
  167. };
  168. }
  169. /**
  170. * Renders a single device.
  171. *
  172. * @param {Device} device - Object representing a single device.
  173. * @private
  174. * @returns {ReactElement}
  175. */
  176. _renderDevice(device: Device) {
  177. const { iconName, selected, text } = device;
  178. const selectedStyle = selected ? styles.selectedText : {};
  179. return (
  180. <TouchableHighlight
  181. key = { device.type }
  182. onPress = { this._onSelectDeviceFn(device) }
  183. underlayColor = { ColorPalette.overflowMenuItemUnderlay } >
  184. <View style = { styles.deviceRow } >
  185. <Icon
  186. name = { iconName }
  187. style = { [ styles.deviceIcon, selectedStyle ] } />
  188. <Text style = { [ styles.deviceText, selectedStyle ] } >
  189. { text }
  190. </Text>
  191. </View>
  192. </TouchableHighlight>
  193. );
  194. }
  195. /**
  196. * Implements React's {@link Component#render()}.
  197. *
  198. * @inheritdoc
  199. * @returns {ReactElement}
  200. */
  201. render() {
  202. const { devices } = this.state;
  203. if (!devices.length) {
  204. return null;
  205. }
  206. return (
  207. <BottomSheet onCancel = { this._onCancel }>
  208. { this.state.devices.map(this._renderDevice, this) }
  209. </BottomSheet>
  210. );
  211. }
  212. }
  213. // Only export the dialog if we have support for getting / setting audio devices
  214. // in AudioMode.
  215. if (AudioMode.getAudioDevices && AudioMode.setAudioDevice) {
  216. AudioRoutePickerDialog_ = translate(connect()(AudioRoutePickerDialog));
  217. }
  218. export default AudioRoutePickerDialog_;