How can I access MainActivity's objects or methods inside BroadcastReceiver? - java

I'm creating an android app that uses AlarmManager to create an alarm by playing a sound. To do this, I first create a PendingIntent, for which I have to create a class called AlarmReceiver, which extends BroadcastReceiver. In this new class, I override the onReceive method, in which I also start the sound. What I have right now works. However, as part of a bigger project, I will be later getting some data from a database. Regarding my question, this data is not important; what's important is that, after analyzing all the data, it will all come down to a boolean variable, which will be true or false. This variable will be in MainActivity and I want to access it in my BroadcastReceiver class to check it and if true, I would stop the music. I've checked many SO questions related to these, but I still haven't found a solution.
The code for the MainActivity is:
package com.example.alarmsound;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
Calendar t = Calendar.getInstance();
t.add(Calendar.SECOND, 5);
Context context = this;
AlarmManager alarmMgr;
alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.set(AlarmManager.RTC_WAKEUP, t.getTimeInMillis(), pendingIntent);
boolean result; //the variable I want to access to BroadcastReceiver class
}
}
And the code for the BroadcastReceiver class is:
public class AlarmReceiver extends BroadcastReceiver{
public AlarmReceiver() {}
#Override
public void onReceive(Context context, Intent intent) {
final MediaPlayer mp = MediaPlayer.create(context, R.raw.music);
Log.d("Music", "It went here.");
mp.start();
//here, I want to access result
}
}
I would really appreciate any help.

In MainActivity:
Intent intent = new Intent(this, AlarmReceiver.class);
intent.putExtra("some_constant", result);
In your broadcast receiver:
boolean result = intent.getBooleanExtra("some_constant", false);

Create an instance of your MainActivity and get it using a method, add following code in your MainActivity:
private static MainActivity instance;
#Override
protected void onStart() { // onStart() of your activity
super.onStart();
instance = this;
}
public void yourMethod(){
// Your code here
}
public static MainActivity getInstance(){
return instance;
}
Now, in your BroadcastReceiver, you can get the instance using:
MainActivity obj = MainActivity.getInstance();
And you can call your method using:
obj.yourMethod();

Related

Change Variable in BroadcastReceiver

I want to change a variable of a BroadcastReceiver. Unfortunately the App crashed everytime I try to access it.
Here is an example:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.buttonAlarm).setOnClickListener(new View.OnClickListener() {
setAlarm();
});
}
public void setAlarm(){
Calendar calendar = Calendar.getInstance();
calendar.set(datePicker.getYear(),datePicker.getMonth(),datePicker.getDayOfMonth(),timePicker.getCurrentHour(),timePicker.getCurrentMinute(), 0);
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, MyAlarm.class);
pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), Interval*60000, pi);
}
}
This is the MyAlarm.java Class that extends the BroadcastReceiver:
public class MyAlarm extends BroadcastReceiver {
int RecordTimeMinutes;
public void onReceive(final Context context, Intent intent) {
new Handler().postDelayed(new Runnable() {
#Override
public void run(){
//do some stuff
}
}, RecordTimeMinutes * 60000);
}
}
So basically I want to acces the Variable RecordTimeMinutes of the MyAlarm class within the MainActivity class. Is that possible?
For your use-case, why don't you save the value of RecordTimeMinutes into a sharedpreference key?
That way you will be able to access it from both the Activity and the Receiver without any issues.
Directly accessing the variable in the activity might not be possible unless you make it static, which is something you should not do!
Pass the variable from the Activity via the Intent extras:
public void setAlarm(){
Calendar calendar = Calendar.getInstance();
calendar.set(datePicker.getYear(),datePicker.getMonth(),datePicker.getDayOfMonth(),timePicker.getCurrentHour(),timePicker.getCurrentMinute(), 0);
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(this, MyAlarm.class);
i.putInt("Interval", 42); // Pass "interval" value here
pi = PendingIntent.getBroadcast(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), Interval*60000, pi);
}
And then in the receiver:
public class MyAlarm extends BroadcastReceiver {
public void onReceive(final Context context, Intent intent) {
int interval = intent.getIntExtra("Interval", 0) // Get "interval" value here
new Handler().postDelayed(new Runnable() {
#Override
public void run(){
//do some stuff
}
}, interval * 60000); // Use "interval" here
}
}
That said - you should not be posting a handler to fire in the future inside a BroadcastReceiver. There is no guarantee that your process will still be alive 1 minute from now to execute your code. Instead you could schedule another alarm or use WorkManager to schedule a job to run later.
Hope that helps!

Calling method of different activity via interface leads to error: system service not available before onCreate

I have an AlarmManager in Activity 'PopShowAlarms'. This is a List of alarms, which the user can activate and deactivate. If he does so, the AlarmManager creates a new Alarm. Obviously the user also needs to be able to add alarms. He can do this in the second activity 'PopAddAlarm'. To make this newly created alarm, 'PopAddAlarm' calls a method of 'PopShowAlarms'. There the new data should be converted into an alarm instantly. This always leads to the error, that I can't use SystemServices before onCreate.
First I tried to call the method simply by making a new object of the class
Class class = new Class();
class.method();
This resulted in the error: can't call SystemServieces before onCreate
Now I've been told, that I have to use an Interface to do so, but I still get the same error. Because I'm new to interfaces, I think that's where the mistake might be.
Here is my code:
Activity 'PopShowAlarms':
public class PopShowAlarms extends AppCompatActivity implements interfaceForwardAlarm {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pop_show_alarms);
...
}
#Override
public void onSwitchChange(int position, boolean switchOnOff) {
// when user activates the alarm, this code is triggered
if (switchOnOff) {
RecyclerViewElement currentElement = savedList.get(position);
String time = currentElement.getmAlarmTime();
// deleted methods that convert String 'time' to a calendar object
setAlarm(Calendar);
}
}
});
...
public void setAlarm(Calendar calendar){
AlarmManager alarmManager = (AlarmManager) getBaseContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
int count = getCount();
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, count, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
...
// this is the implemented method from the interface
#Override
public void forwardAlarm(Context context, String alarmTime, String activeDays, String motivation, String music) {
setCalendar(alarmTime);
}
}
Second Activity: 'PopUpAdd
public class PopupAdd extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_popupadd);
...
imageViewAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int hour = numberPickerHour.getValue();
int minute = numberPickerMin.getValue();
String activeTime = Integer.toString(hour)+":"+Integer.toString(minute);
...
// the chosen alarm Time is forwarded to the activity 'Pop Show Alarms'
PopShowAlarms popShowAlarms = new PopShowAlarms();
popShowAlarms.forwardAlarm(context, activeTime, activeDays, "motivation", "music");
finish();
}
The Mentioned interface:
public interface interfaceForwardAlarm {
void forwardAlarm(Context context, String alarmTime, String activeDays,
String motivation, String music);
}
I'd be greatful for any help. I often struggle with this problem. (Calling methods from different activities) I hope this is at least the right approach

Broadcast Data using Alarm Manager

I am trying to send data to my parse SDK Database using Alarm Manager.
I am able to send data without Alarm manager.
My below code is not working correctly as parse sdk database is not showing updated data.
Here is my code:
Main Activity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_todo);
setTitle(R.string.create_todo);
alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
AlarmManager.INTERVAL_FIFTEEN_MINUTES,
AlarmManager.INTERVAL_FIFTEEN_MINUTES, alarmIntent);
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
MyAsyncTask asyncTask = new MyAsyncTask();
asyncTask.execute(new String[]{});
}
class MyAsyncTask extends AsyncTask<String,Void,String>
{
#Override
protected String doInBackground(String... params) {
try {
ParseObject parseObject = new ParseObject("Todo");
parseObject.put("name", "abc");
parseObject.save();
}
catch(ParseException e)
{
}
return null;
}
}
}
Register your broadcast in AndroidManifest.xml file
Try below code:
alarm.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_FIFTEEN_MINUTES , AlarmManager.INTERVAL_FIFTEEN_MINUTES, pIntent);
From above code, Below things will be happened.
first time Broadcast will be fired after 15 min. then it will be fired every 15 min.

Android receive a broadcast in ActionBarActivity

I have got an action bar activity with a LocalBroadcastManager defined exactly like in the answer here, except the only difference is that it is defined in an ActionBarActivity.
For some reason, no matter what I try I can't manage to get to the receiver's onReceive (i.e. successfuly receiving broadcast message).
Service code:
public class GcmIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
Intent toDrawerActivity = new Intent(syncActionName);
String syncType = extras.getString("data");
toDrawerActivity.putExtra("syncType", syncType);
System.out.println("sending intent in service");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
And the activity code:
public class DrawerActivity extends ActionBarActivity {
private BroadcastReceiver dataUpdaterReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("in broadcast receiver");
}
};
protected void onCreate(){
...
...
LocalBroadcastManager.getInstance(this).registerReceiver(dataUpdaterReceiver,
new IntentFilter(GcmIntentService.syncActionName));
}
protected void onDestroy(){
LocalBroadcastManager.getInstance(this).unregisterReceiver(dataUpdaterReceiver);
}
}
What exactly am I doing wrong here?
Your have that problem due to you used the wrong parameter for sendBroadcast() method:
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
It should be:
LocalBroadcastManager.getInstance(this).sendBroadcast(toDrawerActivity);

Go to previous activity from BroadcastReceiver?

I need to close the current activity from a broadcast receiver. I'm not sure how to call finish from it, maybe there is a way to simulate a "Back" key keypress. Any implementation will be fine as long as it does the job.
#Override
public void onReceive(Context context, Intent intent) {
// How can I finish the current activity here?
}
At your broadcast receiver write:
YourCurrentActivityName.this.finish();
Or you can terminate the front activity with this.finish(); so the last open in stuck comes to front.
Update:
Code for first case:
Use of broadcast receiver to terminate activity at back stack:
public class ActivityFirstName extends Activity {
private BroadcastReceiver mFinishReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// other code
if (mFinishReceiver == null) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.ACTION_TERMINATE");// a string to identify your action
mFinishReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// How can I finish the current activity here?
if ("com.example.ACTION_TERMINATE".equals(intent.getAction())) {
ActivityFirstName.this.finish();
}
}
};
registerReceiver(mFinishReceiver, intentFilter);
}
// other code
}
#Override
protected void onDestroy() {
super.onDestroy();
if (isFinishing()) {
if (mFinishReceiver != null) {
unregisterReceiver(mFinishReceiver);
}
}
}
}
And the front/current running activity, the sender of the broadcast:
public class ActivitySecondName extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
// code code code
final Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Perform action on click
terminateBackActivities();
}
});
}
private void terminateBackActivities() {
Intent i = new Intent("com.example.ACTION_TERMINATE"); // the two action strings MUST be same
// i.putExtra(...); // to send extra data
sendBroadcast(i);
}
}
You can simply call this.finish();
Assuming from your comment that the BroadcastReceiver is not an internal class of the activity, here is what you should do: Rather than having the broadcast receiver in a separate class, define it inside your activity like so:
private BroadcastReceiver mFinishReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
YourActivity.this.finish();
}
};
Then, you will want to register the receiver in onResume() as such:
#Override
public void onResume(){
super.onResume();
registerReceiver(mFinishReceiver, new IntentFilter(yourIntentAction));
}
You will also want to unregister this receiver in onPause() so you don't leak it:
#Override
public void onPause(){
super.onPause();
unregisterReceiver(mFinishReceiver);
}
Then you can remove the other receiver that had its own separate class and also remove its definition in the manifest. The above example will ensure that you can always call finish() with no issues because the receiver is only registered when the activity is running, as it is internal to the activity's class.
EDIT: Change the methods to onCreate() and onDestroy() rather than onPause() and onDestroy(), according to madlymad's comment.
The ActivityManager class can give you the current foreground activity (even if it's not from your app). The getRunningTasks methods will give you a list of the running tasks, the first element of the list being the most recent launched activity.Unfortunately,this method will just give you an object of type RecentTaskInfo , not the activity itself, so there is no way to call its finish() method,I believe :/
On the other hand, if you want to close the current activity from your app, you can implement a static variable on a personal class that each activiy would set in their onResume() method. This way you will always know what activity is the current one. But I guess it's not what you are looking for.
Edit: The getRunningTasks is just intended for debug purposes, as says the doc..
As suggested by other answers you can simply call finish() on the activity in the broadcast receiver code or you can even trigger a back button press key event yourself.
this.dispatchKeyEvent(new Keyevent(ACTION_DOWN, KEYCODE_BACK));
Not sure about whether this is helpfull to you or not but its help me once and i think thats a same case here so i am answering for you.
Whenever the broadcast receiver get call, you can navigate to any activity by clicking on that broadcast message.
Just like:
#Override
public void onReceive(Context context, Intent intent) {
// My Notification Code
notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
int icon = R.drawable.app_icon;
//System.out.println("The ID Number is: "+Long.parseLong(intent.getData().getSchemeSpecificPart()) );
contentText = intent.getStringExtra("MyMessage");
System.out.println("The Message is: "+intent.getStringExtra("MyMessage"));
CharSequence text = "Your tax amount due period";
CharSequence contentTitle = "Tax Toolbox";
long when = System.currentTimeMillis();
intent = new Intent(context, MenuPageActivity.class); // here i am calling activity
intent.putExtra("sixMonth", "sixMonth");
intent.putExtra("messageSixMonth", contentText);
PendingIntent contentIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
notification = new Notification(icon,text,when);
long[] vibrate = {0,100,200,300};
notification.vibrate = vibrate; // To vibrate the Device
notification.ledARGB = Color.RED;
notification.ledOffMS = 300;
notification.ledOnMS = 300;
notification.defaults |= Notification.DEFAULT_LIGHTS;
//notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notificationManager.notify(com.project.TaxToolbox.NotificationConstants.NOTIFICATION_ID_SIX_MONTH, notification);
}
Now, on the onCreate() of that activity you have to identify whether it is call by Notification or not.
As like:
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
System.out.println("The Extra for twoMonth is: "+getIntent().hasExtra("twoMonth"));
System.out.println("The Extra for sixMonth is: "+getIntent().hasExtra("sixMonth"));
System.out.println("The Extra for EveryMonth is: "+getIntent().hasExtra("everyMonth"));
if(getIntent().hasExtra("sixMonth")){
notificationManager.cancel(NotificationConstants.NOTIFICATION_ID_SIX_MONTH);
final AlertDialog alert3 = new AlertDialog.Builder(MenuPageActivity.this).create();
alert3.setTitle("Tax Toolbox");
alert3.setMessage(getIntent().getExtras().getString("messageSixMonth"));
alert3.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
alert3.setIcon(R.drawable.app_icon);
alert3.show();
// here you can do anything more or close the activity.
}
Not sure but might be helpfull to you.
Feel free to comments if it help you.
Create a common Activity class and extend this common class from all activities, that way you can have a centralized code. Have a register the broadcast receiver in onStart of the activity and unregister in onStop that way only one activity, the one which is visible will be registered for the broadcast intent.
Sample code:
public class BaseActivity extends Activity {
/*
* (non-Javadoc)
*
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(receiver, new IntentFilter(YOUR_INTENT_FILTER));
}
/*
* (non-Javadoc)
*
* #see android.app.Activity#onStop()
*/
protected void onStop(){
unregisterReceiver(receiver);
}
/*
* (non-Javadoc)
*
* #see android.app.Activity#onStart()
*/
protected void onStart(){
super.onStart();
registerReceiver(receiver, new IntentFilter(YOUR_INTENT_FILTER));
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
/*
* (non-Javadoc)
*
* #see
* android.content.BroadcastReceiver#onReceive(android.content.Context,
* android.content.Intent)
*/
#Override
public void onReceive(Context context, Intent intent) {
onBackPressed();//on back pressed simply calls finish()
}
};
} // End of BaseActivity
// End of File
Try using:
Intent i = new Intent(context,intent.getClass());
Follow the instructions from gezdy on How to get current foreground activity context in android? to ensure you can get a reference to the current activity from anywhere in your application.
From there you can call .finish() to close the current activity.
Place finish(); after u completed all the tasks in onReceive() of BroadcastReceiver class as below:
#Override
public void onReceive(Context context, Intent intent) {
// Do all the tasks onReceive of BroadCast Receiver
finish(); // This finishes the current activity here....
}

Categories

Resources