I have MainActivity declared with following HOME, DEFAULT category, and MAIN Action. I also do select the app as default launcher. When I click back press it closes MainActivity as expected. But if I leave MainActivity running and restart the device, I cannot get out of MainActivity! Pressing onBackPress() in which I call finish(), pauses the activity as expected. But then I see onCreate called(), onResume() and MainActivity is back up like a clown! What I can do? This only happens after restart of device when activity is left running.
I'm doing everything I can to get rid of this activity including inside
onBackPressed(){
ActivityCompat.finishAffinity(MainActivity.this);
finish();
}
I have seen suggestion to FLAG_ACTIVITY_CLEAR_TOP but its the OS that starts the Activity in the first place, not me.
I cannot leave the app at all!
Add this code in your onBackPressed() method.
Intent intentExit = new Intent(Intent.ACTION_MAIN);
intentExit.addCategory(Intent.CATEGORY_HOME);
intentExit.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentExit);
finish();
This is just an idea, don't know whether it will work perfectly
Try creating a broadcast receiver
Get the event of phone restart
Close the app
In Manifest.xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
BootCompleteReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootCompleteReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags (Intent.FLAG_ACTIVITY_SINGLE_TOP);
i.putExtra("close_activity",true);
context.startActivity(i);
}
}
MainActivity.java add this block
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(intent.getBooleanExtra("close_activity",false)){
this.finish();
}
}
References:
Android BroadcastReceiver, auto run service after reboot of device
Close application from broadcast receiver
Related
i have some functionality in my program i want to expose but i did't seem to get the receiver working.
i tried the Manifest/Receiver:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nohkumado.intstringsynchro" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:resizeableActivity = "true">
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".IntStringReceiver" android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="com.nohkumado.intstringsynchro.EDIT_STRINGXML"/>
</intent-filter>
<intent-filter>
<action android:name="com.nohkumado.intstringsynchro.ADD_STRINGXML"/>
<action android:name="com.nohkumado.intstringsynchro.DEL_STRINGXML"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</receiver>
</application>
</manifest>
package com.nohkumado.intstringsynchro;
import android.content.*;
import android.widget.*;
import android.util.*;
public class IntStringReceiver extends BroadcastReceiver
{
public static final String TAG = "Receiver";
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, "Intent Detected:"+intent.getAction(), Toast.LENGTH_LONG).show();
switch (intent.getAction())
{
case "com.nohkumado.intstringsynchro.EDIT_STRINGXML":
{
Intent intentStartMainActivity = new Intent(context, MainActivity.class);
intentStartMainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentStartMainActivity);
break;
}
case("com.nohkumado.intstringsynchro.ADD_STRINGXML"):
{
Toast.makeText(context, "add token "+intent.getExtras(), Toast.LENGTH_LONG).show();
break;
}
case("com.nohkumado.intstringsynchro.DEL_STRINGXML"):
{
Toast.makeText(context, "del token "+intent.getExtras(), Toast.LENGTH_LONG).show();
break;
}
default:
{
Toast.makeText(context, "no idea what to do with "+intent, Toast.LENGTH_LONG).show();
Log.d(TAG,"no idea what to do with "+intent);
}//default
}// switch (intent.getAction())
}// public void onReceive(Context context, Intent intent)
}//class
as pointed out,i had, erroneously, in the receiver part put a
<category android:name="android.intent.category.DEFAULT"/>
which meant that in the best case only the first intent filter got fired, and not the others.... removed that
now as an other app, i created a small Tester, it has only 3 buttons to trigger the 3 actions i want to pass on as intents, since it was only a small test, i bound the onClick event in the layout file:
package com.nohkumado.istester;
import android.app.*;
import android.content.*;
import android.net.*;
import android.os.*;
import android.view.*;
import android.widget.*;
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}//onCreate
public void callIntString()
{
callIntString(null);
}
public void callIntString(View but)
{
Toast.makeText(this, "call int string", Toast.LENGTH_SHORT).show();
String name="com.nohkumado.intstringsynchro.EDIT_STRINGXML";
Intent callIt = new Intent(name);
try
{
startActivity(callIt);
}
catch (ActivityNotFoundException e)
{
Toast.makeText(this, "no available activity"+callIt, Toast.LENGTH_SHORT).show();
//callGooglePlayStore();
}
}
private void callGooglePlayStore()
{
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.android.vending");
ComponentName comp = new ComponentName("com.android.vending", "com.google.android.finsky.activities.LaunchUrlHandlerActivity"); // package name and activity
launchIntent.setComponent(comp);
launchIntent.setData(Uri.parse("market://details?id=com.nohkumado.intstringsynchro"));
startActivity(launchIntent);
}//callIntString
}
and here was my understanding problem, instead of using startActivity, i should have tried sendBroadcast(launchIntent);
Ok, to close this up...
first, i was not completely aware, that the activity contract in the manifest, opens a way for anyone to call on this activity.
Next, i have a specific application, that i want to open up to others, meaning editing the strings.xml files of android projects, and i wanted to propose the equivalent of a REST API, with LIST/EDIT, ADD, DEL.
Now, if we work with activities, the easiest way to get a handle on my activity from outside is, i think, like this:
Intent call = pm.getLaunchIntentForPackage("com.nohkumado.intstringsynchro");
followed by some putExtra calls to insert a token identifying a specific action, and an eventual value to perform on....
finished by a startActivity
this way, the default activity is started, regardless of its name, and the intent passed on, that can be read in the onCreate method of the MainActivity.
To implement my REST API, i tryed to make 3 entry points to my app, one for each type of access, only the LIST/EDIT firing up a UI, the 2 others spawning a series of Asynctasks doing the work in background. But this meant, that an eventual user has to know wich activities to address.
So i reverted to using the putExtra of the intent with a token/value pair to implement my REST-like API.....
For educations sake i tryed out the way over the broadcast mechanism, the advantage beeing that on the client side there seems no risk of crash, no need to catch a ActivityNotFound Exception, and, from a typo in my code, i noticed that the intent activity doesn't need to be bound to my actual app, i can choose any name i want.
For this, on the client side i needed to :
String name="com.nohkumado.intstringsynchro.EDIT_STRINGXML";
Intent callIt = new Intent(name);
sendBroadcast(callIt);
but on my app side i need to implement a complete BroadCastreceiver....
On the other side, this mechanism was extremely slow, giving the whole operation a very sluggish feel
Please correct me if i got it right this time, and i am open to suggestions if there a better ways to achieve my goal to propose to others those LIST/EDIT, ADD and REMOVE functionalities?
the reports back an activity not found exception
Apparently, you do not have an activity with an <intent-filter> for an action string of com.nohkumado.intstringsynchro.EDIT_STRINGXML. Your manifest in the question certainly does not have such an activity. Your manifest in the question has a <receiver> element with an odd <intent-filter> that, among other things includes that action. However, <receiver> and <activity> are not the same thing.
Either change your code to send a broadcast, or change your code to have an activity with that action string.
I launch a new activity "ActivityB" when keypad is locked.(ActivityA has been backgrounded before the keypad is locked).
ActivityB times out after 30 secs and supposed to close itself, so I called finish after 30 secs, though is not visible, after I unlock I see 2 seperate apps/activities in background.
So I used Intent.ACTION_USER_PRESENT broadcastreceiver to finish activityB, still it doesnt work.
Manifest.xml
<receiver
android:name="com.example.reciever.UnlockReceiver">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
UnlockReceiver:
public class UnlockReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent intent) {
if (ActivityB.b != null) {
ActivityB.b .finish();
}
}
}
ActivityB:
private Activity b;
onCreate() {
b= this;
}
ActivityB is started as we receive push:
Intent pushIntent = new Intent(context.getApplicationContext(), ActivityB.class);
pushIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
I see the onReceive called fine when I unlock the device, but it doesn't finsih ActivityB in the background. Hence I see 2 of the same apps in background
you may have an intent in activity a which is creating the activity b;
The issue was fixed after I set the below property in manifest file
android:launchMode="singleTop"
I have a Radio application that uses a Service for media player and a BroadcastReceiver to check for incoming and outgoing calls to stop or restart the media player service, it works but when I quit my app the BroadcastReceiver is still checking for calls and it crashes my application even that is gone (it has been finish) here is the code
Manifest
<service android:name=".PlayerService"
android:label="#string/app_name">
<intent-filter>
</intent-filter>
</service>
<receiver android:name=".IncomingCallInterceptor">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
the BroadcastReceiver
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
public class IncomingCallInterceptor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (PlayerService.getInstance() != null) {
if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
PlayerService.getInstance().stopMusic();
}
if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) {
PlayerService.getInstance().stopMusic();
}
if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) {
PlayerService.getInstance().startMusic();
}
}else{}
}
}
How can I make it stop?
Remove the receiver from your manifest and register it locally in your service:
Use this code in your service's onCreate() method:
IncomingCallInterceptor interceptor;
public void onCreate() {
super.onCreate();
//your code here
incerceptor = new IncomingCallInterceptor();
IntentFilter f = new IntentFilter();
f.addAction("android.intent.action.PHONE_STATE");
f.addAction("android.intent.action.NEW_OUTGOING_CALL");
registerReceiver(interceptor, f);
}
Don't forget to unregister the receiver in your service's onDestroy method:
public void onDestroy() {
unregisterReceiver(interceptor);
}
You have defined your broadcast receiver in manifest, thats why your app can still listen to those broadcasts even if you haven't launched your app (or run the service). To fix this, register your broadcast receiver programmatically inside your service so that it only works when the player is running and not all the times.
Or in your current receiver, before calling start/stop music methods, you should check if the service is already running or not.
If you provide the crash log it will help more.
By reviewing your code, all is good, but when you check for PlayerService.getInstance() not null
it will give you the instance always, just apply a mediaplayer object not null check as well, while your going to play or stop or pause.
Hope it will help you.
BACKGROUND
I have a task (i.e. app) with multiple activities.
QUESTION
How do I bring a task to the front w/o re-ordering the activity stack for that task?
USER SCENARIO
When the device boots (i.e. after android.intent.action.BOOT_COMPLETED broadcast is received) my app starts, call this task 1, and displays activity A (code below). As the user interacts with task 1, he/she opens activity B on the stack (activity B is now currently displayed on screen). Next the user taps the home key and opens some other task, call it task 2, then locks the screen. The user unlocks the screen, and the android.intent.action.USER_PRESENT intent is broadcast and received by my app (see manifest snippet below). My executes the startActivity() call in my IntentReceiver class as expected, but instead of just bringing the task to the foreground it creates a new task, call it task 3.
CHANGES I'VE TRIED
If I modify or change the Intent.FLAG_ACTIVITY_NEW_TASK to any other intent then I get this error message:
Calling startActivity() from outside of an Activity context requires
the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
...so it looks like I have to use Intent.FLAG_ACTIVITY_NEW_TASK.
If I change the main activity's lauchMode to "singleTask" the correct task is brought to the foreground (i.e. no new task is created), but the activity stack is reordered such that activity_A is on top of the stack.
At this point I am at a loss as to how to bring an existing task to the foreground w/o re-ordering the activity stack while listening for the android.intent.action.USER_PRESENT intent, any help would be appreciated.
BTW, This app is being delivered on to a group of employees, not the general public, on company owned android devices.
CODE SNIPPETS
To start the app I setup a broadcast receiver in my manifest file.
<application
:
<activity
android:label="#string/app_name"
android:launchMode="singleTop"
android:name=".activities.activity_A" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
:
<receiver
android:enabled="true"
android:name=".utilities.IntentReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter >
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
In my IntentReceiver class, I start my main activity...
public class IntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, activity_A.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
Here's a slightly hackish implementation that works for me:
Create a simple BringToFront activity that simply finish() itself on its onCreate():
public class BringToFront extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
finish();
}
}
In your BroadcastReceiver, start the BringToFront activity above instead of your activity_A if the action is USER_PRESENT:
#Override
public void onReceive(Context context, Intent intent) {
Class<? extends Activity> activityClass = activity_A.class;
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
activityClass = BringToFront.class;
}
Intent i = new Intent(context, activityClass);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
This works because the BringToFront activity has the same taskAffinity as your activity_A and starting it will make the system bring the existing task to the foreground. The BringToFront activity then immediately exit, bringing the last activity on your task (activity_B in your scenario) to the front.
It's worth noting that on API level 11 (Honeycomb), a moveTaskToFront() method is added to the system's ActivityManager service that might probably be the better way to achieve what you want.
Ok, I was able to get this to work by adding a static global variable in my main activity (activity_A). In onCreate I set isRunning = true, and onDestory = false. Then in my IntentReceiver class I check the isRunning to determine which activity to start:
:
if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)) {
if (GlobalVariables.isRunning) {
activityClass = BringToFront.class;
} else {
activityClass = activity_A.class;
}
} else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
activityClass = activity_A.class;
}
:
I'm monitoring incoming SMSs.
My app is working perfectly with a BroadcastReceiver. However it is working from an Activity and would like to keep the BroadcastReceiver running all the time (and not just when my Activity is running).
How can I achieve this? I've looked through the lifecycle of the BroadcastReceiver but all that is mentioned in the documentation is that the lifecycle is limited to the onReceive method, not the lifecycle of keeping the BroadcastReceiver checking for incoming SMS.
How can I make this persistent?
Thanks
You need to define a receiver in manifest with action name android.intent.action.BOOT_COMPLETED.
<!-- Start the Service if applicable on boot -->
<receiver android:name="com.prac.test.ServiceStarter">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Make sure also to include the completed boot permission.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Use Service for this to make anything persist. And use receivers to receive Boot Up events to restart the service again if system boots..
Code for Starting Service on boot up. Make Service do your work of checking sms or whatever you want. You need to do your work in MyPersistingService define it your self.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ServiceStarter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent("com.prac.test.MyPersistingService");
i.setClass(context, MyPersistingService.class);
context.startService(i);
}
}
Service or Boot Completed is not mandatory
In fact, you don't need to implement a Service or register to android.intent.action.BOOT_COMPLETED
Some examples shows how to register/unregister a BroadcastReceiver when activity is created and destroyed. However, this is useful for intents that you expect only when app is opened (for internal communication between Service/Activity for example).
However, in case of a SMS, you want to listen to the intent all the time (and not only when you app is opened).
There's another way
You can create a class which extends BroadcastReceiver and register to desired intents via AndroidManifest.xml. This way, the BroadcastReceiver will be indepedent from your Activity (and will not depend from Activity's Life Cycle)
This way, your BroadcastReceiver will be notified automatically by Android as soon as an SMS arrive even if your app is closed.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
...
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application>
....
<receiver android:name=".MyCustomBroadcastReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
MyCustomBroadcastReceiver.java
public class MyCustomBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent != null) {
String action = intent.getAction();
if(action != null) {
if(action.equals("android.provider.Telephony.SMS_RECEIVED")) {
// DO YOUR STUFF
} else if (action.equals("ANOTHER ACTION")) {
// DO ANOTHER STUFF
}
}
}
}
}
Notes
You can add others intent-filters to AndroidManifest and handle all of them in same BroadcastReceiver.
Start a Service only if you will perform a long task. You just need to display a notification or update some database, just use the code above.
Add Broadcast Reciever in manifest:
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
Create Class BootReciever.java
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
// +++ Do Operation Here +++
}
}
}
Beside #Javanator answer I would like to include a case for Android version of (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) In my case this is working for Android SDK 29 (10)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context,FloatingWindow.class));
} else {
context.startService(new Intent(context, FloatingWindow.class));
}
use this code and also mention the broadcast in Manifest also:
public class BootService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){
Toast.makeText(context, "Boot Completed", Toast.LENGTH_SHORT).show();
//write code here
}
}
}
I just want to mention that in case of some Chinese phone brands (e.g. MI), you need to go to Settings and give autostart permission to your app.
Otherwise the battery optimisation feature will kill your service in background and broadcast receiver will not work.
So you can redirect your user to Settings and ask them to give that permission.