Hi @lewistyler
The activities must be added to an array, not an object, so there’s no need for unique names. Although the way xml2js
represents the XML is quite verbose, which makes it tricky to see what you need to do, you can take snippets of XML, parse it with xml2js
and print it out to see what you need to build.
e.g.:
var parseString = require("xml2js").parseString;
var intentFilterXml = `
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="<YOUR_APPLICATION_SCHEME>"
android:host="<YOUR_APPLICATION_CALLBACK>"/>
</intent-filter>
`;
parseString(intentFilterXml, function (err, result) {
console.log(JSON.stringify(result, null, 2));
});
The above prints out the following:
{
"intent-filter": {
"action": [
{
"$": {
"android:name": "android.intent.action.VIEW"
}
}
],
"category": [
{
"$": {
"android:name": "android.intent.category.DEFAULT"
}
},
{
"$": {
"android:name": "android.intent.category.BROWSABLE"
}
}
],
"data": [
{
"$": {
"android:scheme": "<YOUR_APPLICATION_SCHEME>",
"android:host": "<YOUR_APPLICATION_CALLBACK>"
}
}
]
}
}
As you can see there is an action
array, a category
array and a data
array, which is how you are able to add more than one <category>
tag to the <intent-filter>
tag.
You could build up this intent datastructure something like this, for example:
function actionAttribute(action) {
return {
"android:name": `android.intent.action.${action}`,
};
}
function intentFilterAction(action) {
return {
$: actionAttribute(action),
};
}
function categoryAttribute(category) {
return {
"android:name": `android.intent.category.${category}`,
};
}
function intentFilterCategory(category) {
return {
$: categoryAttribute(category),
};
}
function intentFilterData(scheme, host) {
return {
$: {
"android:scheme": scheme,
"android:host": host,
},
};
}
function intentFilter(scheme, host) {
return {
"intent-filter": {
action: [intentFilterAction("VIEW")],
category: [
intentFilterCategory("DEFAULT"),
intentFilterCategory("BROWSABLE"),
],
data: [intentFilterData(scheme, host)],
},
};
}
const authCallbackIntentFilter = intentFilter(
"<YOUR_APPLICATION_SCHEME>",
"<YOUR_APPLICATION_CALLBACK>"
);
console.log(JSON.stringify(authCallbackIntentFilter, null, 2));
If you log that, you’ll see that it produces the same output as the parsed XML.
You can build up the two activities
in a similar way.
Then, like in my code from the other thread, you can find the activity
array like this:
// [...]
// Check if there are any activity tags
let activities = application["activity"];
if (!Array.isArray(activities)) {
console.warn('addCustomActivityToMainApplication: No activity array in .MainApplication?');
return androidManifest;
}
And then you can push the two activities you built onto the array. But it’s probably best to check if they have already been added (for people using “expo prebuild”).
// If we don't find the custom activity, add it
// If we do find it, assume it's correct
if (
!activities.find(
(item) =>
item.$["android:name"] === "com.spotify.sdk.android.authentication.AuthCallbackActivity"
)
) {
activities.push(authCallbackActivity);
}
// If we don't find the custom activity, add it
// If we do find it, assume it's correct
if (
!activities.find(
(item) =>
item.$["android:name"] === "com.spotify.sdk.android.authentication.LoginActivity"
)
) {
activities.push(loginActivity);
}