Skip to content

Commit

Permalink
chore(android): update permissions plugin (#12)
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht authored Feb 10, 2025
1 parent a131ebf commit cea5578
Show file tree
Hide file tree
Showing 9 changed files with 22,565 additions and 15,999 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ object MdocDataTransferManager {

}.build()
}
}
}
2 changes: 1 addition & 1 deletion example/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const App = () => {
}

const shutdown = () => {
mdocDataTransfer.instance().shutdown()
mdocDataTransfer.instance().shutdown()
}

return (
Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"expo-build-properties": "~0.12.5",
"expo-dev-client": "~4.0.29",
"react": "18.2.0",
"react-native": "0.75.3",
"react-native": "0.74.5",
"react-native-gesture-handler": "2.16.2",
"react-native-qrcode-svg": "^6.3.12",
"react-native-reanimated": "~3.10.1",
Expand Down
9,498 changes: 9,498 additions & 0 deletions example/pnpm-lock.yaml

Large diffs are not rendered by default.

7,617 changes: 0 additions & 7,617 deletions example/yarn.lock

This file was deleted.

189 changes: 150 additions & 39 deletions plugin/src/withAndroid.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,160 @@
import type { ConfigPlugin } from '@expo/config-plugins'
import { withAndroidManifest, withAppBuildGradle, withPlugins } from '@expo/config-plugins'

const permissions = [
'android.permission.INTERNET',
'android.permission.BLUETOOTH',
'android.permission.BLUETOOTH_ADMIN',
'android.permission.BLUETOOTH_SCAN',
'android.permission.BLUETOOTH_ADVERTISE',
'android.permission.BLUETOOTH_CONNECT',
'android.permission.ACCESS_BACKGROUND_LOCATION',
'android.permission.ACCESS_FINE_LOCATION',
'android.permission.ACCESS_COARSE_LOCATION',
]

const withAndroidExcludeMetaInf: ConfigPlugin = (expoConfig) =>
withAppBuildGradle(expoConfig, (c) => {
if (c.modResults.contents.includes('resources.excludes.add("META-INF/versions/9/OSGI-INF/MANIFEST.MF")')) return c
c.modResults.contents += `packaging { resources.excludes.add("META-INF/versions/9/OSGI-INF/MANIFEST.MF") }`
return c
import {
AndroidConfig,
type ConfigPlugin,
withAndroidManifest,
withAppBuildGradle,
withPlugins,
} from '@expo/config-plugins'

type InnerManifest = AndroidConfig.Manifest.AndroidManifest['manifest']

type ManifestPermission = InnerManifest['permission']

type ExtraItems = {
'tools:targetApi'?: string
'android:usesPermissionFlags'?: string
'android:maxSdkVersion'?: string
}

type ManifestUsesPermissionWithExtraItems = {
$: AndroidConfig.Manifest.ManifestUsesPermission['$'] & ExtraItems
}

type AndroidManifest = {
manifest: InnerManifest & {
permission?: ManifestPermission
'uses-permission'?: ManifestUsesPermissionWithExtraItems[]
'uses-permission-sdk-23'?: ManifestUsesPermissionWithExtraItems[]
'uses-feature'?: InnerManifest['uses-feature']
}
}

const withBleAndroidManifest: ConfigPlugin = (config) =>
withAndroidManifest(config, (config) => {
config.modResults = addLocationPermissionToManifest(config.modResults)
config.modResults = addScanAndAdvertisePermissionToManifest(config.modResults)
config.modResults = addConnectPermissionToManifest(config.modResults)
config.modResults = addLegacyBlePermissionToManifest(config.modResults)

return config
})

function addLocationPermissionToManifest(androidManifest: AndroidManifest) {
if (!Array.isArray(androidManifest.manifest['uses-permission-sdk-23'])) {
androidManifest.manifest['uses-permission-sdk-23'] = []
}

if (
!androidManifest.manifest['uses-permission-sdk-23'].find(
(item) => item.$['android:name'] === 'android.permission.ACCESS_COARSE_LOCATION'
)
) {
androidManifest.manifest['uses-permission-sdk-23'].push({
$: {
'android:name': 'android.permission.ACCESS_COARSE_LOCATION',
'android:maxSdkVersion': '30',
},
})
}

if (
!androidManifest.manifest['uses-permission-sdk-23'].find(
(item) => item.$['android:name'] === 'android.permission.ACCESS_FINE_LOCATION'
)
) {
androidManifest.manifest['uses-permission-sdk-23'].push({
$: {
'android:name': 'android.permission.ACCESS_FINE_LOCATION',
'android:maxSdkVersion': '30',
},
})
}

return androidManifest
}

function addLegacyBlePermissionToManifest(androidManifest: AndroidManifest) {
if (!Array.isArray(androidManifest.manifest['uses-permission'])) {
androidManifest.manifest['uses-permission'] = []
}

if (
!androidManifest.manifest['uses-permission'].find(
(item) => item.$['android:name'] === 'android.permission.BLUETOOTH'
)
) {
AndroidConfig.Manifest.ensureToolsAvailable(androidManifest)
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': 'android.permission.BLUETOOTH',
'android:maxSdkVersion': '30',
},
})
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': 'android.permission.BLUETOOTH_ADMIN',
'android:maxSdkVersion': '30',
},
})
}
return androidManifest
}

function addScanAndAdvertisePermissionToManifest(androidManifest: AndroidManifest) {
if (!Array.isArray(androidManifest.manifest['uses-permission'])) {
androidManifest.manifest['uses-permission'] = []
}

if (
!androidManifest.manifest['uses-permission'].find(
(item) => item.$['android:name'] === 'android.permission.BLUETOOTH_SCAN'
)
) {
AndroidConfig.Manifest.ensureToolsAvailable(androidManifest)
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': 'android.permission.BLUETOOTH_SCAN',
'android:usesPermissionFlags': 'neverForLocation',
'tools:targetApi': '31',
},
})
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': 'android.permission.BLUETOOTH_ADVERTISE',
'tools:targetApi': '31',
},
})
}
return androidManifest
}

function addConnectPermissionToManifest(androidManifest: AndroidManifest) {
if (!Array.isArray(androidManifest.manifest['uses-permission'])) {
androidManifest.manifest['uses-permission'] = []
}

if (
!androidManifest.manifest['uses-permission'].find(
(item) => item.$['android:name'] === 'android.permission.BLUETOOTH_CONNECT'
)
) {
AndroidConfig.Manifest.ensureToolsAvailable(androidManifest)
androidManifest.manifest['uses-permission']?.push({
$: {
'android:name': 'android.permission.BLUETOOTH_CONNECT',
},
})
}
return androidManifest
}

const withAndroidExcludeBcProv: ConfigPlugin = (expoConfig) =>
withAppBuildGradle(expoConfig, (c) => {
if (c.modResults.contents.includes("all*.exclude module: 'bcprov-jdk15to18'")) return c
c.modResults.contents += `android { configurations { all*.exclude module: 'bcprov-jdk15to18' } }`
return c
})

// TODO: make it possible to optionally enable NFC
const withAndroidNfcProperties: ConfigPlugin = (expoConfig) =>
withAndroidManifest(expoConfig, (c) => {
const androidManifest = c.modResults.manifest
Expand Down Expand Up @@ -79,26 +206,10 @@ const withAndroidNfcProperties: ConfigPlugin = (expoConfig) =>
return c
})

const withAndroidPermissions: ConfigPlugin = (expoConfig) =>
withAndroidManifest(expoConfig, (c) => {
const androidManifest = c.modResults.manifest

for (const permission of permissions) {
if (androidManifest['uses-permission']?.some((p) => p.$['android:name'] === permission)) continue
androidManifest['uses-permission']?.push({
$: {
'android:name': permission,
},
})
}

return c
})

export const withAndroid: ConfigPlugin = (config) =>
withPlugins(config, [
withAndroidExcludeMetaInf,
(c) => AndroidConfig.Permissions.withPermissions(c, ['android.permission.BLUETOOTH_CONNECT']),
withBleAndroidManifest,
withAndroidExcludeBcProv,
withAndroidNfcProperties,
withAndroidPermissions,
])
54 changes: 10 additions & 44 deletions plugin/src/withIos.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,15 @@
import { type ConfigPlugin, withInfoPlist, withPlugins, withPodfile } from '@expo/config-plugins'
import type { ExpoConfig } from '@expo/config-types'
import type { Props } from '.'
import { type ConfigPlugin, withInfoPlist } from '@expo/config-plugins'

const withIosPodfile: ConfigPlugin<Props> = (expoConfig: ExpoConfig, props) =>
withPodfile(expoConfig, (c) => {
if (!props.ios?.buildStatic) return c
const staticLibraries = `mdoc_data_transfer_static_libraries=[${props.ios.buildStatic.map((i) => `"${i}"`).join(', ')}]`
if (c.modResults.contents.includes('mdoc_data_transfer_static_libraries')) {
c.modResults.contents = c.modResults.contents.replace(/mdoc_data_transfer_static_libraries=.*/, staticLibraries)
} else {
c.modResults.contents += staticLibraries
}

if (c.modResults.contents.includes('Pod::BuildType.static_library')) return c

c.modResults.contents += `
pre_install do |installer|
installer.pod_targets.each do |pod|
if mdoc_data_transfer_static_libraries.include?(pod.name)
def pod.build_type;
Pod::BuildType.static_library
end
end
end
end
`
return c
})
const BLUETOOTH_ALWAYS = 'Allow $(PRODUCT_NAME) to connect to bluetooth devices for data sharing'

const withIosBluetoothUsage: ConfigPlugin<Props> = (config, props) => {
return withInfoPlist(config, (c) => {
const defaultDescription = 'This app uses Bluetooth to connect to external devices for credential sharing.'

const usageDescription = props?.ios?.bluetoothDescription || defaultDescription

if (c.ios?.infoPlist) {
c.ios.infoPlist.NSBluetoothAlwaysUsageDescription = usageDescription
export const withIos: ConfigPlugin<{
bluetoothAlwaysPermission?: string | false
}> = (c, { bluetoothAlwaysPermission } = {}) => {
return withInfoPlist(c, (config) => {
if (bluetoothAlwaysPermission !== false) {
config.modResults.NSBluetoothAlwaysUsageDescription =
bluetoothAlwaysPermission || config.modResults.NSBluetoothAlwaysUsageDescription || BLUETOOTH_ALWAYS
}

return c
return config
})
}

export const withIos: ConfigPlugin<Props> = (config, props) =>
withPlugins(config, [
[withIosPodfile, props],
[withIosBluetoothUsage, props],
])
Loading

0 comments on commit cea5578

Please sign in to comment.