Accessing MainActivity once BOOT_COMPLETE is received - android

I have a reminder app that will notify me at a given time when the next reminder from an array of objects it due.
I am trying to make it set the notification again on boot.
I have my boot receiver all set in the Manifest, but how do I access any information from MainActivity once the phone has booted, given that the app hasn't been opened yet?
I was hoping to use this -
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("RegularReminders","onReceive");
new MainActivity().setNotifications();
}
}
But it returns a null error from within that notification once it tries to run the method in MainActivity, the app crashes as the emulator boots up and I see this in the logcat -
java.lang.RuntimeException: Unable to start receiver com.androidandyuk.regularreminders.BootReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
This points to a line -
if (reminders.size() >= 0) {
I did wonder if I could save the notification message to SharedPrefs and call it back in the receiver, but I got errors of null object reference doing that too.
I tried sending back another broadcast adding extra info, but I guess the receiver set up in MainActivity isn't listening as the app hasn't been run?
I know Google is protecting us from Malware, not letting them do much after book, but is there any way round this so I can set my notification after a reboot?
Thanks.

Although you can't access variables from the MainActivity, you can access SharedPreferences and the SQLite Database used in the app, so in this case I made a new array and read the database into it and worked on that.

The problem is that your activity is not created when you receive ACTION_BOOT_COMPLETED.
What you could do is to start your activity after the boot is completed and pass it arguments to tell it do some work.
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("RegularReminders","onReceive");
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Intent i = new Intent(context, YourActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bundle args = new Bundle();
//put some args inside the bundle
//attach the bundle
i.putExtras(args);
//start the activity
context.startActivity(i);
}
}
}
If you don't what to start and show your activity. You can use Android Services.

Related

Get view of activity and fragments from service

I have on my app a service who get my location (gps). This service get information like latitude, longitude, etc, and I want wrote these information in layouts fragment and activity (TextView).
But how can I get view reference of theses fragments since my service ?
You can do one thing to achieve this. send broadcast to the activity. And on that activity where you want to update view register broadcast there. Once you get the location stuff then send broadcast. Hope that helps you. Write this code in service when you get location stuff and pass that stuff via intent as mentioned in below code
Intent broadcastIntent = new Intent(your action here);
broadcastIntent.putExtra(BaseBroadCastReceiver.BROADCAST_KEY_AUDIO_INDEX, audioIndex);
broadcastIntent.putExtra(BaseBroadCastReceiver.BROADCAST_KEY_AUDIO_LIST, mSongList.size());
sendBroadcast(broadcastIntent);
And in your activity write below code
private void registerMyReceiver() {
IntentFilter filter = new IntentFilter(your action here you passed in service);
registerReceiver(playNewAudio, filter);
}
NOTE: your action can be any string but string must be same on both side
When activity's oncreate method is called, call registerMyReceiver() method
and in onDestroy method unregister is else you may get RUNTIME exception.
Below will be your code for actual broad cast receiver
private BroadcastReceiver myBroadcast= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//do your stuff here... get extra which you passed
// from service and set that value in views
}
};

How to have BroadcastService communicate with Activity?

I'm trying to get push notification working in my Android app, using parse.com's push notification service. They implement a Broadcast receiver, which I am extending in my own class:
public class MyPushBroadcastReceiver extends ParsePushBroadcastReceiver
{
#Override
protected void onPushReceive(Context context, Intent intent)
{
JSONObject data = getDataFromIntent(intent);
[...]
super.onPushReceive(context, intent);
}
#Override
protected void onPushOpen(Context context, Intent intent)
{
ParseAnalytics.trackAppOpenedInBackground(intent);
Intent i = new Intent(context, MyActivity.class);
i.putExtras(intent.getExtras());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
I'm over-riding two methods:
onPushReceive - this is called when a notification arrives
onPushOpen - this is called when the user clicks on a notification in the notification area
There are three things I need to do:
When a notification arrives, I need to save it
When the user clicks on a notification, I need to open the app to an activity that displays the notifications I've saved
If a notification arrives while I have the app open to the display notifications activity, I need to update the UI to include the new activity.
The first part was easy. I'm just writing some JSON to a file.
The second I've had no trouble with. I create an intent, and that opens my activity. It reads the JSON from the file, and Bob's your uncle.
I've not been able to find a clean way of handling the third part.
I think where I'm stuck is that I haven't a clear understanding of the lifecycle of Activities or BroadcastServices. I'm not creating either, in my app, they're declared in the manifest, and constructed whenever.
Does the Android framework create one of each, as it processes the manifest? Is it possible for an activity to find the instance of the BroadcastReceiver? If I could, it'd be easy enough for me to wire up a callback between them.
Or do I need to define my own BroadcastService, that the ParsePushBroadcastReceiver would use to publish events, and that the activity would consume? The examples I've seen on doing that seem excessively complicated for what should be a fairly simple thing.
Help would be appreciated.
The suggestion on using a static variable got me thinking, and I think I've found a workable solution.
There may be multiple instances of an Activity, but only one can be active at any time.
I spent some time playing around with setting various flags on the Intent I'd pass to startActivity(), and didn't like any of the behaviors I'd see. (Some combinations would crash, some would create multiple entries in the stack so that the back button returned you to an older instance of the activity, all of them created visual effects as the old activity was replaced by the new.)
So, why not create a static field that points to the currently active Activity?
public class MyActivity extends Activity implements ReceiveNotifications
{
public static ReceiveNotifications notificationReceiver = null;
#Override
protected void onResume()
{
super.onResume();
NotificationsActivity.notificationReceiver = this;
updateMessages();
}
#Override
protected void onPause()
{
NotificationsActivity.notificationReceiver = null;
super.onPause();
}
#Override
public void notificationReceived()
{
updateMessages();
}
private void updateMessages()
{
[...]
}
}
Whenever an instance of MyActivity is active, the static variable notificationReceiver will point to it. Of course, I'm using an interface to control how much of MyActivity is visible through that variable:
public interface ReceiveNotifications
{
void notificationReceived();
}
Then, when we receive a notification, if notificationReceiver is not null, we call notificationReceived():
public class MyPushBroadcastReceiver extends ParsePushBroadcastReceiver
{
#Override
protected void onPushReceive(Context context, Intent intent)
{
JSONObject data = getDataFromIntent(intent);
[...]
super.onPushReceive(context, intent);
if (MyActivity.notificationReceiver != null)
MyActivity.notificationReceiver.notificationReceived();
}
#Override
protected void onPushOpen(Context context, Intent intent)
{
ParseAnalytics.trackAppOpenedInBackground(intent);
Intent i = new Intent(context, MyActivity.class);
i.putExtras(intent.getExtras());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
You can use the onNewIntent() (docs) method of the Activity to send the new info about something happened and then display some prompt.

WakefulBroadcastReceiver intent not starting only in some cases (strange)

I have a very strange bug happening in my app. I am building an Alarm APP and I am using SQLite to store alarm data and broadcast receivers to manage the alarm manager calls.
The code of onReceive strangely does not behave in the same way in some cases. I try to start an Activity when the receiver receives a broadcast, nearly 90% of cases everything goes well and I manage to start the Activity, but in some cases strangely enough the receiver executes the instruction "startActivity(i)" but nothing happens.
It is really hard to reproduce the BUG and during my debugging I have learned what I have mentioned, but more than this is really difficult for me to understand how a call to startActivity() in most cases works and in some cases does not work. I have searched through the Stack community but no one seamed to have this kind of problem, everybody just had problems in starting the activity because they had not set the flag or because they had not registered the receiver in the manifest. Below I am posting the code.
public class AlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Utils.logToFile("Received Alarm ,I am in onReceive(), ALARM ID: "+intent.getExtras().getInt(Constants.ALARM_ID));
Intent intent = new Intent(context, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//this flag is needed to start an Activity from a BroadcastReceiver
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
//this method reads from the DB and sets the next alarm
//I tried commenting this method so that no DB action is
//performed and still the bug happened
setAlarm(context.getApplicationContext());
//this method just logs data into a file that I have created to keep track of events
//since not always the device is connected with LogCat
Utils.logToFile("Received Alarm, Intent(context, StopAlarm.class);");
}
Do I need to set any other flag and how is it possible that startActivity(intent) behaves incorrectly in some cases?
EDIT
<activity
android:label="#string/app_name"
android:name="package.activity.StopAlarm"
android:windowSoftInputMode="stateAlwaysHidden"
android:screenOrientation="sensorPortrait">
</activity>
<receiver android:name="package.receivers.AlarmReceiver" />
I have finally solved the issue by creating an IntentService and by starting the activity from the IntentService and setting two flags to the Intent. After doing this I placed the code which reads from DB in the activity that is started from the IntentService. I have tested nearly 60 times the behaviour and in all the tests the app behaved correctly. I am posting the code below.
public class MyAlarmReceiver extends WakefulBroadcastReceiver {
// The app's AlarmManager, which provides access to the system alarm services.
private static AlarmManager alarmMgr;
// The pending intent that is triggered when the alarm fires.
private static PendingIntent alarmIntent;
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, AlarmIntentService.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startWakefulService(context, i);
}
This is the IntentService I needed to implement
public class AlarmIntentService extends IntentService {
public AlarmIntentService() {
super("AlarmIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
Intent i = new Intent(this, StopAlarm.class);
Bundle b = new Bundle();
b.putInt(Constants.ALARM_ID, intent.getExtras().getInt(Constants.ALARM_ID));
if(intent.getExtras().containsKey(Constants.SNOOZE_ALARM)){
b.putString(Constants.SNOOZE_ALARM, intent.getExtras().getString(Constants.SNOOZE_ALARM));
}
i.putExtras(b);
//THESE ARE THE FLAGS NEEDED TO START THE ACTIVITY AND TO PREVENT THE BUG
//(CLEAR_TASK is crucial for the bug and new task is needed to start activity from outside of an activity)
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
MyAlarmReceiver.completeWakefulIntent(intent);
}
}
This is the activity started by the IntentService. Here I set the next alarm.
public class StopAlarm extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stop_alarm);
//this method reads from the DB and sets the next alarm
MyAlarmReceiver.setAlarm(getApplicationContext());
...
I had a similar issue. From my experience the problem is,that startActivity(i) is asynchronous. So in this case the program will simultaneously call activities onCreate(),onStart(), etc. but also call QlokAlarmReceiver.completeWakefulIntent(intent) (without waiting for the activity to be closed), which will release the wakeLock. Because of that the device can go to sleep during executing the activities onCreate() or onStart().
Rubin, I know, that my answer is a contradiction of your solution, but my logs clearly indicated such order of events:
- startActivity called
- onCreate of the activity called
- completeWakefulIntent(intent); called in between logs from onStart of the activity
My workaround this is to start a wakelock with a timeout of eg 20 seconds just before calling startActivity and then start another wakeLock in the activities onCreate, which will be released in the onDestroy method.
I'm not sure if my solution goes along with best practises, but I haven't found a better solution so far.

How to let Activity know that cyclic Service has finished its task once?

I have some problems working with Android Services. I already have a Service which downloads a file from a server. (The Service checks cyclic for new data) Aftwerwards it parses the file and adds values to an ArrayList wich will be saved to SharedPreferences.
In my Activity there are two methods. One will display the values from the ArrayList/SharedPreferences in UI and the second method sets a Notification if needed.
But how do I now when my Service completed its task so the two methods can be started?
Register a BroadcastReceiver in your Activity something like:
myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do my stuff
}
};
registerReceiver(myReceiver , new IntentFilter("com.myapp.DOWNLOADCOMPLETE"));
Then in your service send the broadcast:
Intent i = new Intent("com.myapp.DOWNLOADCOMPLETE");
sendBroadcast(i);
You can also putExtras on your intent if you need to pass some values:
Documentation BroadcastReceiver

How to execute methods from a running service from broadcast?

on a broadcast I want to call a non static method from Service XYZ. The Service is start by the receiver on boot.
Has someone a idea to run methods from this running service?
One solution in this forum is to make the method static and use a singleton pattern to execute. But is there another method? Maybe with a binder?
//EDIT for example i have the following clases:
public class MyService extends Service{
.....
public void method(){
//TODO
}
}
public class MyBroadcastReceiver extends BroadcastReceiver{
.....
public void onReceive(Context context, Intent intent) {
String intentAction=intent.getAction();
if(intentAction.equals(Intent.ACTION_BOOT_COMPLETED)){
//start service
Intent serviceIntent = new Intent(context, MyService.class);
context.startService(serviceIntent);
}
else{
//TODO call method() in MyService
}
}
how can i call the method method()? I know that i can cast with context.getSystemService() system services. But how can i get my own service object?
greetings
You can add an action string to your intent using setAction in the intent that launches the Service. In your service's onStartcommand you can extract the intent's action, and based off that you can execute the method in your service.
You will always send commands to your service using startService this will not launch your service twice. It will either get started once, or the new intent is sent to the service.
So, in your on boot completed section you should set the intent action to whatever you want, and start the service - you can remove the else block completely.
In your Service implement the onStartCommand, extract the intent's action, and based off that action you can just execute your method.

Categories

Resources