Unable to start activity from onReceive method of broadCastReceiver - java

I am working on an alarm application.
In this, I am using a broadcast receiver and alarm manager. When the alarm goes off the onReceive method is called and then from the onReceive method, I am calling startActivity to start an intent
BUT the problem that I am facing is that in some devices activity is not getting started when the app is in the background or killed.
In onReceive method, I have three things:
Toast
RingtoneManager
Intent
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "In broadcast receiver", Toast.LENGTH_LONG).show();
Bundle args = intent.getBundleExtra(Constants.BROADCAST_RECEIVER_BUNDLE);
Alarms alarms = (Alarms) args.getSerializable(Constants.ALARM_OBJECT);
int alarmRequestCode = args.getInt(Constants.ALARM_REQUEST_CODE);
Intent OpenAppIntent = new Intent();
// I also tried with adding classes in intent object directly eg new Intent(context, MyActivity.class) but no luck
OpenAppIntent.setClass(context,com.example.MyActivity.class);
OpenAppIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
OpenAppIntent.putExtra(Constants.BROADCAST_RECEIVER_BUNDLE , args);
context.startActivity(OpenAppIntent);
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
if (alarmUri == null) {
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
ringtone.play();
}
Now we have two cases
When we have all three items in onReceive method ( toast, ringtone manager and intent ) - Then toast and ringtone is getting played and displayed respectively and activity is not getting started
When we remove the ringtone manager from onReceive method ( toast and intent ) - Then Toast is also not getting displayed ( activity is not started as well )
Devices in which this problem is noticed are Gionee F9, Motorola g5s, and Redmi k20 pro. Also, sometimes the same thing happens with Nokia 7 Plus
Please tell me what's wrong with the code or what is the best way to start an activity when the broadcast is received.
Test 1
Instead of starting activity I tried displaying a fullscreen notification as shown here and as suggested in a comment but unable to do so because of Error Non-static method startForeground(int, notification) cannot be referenced from a static context
#Override
public void onReceive(Context context, Intent intent) {
Intent notificationIntent = new Intent(context, MyActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable(Constants.ALARM_OBJECT , alarms);
notificationIntent.putExtra(Constants.BROADCAST_RECEIVER_BUNDLE , bundle);
PendingIntent pendingIntent = PendingIntent.getActivity(context,
0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(Constants.APP_NAME)
.setContentText("Stop the alarm")
.setSmallIcon(R.drawable.logo_inner)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setFullScreenIntent(pendingIntent, true)
.build();
Service.startForeground(1, notification); // Non-static method startForeground(int, notification) cannot be referenced from a static context
}
Test 2
I Also tried starting foreground service directly from the onReceive method but the same problem is occurring as with the starting activity. I also tried debugging by placing breakpoints on the onCreate method of service but onCreate never get called these devices ( Gionee F9, Motorola g5s, and Redmi k20 pro )
#Override
public void onReceive(Context context, Intent intent) {
Intent alarmServiceIntent = new Intent(context, AlarmSoundService.class);
Bundle alarmServiceBundle = new Bundle();
alarmServiceBundle.putSerializable(Constants.ALARM_OBJECT , alarms );
alarmServiceIntent.putExtra(Constants.ALARM_BUNDLE , alarmServiceBundle);
// startService(alarmServiceIntent);
ContextCompat.startForegroundService(context, alarmServiceIntent);
}
Any suggestions will be appreciated.
Thank you

Related

Problems with android alarm manager, when the app is closed

During development of a small android app, I am having problem in running an alarm manager, after the app is terminated by the user. While the app is running in the foreground or background, everything works fine.
I did the following steps:
AndroidManifest.xml
<receiver android:name="MyBroadcastReceiver" ></receiver>
MainActivity.java
Within the OnClick method of a button, I call
startAlert( x*60*1000);
x is a class-wide visible variable
public void startAlert(long timeInMillis){
Intent intent = new Intent(this, MyBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this.getApplicationContext(), 234324243, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+(timeInMillis),pendingIntent);
}
Toast.makeText(this, "Alarm in " + x + " Minuten",Toast.LENGTH_LONG).show();
}
MyBroacastReciever.java
public void onReceive(Context context, Intent intent) {
MediaPlayer player = MediaPlayer.create(context,MainActivity.link);
player.start();
Toast.makeText(context, "Alarm....", Toast.LENGTH_LONG).show();}
What should I do to get the alarmManager successfully running, after the app is closed?
You could put the alarm manager in the service. The foreground service is used to keep our application alive even though the main application has been quit by the user. And then the alarm manager could trigger the code in the broadcast receiver class

How to implement an alarm receiver?

I am starting to learn about the AlarmManager, and I want to fire a broadcast to fetch some info from a server. The documentation is clear about the intents and the alarms, but I cannot find anything on how the receiving end should look.
This is my alarm code:
AlarmManager aMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
aMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 10000,
AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);
and my AlarmReceiver is like this:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(MainActivity.TAG, "Received intent");
}
}
But nothing happens. I added a button to just fire the broadcast like this:
public void btnTrigger_onClick(View view) {
Intent i = new Intent(this, AlarmReceiver.class);
sendBroadcast(i);
}
and also nothing happens here.
I have used broadcasts before, but only with registerReceiver, but I do not want to process the broadcast in my Activity now.
Explicit broadcasts — ones where the Intent has the component name (i.e., class) — require a <receiver> element in the manifest in order to work.

Android home screen widget

I am doing the widget development first time. So please bear with me. I have an Android service that starts the widget from onStart(). So I am in my WidgetProvider class onUpdate() and update the views like buttons.
Now my question is -
1) How do I handle press of buttons in widget and tell the service about button press so that service does its work?
2) After the service has done its work, it needs to update the widget buttons and how do I handle this?
3) When the app is killed, the service is also killed. So when I click on widget, how do I determine that app is killed and open the app?
4) If the UI in app is talking to service and receiving responses from service, how do I tell these responses to widget too?
My widget code is like this -
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteViews;
ComponentName componentName;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
componentName = new ComponentName(context, MyWidgetProvider.class);
remoteViews.setOnClickPendingIntent(R.id.button1,getPendingSelfIntent(context, "Play",componentName));
remoteViews.setOnClickPendingIntent(R.id.button2,getPendingSelfIntent(context, "Pause",componentName));
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if ("Play".equals(intent.getAction())) {
//I really dont know what to do here....
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
watchWidget = new ComponentName(context, MyWidgetProvider.class);
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
}
protected PendingIntent getPendingSelfIntent(Context context, String action, ComponentName componentName) {
Intent intent = new Intent(context, MyService.class);
intent.setAction(action);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, componentName);
return PendingIntent.getService(context, 0, intent, 0);
}
}
Request you to please help me in this
1) You're already doing that with your getPendingSelfIntent() method. Although your target should be MyWidgetProvider like this:
Intent intent = new Intent(context, MyWidgetProvider.class);
Then in your onReceive you will check if the intent action is "Play" and start your service from there:
Intent serviceIntent = new Intent(context, MyService.class);
2) In order to your service communicate to your Widget you need to send a broadcast:
Intent widgetUpdateIntent = new Intent(this, MyWidgetProvider.class);
widgetUpdateIntent.setAction(ApplicationEvents.DATA_FETCHED);//this is your custom action
widgetUpdateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
sendBroadcast(widgetUpdateIntent);
3) You don't need to explicit open the app (you can't do that, actually), if you have your update cycle set to 30 mins you will receive the onUpdate callback and you need to start your service from there too.
4) You will update the UI in the onReceive event when the action matches your custom action (ApplicationEvents.DATA_FETCHED).

Android - How to re-display Main Activity

I've searched for a solution to this problem but have been unable to find one.
The problem I have is that an alarm is sounded OK, however the MainActivity has closed and is not re-displayed .
How can I re-display the MainActivity in the following code:
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
//this will update the UI with message
AlarmActivity inst = AlarmActivity.instance();
inst.setAlarmText("Alarm! Wake up! Wake up!");
//this will sound the alarm tone
//this will sound the alarm once, if you wish to
//raise alarm in loop continuously then use MediaPlayer and setLooping(true)
Uri alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
if (alarmUri == null) {
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
}
Ringtone ringtone = RingtoneManager.getRingtone(context, alarmUri);
ringtone.play();
//this will send a notification message
ComponentName comp = new ComponentName(context.getPackageName(),
AlarmService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
The code is code that I downloaded as an example to work with from JavaPapers
http://javapapers.com/android/android-alarm-clock-tutorial/
my suggestion
send a broadcast and have the reciever / or service start the activity for you
I did attempt to use a broadcast, however it did not work, so likely what I did was incomplete.
While the following code did work (last 3 lines were added), it seems to me that it is wrong because I presume it creates a new instance of MainActivity. The MainActivity is AlarmActivity in the original version.
The code that "works" in AlarmService.java is as follows:
private void sendNotification(String msg) {
Log.d("AlarmService", "Preparing to send notification...: " + msg);
alarmNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder alarmNotificationBuilder = new NotificationCompat.Builder(
this).setContentTitle("Alarm").setSmallIcon(R.mipmap.ic_launcher)
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setContentText(msg);
alarmNotificationBuilder.setContentIntent(mPendingIntent);
alarmNotificationManager.notify(1, alarmNotificationBuilder.build());
Log.d("AlarmService", "Notification sent.");
Intent mIntentMain = new Intent(this, MainActivity.class);
mIntentMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
this.startActivity(mIntentMain);
}
The relevant code that I added is the last 3 lines. I put this into AlarmService.java because it extends IntentService. The original code posted in first posting (above) is from AlarmReceiver.java which extends WakefulBroadcastReceiver. I presume that what I should be doing is to cause the existing MainActivity to display rather than creating another.
Edited: 29-Sep-2016
Because the Main Activity may already be displayed, what I finally did to handle this was as follows:
if (fn_IsMainActivityDisplayed(this) == false) {
System.out.println("Main activity is not running. Now recreating");
try {
PendingIntent mPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), PendingIntent.FLAG_ONE_SHOT);
mPendingIntent.send(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
} catch (Exception jExc) {
System.out.println("AlarmService: PendingIntent failed: Error = " + jExc.getMessage());
}
}
The code to determine if Main Activity is displayed was found on SO. I'm unsure if this is 100% correct, but it appears to work OK.

Notification PendingIntent hard bug (or really simple one)

I'm trying to debug this for ages now and I just can't seem to find the problem:
I have a broadcast receiver, which receives the broadcast successfully.
The notification has two actions ("buttons"):
firstIntent = null;
secondIntent = null;
firstPendingIntent = null; //first "button" to join with the first intent
secondPendingIntent = null; //second "button" to join with the second intent
if(boolean){
//relevant
firstIntent = new Intent(getBaseContext(), NotificationFunctions.class).putExtra("action", "do_this");
secondIntent = new Intent(getBaseContext(), NotificationFunctions.class).putExtra("action", "do_that");
}else{
firstIntent = new Intent(getBaseContext(), NotificationFunctions.class).putExtra("action", "do_another_this");
secondIntent = new Intent(getBaseContext(), NotificationFunctions.class).putExtra("action", "do_another_that");
}
firstPendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, firstIntent, PendingIntent.FLAG_UPDATE_CURRENT);
secondPendingIntent = PendingIntent.getBroadcast(getBaseContext(), 0, secondIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle(notification_title)
.setContentText(notification_text)
.setTicker("Notification!")
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.DEFAULT_SOUND)
.addAction(R.drawable.abc_cab_background_top_holo_light, first_option, firstPendingIntent)
.addAction(R.drawable.abc_cab_background_top_holo_dark, second_option, secondPendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_stat_name)
.build();
Whenever I debug in the broadcastReceiver, for some reason, action from extras always logs "do_that", even if I click the first or second button of the notification. Any reason for this? I cant really seem to understand why.
public class NotificationFunctions extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean feedback;
String action = intent.getExtras().getString("action");
Log.wtf("...", action); //logs do_that
}}
Any reason for this?
Because firstPendingIntent == secondPendingIntent.
If there already is a PendingIntent matching your request, getBroadcast() returns the existing PendingIntent. FLAG_UPDATE_CURRENT says to replace the extras in the Intent wrapped inside the PendingIntent.
In one of your two getBroadcast() calls, replace 0 with some other number, to get distinct PendingIntent objects.
Also, I recommend that you replace getBaseContext() and getApplicationContext() with this. Only use getBaseContext() and getApplicationContext() when you know precisely why you are using them.

Categories