Skip to main content
Version: 5.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

Launching a Capacitor app on an Android device typically initiates the display of your application's content in a webview. However, if the user is in the process of authentication and leaves the app it might not display the login page in the same state when the user returns. To fix this behavior you would want the app to direct users back to the login page instead of the application's content in such scenarios. To achieve this, specific modifications are necessary, as outlined in the following steps.

Go to your Capacitor Android project and navigate to the directory that contains your application's package. Your path will be something like android/app/src/main/java/io/ionic/starter/. However, the end of the path will reflect your unique bundle ID.

Create a new Java file in this folder named LauncherActivity.java.

LauncherActivity.java

_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
}

Copy and paste the provided code into the new activity file (LauncherActivity.java).

LauncherActivity.java

_16
package your.project.identifier;
_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
}

Replace io.ionic.starter with your project's identifier.

LauncherActivity.java
AndroidManifest.xml

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

Navigate to the AndroidManifest.xml file which is located at the following file path android/app/src/main/ and locate the activity section.

LauncherActivity.java
AndroidManifest.xml

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

Update the AndroidManifest.xml to set the new Activity as the launcher activity. Add an intent-filter to LauncherActivity to define it as the launcher.

LauncherActivity.java
AndroidManifest.xml

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

Remove the intent-filter on the MainActivity that previously defined it as the launcher.

LauncherActivity.java
AndroidManifest.xml

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

On the MainActivity change exported to false and make sure to keep launchMode as singleTask.

Copy and paste the provided code into the new activity file (LauncherActivity.java).

Replace io.ionic.starter with your project's identifier.

Navigate to the AndroidManifest.xml file which is located at the following file path android/app/src/main/ and locate the activity section.

Update the AndroidManifest.xml to set the new Activity as the launcher activity. Add an intent-filter to LauncherActivity to define it as the launcher.

Remove the intent-filter on the MainActivity that previously defined it as the launcher.

On the MainActivity change exported to false and make sure to keep launchMode as singleTask.

LauncherActivity.java

_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
}

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

To test that you have correctly applied these changes:

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

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

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

  • 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