Skip to main content
Version: 4.0

Fix Android Launch From Home

note

This documentation is a work in progress. It should suffice to fix this particular issue but additional changes are likely to be made.

Cordova and Capacitor applications, when launched on an Android device, will startup and display the webview your application is served in. However, for Auth Connect this may not be the behavior you want when a user is on the login page: you will likely want your application to return to the login page.

To make this behavior possible you'll need to alter your Cordova or Capacitor application using the following instructions.

Capacitor

In your Capacitor project create a new activity with the following code replacing io.ionic.starter with your projects identifier.


_16
package io.ionic.starter;
_16
_16
import android.content.Intent;
_16
import android.os.Bundle;
_16
import androidx.appcompat.app.AppCompatActivity;
_16
_16
public class LauncherActivity extends AppCompatActivity {
_16
@Override
_16
protected void onCreate(Bundle savedInstanceState) {
_16
super.onCreate(savedInstanceState);
_16
Intent i = new Intent(this, MainActivity.class);
_16
i.replaceExtras(this.getIntent());
_16
startActivity(i);
_16
finish();
_16
}
_16
}

Update the AndroidManifest.xml to set the new Activity as the launcher activity, and keep the MainActivity launchMode as singleTask

note

make sure to remove the intent-filter on the MainActivity that defines it as the launcher


_16
<activity android:name="io.ionic.starter.LauncherActivity"
_16
android:label="@string/title_activity_main"
_16
android:theme="@style/AppTheme.NoActionBarLaunch">
_16
<intent-filter>
_16
<action android:name="android.intent.action.MAIN" />
_16
<category android:name="android.intent.category.LAUNCHER" />
_16
</intent-filter>
_16
</activity>
_16
_16
<activity
_16
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
_16
android:name="io.ionic.starter.MainActivity"
_16
android:label="@string/title_activity_main"
_16
android:theme="@style/AppTheme.NoActionBarLaunch"
_16
android:launchMode="singleTask">
_16
</activity>

Be sure to run npx cap sync after applying these changes.

Cordova

There are a few steps to apply on a Cordova project.

  • Create a scripts folder in your project.
  • Create a file called LaunchActivity.java in the scripts folder and paste below in the contents.
  • Replace the line package io.ionic.starter with the id from your config.xml

_16
package io.ionic.starter;
_16
_16
import android.content.Intent;
_16
import android.os.Bundle;
_16
import androidx.appcompat.app.AppCompatActivity;
_16
_16
public class LauncherActivity extends AppCompatActivity {
_16
@Override
_16
protected void onCreate(Bundle savedInstanceState) {
_16
super.onCreate(savedInstanceState);
_16
Intent i = new Intent(this, MainActivity.class);
_16
i.replaceExtras(this.getIntent());
_16
startActivity(i);
_16
finish();
_16
}
_16
}

  • Create a file called update-launcher.js in the scripts folder and paste the below file
  • Set the path of LAUNCHER_DESTINATION in update-launcher.js to match your id from config.xml

_178
var ANDROID_PROJECT_ROOT = "platforms/android/app/src/main";
_178
var LAUNCHER_DESTINATION =
_178
"platforms/android/app/src/main/java/io/ionic/starter";
_178
var LAUNCHER_SOURCE = "scripts/LauncherActivity.java";
_178
var fs = require("fs");
_178
var path = require("path");
_178
var xml2js = require("xml2js");
_178
_178
function copyJavaLauncher() {
_178
return new Promise((resolve, reject) => {
_178
fs.access(LAUNCHER_DESTINATION, (err) => {
_178
if (err) {
_178
reject("INVALID LAUNCHER DESTINATION");
_178
return;
_178
}
_178
copyFile(
_178
LAUNCHER_SOURCE,
_178
path.join(LAUNCHER_DESTINATION, "LauncherActivity.java")
_178
);
_178
});
_178
});
_178
}
_178
_178
function copyFile(src, dest) {
_178
return new Promise((resolve, reject) => {
_178
let readStream = fs.createReadStream(src);
_178
_178
readStream.once("error", (err) => {
_178
reject(err);
_178
});
_178
_178
readStream.once("end", () => {
_178
resolve("done");
_178
});
_178
_178
readStream.pipe(fs.createWriteStream(dest));
_178
});
_178
}
_178
_178
function readManifest() {
_178
return new Promise((resolve, reject) => {
_178
fs.readFile(
_178
path.join(ANDROID_PROJECT_ROOT, "AndroidManifest.xml"),
_178
"utf-8",
_178
(err, input) => {
_178
if (!!err) {
_178
reject(err);
_178
} else {
_178
resolve(input);
_178
}
_178
}
_178
);
_178
});
_178
}
_178
_178
function writeManifest(data) {
_178
return new Promise((resolve, reject) => {
_178
fs.writeFile(
_178
path.join(ANDROID_PROJECT_ROOT, "AndroidManifest.xml"),
_178
data,
_178
(err) => {
_178
if (!!err) {
_178
reject(err);
_178
} else {
_178
resolve();
_178
}
_178
}
_178
);
_178
});
_178
}
_178
_178
function convertToJson(input) {
_178
return new Promise((resolve, reject) => {
_178
xml2js.parseString(input, (err, result) => {
_178
if (!!err) {
_178
reject(err);
_178
} else {
_178
resolve(result);
_178
}
_178
});
_178
});
_178
}
_178
_178
function convertToXML(input) {
_178
return new Promise((resolve, reject) => {
_178
let builder = new xml2js.Builder();
_178
let xml = builder.buildObject(input);
_178
resolve(xml);
_178
});
_178
}
_178
_178
function removeLegacyActivityIntent(data) {
_178
return new Promise((resolve, reject) => {
_178
let applications = data.manifest.application;
_178
if (!applications) {
_178
reject();
_178
return;
_178
}
_178
applications.forEach((application) => {
_178
if (!!application.activity) {
_178
application.activity.forEach((activity) => {
_178
if (activity["intent-filter"]) {
_178
activity["intent-filter"].forEach((intent, idx) => {
_178
let shouldRemove = false;
_178
if (intent.action) {
_178
intent.action.forEach((action) => {
_178
if (action["$"]["android:name"].includes("MAIN")) {
_178
shouldRemove = true;
_178
}
_178
});
_178
}
_178
if (shouldRemove) {
_178
delete activity["intent-filter"][idx];
_178
}
_178
});
_178
}
_178
});
_178
}
_178
});
_178
resolve(data);
_178
});
_178
}
_178
_178
function addLauncherActivityIntent(data) {
_178
return new Promise((resolve, reject) => {
_178
let applications = data.manifest.application;
_178
if (!applications) {
_178
reject();
_178
return;
_178
}
_178
applications.forEach((application) => {
_178
if (typeof application.activity === "undefined") {
_178
application.activity = [];
_178
}
_178
application.activity.push({
_178
$: {
_178
"android:name": "LauncherActivity",
_178
"android:label": "@string/app_name",
_178
"android:theme": "@android:style/Theme.DeviceDefault.NoActionBar",
_178
},
_178
"intent-filter": [
_178
{
_178
action: {
_178
$: {
_178
"android:name": "android.intent.action.MAIN",
_178
},
_178
},
_178
category: {
_178
$: {
_178
"android:name": "android.intent.category.LAUNCHER",
_178
},
_178
},
_178
},
_178
],
_178
});
_178
});
_178
resolve(data);
_178
});
_178
}
_178
_178
module.exports = function (context) {
_178
return new Promise((resolve, reject) => {
_178
readManifest()
_178
.then((input) => convertToJson(input))
_178
.then((data) => removeLegacyActivityIntent(data))
_178
.then((data) => addLauncherActivityIntent(data))
_178
.then((data) => convertToXML(data))
_178
.then((input) => writeManifest(input))
_178
.then(() => copyJavaLauncher())
_178
.then((data) => {
_178
resolve("done");
_178
})
_178
.catch((err) => {
_178
console.log(err);
_178
reject("done");
_178
});
_178
});
_178
};

  • Next step is to install xml2js with:

_10
npm install xml2js --save-dev

Finally update config.xml by adding the following line underneath <platform name="android">:


_10
<hook src="scripts/update-launcher.js" type="after_platform_add" />

This script will run when you add the Android platform is added so to test it you can run: ionic cordova platform remove android ionic cordova platform add android

note

Be sure that the package line in LaunchActivity.java and the LAUNCHER_DESTINATION match what is used for your id in config.xml.

Testing

To test that you have correctly applied these changes:

  • Launch your application with an Android device
  • Go to the login page
  • Return to the home screen of your Android device
  • Launch your app again by pressing the icon
  • Your application should now open displaying the login page in the state it was left in