How to reopen app correctly from foreground service? - android

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)

Related

How to manually close the quick settings panel launched from Settings.Panel.ACTION_INTERNET_CONNECTIVITY Intent in Android?

I am launching an Settings.Panel.ACTION_INTERNET_CONNECTIVITY Intent from the app's MainActivity when the internet gets disconnected. When the user minimize the app without closing the Intent, the app's state is cleared and opens again from login when launched from packageManager.getLaunchIntentForPackage. But if the user close the opened Intent before minimizing the app, then the app can be opened from package manager with the current state as it is minimized.
I used the following code to launch the quick settings panel.
val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
startActivityForResult(panelIntent,101)
Could someone show a way to close the Settings.Panel.ACTION_INTERNET_CONNECTIVITY Intent manually?
After trying to solve this for a day, I found out that this issue happens only if we launch the intent from an Activity.
I tried launching this from a Broadcast Receiver(which i already had) like this:
val panelIntent = Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY)
panelIntent.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
context.startActivity(panelIntent)
Now, the intent is auto closed when the app gets to background. Launching the app packageManager.getLaunchIntentForPackage worked as expected.

Pending intent stays after leaving activity with back button

I'm using pending intent with notification to send a push to my device.
My pending intent contains fields that will be used in my MainActivity onCreate() method to handle navigation. After successful navigation to any point in my application I tapping on the back button until my app goes to the background. Right after that my activity instantly invokes onDestroy() for some reasons (using android emulator API 28). The problem is that after lifting up app to the foreground, PendingIntent still there and my app performs navigation again.
It's really strange. I always thought that intents with FLAG_ONE_SHOT could be used only once. I also tried to clear intent by myself but it didn't work out too.
My Intent construction
const val NAVIGATION_BUDNLE = "nav_bundle"
private fun createPendingIntent(): PendingIntent? {
val destination = resolveDestionation()
val intent = Intent(context.applicationContext, MainActivity::class.java).apply {
flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
putExtra(NAVIGATION_BUDNLE, destination)
}
return PendingIntent.getActivity(context, Random.nextInt(), intent, FLAG_ONE_SHOT)
}
At the end of the activity onCreate() method, I've got these lines to handle navigation.
intent?.extras?.get(NAVIGATION_BUNDLE)?.let {
navController.setGraph(R.navigation.nav_graph, it as Bundle)
clearCurrentIntent() // intent = null and intent?.extras?.remove(NAVIGATION_BUNDLE)
}
Ok, I've got it (great thanks to this answer)! So this behavior can happen if you leave your activity with back button press. In this cases your activity will call onDestroy() method without calling onSaveInstanceState() and you will receive same intent over and over again (while clicking back to quit your app, of course). All you need to do is check if your app was called from recent. To do that you can implement this check before your main activity reading your intent:
if (intent.flags and FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0) {
intent?.extras?.get(NAVIGATION_BUNDLE)?.let {
navController.setGraph(R.navigation.nav_graph, it as Bundle)
}
}
Flag FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY will appear if your app is launched from recent and that's a very tiny workaround for this particular problem. If this flag presented just don't read your intent and that's it.

Launch activity from HostApduService running in the backgound

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.

Detect if user starts an application, android

Thanks in advance for the help.
I have an app that can be started by either the user physically starting the app (like you would any normal app) or by a repeating service. Depending on what starts the app (the user or the service) I want to preform different initialization actions. How might I be able to detect if an user starts the app without doing anything custom (I imagine that there has to be some kind of built in setting in android for me to determine this)?
If service, that starts your Activity, is yours service, you can put some custom information (using Intent#putExtra for example) in Intent you use to start Activity from Service.
In Activity you can use Activity#getIntent(), that returns the intent that started this activity.
If you started Activity from Service, that Intent will be the one you passed in Service#startActivity, and will have your custom information. Otherwise, that was not your Service, that started your Activity.
That could look somehow like that, for example:
//in Activity
public static final String EXTRA_STARTED_FROM_MY_SERVICE = "com.example.extra_started_from_sevice";
private boolean wasActivityStartedFromService() {
Intent startingIntent = getIntent();
//assuming you will use Intent#putExtra in your service when starting activity
return startingIntent.getBooleanExtra(EXTRA_STARTED_FROM_MY_SERVICE, false);
}
//...
//in Service
//...
Intent startingIntent = new Intent(this, MainActivity.class);
startingIntent.putExtra(MainActivity.EXTRA_STARTED_FROM_MY_SERVICE, true);
startActivity(startingIntent);

Can you start the default music player in the background?

I have 2 areas in my app that launch the default media player on the device and play an audio file (a podcast). One gets it from the file system after a download, the other streams it from the file on the site.
This is working fine right now, problem is if I hit back or home to try and get back to my app after it launches the media player, the player closes and the playback stops.
Is there a way to launch it in the background or as a service so that when I go back to the app, the music keeps playing in the player. I want it to function as if you started a song from the music player. So the user can use the device as normal and the playback will continue.
Here's my code for launching the music player for streaming.
private void playPodcast( Podcast podcast ) {
Intent intent = new Intent( Intent.ACTION_VIEW );
intent.setDataAndType( Uri.parse( podcast.getContentLink() ), "audio/*" );
startActivity( intent );
}
Thanks in advance.
EDIT! After receiving the advice below, I tried launching it as a Service. I've tried this in 2 ways.
Both ways have this as the manifest entry:
<service android:name=".DefaultMusicPlayerLaunchService">
</service>
The first attempt was to make a Service class which was merely a skeleton and launching it with a similar method as above.
DefaultMusicPlayerLaunchService V1:
public class DefaultMusicPlayerLaunchService extends Service {
#Override
public IBinder onBind( Intent intent ) {
// TODO Auto-generated method stub
return null;
}
public int onStartCommand( Intent intent, int flags, int startId ) {
return START_STICKY;
}
}
playPodcast Method:
private void playPodcast( Podcast podcast ) {
Intent intent = new Intent( getApplicationContext(),
DefaultMusicPlayerLaunchService.class );
intent.setDataAndType( Uri.parse( podcast.getContentLink() ), "audio/*" );
startService( intent );
}
My thought was that this way, it would launch the music player similarly to the way it was in the Activity I had before, but launch it without leaving the screen it was on. I didn't see anything happen in the LogCat. I feel like this should work but I'm missing something.
The other method I attempted involved serializing the podcast object as an extra and putting it on the Intent I made for the DefaultMusicPlayerLaunchService. Then, in the onStartCommand method, I grabbed the object, created a new Intent and then called startActivity similarly to how the original playPodcast method at the top does. And as expected, it does just what the top one does... launches the player but as soon as I hit back or home, it closes.
I apologize if I'm missing something obvious. I'm new to Android and everything I've worked with up to now have used Activity. This is my first attempt at starting a Service.
Yes you can start it as a service. You need to create a class that extends android.app.Service. And call startService(intent) from an Activity passing it an intent that will start the service. Override the onStartCommand method and have it return START_STICKY which will have the service run until its explicitly stopped. Either by you in code or by the Android OS to conserve resources. You can also override onBind to bind the service to an Activity which will give the service the same importance as a foreground Activity which makes it less likely that Android will kill the service to save resources.

Categories

Resources