I am currently working on a test application that will show a dialog that asks the user to stop using the app if he/she is driving. Supposing that the user has their GPS on, I have created a custom intent to be broadcast and then on the onReceive() method of the subclassed BroadcastReceiver class, the speed of the vehicle is calculated and if it's found to be in the range of usual car speeds, a dialog is shown to the user.
The second part (the onReceive() code) works great: the speed is calculated and the dialog is shown. However, I am at a loss about how the dialog can be shown at any point of the lifeycle of that specific activity. If the app is launched while the user is driving or if the user navigates to another activity and then back to the activity that does the speed check (essentially, if onCreate() or onResume() are called), the dialog is shown. If the dialog is dismissed and the user stays on the same activity, the dialog is not reshown, even if the user keeps driving (to be clear, the desired behavior is for the dialog to keep getting shown for as long as the user keeps driving and is in the activity). Here is the code of the activity that sends the Broadcast(the relevant code, everything else is omitted):
public class MainActivity extends Activity {
IntentFilter intentFilter;
SpeedReceiver speedceiver = new SpeedReceiver();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
broadcastSender();
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.animationshitz.ACTION_GET_SPEED");
}
public void onResume() {
super.onResume();
registerReceiver(speedceiver, intentFilter);
Log.d("Loggger", "BROADCAST RECEIVER REGISTERED");
}
public void onPause() {
super.onPause();
unregisterReceiver(speedceiver);
}
public void broadcastSender() {
Intent intent = new Intent();
intent.setAction("com.example.animationshitz.ACTION_GET_SPEED");
sendStickyBroadcast(intent);
Log.d("Loggger", "BROADCAST SENT");
}
}
Am I completely wrong about this? I thought that the sticky broadcast would keep calling the onReceive() method of the receiver so that it would keep checking the speed in the background. Is there some other way to achieve what I want? Maybe the onReceive() method should start a service that keeps checking the speed in the background? Any help would be really appreciated.
Related
I am trying to create an Application which toasts the battery level whenever the battery level changes. I am using Broadcast receiver and which is registered with 'ACTION_BATTERY_CHANGED'. It works fine in the background only when the app is still in the recents (i.e. the app is not closed), but it doesn't work when the app is closed!
['ACTION_BATTERY_CHANGED' is fired when the BatteryLevel changes.]
Below is the code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//registering the reciever.
this.registerReceiver(this.mBatInfoReciever,new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
private BroadcastReceiver mBatInfoReciever = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra("level",0);
/*To get the Battery Level.*/
Toast t=Toast.makeText(getApplicationContext(),String.valueOf(level),Toast.LENGTH_LONG);
t.show();
}
};
}
Once your MainActivity is destroyed, your BroadcastReceiver goes away. This will occur:
If the user presses BACK, or
If the user navigates away by some other means (e.g., presses HOME) and your process is terminated
You seem to be defining "the app is closed" as being when the user swipes the app out of the overview screen (recent task list). That usually terminates the app's process, so your BroadcastReceiver will be gone at that point.
This problem is a little different from most 'display toast' issues. I am not asking to display Toast messages in other activities but to see Toast messages sent by a background service while in another activity besides the main activity. I DO see the Toast messages in the main activity!
I have an application that has a background service. When certain events happen in this background service a Toast message is displayed. The background service receives data from external BT and BLE devices and sends messages out over wifi. The toast messages show certain important events in those processes. The MainActivity and the background service use the application context from getApplicationContext() for the display of this toast message.
However, these messages do not display if I move to another Activity. For example, to configure some parameters. I am not trying to display toast messages from other activities; that I can do. But how can I get the toast messages from the background service to also display while I am in another Activity besides the MainActivity? I think I will need to do something like 'run the activity on the application context' though I have no idea how to do it (or even if it is possible).
Use BroadcastReceiver for this purpose. In each of your Activity you need to declare a BroadcastReceiver like this.
private class ShowToastBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("Message");
Toast.makeText(OtherActivity.this, message, Toast.LENGTH_LONG).show();
}
}
You need to register the BroadcastReceiver each time in your onResume and unregister it back in onPause function of your activities.
// This is in your Activity
private ShowToastBroadcastReceiver showToastBroadcastReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showToastBroadcastReceiver = new ShowToastBroadcastReceiver();
}
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(showToastBroadcastReceiver, new IntentFilter("SHOW_TOAST"));
}
#Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(showToastBroadcastReceiver);
}
Now you need to send the broadcast from your Service to get the toast to be shown in your Activity.
// This is in your Service
Intent intent = new Intent("SHOW_TOAST");
intent.putExtra("Message", "This toast needs to be shown");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
You should be able to display a Toast from any context, including a Service. The Toast Notifications developer guide has sample code that shows how:
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
In other words, you don't need an Activity at all. You can display the Toast directly from the Service. If you're worried about doing it from a separate thread, create a Handler in the main thread of the Service and post a Runnable or message to that handler to display the Toast.
One other possibility is that you are using a bound service and it is going away when you switch away from the main activity. Make sure the service itself is still running.
Oh, and also make sure that the 'Show notifications' setting for your app hasn't been unchecked.
Thanks to Reaz I found a stupid error I had made. I was unregistering the BroadcastReceiver handling the Toasts at onStop() instead of onDestroy(). Moving the unregistration to onDestroy() and registration and filters to onCreate() solved the problem.
When my service is running and I can see my app on the screen, everything works fine. My service sends broadcast messages and I can see them in my "MainActivity". But as soon as I push the home button and my app is no longer in the foreground, it stops. The service is still running but the broadcasts don't arrive at my "dead" or "pausing" app. This is my code:
Main Activity:
onCreate:
Intent intent = new Intent(getApplicationContext(), MainGate_Service.class);
startService(intent);
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
private BroadcastReceiver ServiceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("ServiceMessage");
if(message != null) {
if(alarmActive && message.equals("ring"))
new soundPlayer().execute();
}
setNoti(message);
}
}
Service:
#Override
protected void onHandleIntent(Intent intent) {
ring = rcv_ring();
Intent ServiceIntent = new Intent("MainGate_ring");
ServiceIntent.putExtra("ServiceMessage", ring);
LocalBroadcastManager.getInstance(this).sendBroadcast(ServiceIntent);
}
whenever you press home button onStop gets call and there you are unregister receiver so there is not broadcast receiver who can receive broadcast.
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
Remove above line from onStop() or unregister it whenever your service stop.
onStop#LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
literally unregister receiver. So there is no receiver to receive broadcasted message from service.
The problem is here
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(ServiceReceiver);
}
Activity.onStart() :
Called after onCreate(Bundle) — or after onRestart() when the activity had been stopped, but is now again being displayed to the user. It will be followed by onResume().
Activity.onStop()
Called when you are no longer visible to the user. You will next receive either onRestart(), onDestroy(), or nothing, depending on later user activity.
So as soon as you leave your activity onStop() is called and you unregister your receiver.
Don't put your unregister code in onDestroy() either, because it may or may not be called and the Activity might be killed without calling it.
As suggested by #Naveen Dissanayake you might want to reconsider your logic.
You register your BroadcastReceiver inside activity, so, it will depend on Activity's lifecycle. When you press back button, activity goes into 'stopped' state - methods onPause and onStop is called. Android can also destroy this activity in it is low on resources.
Service, on the other hand, i smeant to be running inndefinetely, even when ui is not ready. If you wanat to receive notifications from Activity, there is two
possible solutions:
- Create, store and manage BroadcastReceiver in Application instance - Application class is still running until your app is destroyed. It seems like you want to play sound when service notify you about some action.
Register BroadcastReceiver in onCreate and unregister in onDestroy in notifications.
- Another solution - use another Service if you want to trigger some action or IntentService, reacting to that broadcast.
I woud consider solution - create some ResponseService, start it along with your MainGate_Service (from Application) and stop it from application too. In that Service register BroadcastReceiver or, add IntentFilter into manifest if you want it to start even when app is not running. In your Activity, bind to that service. Serive will know if some UI is attached (if Activity is bound), and, if it is, will notify activity. If it don't - will do some other things (perhaps, show notification)
If you want to receive Broadcast even if your Activity is in
Background then,
Register in onCreate(...)
#Override
protected void onCreate(Bundle sis) {
super.onCreate(sis);
setContentView(...);
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
Unregister in onDestroy()
#Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManager.getInstance(this).registerReceiver(ServiceReceiver, new IntentFilter("MainGate_ring"));
}
I am creating a replacement Car Home app for Android 2.0+ devices. The app needs to launch when the phone is inserted into the car dock, as well as terminate when it is removed from the dock. It also needs to be able to be launched from the app drawer.
I'm having a problem right now where once the phone is inserted and removed from the dock, I can no longer launch the app from the app drawer because every time I launch the app my BroadcastReceiver picks up a DOCK_EVENT action for some reason. I created a test project that only registers my BroadcastReceiver, and the same thing happens.
Here's the code for the BroadcastReceiver:
public class CarDockBroadcastReceiver extends BroadcastReceiver {
/**
* #see android.content.BroadcastReceiver#onReceive(Context,Intent)
*/
#Override
public void onReceive(Context context, Intent intent) {
// TODO Put your code here
if(intent.getExtras().containsKey("android.intent.extra.DOCK_STATE")){
int state = intent.getExtras().getInt("android.intent.extra.DOCK_STATE",1);
if(state == 0){
Log.i("Dock", "Removed from dock!");
((Activity)context).finish();
}
}
}
}
My main Activity is as follows:
public class MainActivity extends Activity {
/** Called when the activity is first created. */
CarDockBroadcastReceiver receiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
IntentFilter filter = new IntentFilter("android.intent.action.DOCK_EVENT");
receiver = new CarDockBroadcastReceiver();
registerReceiver(receiver, filter);
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
unregisterReceiver(receiver);
super.onDestroy();
}
}
The main Activity has an intent filter for action.MAIN, category.LAUNCHER, category.DEFAULT, and category.CAR_DOCK. Any ideas on why this is happening?
Intent.ACTION_DOCK_EVENT is a sticky broadcast. This means that when you register a receiver for it, you will immediately get the last-broadcast Intent for that action, then all subsequent broadcasts until it is unregistered. There is nothing much you can do about it, other than finding a way of dealing with the situation.
BTW, I recommend using Intent.ACTION_DOCK_EVENT rather than "android.intent.action.DOCK_EVENT" in your IntentFilter. This way, if for some goofy reason they change the actual string, your code does not need to change.
Maybe it's easy, but I couldn't really figure this out right so far... I got a BroadcastReceiver waiting to get triggered by the AlarmMangager - this works fine.
Now: because the event, if it occurs, needs to refresh some elements on screen of the main Activity, I would like to send an Intent from that background BroadcastReceiver to my Activity - but only if it is currently in the foreground, aka active.
If it is not running or not visible, I don't care - and the last thing I want to do is start the Activity by my intent! I handle repainting of the views in my onResume() method, so I don't care at all.
Any hints on how to do that?
Thanks!
EDIT: my BroadcastReceiver is waiting for alarms that must be notified to the user. So, it must be there and declared in the manifest. The problem is: it will have to decide whether the mentioned Activity is currently up in front or not.
I believe that you're familiar with AlarmManager now (creating a new Alarm, register a receiver...) so I will not talk about that. Just give you a solution for your question.
Instead of registering a BroadcastReceiver in a class file and in manifest, you only create a new BroadcastReceiver in your activity, and then, register it in onResume method, and unregister it in onPause method, sth like this in your activity:
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//do something
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mIntentFilter = new IntentFilter();
mIntentFilter.addAction("your alarm action");
...
}
#Override
protected void onResume() {
registerReceiver(mIntentReceiver, mIntentFilter);
...
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(mIntentReceiver);
...
super.onPause();
}
The receiver will only receive the alarm intent when your activity is in foreground :)
(Sorry if my English is not clear)
So this is almost Bino's answer, but: instead of moving the receiver into the activity, use two receivers, with different Intents. The first one is your original alarm Intent, with a receiver registered in the manifest as you already have, and then that receiver sends a second broadcast intent, which is handled by a receiver registered by the activity as Bino says.
I've done this in my own timer project, on github. Here are the alarm receiver and the requery receiver. Hope that helps.