Trying to read preferences upon BOOT_COMPLETED - android

I am setting an alarm from my app, and when rebooting, I see I need to reset the alarm since it does not survive reboots. I created a broadcast receiver to receive BOOT_COMPLETED and this works so my manifest must be correct.
When I try this line of code below I get in trouble. I need to retrieve the time that I need to set the alarm to but it looks like I cannot access the prefs of my app (called S) because my app has never started. NullPointerException :
if ( S.prefs.getBoolean(S.SCHEDULEDSTATUS, false) == true ) { }
I suppose it should be obvious that I cannot read a public static final of an activity that has not been created.
Do I have to store my alarm time in a file or am I missing something here?

You have to access it via the context you get in your reciever:
public void onReceive(Context con, Intent intent) {
final SharedPreferences settings = con.getSharedPreferences(PREFS, 0);
boolean boolValue = settings.getBoolean(BOOL, false);
}

Related

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
}
}

Pass data to onDestroy() of Service

I want to know if the service was terminated from a particular activity, so I'm passing a string from that activity while calling stopServivce(service).
Here's the code :
Intent service = new Intent(Activity.this,
service.class);
service.putExtra("terminate", "activity terminated service");
stopService(service);
But I can seem to access this variable with getIntent().getExtras().getString("terminate); in the onDestroy() method.
[EDIT]
I found a way around this obstacle, but I'd like my question to still be answered. I just did whatever I had to do in the onDestroy() method in the activity and then called stopService(service). I was lucky that my situation didn't need anything more complicated.
There is no way of accessing the Intent in onDestroy. You have to signal the service some other way (Binder, Shared Preferences, Local Broadcast, global data, or Messenger). A nice example of using broadcast is given in this answer. You can also get this to work by calling startService instead of stopService. startService only starts a new services if one does not already exist, so multiple calls to startService is mechanism yo send Intents to the service. You see this trick is use by BroadcastReceivers. Since you have access to the Intent in onStartCommand, so you can implement termination by check the Intent extras and calling stopSelf if instructed to terminate. Here is a sketch of it in action --
public int onStartCommand(Intent intent, int flags, int startId) {
final String terminate = intent.getStringExtra("terminate");
if(terminate != null) {
// ... do shutdown stuff
stopSelf();
}
return START_STICKY;
}
Just to illustrate what iagreen suggests;
In Activity
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("com.package.yourfilter");
broadcastIntent.putExtra("activity_name", "your_activity");
sendBroadcast(broadcastIntent);
In Service
private YourActionReceiver abc;
this.abc = new YourActionReceiver();
registerReceiver(this.abc, new IntentFilter("com.package.yourfilter"));
public class YourActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Get the name of activity that sent this message
}
}
Global State
is your friend. :)
Check a Global String whenever you need to (say before terminating). You may also want to have an enumeration state. Or a flag to see if state is valid.
Recipe:
The more general problem you are encountering is how to save state across several Activities and all parts of your application. A static variable (for instance, a singleton) is a common Java way of achieving this. I have found however, that a more elegant way in Android is to associate your state with the Application context.
As you know, each Activity is also a Context, which is information about its execution environment in the broadest sense. Your application also has a context, and Android guarantees that it will exist as a single instance across your application.
The way to do this is to create your own subclass of android.app.Application, and then specify that class in the application tag in your manifest. Now Android will automatically create an instance of that class and make it available for your entire application. You can access it from any context using the Context.getApplicationContext() method (Activity also provides a method getApplication() which has the exact same effect):
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
#Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
class BlahBlah extends Service {
#Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
This has essentially the same effect as using a static variable or singleton, but integrates quite well into the existing Android framework. Note that this will not work across processes (should your app be one of the rare ones that has multiple processes).
Credits go to #Soonil

How do I cancel all pending intents that are qued for intent Service

I have an intentservice that gets qued by the user and by my app automatically. I need to be able to kill all pending intents that are qued when the user logs out of my application, but I cannot seem to get that to work. I have tried stopService() and stopself(), but the intents continue to fire off the intentservice after the user has logged out. I would try to get the id of the intent but that is difficult as everytime the intentservice starts, the variable holding the intent id's is empty. Here is my intentservice code:
public class MainUploadIntentService extends IntentService {
private final String TAG = "MAINUPLOADINTSER";
private GMLHandsetApplication app = null;
private SimpleDateFormat sdf = null;
public boolean recStops = true;
public MainUploadIntentService() {
super("Main Upload Intent Service");
GMLHandsetApplication.writeToLogs(TAG,
"GMLMainUploadIntentService Constructor");
}
#Override
protected void onHandleIntent(Intent intent) {
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Started");
if (app == null) {
app = (GMLHandsetApplication) getApplication();
}
uploadData(app);
GMLHandsetApplication.writeToLogs(TAG, "onHandleIntent Finished");
}
#Override
public void onDestroy() {
GMLHandsetApplication.writeToLogs(TAG, "onDestroy Started");
app = null;
stopSelf();
GMLHandsetApplication.writeToLogs(TAG, "onDestroy completed");
}
public void uploadData(GMLHandsetApplication appl) {
//All of my code that needs to be ran
}
Unfortunately, I don't think it's possible to accomplish that with the standard IntentService methods since it doesn't offer a way to interrupt it while it's already going.
There are a few options I can think of that you can try to see if they fit your need.
Copy the IntentService code to make your own modifications to it that would allow you to remove pending messages. Looks like someone had some success with that here: Android: intentservice, how abort or skip a task in the handleintent queue
Instead of copying all the IntentService code, you might also be able to Bind to it like a normal Service (since IntentService extends Service) so you can write your own function to remove pending messages. This one is also mentioned in that link.
Rewrite the IntentService as a regular Service instead. With this option, you'd have more control over adding and removing messages.
I had what sounds like a similar situation where I was using an IntentService, and I eventually just converted it to a Service instead. That let me run the tasks concurrently and also cancel them when I needed to clear them.
Here
When should I free the native (Android NDK) handles? is the HangAroundIntentService class that has the method cancelQueue().
The class also has the method
public static Intent markedAsCancelIntent(Intent intent)
that converts an intent into a cancel intent, and
public static boolean isCancelIntent(Intent intent).
The class is based on the open-sourced Google's code.
Just a thought but inside of your onhandleintent can you have an argument that checks to see if app is running if not then don't run the code? example. In the start of your app you could have a static var
boolean appRunning;
Next in your onhandle of the intent, when you set the appRunning to false, after an onPause or onDestroy of activity, you could wrap the onhandleintent code in a boolean:
protected void onHandleIntent(final Intent intent) {
if(MainActivity.appRunning){
...
}
}
Just a thought

cancelling an alarm after program crash

I have a background service which sets a repeating alarm, does its task and stops itself. Then when the alarm wakes up it starts the service again. If the program crashes the alarm is still around and wakes up the alarm broadcastreceiver. Is there any way to cancel the alarm on a crash - I suppose I might be able to cancel the alarm from any caught exceptions but what about other causes?
Or when the alarm Broadcast receiver is triggered is there any way to tell if the program that set it has crashed?
The activity lifecycle can detect a clean finish from an "unexpected" termination through use of the isFinishing() function in the onDestroy() callback. You could add this to each activity in your app:
protected void onDestroy () {
boolean crashedOnLastClose = false;
if (!isFinishing()) {
// The application is being destroyed by the O/S
crashedOnLastClose = true;
// Store the result somewhere for the BroadcastReceiver to check later
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", crashedOnLastClose);
editor.commit();
}
}
Then in your BroadcastReceiver, pull the result back from the SharedPreferences framework (or wherever you decide to store it), and cancel the action if the value is true:
public class MyAlarmReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean crashedOnLastClose = settings.getBoolean("crashedOnLastClose", false);
if (!crashedOnLastClose) {
// No crash on last run, handle the alarm
// ...
}
}
}
Remember to reset it back to false on next launch though! Something like this should do:
protected void onCreate (Bundle savedInstanceState) {
// Make sure this always resets to FALSE on launch
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", false);
editor.commit();
}
This should capture your crash events; but remember it will also detect other times when your app is force-closed - such as if the memory is low on the device. To distinguish between these kinds of events you would need to inspect the system status as well as isFinishing().
EDIT - Services
Your Service has a separate lifecycle, you have to treat it almost like a separate app. As you've noticed, there is no "isFinishing()" here, but Services are actually quite simple since they are only terminated cleanly by your code, which you can trap pretty easily.
In your Service, add a new boolean (perhaps called "isFinishing") and set it to true when your service is finished with. Then override onDestroy() as well and add a check similar to the one I described for your activities:
protected void onDestroy () {
// Store the result somewhere for the BroadcastReceiver to check later
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("crashedOnLastClose", !isFinishing);
editor.commit();
}
The broadcast receiver will then use the same code we added earlier to detect if either the service or an activity crashed.

Lock android app after a certain amount of idle time

My android application requires a password to be entered in the first activity. I want to be able to automatically send the application back to the password entry screen after the application has been idle for a fixed amount of time.
The application has multiple activities, but I would like the timeout to be global for all activities. So, it wouldn't be sufficient to create a timer thread in the onPause() method of an Activity.
I'm not sure what the best definition for the application being idle is, but no activities being active would be sufficient.
I know another answer is accepted already, but I came across this working on a similar problem and think I'm going to try an alternate much simpler approach that I figured I may as well document if anyone else wants to try to go down the same path.enter code here
The general idea is just to track the system clock time in a SharedPreference whenever any Activity pauses - sounds simple enough, but alas, there's a security hole if that's all you use, since that clock resets on reboot. To work around that:
Have an Application subclass or shared static singleton class with a global unlocked-since-boot state (initially false). This value should live as long as your Application's process.
Save the system time (realtime since boot) in every relevant Activity's onPause into a SharedPreference if the current app state is unlocked.
If the appwide unlocked-since-boot state is false (clean app start - either the app or the phone restarted), show the lock screen. Otherwise, check the SharedPreference's value at the lockable activity's onResume; if it's nonexistent or greater than the SharedPreference value + the timeout interval, also show the lock screen.
When the app is unlocked, set the appwide unlocked-since-boot state to true.
Besides the timeout, this approach will also automatically lock your app if your app is killed and restarts or if your phone restarts, but I don't think that's an especially bad problem for most apps. It's a little over-safe and may lock unecessarily on users who task switch a lot, but I think it's a worthwhile tradeoff for reduced code and complexity by a total removal of any background process / wakelock concerns (no services, alarms, or receivers necessary).
To work around process-killing locking the app regardless of time, instead of sharing an appwide singleton for unlocked-since-boot, you could use a SharedPreference and register a listener for the system boot broadcast intent to set that Preference to false. That re-adds some of the complexity of the initial solution with the benefit being a little more convenience in the case that the app's process is killed while backgrounded within the timeout interval, although for most apps it's probably overkill.
I dealt with this by using the AlarmManager to schedule and cancel timeout action.
Then in the onPause() event of all of my activites, I schedule the alarm. In the onResume() event of all of my activities, I check to see if the alarm goes off. If the alarm went off, I shutdown my app. If the alarm hasn't gone off yet I cancel it.
I created Timeout.java to manage my alarms. When the alarm goes off a intent is fired:
public class Timeout {
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes
private static PendingIntent buildIntent(Context ctx) {
Intent intent = new Intent(Intents.TIMEOUT);
PendingIntent sender = PendingIntent.getBroadcast(ctx, REQUEST_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return sender;
}
public static void start(Context ctx) {
ctx.startService(new Intent(ctx, TimeoutService.class));
long triggerTime = System.currentTimeMillis() + DEFAULT_TIMEOUT;
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, triggerTime, buildIntent(ctx));
}
public static void cancel(Context ctx) {
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.cancel(buildIntent(ctx));
ctx.startService(new Intent(ctx, TimeoutService.class));
}
}
Then, I created a service to capture the intent generated by the alarm. It sets some global state in my instance of the application class to indicate that the app should lock:
public class TimeoutService extends Service {
private BroadcastReceiver mIntentReceiver;
#Override
public void onCreate() {
super.onCreate();
mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ( action.equals(Intents.TIMEOUT) ) {
timeout(context);
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(Intents.TIMEOUT);
registerReceiver(mIntentReceiver, filter);
}
private void timeout(Context context) {
App.setShutdown();
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.cancelAll();
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mIntentReceiver);
}
public class TimeoutBinder extends Binder {
public TimeoutService getService() {
return TimeoutService.this;
}
}
private final IBinder mBinder = new TimeoutBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Finally, I created a subclass of Activity that all of my app's activities subclass from to manage locking and unlocking:
public class LockingActivity extends Activity {
#Override
protected void onPause() {
super.onPause();
Timeout.start(this);
}
#Override
protected void onResume() {
super.onResume();
Timeout.cancel(this);
checkShutdown();
}
private void checkShutdown() {
if ( App.isShutdown() ) {
finish();
}
}
}
Using onPause and onResume to start and stop the timeout gives me the following semantics. As long as one of my application's activities is active, the timeout clock is not running. Since I used an Alarm type of AlarmManager.RTC, whenever the phone goes to sleep the timeout clock runs. If the timeout happens while the phone is asleep, then my service will pick up the timeout as soon as the phone wakes up. Additionally, the clock runs when any other activity is open.
For a more detailed version of these, you can see how I actually implemented them in my application https://github.com/bpellin/keepassdroid
Check out how OpenIntents Safe implements this functionality.
This has been a really helpful post for me. To back the concept given by #Yoni Samlan . I have implemented it this way
public void pause() {
// Record timeout time in case timeout service is killed
long time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor edit = preferences.edit();
edit.putLong("Timeout_key", time);// start recording the current time as soon as app is asleep
edit.apply();
}
public void resume() {
// Check whether the timeout has expired
long cur_time = System.currentTimeMillis();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
long timeout_start = preferences.getLong("Timeout_key", -1);
// The timeout never started
if (timeout_start == -1) {
return;
}
long timeout;
try {
//timeout = Long.parseLong(sTimeout);
timeout=idle_delay;
} catch (NumberFormatException e) {
timeout = 60000;
}
// We are set to never timeout
if (timeout == -1) {
return;
}
if (idle){
long diff = cur_time - timeout_start;
if (diff >= timeout) {
//Toast.makeText(act, "We have timed out", Toast.LENGTH_LONG).show();
showLockDialog();
}
}
}
Call pause method from onPause and resume method from onResume.

Categories

Resources