Skip to content

Commit

Permalink
chore: improve biometrics permission prompting. (#1301)
Browse files Browse the repository at this point in the history
Signed-off-by: Nguyen, Tom CITZ:EX <[email protected]>
Co-authored-by: Nguyen, Tom CITZ:EX <[email protected]>
  • Loading branch information
tom11-nguyen and Nguyen, Tom CITZ:EX authored Nov 6, 2024
1 parent 3cd3556 commit 4766aa2
Show file tree
Hide file tree
Showing 8 changed files with 359 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ariesbifold">

<uses-permission android:name="android.permission.USE_BIOMETRICS" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/>
Expand Down
3 changes: 3 additions & 0 deletions packages/legacy/core/App/components/buttons/ToggleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const ToggleButton: React.FC<ToggleButtonProps> = ({
testID={testID}
accessibilityLabel="Toggle Button"
accessibilityRole="switch"
accessibilityState={{
checked: isEnabled
}}
onPress={isAvailable && !disabled ? toggleAction : undefined} // Prevent onPress if not available or disabled
disabled={!isAvailable || disabled}
>
Expand Down
7 changes: 6 additions & 1 deletion packages/legacy/core/App/localization/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,12 @@ const translation = {
"Warning": "Ensure only you have access to your wallet.",
"UseToUnlock": "Use biometrics to unlock wallet?",
"UnlockPromptTitle": "Wallet Unlock",
"UnlockPromptDescription": "Use biometrics to unlock your wallet"
"UnlockPromptDescription": "Use biometrics to unlock your wallet",
"AllowBiometricsTitle": "Enable biometrics",
"AllowBiometricsDesc": "To unlock BC Wallet with your biometrics, please allow biometrics use within your device's settings.",
"SetupBiometricsTitle": "Biometrics is not enabled",
"SetupBiometricsDesc": "To unlock BC Wallet with your biometrics, please set up your biometrics in your device's settings.",
"OpenSettings": "Open settings"
},
"ActivityHistory": {
"Header": "Activity history",
Expand Down
7 changes: 6 additions & 1 deletion packages/legacy/core/App/localization/fr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,12 @@ const translation = {
"Warning": "\n\nAssurez-vous que vous seul avez accès à votre portefeuille.",
"UseToUnlock": "Utiliser la biométrie pour déverrouiller le portefeuille ?",
"UnlockPromptTitle": "Déverrouillage du portefeuille",
"UnlockPromptDescription": "Utilisez la biométrie pour déverrouiller votre portefeuille"
"UnlockPromptDescription": "Utilisez la biométrie pour déverrouiller votre portefeuille",
"AllowBiometricsTitle": "Activer la biométrie",
"AllowBiometricsDesc": "Pour déverrouiller BC Wallet avec votre biométrie, permettrez la biométrie dans les paramètres de votre appareil.",
"SetupBiometricsTitle": "La biométrie n'est pas activée",
"SetupBiometricsDesc": "Pour déverrouiller BC Wallet avec votre biométrie, configurez votre biométrie dans les paramètres de votre appareil.",
"OpenSettings": "Ouvrir les paramètres"
},
"ActivityHistory": {
"Header": "Activity history(fr)",
Expand Down
101 changes: 93 additions & 8 deletions packages/legacy/core/App/screens/UseBiometry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { CommonActions, useNavigation } from '@react-navigation/native'
import { StackNavigationProp } from '@react-navigation/stack'
import React, { useState, useEffect, useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, Text, View, Modal, ScrollView, DeviceEventEmitter } from 'react-native'
import { StyleSheet, Text, View, Modal, ScrollView, DeviceEventEmitter, Linking, Platform } from 'react-native'
import { PERMISSIONS, RESULTS, request, check, PermissionStatus } from 'react-native-permissions'
import { SafeAreaView } from 'react-native-safe-area-context'

import Button, { ButtonType } from '../components/buttons/Button'
Expand All @@ -15,6 +16,7 @@ import { useStore } from '../contexts/store'
import { useTheme } from '../contexts/theme'
import { OnboardingStackParams, Screens } from '../types/navigators'
import { testIdWithKey } from '../utils/testable'
import DismissiblePopupModal from '../components/modals/DismissiblePopupModal'

import PINEnter, { PINEntryUsage } from './PINEnter'
import { TOKENS, useServices } from '../container-api'
Expand All @@ -32,6 +34,7 @@ const UseBiometry: React.FC = () => {
const [biometryAvailable, setBiometryAvailable] = useState(false)
const [biometryEnabled, setBiometryEnabled] = useState(store.preferences.useBiometry)
const [continueEnabled, setContinueEnabled] = useState(true)
const [settingsPopupConfig, setSettingsPopupConfig] = useState<null | {title: string, description: string}>(null)
const [canSeeCheckPIN, setCanSeeCheckPIN] = useState<boolean>(false)
const { ColorPallet, TextTheme, Assets } = useTheme()
const { ButtonLoading } = useAnimatedComponents()
Expand All @@ -40,6 +43,8 @@ const UseBiometry: React.FC = () => {
return store.onboarding.didCompleteOnboarding ? UseBiometryUsage.ToggleOnOff : UseBiometryUsage.InitialSetup
}, [store.onboarding.didCompleteOnboarding])

const BIOMETRY_PERMISSION = PERMISSIONS.IOS.FACE_ID;

const styles = StyleSheet.create({
container: {
height: '100%',
Expand Down Expand Up @@ -102,17 +107,88 @@ const UseBiometry: React.FC = () => {
}
}, [biometryEnabled, commitPIN, dispatch, enablePushNotifications, navigation])

const toggleSwitch = useCallback(() => {
// If the user is toggling biometrics on/off they need
// to first authenticate before this action is accepted
const onOpenSettingsTouched = async () => {
await Linking.openSettings()
onOpenSettingsDismissed()
}

const onOpenSettingsDismissed = () => {
setSettingsPopupConfig(null)
}

const onSwitchToggleAllowed = useCallback((newValue: boolean) => {
if (screenUsage === UseBiometryUsage.ToggleOnOff) {
setCanSeeCheckPIN(true)
DeviceEventEmitter.emit(EventTypes.BIOMETRY_UPDATE, true)
} else {
setBiometryEnabled(newValue)
}
}, [screenUsage])

const onRequestSystemBiometrics = useCallback(async (newToggleValue: boolean) => {
const permissionResult: PermissionStatus = await request(BIOMETRY_PERMISSION)
switch (permissionResult) {
case RESULTS.GRANTED:
case RESULTS.LIMITED:
// Granted
onSwitchToggleAllowed(newToggleValue)
break
default:
break
}
}, [onSwitchToggleAllowed, BIOMETRY_PERMISSION])

const onCheckSystemBiometrics = useCallback(async (): Promise<PermissionStatus> => {
if (Platform.OS === 'android') {
// Android doesn't need to prompt biometric permission
// for an app to use it.
return biometryAvailable ? RESULTS.GRANTED : RESULTS.UNAVAILABLE
} else if (Platform.OS === 'ios') {
return await check(BIOMETRY_PERMISSION)
}

return RESULTS.UNAVAILABLE
}, [biometryAvailable, BIOMETRY_PERMISSION])

const toggleSwitch = useCallback(async () => {
const newValue = !biometryEnabled

if (!newValue) {
// Turning off doesn't require OS'es biometrics enabled
onSwitchToggleAllowed(newValue)
return
}

setBiometryEnabled((previousState) => !previousState)
}, [screenUsage])
// If the user is turning it on, they need
// to first authenticate the OS'es biometrics before this action is accepted
const permissionResult: PermissionStatus = await onCheckSystemBiometrics()
switch (permissionResult) {
case RESULTS.GRANTED:
case RESULTS.LIMITED:
// Already granted
onSwitchToggleAllowed(newValue)
break
case RESULTS.UNAVAILABLE:
setSettingsPopupConfig({
title: t('Biometry.SetupBiometricsTitle'),
description: t('Biometry.SetupBiometricsDesc')
})
break
case RESULTS.BLOCKED:
// Previously denied
setSettingsPopupConfig({
title: t('Biometry.AllowBiometricsTitle'),
description: t('Biometry.AllowBiometricsDesc')
})
break
case RESULTS.DENIED:
// Has not been requested
await onRequestSystemBiometrics(newValue)
break
default:
break
}
}, [onSwitchToggleAllowed, onRequestSystemBiometrics, onCheckSystemBiometrics, biometryEnabled, t])

const onAuthenticationComplete = useCallback((status: boolean) => {
// If successfully authenticated the toggle may proceed.
Expand All @@ -125,6 +201,15 @@ const UseBiometry: React.FC = () => {

return (
<SafeAreaView edges={['left', 'right', 'bottom']}>
{settingsPopupConfig && (
<DismissiblePopupModal
title={settingsPopupConfig.title}
description={settingsPopupConfig.description}
onCallToActionLabel={t('Biometry.OpenSettings')}
onCallToActionPressed={onOpenSettingsTouched}
onDismissPressed={onOpenSettingsDismissed}
/>
)}
<ScrollView style={styles.container}>
<View style={{ alignItems: 'center' }}>
<Assets.svg.biometrics style={styles.image} />
Expand Down Expand Up @@ -153,9 +238,9 @@ const UseBiometry: React.FC = () => {
<ToggleButton
testID={testIdWithKey("ToggleBiometrics")}
isEnabled={biometryEnabled}
isAvailable={biometryAvailable}
isAvailable={true}
toggleAction={toggleSwitch}
disabled={!biometryAvailable}
disabled={false}
enabledIcon="check"
disabledIcon="close"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exports[`ToggleButton Component renders correctly when disabled 1`] = `
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"checked": false,
"disabled": false,
"expanded": undefined,
"selected": undefined,
Expand Down Expand Up @@ -100,7 +100,7 @@ exports[`ToggleButton Component renders correctly when enabled 1`] = `
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"checked": true,
"disabled": false,
"expanded": undefined,
"selected": undefined,
Expand Down Expand Up @@ -193,7 +193,7 @@ exports[`ToggleButton Component renders correctly when not available 1`] = `
accessibilityState={
Object {
"busy": undefined,
"checked": undefined,
"checked": false,
"disabled": true,
"expanded": undefined,
"selected": undefined,
Expand Down
Loading

0 comments on commit 4766aa2

Please sign in to comment.