How to stop service when ACTION_SCREEN_OFF - android

I am trying to make my UpdateService for my digital clock widget stop when the screen is turned off to conserve battery, and then back on when the screen is activated. I currently have it in my onReceive() in my AppWidgetProvider, but I have also tried it in a BroadcastReciever.
My current code is:
public static boolean wasScreenOn = true;
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.d("Screen switched on. ", "Starting DigiClock UpdateService.");
context.startService(new Intent(UpdateService2by2.ACTION_UPDATE));
wasScreenOn = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.d("Screen switched off. ", "Stopping DigiClock UpdateService.");
context.stopService(new Intent(context, UpdateService2by2.class));
wasScreenOn = true;
}
Can anyone help me out here? I am stumped.

I'm fairly sure that you have to register your receiver in code for ACTION_SCREEN_OFF/ON. I don't think registering them in the manifest will work.

It seems you cannot register for the ACTION_SCREEN_ON/OFF intents with a filter in the manifest. You have to register your BroadcastReceiver in code. See here for an examples for an activity and a service.
You'll only receive the intent if your service or activity is running. In contrast to other broadcast events, the system will not start your process to handle the SCREEN_ON intent. It is similar to ACTION_BATTERY_CHANGED in this regard.
To handle this intent with a widget, I think you have to start a service that listens for the intent and then notifies your widget.

The article linked from Jens' answer to this same question provides a great presentation on this topic. I used it to implement a solution that worked for me.
The key insight is that your BroadcastReceiver can only be registered in code; you neither want nor need an entry in your manifest.
I recommend the article, but for those in a hurry, the simplest possible functioning approach would be to just paste something like this into your activity's onCreate() method:
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver screenoffReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.v("screenoffReceiver", "SCREEN OFF");
}
else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.v("screenoffReceiver", "SCREEN ON");
}
return;
}
};
registerReceiver(screenoffReceiver, filter);
As the article points out, for a running activity, onPause() / onResume() are always called when the power button is used to blank/unblank the display, so if you have something that you don't mind doing even in those cases for which the power button was not the reason for the onPause() or onResume() call, you can do it in those methods and thereby catch every instance of the power button being used to blank or unblank the screen (along with calls due to other causes), without the overhead of creating a BroadcastReceiver.
That was the case for me; I had a button that made my activity's main view invisible when pressed and visible when released, and if the user held the button down and then tapped the power button while the app was hidden, it would stay hidden permanently when the screen was unblanked by another tap of the power button. So in my case I only had to put a setVisibility(VISIBLE) call for my main view in my activity's onResume() override.
In fact, as the article shows, even if you only want to respond to the power button events, the proper way to do that is to set a flag in the BroadcastReceiver and then test that flag in onPause() and/or onResume() to see if the power button was the specific cause of the call to those methods.

Maybe you should use
public class MyReceiver extends BroadcastReceiver {..}
And then use this classname in the manifest

Related

Broadcast listener to user touch in Android

I want to know the time left before screen off.
I can easily get the timeout defined by system, but the counter resets with each touch the user do. so I need to reset the counter on my side as well.
I don't want to have an activity in foreground but to use (probably) a broadcastReceiver.
You can't do this but you can detect when screen off and screen on - Use actions:
Intent.ACTION_SCREEN_ON
Intent.ACTION_SCREEN_OFF
But do not put it in AndroidManifest.xml you must register this broadcast, example:
public class ScreenReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
if(action.equals(Intent.ACTION_SCREEN_ON)){
// screen on
} else {
// screen off
}
}
}
To register broadcast (do not call it in this broadcast (ScreenReceiver) - call it in Service, Activity or Application):
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new ScreenReceiver(), filter);
I guess the best solution is register broadcast in service because when Activity calls onDestroy() broadcast may be automatically unregistered.

I want to create a "global" screen wake listener that stays active in all activities.. how do I go about doing this?

I want my app to be globally aware that the screen has come out of sleep mode.
I can put this code into every activity class
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "screen woke up", Toast.LENGTH_SHORT).show();
}
}, new IntentFilter(Intent.ACTION_SCREEN_ON));
But I don't want it listening in the background multiple times when multiple activities are active.
How would I go about setting this up so every activity shares the same "listener" if that makes sense?
Solved. I wrote a class that extends Application, and then added that in my manifest. <Application android:name="com.blahblah.myclass" >

How can I tell if my activity was started by a broadcast receiver?

I have an alarm clock application I am making. I have one activity where the user sets the time for the alarm. This registers a broadcast receiver class I have made to receive a broadcast at the time of the alarm, using AlarmManager. I then start a new activity in the receivers onReceive(). When this second activity starts, the alarm clock sound is played in onStart(). How can I tell if my activity has been started by a receiver or if the user is just multitasking with the application? I don't want my sound to play when the user silences the alarm, presses the home button, and then renters the app (while still on the sound playing activity).
Just send an extra via the intent you use in your onReceive() method:
Intent intent = new Intent(this, NextActivity.class);
intent.putExtras("playSound", true);
in your "sound playing" activity, you have to play the sound in onCreate():
boolean playSound = getIntent().getBooleanExtra("playSound", false);
This will return false if the intent-extra "playSound" does not exist or is set to false, true if it is set to true.
onCreate() is only called once (when the activity starts), while onStart() gets called everytime a user reenters your activity (i.e. through recent apps). You can see this in the lifecycle:
(diagram source)
Where Paused is called when something draws over you activity (e.g. low battery dialog), Stopped is called if you "exit" your app (e.g. through the home-button).
Start an activity or a service, etc., based on a received broadcast then you need a standalone broadcast receiver and you put that in your android manifest file. If you want your activity itself to respond to broadcasts then you create an instance of a broadcast receiver in your activity and register it there.
public class BRActivity extends Activity {
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
..................
..................
}
};
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(BROADCAST_ACTION);
this.registerReceiver(this.broadcastReceiver , filter);
}
public void onPause() {
super.onPause();
this.unregisterReceiver(this.broadcastReceiver );
}
}
So, this way the receiver is instantiated when the class is created (could also do in onCreate). Then in the onResume/onPause I handle registering and unregistering the receiver. Then in the reciever's onReceive method I do whatever is necessary to make the activity react the way I want to when it receives the broadcast.
You can do as below:
For each alarm user sets, you put an boolean flag in sharedpreference to true. E.g. you have three alarms then in sharedpreference you will have 3 flags.
Now suppose a alarm broadcast is received for alarm1 and activity2 is started.
Now in activity2 first thing you check is whether the flag for alarm1 which you set in sharedpreference is true or false, if true play sound.
When user silences the alarm or press home button then you can mark this flag to false, so next time if user starts activity from background, flag in sharedpreference will be false and sound will not be played.
Same thing you can achieve using sqlite db, by setting flags in sqlite db table instead of sharedpreference.
For the intent used to launch the sound playing activity use the FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS flag. So if the user moves out of the activity, it cannot be resumed.
Intent intent = new Intent(context, SoundActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
or in manifest
<activity
android:name="SoundActivity"
android:excludeFromRecents="true" >
</activity>
There are couple of solutions:
(1) one approach is to use a Singleton Class which can be shared across activity.
In this approach set a boolean flag of Singleton class in BroadcastReceiver and then check that flag in the activity (which is fired from your BroadcastReceiver) where you play sound. Please reset the flag if it is set. This solution assumes that the Broadcast receiver is part of your Android App package.
(2)
Alternatively, you can also use Intet.putExtra ("Key", Value) method when you start an activity from BroadcastReceiver. You can check this key in the Activity you started from BroadcastReceiver to know who started this activity.
So this will take care of detecting where you come from.
If you are simply trying to set single (one-shot ) alarm then creating another activity for playing the sound is OK. If you set repeat alarm ( alarm plays at a multiple interval), I am not sure how your application will behave.
I prefer to play the sound in the Broadcast receiver itself (registered as remote receiver in manifest) for a specified duration for a given alarm ( like 30 sec of sound or you can ask user to configure it).
So this way you can use the same BroadcastReceiver for playing sound for single-shot & multi-repeat alarm.
I will use the same PendingIntent for setting both Single-shot and multi-repeat alarm.
You can simply set flag or any value in Intent that will determine what's your purpose in that class ..
For Ex: For playing sound set a Boolean value to TRUE in Intent and send same over that class using bundle ..
Else
Set that Boolean value to FALSE if starting alarm class from some other class.
This is the code to find if app is started via broadcast receiver.
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String AlarmTriggerString = (String)intent.getSerializableExtra("AlarmTrigger");
Intent i = new Intent();
i.setClassName("com.prasath.viki.bot","com.prasath.viki.bot.MainActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT|Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("FromBroacastReceiver",true);
i.putExtra("AlarmTrigger",AlarmTriggerString);
context.startActivity(i);
}
}
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean FromReceiver = (boolean)getIntent().getSerializableExtra("FromBroacastReceiver");
String AlarmTriggerString = (String) getIntent().getSerializableExtra("AlarmTrigger");
if(AlarmTriggerString != null && FromReceiver != null && FromReceiver == true)
{
// do something
}
}

Getting SCREEN_ON and SCREEN_OFF intents from a widget

I have a widget and I would like to check if the screen is off or on.
I can't use PowerMananger.isScreenOn because I want to support Android 1.5/1.6 .
So I tried to register SCREEN_ON/SCREEN_OFF actions in the manifest but that doesn't work. Seems like only registerReceiver works for those intents. (Android - how to receive broadcast intents ACTION_SCREEN_ON/OFF?)
The question is, where should I register my widget?
I can't register the screen intents receiver from my widget because you can't call registerReceiver from another BroadcastReceiver that is stated in the manifest.
I thought about calling it in the onCreate of my configuration activity.
The problem is that I don't call unregisterReceiver, so I get an exception for a leak.
Is there any other solution to this?
Thanks.
My solution is to start a service in the public void onReceive(Context context, Intent intent) method in the AppwidgetProvider subclass. Like:
if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_ENABLED)) {
Intent listenerService=new Intent(context,ScreenMoniterService.class);
startService(listenerService);
return;
}
Then in the public void onCreate() method of this service, register the BroadcastReceiver and in the public void onDestroy() method, unregister it.
Of course, you should stop that service when all of the appwidget are deleted.
if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_DISABLED)) {
Intent listenerService=new Intent(context,ScreenMoniterService.class);
stopService(listenerService);
return;
}
registerReceiver:
final IntentFilter bcFilter = new IntentFilter();
bcFilter.addAction(Intent.ACTION_SCREEN_ON);
bcFilter.addAction(Intent.ACTION_SCREEN_OFF);
context.getApplicationContext().registerReceiver(this, bcFilter);
unregisterReceiver:
context.getApplicationContext().unregisterReceiver(this);
(Just at AppWidgetProvider!)

Inform Activity from a BroadcastReceiver ONLY if it is in the foreground

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.

Categories

Resources