Fix Android Launch From Home
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.
_16package io.ionic.starter;_16_16import android.content.Intent;_16import android.os.Bundle;_16import androidx.appcompat.app.AppCompatActivity;_16_16public 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
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 thescripts
folder and paste below in the contents. - Replace the line
package io.ionic.starter
with the id from yourconfig.xml
_16package io.ionic.starter;_16_16import android.content.Intent;_16import android.os.Bundle;_16import androidx.appcompat.app.AppCompatActivity;_16_16public 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 thescripts
folder and paste the below file - Set the path of
LAUNCHER_DESTINATION
inupdate-launcher.js
to match your id fromconfig.xml
_178var ANDROID_PROJECT_ROOT = "platforms/android/app/src/main";_178var LAUNCHER_DESTINATION =_178 "platforms/android/app/src/main/java/io/ionic/starter";_178var LAUNCHER_SOURCE = "scripts/LauncherActivity.java";_178var fs = require("fs");_178var path = require("path");_178var xml2js = require("xml2js");_178_178function 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_178function 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_178function 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_178function 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_178function 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_178function 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_178function 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_178function 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_178module.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:
_10npm 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
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