Adding same name entries into AndroidManifest.xml in managed workflow

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);
  }
1 Like