I am implementing Firebase Authentication in my Cordova application. The android app integrates Cordova in an activity that is not the main/launcher activity. Due to this, a required dependency plugin "cordova-universal-links-plugin" does not target the correct activity (it targets the main/launcher activity).
Is there a way to specify the target activity for universal-links? If not, how can I make a workaround/hack to fix this issue (as the plugin is not being maintained anymore)?
There is no known method of specifying the target activity for cordova-universal-links-plugin
. You can create a workaround by running scripts before/after cordova prepare
.
The fix will work like this:
AndroidManifest.xml
so that the target activity is ordered before the main/launcher activity. I am assuming the target activity initially looks like this:<activity android:name=".CordovaActivity">
<intent-filter android:label="@string/launcher_name">
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Now, you'll write two NodeJS scripts: before_prepare.js
and after_prepare.js
.
a) before_prepare.js
: This will add an <action android:name="android.intent.action.VIEW" />
after the <category />
tag for your target activity. Since "CordovaActivity" is temporarily also a main/launcher activity & is before the actual main activity, the universal-links plugin should target it instead.
b) after_prepare.js
: We want this to run after the plugin applies the universal-links intent-filters to your target activity. It will remove the <action />
tag you've added. This is necessary since Android can't allow two main activities.
Add before_prepare.js
to your project-level config.xml
as a before_prepare
Cordova hook.
Because Cordova runs your hooks before the plugin hooks, you can't add after_prepare.js
as an after_prepare
Cordova hook. That's because the <action />
tag would be removed before the universal-links plugin gets a chance to run. Instead, you'll have to run it after the cordova prepare
command. I recommend doing this by using an npm script (npm run prepare
) that runs after_prepare.js
after cordova prepare
.
My implementation depends on elementtree
for XML editing (it is also used by Cordova internally):
npm install elementtree
before_prepare.js
:
const et = require('elementtree')
const fs = require('fs')
const MANIFEST_FILE = '/.../CordovaProject/platforms/android/app/src/main/AndroidManifest.xml'
/**
* Add main action intent from `SurkartaActivity`.
*
* @param {ElementTree} manifestTree
*/
function addMainAction (manifestTree) {
const intentFilterElement = manifestTree.find("./application/activity[@android:name='.SurakartaActivity']")
.getchildren()[0]
const mainActionElement = et.SubElement(intentFilterElement, 'action')
mainActionElement.set('android:name', 'android.intent.action.MAIN')
}
// Cordova hook executes module.exports
module.exports = function () {
const manifestTree = et.parse(fs.readFileSync(MANIFEST_FILE, 'utf8'))
addMainAction(manifestTree)
console.log(manifestTree.write())
fs.writeFileSync(MANIFEST_FILE, manifestTree.write())
}
after_prepare.js
:
const et = require('elementtree')
const fs = require('fs')
const MANIFEST_FILE = '/.../CordovaProject/platforms/android/app/src/main/AndroidManifest.xml'
/**
* Remove main action intent from `SurkartaActivity`.
*
* @param {ElementTree} manifestTree
*/
function stripMainAction (manifestTree) {
const intentFilterElement = manifestTree.find("./application/activity[@android:name='.SurakartaActivity']")
.getchildren()[0]
intentFilterElement.remove(intentFilterElement.getchildren()[1])
}
function hey () {
const manifestTree = et.parse(fs.readFileSync(MANIFEST_FILE, 'utf8'))
stripMainAction(manifestTree)
fs.writeFileSync(MANIFEST_FILE, manifestTree.write())
}
module.exports = hey
// This isn't an Cordova hook, so run it manually
hey()
Changes to existing files:
config.xml
:
<hook type="before_prepare" src="./path/to/before_prepare.js" />
package.json
:
{
"scripts": {
"prepare": "cordova prepare; node ./path/to/after_prepare.js"
},
"dependencies": {
"elementtree": "@latest" /* npm install elementtree should do this for you */
}
}