import { ExpoConfig } from '@expo/config-types' ; import assert from 'assert' ; import { ConfigPlugin } from '../Plugin.types' ; import { withAndroidManifest } from '../plugins/android-plugins' ; import { AndroidManifest , ManifestUsesPermission } from './Manifest' ; const USES_PERMISSION = 'uses-permission' ; export const withPermissions = ( config , permissions ) => { if ( Array . isArray ( permissions ) ) { permissions = permissions . filter ( Boolean ) ; if ( ! config . android ) config . android = { } ; if ( ! config . android . permissions ) config . android . permissions = [ ] ; config . android . permissions = [ ... new Set ( config . android . permissions . concat ( permissions ) ) , ] ; } return withAndroidManifest ( config , async ( config ) => { config . modResults = await setAndroidPermissions ( config , config . modResults ) ; return config ; } ) ; } ; export const withBlockedPermissions = ( config , permissions ) => { assert ( Array . isArray ( permissions ) , 'permissions prop must be an array' ) ; if ( config ?. android ?. permissions && Array . isArray ( config . android . permissions ) ) { config . android . permissions = config . android . permissions . filter ( permission => ! permissions . includes ( permission ) ) ; } return withAndroidManifest ( config , async ( config ) => { config . modResults = ensureToolsAvailable ( config . modResults ) ; config . modResults = addBlockedPermissions ( config . modResults , permissions ) ; return config ; } ) ; } ; param manifest returns function ensureToolsAvailable ( manifest ) { if ( manifest ?. manifest ?. $ ?. [ 'xmlns:tools' ] ) { return manifest ; } manifest . manifest . $ [ 'xmlns:tools' ] = 'http://schemas.android.com/tools' ; return manifest ; } export function addBlockedPermissions ( androidManifest , permissions ) { if ( ! Array . isArray ( androidManifest . manifest [ 'uses-permission' ] ) ) { androidManifest . manifest [ 'uses-permission' ] = [ ] ; } for ( const permission of permissions ) { androidManifest . manifest [ 'uses-permission' ] = ensureBlockedPermission ( androidManifest . manifest [ 'uses-permission' ] , permission ) ; } return androidManifest ; } param manifestPermissions param permission returns function ensureBlockedPermission ( manifestPermissions , permission ) { manifestPermissions = manifestPermissions . filter ( e => e . $ [ 'android:name' ] !== permission ) ; manifestPermissions . push ( { $ : { 'android:name' : permission , 'tools:node' : 'remove' } } ) ; return manifestPermissions ; } function prefixAndroidPermissionsIfNecessary ( permissions ) { return permissions . map ( permission => { if ( ! permission . includes ( '.' ) ) { return `android.permission.${ permission }` ; } return permission ; } ) ; } export function getAndroidPermissions ( config ) { return config . android ?. permissions ?? [ ] ; } export function setAndroidPermissions ( config , androidManifest ) { const permissions = getAndroidPermissions ( config ) ; const providedPermissions = prefixAndroidPermissionsIfNecessary ( permissions ) ; const permissionsToAdd = [ ... providedPermissions ] ; if ( ! androidManifest . manifest . hasOwnProperty ( 'uses-permission' ) ) { androidManifest . manifest [ 'uses-permission' ] = [ ] ; } const manifestPermissions = androidManifest . manifest [ 'uses-permission' ] ?? [ ] ; permissionsToAdd . forEach ( permission => { if ( ! isPermissionAlreadyRequested ( permission , manifestPermissions ) ) { addPermissionToManifest ( permission , manifestPermissions ) ; } } ) ; return androidManifest ; } export function isPermissionAlreadyRequested ( permission , manifestPermissions ) { return manifestPermissions . some ( e => e . $ [ 'android:name' ] === permission ) ; } export function addPermissionToManifest ( permission , manifestPermissions ) { manifestPermissions . push ( { $ : { 'android:name' : permission } } ) ; return manifestPermissions ; } export function removePermissions ( androidManifest , permissionNames ? ) { const targetNames = permissionNames ? permissionNames . map ( ensurePermissionNameFormat ) : null ; const permissions = androidManifest . manifest [ USES_PERMISSION ] || [ ] ; const nextPermissions = [ ] ; for ( const attribute of permissions ) { if ( targetNames ) { const value = attribute . $ [ 'android:name' ] || attribute . $ . name ; if ( ! targetNames . includes ( value ) ) { nextPermissions . push ( attribute ) ; } } } androidManifest . manifest [ USES_PERMISSION ] = nextPermissions ; } export function addPermission ( androidManifest , permissionName ) { const usesPermissions = androidManifest . manifest [ USES_PERMISSION ] || [ ] ; usesPermissions . push ( { $ : { 'android:name' : permissionName } } ) ; androidManifest . manifest [ USES_PERMISSION ] = usesPermissions ; } export function ensurePermissions ( androidManifest , permissionNames ) { const permissions = getPermissions ( androidManifest ) ; const results = { } ; for ( const permissionName of permissionNames ) { const targetName = ensurePermissionNameFormat ( permissionName ) ; if ( ! permissions . includes ( targetName ) ) { addPermission ( androidManifest , targetName ) ; results [ permissionName ] = true ; } else { results [ permissionName ] = false ; } } return results ; } export function ensurePermission ( androidManifest , permissionName ) { const permissions = getPermissions ( androidManifest ) ; const targetName = ensurePermissionNameFormat ( permissionName ) ; if ( ! permissions . includes ( targetName ) ) { addPermission ( androidManifest , targetName ) ; return true ; } return false ; } export function ensurePermissionNameFormat ( permissionName ) { if ( permissionName . includes ( '.' ) ) { const com = permissionName . split ( '.' ) ; const name = com . pop ( ) as string ; return [ ... com , name . toUpperCase ( ) ] . join ( '.' ) ; } else { return ensurePermissionNameFormat ( `android.permission.${ permissionName }` ) ; } } export function getPermissions ( androidManifest ) { const usesPermissions = androidManifest . manifest [ USES_PERMISSION ] || [ ] ; const permissions = usesPermissions . map ( permissionObject => { return permissionObject . $ [ 'android:name' ] || permissionObject . $ . name ; } ) ; return permissions ; }