Skip to content

Commit

Permalink
fix audio attachment permission flow (#20835)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmaxim committed Nov 7, 2019
1 parent 239adcb commit 5da19e0
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 29 deletions.
13 changes: 13 additions & 0 deletions shared/actions/chat2-gen.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions shared/actions/json/chat2.json
Expand Up @@ -779,6 +779,10 @@
"conversationIDKey": "Types.ConversationIDKey",
"meteringCb": "(amp: number) => void"
},
"attemptAudioRecording": {
"conversationIDKey": "Types.ConversationIDKey",
"meteringCb": "(amp: number) => void"
},
"stopAudioRecording": {
"conversationIDKey": "Types.ConversationIDKey",
"stopType": "Types.AudioStopType",
Expand Down
74 changes: 55 additions & 19 deletions shared/actions/platform-specific/index.native.tsx
Expand Up @@ -58,24 +58,27 @@ const requestPermissionsToWrite = async () => {
}

export const requestAudioPermission = async () => {
if (isIOS) {
const {Permissions} = require('react-native-unimodules')
const {status} = await Permissions.getAsync(Permissions.AUDIO_RECORDING)
if (status === Permissions.PermissionStatus.DENIED) {
throw new Error('Please allow Keybase to access the microphone in the phone settings.')
let chargeForward = true
const {Permissions} = require('react-native-unimodules')
let {status} = await Permissions.getAsync(Permissions.AUDIO_RECORDING)
if (status === Permissions.PermissionStatus.UNDETERMINED) {
if (isIOS) {
const askRes = await Permissions.askAsync(Permissions.AUDIO_RECORDING)
status = askRes.status
} else {
const askRes = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO)
switch (askRes) {
case 'never_ask_again':
case 'denied':
status = Permissions.PermissionStatus.DENIED
}
}
chargeForward = false
}
if (isAndroid) {
const permissionStatus = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, {
buttonNegative: 'Cancel',
buttonPositive: 'OK',
message: 'Keybase needs access to your microphone to send an audio attachment',
title: 'Keybase Record Audio Permission',
})
if (permissionStatus !== 'granted') {
throw new Error('Unable to acquire record audio permissions')
}
if (status === Permissions.PermissionStatus.DENIED) {
throw new Error('Please allow Keybase to access the microphone in the phone settings.')
}
return chargeForward
}

export const requestLocationPermission = async (mode: RPCChatTypes.UIWatchPositionPerm) => {
Expand Down Expand Up @@ -620,7 +623,7 @@ function* setupLocationUpdateLoop() {
}
}

const setLocationDeniedCommandStatus = (conversationIDKey: Types.ConversationIDKey, text: string) =>
const setPermissionDeniedCommandStatus = (conversationIDKey: Types.ConversationIDKey, text: string) =>
Chat2Gen.createSetCommandStatusInfo({
conversationIDKey,
info: {
Expand All @@ -640,7 +643,7 @@ const onChatWatchPosition = async (
await requestLocationPermission(action.payload.params.perm)
} catch (err) {
logger.info('failed to get location perms: ' + err)
return setLocationDeniedCommandStatus(
return setPermissionDeniedCommandStatus(
Types.conversationIDToKey(action.payload.params.convID),
`Failed to access location. ${err.message}`
)
Expand All @@ -659,7 +662,7 @@ const onChatWatchPosition = async (
logger.warn(err.message)
if (err.code && err.code === 1 && locationEmitter) {
locationEmitter(
setLocationDeniedCommandStatus(
setPermissionDeniedCommandStatus(
Types.conversationIDToKey(action.payload.params.convID),
`Failed to access location. ${err.message}`
)
Expand Down Expand Up @@ -767,6 +770,30 @@ const stopAudioRecording = async (
return Chat2Gen.createSendAudioRecording({conversationIDKey, fromStaged: false, info: audio})
}

const onAttemptAudioRecording = async (
_: Container.TypedState,
action: Chat2Gen.AttemptAudioRecordingPayload,
logger: Saga.SagaLogger
) => {
let chargeForward = true
try {
chargeForward = await requestAudioPermission()
} catch (err) {
logger.info('failed to get audio perms: ' + err)
return setPermissionDeniedCommandStatus(
action.payload.conversationIDKey,
`Failed to access audio. ${err.message}`
)
}
if (!chargeForward) {
return false
}
return Chat2Gen.createEnableAudioRecording({
conversationIDKey: action.payload.conversationIDKey,
meteringCb: action.payload.meteringCb,
})
}

const onEnableAudioRecording = async (
state: Container.TypedState,
action: Chat2Gen.EnableAudioRecordingPayload,
Expand All @@ -781,7 +808,15 @@ const onEnableAudioRecording = async (

Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
const outboxID = ChatConstants.generateOutboxID()
await requestAudioPermission()
try {
await requestAudioPermission()
} catch (err) {
logger.info('failed to get audio perms: ' + err)
return setPermissionDeniedCommandStatus(
action.payload.conversationIDKey,
`Failed to access audio. ${err.message}`
)
}
const audioPath = await RPCChatTypes.localGetUploadTempFileRpcPromise({filename: 'audio.m4a', outboxID})
AudioRecorder.prepareRecordingAtPath(audioPath, {
AudioEncoding: 'aac',
Expand Down Expand Up @@ -865,6 +900,7 @@ export function* platformConfigSaga() {

// Audio
yield* Saga.chainAction2(Chat2Gen.stopAudioRecording, stopAudioRecording, 'stopAudioRecording')
yield* Saga.chainAction2(Chat2Gen.attemptAudioRecording, onAttemptAudioRecording, 'onAttemptAudioRecording')
yield* Saga.chainAction2(Chat2Gen.enableAudioRecording, onEnableAudioRecording, 'onEnableAudioRecording')
yield* Saga.chainAction2(Chat2Gen.sendAudioRecording, onSendAudioRecording, 'onSendAudioRecording')
yield* Saga.chainAction2(
Expand Down
1 change: 1 addition & 0 deletions shared/actions/typed-actions-gen.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion shared/chat/audio/audio-recorder.native.tsx
Expand Up @@ -54,7 +54,7 @@ const AudioRecorder = (props: Props) => {
}, [dispatch, conversationIDKey])
const enableRecording = React.useCallback(() => {
ampTracker.reset()
dispatch(Chat2Gen.createEnableAudioRecording({conversationIDKey, meteringCb}))
dispatch(Chat2Gen.createAttemptAudioRecording({conversationIDKey, meteringCb}))
}, [dispatch, conversationIDKey])
const stopRecording = (stopType: Types.AudioStopType) => {
dispatch(
Expand Down
18 changes: 9 additions & 9 deletions shared/ios/Keybase/Info.plist
Expand Up @@ -42,14 +42,14 @@
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCalendarsUsageDescription</key>
<string>Keybase does not use this data! Please send feedback if you see this message.</string>
Expand All @@ -64,7 +64,7 @@
<key>NSLocationWhenInUseUsageDescription</key>
<string>Used for sharing your location with others on Keybase.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Recording audio when taking a video</string>
<string>Recording audio for audio and video chat attachments</string>
<key>NSMotionUsageDescription</key>
<string>Keybase does not use this data! Please send feedback if you see this message.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
Expand Down

0 comments on commit 5da19e0

Please sign in to comment.