Merge branch 'CB-13145' of https://github.com/stevengill/cordova-common into CB-13145-steve
diff --git a/spec/ConfigChanges/ConfigChanges.spec.js b/spec/ConfigChanges/ConfigChanges.spec.js
index 79e109a..ec7f250 100644
--- a/spec/ConfigChanges/ConfigChanges.spec.js
+++ b/spec/ConfigChanges/ConfigChanges.spec.js
@@ -45,6 +45,7 @@
 var ConfigParser = require('../../src/ConfigParser/ConfigParser');
 var xml = path.join(__dirname, '../fixtures/test-config.xml');
 var editconfig_xml = path.join(__dirname, '../fixtures/test-editconfig.xml');
+var configfile_xml = path.join(__dirname, '../fixtures/test-configfile.xml');
 var cfg = new ConfigParser(xml);
 
 // TODO: dont do fs so much
@@ -374,6 +375,25 @@
                     expect(sdk.attrib['android:minSdkVersion']).toEqual('5');
                     expect(sdk.attrib['android:maxSdkVersion']).toBeUndefined();
                 });
+                it('should append new children to XML document tree', function () {
+                    var configfile_cfg = new ConfigParser(configfile_xml);
+                    var platformJson = PlatformJson.load(plugins_dir, 'android');
+                    var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                    munger.add_config_changes(configfile_cfg, true).save_all();
+                    var am_xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf-8')));
+                    var activity = am_xml.find('./application/activity[@android:name="com.foo.Bar"]');
+                    expect(activity).toBeDefined();
+                    expect(activity.attrib['android:label']).toEqual('@string/app_name');
+                });
+                // testing the "after" attribute of the <config-file> tag in config.xml
+                it('should append new children to XML document tree in the correct order', function () {
+                    var configfile_cfg = new ConfigParser(configfile_xml);
+                    var platformJson = PlatformJson.load(plugins_dir, 'android');
+                    var munger = new configChanges.PlatformMunger('android', temp, platformJson, pluginInfoProvider);
+                    munger.add_config_changes(configfile_cfg, true).save_all();
+                    var am_file = fs.readFileSync(path.join(temp, 'AndroidManifest.xml'), 'utf-8');
+                    expect(am_file.indexOf('android:name="zoo"')).toBeLessThan(am_file.indexOf('android:name="com.foo.Bar"'));
+                });
                 it('should throw error for conflicting plugin config munge with config.xml config munge', function () {
                     shell.cp('-rf', editconfigplugin_two, plugins_dir);
                     var platformJson = PlatformJson.load(plugins_dir, 'android');
diff --git a/spec/ConfigParser/ConfigParser.spec.js b/spec/ConfigParser/ConfigParser.spec.js
index 1075e83..2129565 100644
--- a/spec/ConfigParser/ConfigParser.spec.js
+++ b/spec/ConfigParser/ConfigParser.spec.js
@@ -245,10 +245,14 @@
                 var intents = cfg.getAllowIntents();
                 expect(intents.length).not.toEqual(0);
             });
-            it('it should read <edit-config> tag entries', function () {
+            it('Test 035: it should read <edit-config> tag entries', function () {
                 var editConfigs = cfg.getEditConfigs('android');
                 expect(editConfigs.length).not.toEqual(0);
             });
+            it('Test 036: it should read <config-file> tag entries', function () {
+                var configFiles = cfg.getConfigFiles('android');
+                expect(configFiles.length).not.toEqual(0);
+            });
         });
         describe('static resources', function () {
             var hasPlatformPropertyDefined = function (e) { return !!e.platform; };
diff --git a/spec/fixtures/test-config.xml b/spec/fixtures/test-config.xml
index 7969d64..5dd79e6 100644
--- a/spec/fixtures/test-config.xml
+++ b/spec/fixtures/test-config.xml
@@ -99,6 +99,12 @@
         <edit-config file="AndroidManifest.xml" target="/manifest/uses-sdk" mode="remove">
             <uses-sdk android:maxSdkVersion="24" />
         </edit-config>
+        <config-file target="AndroidManifest.xml" parent="/manifest/application">
+            <activity android:name="com.foo.Foo" android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
     </platform>
     <platform name="windows">
         <icon src="res/windows/logo.scale-200.png" target="logo.png"/>
diff --git a/spec/fixtures/test-configfile.xml b/spec/fixtures/test-configfile.xml
new file mode 100644
index 0000000..4ca15cd
--- /dev/null
+++ b/spec/fixtures/test-configfile.xml
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget android-packageName="io.cordova.hellocordova.android" id="io.cordova.hellocordova" ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
+    <name>Hello Cordova</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <access origin="*" />
+    <platform name="android">
+        <config-file parent="/manifest/application" target="AndroidManifest.xml">
+            <meta-data android:name="zoo" android:value="@string/kangaroo" />
+        </config-file>
+        <config-file target="AndroidManifest.xml" parent="/manifest/application" after="meta-data">
+            <activity android:name="com.foo.Bar" android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
+    </platform>
+</widget>
diff --git a/src/ConfigChanges/ConfigChanges.js b/src/ConfigChanges/ConfigChanges.js
index f0cf8d9..e0af8a9 100644
--- a/src/ConfigChanges/ConfigChanges.js
+++ b/src/ConfigChanges/ConfigChanges.js
@@ -168,17 +168,24 @@
     var platform_config = self.platformJson.root;
 
     var config_munge;
-    var edit_config_changes = null;
+    var changes = [];
+
     if (config.getEditConfigs) {
-        edit_config_changes = config.getEditConfigs(self.platform);
+        var edit_config_changes = config.getEditConfigs(self.platform);
+        if (edit_config_changes) {
+            changes = changes.concat(edit_config_changes);
+        }
     }
 
-    if (!edit_config_changes || edit_config_changes.length === 0) {
-        // There are no edit-config changes to add, return here
-        return self;
-    } else {
-        var isConflictingInfo = is_conflicting(edit_config_changes, platform_config.config_munge, self, true /* always force overwrite other edit-config */);
+    if (config.getConfigFiles) {
+        var config_files_changes = config.getConfigFiles(self.platform);
+        if (config_files_changes) {
+            changes = changes.concat(config_files_changes);
+        }
+    }
 
+    if (changes && changes.length > 0) {
+        var isConflictingInfo = is_conflicting(changes, platform_config.config_munge, self, true /* always force overwrite other edit-config */);
         if (isConflictingInfo.conflictFound) {
             var conflict_munge;
             var conflict_file;
@@ -200,10 +207,10 @@
                 }
             }
         }
-        // Add config.xml edit-config munges
-        config_munge = self.generate_config_xml_munge(config, edit_config_changes, 'config.xml');
     }
 
+    // Add config.xml edit-config and config-file munges
+    config_munge = self.generate_config_xml_munge(config, changes, 'config.xml');
     self = munge_helper(should_increment, self, platform_config, config_munge);
 
     // Move to installed/dependent_plugins
@@ -251,13 +258,11 @@
 // generate_plugin_config_munge
 // Generate the munge object from config.xml
 PlatformMunger.prototype.generate_config_xml_munge = generate_config_xml_munge;
-function generate_config_xml_munge (config, edit_config_changes, type) {
-
+function generate_config_xml_munge (config, config_changes, type) {
     var munge = { files: {} };
-    var changes = edit_config_changes;
     var id;
 
-    if (!changes) {
+    if (!config_changes) {
         return munge;
     }
 
@@ -267,13 +272,15 @@
         id = config.id;
     }
 
-    changes.forEach(function (change) {
+    config_changes.forEach(function (change) {
         change.xmls.forEach(function (xml) {
             // 1. stringify each xml
             var stringified = (new et.ElementTree(xml)).write({xml_declaration: false});
             // 2. add into munge
             if (change.mode) {
                 mungeutil.deep_add(munge, change.file, change.target, { xml: stringified, count: 1, mode: change.mode, id: id });
+            } else {
+                mungeutil.deep_add(munge, change.target, change.parent, { xml: stringified, count: 1, after: change.after });
             }
         });
     });
diff --git a/src/ConfigParser/ConfigParser.js b/src/ConfigParser/ConfigParser.js
index ebaa2a3..e2c41bc 100644
--- a/src/ConfigParser/ConfigParser.js
+++ b/src/ConfigParser/ConfigParser.js
@@ -554,6 +554,29 @@
             return editConfig;
         });
     },
+
+    /* Get all config-file tags */
+    getConfigFiles: function (platform) {
+        var platform_tag = this.doc.find('./platform[@name="' + platform + '"]');
+        var platform_config_files = platform_tag ? platform_tag.findall('config-file') : [];
+
+        var config_files = this.doc.findall('config-file').concat(platform_config_files);
+
+        return config_files.map(function (tag) {
+            var configFile =
+                {
+                    target: tag.attrib['target'],
+                    parent: tag.attrib['parent'],
+                    after: tag.attrib['after'],
+                    xmls: tag.getchildren(),
+                    // To support demuxing via versions
+                    versions: tag.attrib['versions'],
+                    deviceTarget: tag.attrib['device-target']
+                };
+            return configFile;
+        });
+    },
+
     write: function () {
         fs.writeFileSync(this.path, this.doc.write({indent: 4}), 'utf-8');
     }