@@ -2,7 +2,7 @@ import { Config } from './config';
2
2
import { Plugin , PluginType , getJSModules , getPlatformElement , getPluginPlatform , getPluginType , getPlugins , printPlugins } from './plugin' ;
3
3
import { copySync , ensureDirSync , readFileAsync , removeSync , writeFileAsync } from './util/fs' ;
4
4
import { basename , extname , join , resolve } from 'path' ;
5
- import { buildXmlElement , installDeps , log , logError , logFatal , logInfo , logWarn , readXML , resolveNode , writeXML } from './common' ;
5
+ import { buildXmlElement , installDeps , log , logError , logFatal , logInfo , logWarn , parseXML , readXML , resolveNode , writeXML } from './common' ;
6
6
import { copy as fsCopy , existsSync } from 'fs-extra' ;
7
7
import { getAndroidPlugins } from './android/common' ;
8
8
import { getIOSPlugins } from './ios/common' ;
@@ -214,17 +214,16 @@ export async function handleCordovaPluginsJS(cordovaPlugins: Plugin[], config: C
214
214
await autoGenerateConfig ( config , cordovaPlugins , platform ) ;
215
215
}
216
216
217
- export async function copyCordovaJSFiles ( config : Config , platform : string ) {
217
+ export async function getCordovaPlugins ( config : Config , platform : string ) : Promise < Plugin [ ] > {
218
218
const allPlugins = await getPlugins ( config ) ;
219
219
let plugins : Plugin [ ] = [ ] ;
220
220
if ( platform === config . ios . name ) {
221
221
plugins = getIOSPlugins ( allPlugins ) ;
222
222
} else if ( platform === config . android . name ) {
223
223
plugins = getAndroidPlugins ( allPlugins ) ;
224
224
}
225
- const cordovaPlugins = plugins
225
+ return plugins
226
226
. filter ( p => getPluginType ( p , platform ) === PluginType . Cordova ) ;
227
- await handleCordovaPluginsJS ( cordovaPlugins , config , platform ) ;
228
227
}
229
228
230
229
export async function logCordovaManualSteps ( cordovaPlugins : Plugin [ ] , config : Config , platform : string ) {
@@ -365,3 +364,76 @@ export async function getCordovaPreferences(config: Config) {
365
364
}
366
365
return cordova ;
367
366
}
367
+
368
+ export async function writeCordovaAndroidManifest ( cordovaPlugins : Plugin [ ] , config : Config , platform : string ) {
369
+ const pluginsFolder = resolve ( config . app . rootDir , 'android' , config . android . assets . pluginsFolderName ) ;
370
+ const manifestPath = join ( pluginsFolder , 'src' , 'main' , 'AndroidManifest.xml' ) ;
371
+ let rootXMLEntries : Array < any > = [ ] ;
372
+ let applicationXMLEntries : Array < any > = [ ] ;
373
+ let applicationXMLAttributes : Array < any > = [ ] ;
374
+ cordovaPlugins . map ( async p => {
375
+ const editConfig = getPlatformElement ( p , platform , 'edit-config' ) ;
376
+ const configFile = getPlatformElement ( p , platform , 'config-file' ) ;
377
+ editConfig . concat ( configFile ) . map ( async ( configElement : any ) => {
378
+ if ( configElement . $ && ( configElement . $ . target && configElement . $ . target . includes ( 'AndroidManifest.xml' ) || configElement . $ . file && configElement . $ . file . includes ( 'AndroidManifest.xml' ) ) ) {
379
+ const keys = Object . keys ( configElement ) . filter ( k => k !== '$' ) ;
380
+ keys . map ( k => {
381
+ configElement [ k ] . map ( ( e : any ) => {
382
+ const xmlElement = buildXmlElement ( e , k ) ;
383
+ const pathParts = getPathParts ( configElement . $ . parent || configElement . $ . target ) ;
384
+ if ( pathParts . length > 1 ) {
385
+ if ( pathParts . pop ( ) === 'application' ) {
386
+ if ( configElement . $ . mode && configElement . $ . mode === 'merge' ) {
387
+ Object . keys ( e . $ ) . map ( ( ek : any ) => {
388
+ applicationXMLAttributes . push ( `${ ek } ="${ e . $ [ ek ] } "` ) ;
389
+ } ) ;
390
+ } else if ( ! applicationXMLEntries . includes ( xmlElement ) && ! contains ( applicationXMLEntries , xmlElement , k ) ) {
391
+ applicationXMLEntries . push ( xmlElement ) ;
392
+ }
393
+ } else {
394
+ logInfo ( `plugin ${ p . id } requires to add \n ${ xmlElement } to your AndroidManifest.xml to work` ) ;
395
+ }
396
+ } else {
397
+ if ( ! rootXMLEntries . includes ( xmlElement ) && ! contains ( rootXMLEntries , xmlElement , k ) ) {
398
+ rootXMLEntries . push ( xmlElement ) ;
399
+ }
400
+ }
401
+ } ) ;
402
+ } ) ;
403
+ }
404
+ } ) ;
405
+ } ) ;
406
+ let cleartext = config . app . extConfig . server ?. cleartext ? 'android:usesCleartextTraffic="true"' : '' ;
407
+ let content = `<?xml version='1.0' encoding='utf-8'?>
408
+ <manifest package="capacitor.android.plugins"
409
+ xmlns:android="http://schemas.android.com/apk/res/android"
410
+ xmlns:amazon="http://schemas.amazon.com/apk/res/android">
411
+ <application ${ applicationXMLAttributes . join ( '\n' ) } ${ cleartext } >
412
+ ${ applicationXMLEntries . join ( '\n' ) }
413
+ </application>
414
+ ${ rootXMLEntries . join ( '\n' ) }
415
+ </manifest>` ;
416
+ content = content . replace ( new RegExp ( ( '$PACKAGE_NAME' ) . replace ( '$' , '\\$&' ) , 'g' ) , config . app . appId ) ;
417
+ await writeFileAsync ( manifestPath , content ) ;
418
+ }
419
+
420
+ function getPathParts ( path : string ) {
421
+ const rootPath = 'manifest' ;
422
+ path = path . replace ( '/*' , rootPath ) ;
423
+ let parts = path . split ( '/' ) . filter ( part => part !== '' ) ;
424
+ if ( parts . length > 1 || parts . includes ( rootPath ) ) {
425
+ return parts ;
426
+ }
427
+ return [ rootPath , path ] ;
428
+ }
429
+
430
+ function contains ( a : Array < any > , obj : any , k : string ) {
431
+ const element = parseXML ( obj ) ;
432
+ for ( var i = 0 ; i < a . length ; i ++ ) {
433
+ const current = parseXML ( a [ i ] ) ;
434
+ if ( element && current && current [ k ] && element [ k ] && current [ k ] . $ && element [ k ] . $ && element [ k ] . $ [ 'android:name' ] === current [ k ] . $ [ 'android:name' ] ) {
435
+ return true ;
436
+ }
437
+ }
438
+ return false ;
439
+ }
0 commit comments