Launch activity from HostApduService running in the backgound - android

I would like to start an activity with an image every time HostApduService receives any Apdu command.
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray {
val intent = Intent(this, ImageActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.setAction(Intent.ACTION_VIEW)
startActivity(intent)
return hexStringToByteArray("...")
}
This solution works well if my application is currently in the foreground. Otherwise (app is in background or not launched) even though apdu command is processed correctly, the activity doesn't show on the screen.
According to doc ( link ) activity should be shown.
How to start an activity this way without additional permissions? Apps like GooglePay seem to work that way.
I am not interested in solution including android.permission.SYSTEM_ALERT_WINDOW permission. I know it is possible to show view using WindowManager with WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY. Unfortunately this solution requires explicit user permission - redirecting user to change value in device display settings.

Related

How to inspect an intent to see if it was an intent that started/launched the app?

How to inspect an intent to see if it was an intent that started/launched the app versus an intent that was used to navigate from within the app or once the app was already opened?
We have special UI handling based on if the intent was launched using a deeplink that doesn't work well if the app is launched from a deeplink. Right now, I am adding an extra boolean to the intent to workaround the issue we have, but was wondering if there was something available in the framework/SDK to determine if a particular intent was the app start/launch intent.
NOTE: We are using jetpack navigation to handle incoming intents/deeplinks and to route deeplinks within the app. That is why in the snippet below you can see I am accessing the intent from currentBackStackEntry
Here is the code I've put in to support it so far:
In MainActivity onCreate
intent.putExtra(KEY_IS_APP_CREATED_INTENT, true)
In our UI logic code
val isAppCreationIntent = navController.currentBackStackEntry?.arguments
?.parcelable<Intent>(KEY_DEEP_LINK_INTENT)
?.getBooleanExtra(KEY_IS_APP_CREATED_INTENT, false) ?: false
I logged a bug/feature request to provide this capability here:
https://issuetracker.google.com/u/1/issues/265981498

Bring app back to foreground from code doesn't work on Android 12+

I made a Xamarin.Forms project to create and show local notifications, and it's supposed to be able to put the app back to the foreground when the notification is clicked.
The thing is, my code works on Android 11 and before, but on Android 12 & 13 the notification click is received by the app, if I have a callback for that notification it is called, but the app stays in background.
This is the part of the code that runs when I received a notification click and that I want to set the app in foreground (this is in the Xamarin Android project) :
var packageManager = Application.Context.PackageManager;
Intent launchIntent = packageManager.GetLaunchIntentForPackage(Constants.PackageName);
if (launchIntent != null)
{
launchIntent.AddCategory(Intent.CategoryLauncher);
Application.Context.StartActivity(launchIntent);
}
I have found a lot of posts on how to start/set to foreground an app, and the code I use is what's working for others, but all these posts where from 2020 and before, so no Android 12+ at the time and I can't find anything about a new way of doing this.
Does anyone have this functionality working on the newest Androids ?
I have found the solution so I'll post it here if someone needs it.
The code I use to put the application back to the foreground is correct, but I was missing the System_Alert_Window permission in my main application.
So to handle this permission I did :
Add it to my main application's manifest
Create a native method that checks if it is enabled
Create a native method that redirect the user to the overlay settings so they can allow the permission.
To check if the permission is enabled :
public bool HasOverlayPermission()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
return true;
return Android.Provider.Settings.CanDrawOverlays(Application.Context);
}
To redirect the user to their phone settings for AppOverlay (this permission can only be allowed from the settings) :
public void AskForOverlayPermission()
{
if (Android.Provider.Settings.CanDrawOverlays(Application.Context))
return;
var uri = Android.Net.Uri.Parse("package:" +
Application.Context.PackageName);
Intent intent = new Intent(
Android.Provider.Settings.ActionManageOverlayPermission, uri);
_mainActivity.StartActivityForResult(intent, 101);
}
The StartActivityFromResult method is only accessible in Activity classes, so you can either write it in you MainActivity or give your MainActivity as constructor parameter of another class.
This code will directly redirect the user to the settings page, so it's better if you ask them if they want to allow this permission in a popup or something beforehand (so they can understand why they're redirected).
I have found the code in this post : How to enable screen overlay permission by default

Open an app from a JobService without "Display Over Other Apps" permission

I am new to android development. I've been trying to open an app from a JobService that is scheduled from the onReceive() method of an implicit broadcast-receiver, declared in my manifest.
From some posts, I found that one can use the packageManager.getLaunchIntentForPackage() method and then use it to launch said package by starting a new activty with context.startActivity(), so I declared the function below:
fun openApp(packageName: String, context: Context){
val startIntent: Intent? =
context.packageManager.getLaunchIntentForPackage(packageName)
startIntent?.addFlags(
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or
Intent.FLAG_ACTIVITY_NEW_TASK
);
context.startActivity(startIntent)
}
When used on the MainActivity file, this function works as expected, but when I try using it on my JobService, I noticed that it only works if my app has the Display Over Other Apps permission.
Is this to be expected? Is there any way to contourn this requirement? Is this because of the way I am passing the context in my openApp() function?
Thx in advance!
Thanks to CommonsWare's comments, I've found the documentation I needed:
Restrictions on starting activities from the background.
In my use case, I needed to cancel a call to a specific number and open my app instead, so I adapted my code to take advantage of the CallRedirectionService class, which is treated as an exception and therefore can start activities from the background.

How to launch kiosk app from single app in Android

I have an Android device that is running a single application. This application starts on boot and is the only one the user shall be able to see.
But once this app is running, I would like to be able to launch another app by clicking a button on this "boot" app, in a way that this second app is launched in "kiosk" mode, so that the user cannot go back, cannot go home, cannot get any notification, and so on, until certain action is finished.
For this I am trying to follow the Google documentation for lock mode here.
But it is not clear for me, when it tells "DPC must allow apps..." this code should be placed in the initial app (the one on boot), or for the one I want to run in kiosk mode?.
Would appreciate a bit more detailed info on that.
The part when it tells:
// Set an option to turn on lock task mode when starting the activity.
val options = ActivityOptions.makeBasic()
options.setLockTaskEnabled(true)
// Start our kiosk app's main activity with our lock task mode option.
val packageManager = context.packageManager
val launchIntent = packageManager.getLaunchIntentForPackage(KIOSK_PACKAGE)
if (launchIntent != null) {
context.startActivity(launchIntent, options.toBundle())
}
I assume that is indeed to be placed on the "boot" app.
Yes, that part will reside in your boot app, which should work for you but you can also add the second apps packagename to the
DevicePolicyManager.setLockTaskPackages()
If you don't want to try that method as seen below:
private val YOUR_BOOT_APP_PACKAGE_NAME = "your.boot.app.packagename"
private val YOUR_SECOND_APP_PACKAGE_NAME = "your.second.app.packagename"
private val APP_PACKAGES = arrayOf(YOUR_BOOT_APP_PACKAGE_NAME, YOUR_SECOND_APP_PACKAGE_NAME)
mDevicePolicyManager.setLockTaskPackages(mAdminComponentName, APP_PACKAGES)

How to reopen app correctly from foreground service?

I've been facing a problem for a while regarding startActivity(intent). What i'm developing is a kind of key word detection like "ok google" that triggers an alert when the user says the word. To achieve that when the user is not using the app i have a LifecycleService that runs in foreground and listens to the user. When the user says the word and the app is killed it opens the activity i need using startActivity from the service, but the problem is that if i keep listening and change to other activity (using the app normally) the detection works (because i hear the sound i put when the word is recognized) but the app doesn't start the activity it should (although the startActivity(intent) is called). I'm pretty sure that the problem has to be with the context that maybe is not the correct one when i open the app with startActivity from the service, but i don't know how to fix it. I tried to user some other variables like applicationContext or the androidContext() from Koin but it is not working.
class SpeechRecognitionService : LifecycleService() {
...
//onStartCommand starts the audio recognizer and startAlert() is triggered when the alert is recognized. It is always correctly called
private fun startAlert() {
//This is not showing MainActivity although i execute it
startActivity(MainActivity.getDialogIntent(this))
//I always hear this audio when the app detects word
val audio = MediaPlayer.create(this, R.raw.alert_detected_audio)
audio.start()
}
}
The MainActivity.getDialogIntent(this) is just a common Intent
fun getDialogIntent(context: Context): Intent {
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.putExtra(SHOW_ALERT_DIALOG_KEY, true)
return intent
}
The problem only happens if the app is started with the voice recognition (after being killed). If i for example kill the app but open the app again pressing the app icon it works correctly. If i start the app with voice (so, from the startActivity i have above) i worked that time and open the app but when i change to other activity it fails to start.
You need to use a proper "launcher Intent". The easiest way to get one is to call
PackageManager pm = getPackageManager()
Intent intent = pm.getLaunchIntentForPackage("my.package.name")
Use this Intent to start your MainActivity instead of calling MainActivity.getDialogIntent(this)

Categories

Resources