I have a service that sends a notification at a random time, telling me to press a button. This button needs to be pressed quickly because after 2 minutes it will disappear again. But after those 2 minutes I don't know how I can see if the button has or hasn't been pressed.
Somehow I need to get something like a boolean from my MainActivity to my service, but I don't believe I can do that with an intent because then I would restart my service.
I have looked for an answer but wasn't able to find a solution, any help will be much appreciated!
My service:
`package com.example.pressme_alpha;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
public class ButtonAlarmService extends IntentService{
private static final String INTENT_NAME = "notification";
private NotificationManager nm;
private Notification notification;
public ButtonAlarmService() {
super("Imma button!");
}
#SuppressWarnings("static-access")
#Override
protected void onHandleIntent(Intent intent) {
nm = (NotificationManager) this.getApplicationContext().getSystemService(this.getApplicationContext().NOTIFICATION_SERVICE);
Intent newIntent = new Intent(this.getApplicationContext(), MainActivity.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
newIntent.putExtra(INTENT_NAME, true);
PendingIntent pendingIntent = PendingIntent.getActivity(this.getApplicationContext(), 0, newIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(this);
notification = notifBuilder.setContentIntent(pendingIntent).setSmallIcon(R.drawable.ic_launcher).setContentTitle("Press me - alpha").setContentText("You need to press the button!").build();
notifBuilder.setAutoCancel(true);
nm.notify(0, notification);
startActivity(newIntent);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Check if the button is pressed here
ButtonAlarmReceiver.completeWakefulIntent(intent);
}
}
`
Extending Service (not IntentService) is what you want to do as it will keep runing untill you explicitly tell it to stop via stopService(Intent) method or if the service calls stopSelf() on its self.
You can send signals to the service via startService(Intent) method. This will start the service the first time its called (when the service is not running) and just send data to it if called subsequent times.
Make sure to spawn a new thread if you are doing heavy proccessing in the service as this will run on the Main thread (or UI thread depending on what you want to call it). You do not want to block the main thread.
Related
Excuse me, I have a question please.
I want to create a service to start once the the device is up and running by extending BroadcastReceiver. Inside the service class, I want to initiate an alarm manager to display a notification after certain time. Then, once the time is elapsed the notification is displayed, the alarm manger should be rescheduled for the next specified time. How could achieve that as alarm manager set function requires pending intent where no pending intent is required as I am inside a service class. I insist on using alarm manager instead of Timer as the following link suggests using it: Which is better for a background service WakeLock or startForeground
The code:
public class BootCompletedReceiver extends BroadcastReceiver {
final static String TAG = "BootCompletedReceiver";
#Override
public void onReceive(Context context, Intent arg1) {
Log.w(TAG, "starting service...");
context.startService(new Intent(context, BookkeeperService.class));
}
}
import android.app.AlarmManager;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.PowerManager;
public class BookkeeperService extends Service {
AlarmManager alarmManager;
#Override
public void onCreate(){
super.onCreate();
alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
// here I need to set the alarm to notify the user after certain time and then reschedule
// to the next event
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Thank you
If you want to pass messages through a Alarm to Broadcast Reciever, just have the Alarm dispatch a message at your leisure and then have that start your registration for more alarm(s). You can start the Alarm from your service and from there have the Alarms maintain themselves.
I am trying to display a notification in the Android notifications bar even if my application is closed.
I've tried searching, but I have had no luck finding help.
An example of this is a news application. Even if the phone screen is off or the news application is closed, it can still send a notification for recent news and have it appear in the notification bar.
How might I go about doing this in my own application?
You have to build a Service that handles your news and shows notifications when it knows that are new news (Service Doc).
The service will run in background even if your application is closed.
You need a BroadcastReciever to run the service in background after the boot phase is completed. (Start service after boot).
The service will build your notifications and send them through the NotificationManager.
EDIT: This article may suit your needs
The selected answer is still correct, but only for devices running Android 7 versions and below.
As of Android 8+, you can no longer have a service running in the background while your app is idle/closed.
So, it now depends on how you set up your notifications from your GCM/FCM server. Ensure to set it to the highest priority. If your app is in the background or just not active and you only send notification data, the system process your notification and send it to the Notification tray.
I used this answer to write a service, and as an exmaple you need to call ShowNotificationIntentService.startActionShow(getApplicationContext()) inside one of your activities:
import android.app.IntentService;
import android.content.Intent;
import android.content.Context;
public class ShowNotificationIntentService extends IntentService {
private static final String ACTION_SHOW_NOTIFICATION = "my.app.service.action.show";
private static final String ACTION_HIDE_NOTIFICATION = "my.app.service.action.hide";
public ShowNotificationIntentService() {
super("ShowNotificationIntentService");
}
public static void startActionShow(Context context) {
Intent intent = new Intent(context, ShowNotificationIntentService.class);
intent.setAction(ACTION_SHOW_NOTIFICATION);
context.startService(intent);
}
public static void startActionHide(Context context) {
Intent intent = new Intent(context, ShowNotificationIntentService.class);
intent.setAction(ACTION_HIDE_NOTIFICATION);
context.startService(intent);
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_SHOW_NOTIFICATION.equals(action)) {
handleActionShow();
} else if (ACTION_HIDE_NOTIFICATION.equals(action)) {
handleActionHide();
}
}
}
private void handleActionShow() {
showStatusBarIcon(ShowNotificationIntentService.this);
}
private void handleActionHide() {
hideStatusBarIcon(ShowNotificationIntentService.this);
}
public static void showStatusBarIcon(Context ctx) {
Context context = ctx;
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx)
.setContentTitle(ctx.getString(R.string.notification_message))
.setSmallIcon(R.drawable.ic_notification_icon)
.setOngoing(true);
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, STATUS_ICON_REQUEST_CODE, intent, 0);
builder.setContentIntent(pIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = builder.build();
notif.flags |= Notification.FLAG_ONGOING_EVENT;
mNotificationManager.notify(STATUS_ICON_REQUEST_CODE, notif);
}
}
I am almost finished with this toy app game I am making.
Problem:
My notification is always showing my counter to be 30000. Why isn't it timing down?
What I have done:
I have implemented a simple Service class and a custom timer to tick down. Eventually once I am sure the timer is working I will exit the entire game.
Here is my code:
package com.example.redshirtmarblez;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.widget.Toast;
public class TimingService extends Service{
public long counter = 30000;
private Context ctx;
private Activity localActivity;
private NotificationManager nm;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate()
{
super.onCreate();
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
declareNotification();
timer.start();
//Toast.makeText(this, "Timer is :" + counter, Toast.LENGTH_LONG).show();
//showNotification();
}
public void getActivity(Activity activity)
{
localActivity = activity;
}
//count to end the game
public CountDownTimer timer = new CountDownTimer(30000, 1000){
public void onTick(long millisUntilFinished){
counter = millisUntilFinished / 1000;
}
public void onFinish(){
counter = 0;
//Kill the game
int i = android.os.Process.myPid();
android.os.Process.killProcess(i);
}
};
/*
* Show a notification while this service is running
*/
public void declareNotification()
{
//Declare a new notification
Notification notification = new Notification(R.drawable.ic_launcher, "A New Notification", System.currentTimeMillis());
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
Intent intent = new Intent(this, TimingService.class);
PendingIntent activity = PendingIntent.getActivity(this, 0, intent, 0);
notification.setLatestEventInfo(this, "herp", "counter: " + counter, activity);
//This is clearly not 1337, but a good joke
startForeground(1337, notification);
}
}
All this does when it runs is shows "A New Notification", and then changes to "herp counter: 30000". However, this notification never changes. It just stays 30000. Why? I thought I fixed this with making the flag ongoing?
https://developer.android.com/reference/android/app/Notification.Builder.html#setUsesChronometer(boolean)
Show the when field as a stopwatch. Instead of presenting when as a
timestamp, the notification will show an automatically updating
display of the minutes and seconds since when. Useful when showing an
elapsed time (like an ongoing phone call). The counter can also be set
to count down to when when using setChronometerCountDown(boolean).
No updating required, works off the setWhen() value
NotificationCompat.Builder notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.ic_default_notification)
.setTicker("Ticker Text")
.setWhen(when) // the time stamp, you will probably use System.currentTimeMillis() for most scenarios
.setUsesChronometer(true)
.setContentTitle("Title Text")
.setContentText("Content Text");
counter is not a reference; the notification will not update with its new value until you explicitly tell it to.
Have a look at the documentation on updating an existing notification. Your ID is 1337 here, so you can use that to update it.
In fact, you may just be able to call declareNotification() again from your onTick() method... If this doesn't work, however, I would suggest keeping a reference to the Notification object (as a member variable), then updating it, and use nm.notify(1337, /* your notification object */);.
I don't know why you want to use a notification. But you need to keep updating your notification. For a simple fix add
declareNotification();
underneath this line:
counter = millisUntilFinished / 1000;
Note that this isn't a great way to code it. Really you should pass a method only updating the notification rather than "creating" a new one. However as long as they have the same ID, one will replace the other.
Also just to use a more up to date way of managing notifications, use
NotificationCompat.builder b = new NotificationCompat.Builder(context);
If I have an Activity which is in the foreground and I navigate to another Activity within my app, I can detect this by setting a flag when you begin the transfer and wiping it when you have transferred to the new activity. This allows me to distinguish between an activity onPause due to an internal (flag set) or external (flag not set) event.
However, I am having trouble doing this for PendingIntents embedded in Notifications. Is it possible to detect that my Activity is being onPaused because they selected the notification I created on the notification bar? Is there some kind of notification listener I can use which will trigger before the notification fires and the pending intent is executed which onPauses my Activity?
I appreciate that this is somewhat confusing, so I've made a skeleton project which demonstrates the problem:
package com.example.notificationtest;
import android.os.Bundle;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private static final int INCOMING_NOTIFICATION_ID = 1;
private static final String TAG = "NotificationTest";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onPause() {
super.onPause();
Log.e(TAG,"onPause");
}
#Override
public void onResume() {
super.onResume();
Log.e(TAG,"onResume");
}
public void onTransitionButtonClick(View view) {
Intent intent = new Intent(this, MainActivity.class);
Log.e(TAG,"About to start activity: onPause will be invoked.");
startActivity(intent);
}
public void onNotificationButtonClick(View view) {
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new Notification(android.R.drawable.alert_dark_frame, "Alert!", System.currentTimeMillis());
notification.defaults |= Notification.DEFAULT_SOUND;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_LIGHTS;
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.flags |= Notification.FLAG_INSISTENT;
notification.setLatestEventInfo(this, "Click to open", "Click to open", pendingIntent);
// Show the alert.
final NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(INCOMING_NOTIFICATION_ID, notification);
}
}
With the activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="#+id/notify_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="#dimen/padding_medium"
android:text="Make a notification"
android:onClick="onNotificationButtonClick"
tools:context=".MainActivity" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/notify_button"
android:padding="#dimen/padding_medium"
android:text="Normal transition"
android:onClick="onTransitionButtonClick"
tools:context=".MainActivity" />
</RelativeLayout>
If you select the "Normal transition" button, the log prints "About to start activity: onPause will be invoked." prior to onPause.
If you select the "Make a notification" button and then drag down the notification bar and tap on the notification, I want to be informed of that tap prior to onPause so I can insert the same line "About to start activity: onPause will be invoked.". How can I do that?
beside holding intent to start activity, PendingIntent can hold also intent to "fire" a broadcast.
you can use such custom broadcast as trigger to the notification, then implement BroadcastReceiver that will be registered to that broadcast, and will show whatever you want , and only then will start the desired activity.
Intent intent = new Intent("my.custom.broadcast.action");
pendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
the receiver will look something like this:
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//do whatever you want
// if this code is being executed - it's a sign that the user clicked on the notification...
Intent intent = new Intent(this, MainActivity.class);
Log.e(TAG,"About to start activity: onPause will be invoked.");
startActivity(intent);
}
};
don't forget to register the mReceiver when onCreate() invokes, and unregister it onDestroy()
registerReceiver(mReceiver , new IntentFilter("my.custom.broadcast.action"));
unregisterReceiver(mReceiver);
in this approach you can control exactly what will happen when the user click on the notification. as you can see - it's possible only to send broadcast when notification clicked. no one said you must start activity at all..
Let me begin by trying to translate your question to another (just for clarity): How can I have my Activity execute code before it is paused because an Activity associated with my Notification (which I can control) is going to be displayed?
Provided that the translation is good enough for the purpose of answering:
Are you sure that this approach would be a solution? Because there are many cases where your Activity will get paused and the user displays your Notification's Activity "almost immediately after", while there will still be steps in between (like when other notifications are viewed first). I presume that technically, this means that you still must perform the same code there, while at the same time you can't have any indication that your notification will be viewed "in the very near future".
So for a suitable answer, I believe one will have to think outside of the box a bit, depending on the nature of the code you intend to execute and the motivation for it.
So if you update your question in this regard and notify me, I'll be glad to have a second look.
Update after update of question:
You can avoid firing the next Activity when the user presses the notification, and instead just fire an event. If your activity registers a BroadcastReceiver programmatically, it should be able to receive this event, and then update itself and start the next Activity, just as you wish.
I know, there are tons of these on here, but I've been trying solutions all day and haven't gotten anywhere.
Neither the example on google's docs, nor any of the 5 other ways I've found on here have worked for me at all.
As is the typical case, when I click the notification it closes the status bar and nothing new is shown onscreen.
I am creating the notification from a service and need the notification to trigger a new activity that has not yet been created.
I also will need a way to pass information to that activity via intent.
And yes... this is java for Android
What follows are the shattered remnants of my code.
package com.bobbb.hwk2;
import java.io.FileOutputStream;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.provider.ContactsContract;
import android.widget.Toast;
public class contactBackup extends Service
{
private NotificationManager nManager;
private static final int NOTIFY_ID = 1100;
#Override
public void onCreate()
{
super.onCreate();
String ns = Context.NOTIFICATION_SERVICE;
nManager = (NotificationManager)getSystemService(ns);
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
// inform user that service has started
Toast.makeText(getApplicationContext(), R.string.service_started,Toast.LENGTH_LONG).show();
String data = lookUpContacts();
if( saveToSDCard(getResources().getString(R.string.backup_file_name),data) )
{
Context context = getApplicationContext();
// create the statusbar notification
Intent nIntent = new Intent(this,contactViewer.class);//Intent nIntent = new Intent(Intent.ACTION_MAIN);
nIntent.setClass(context,contactViewer.class);
//nIntent.putExtra("data",data);
Notification msg = new Notification(R.drawable.icon,"All contacts records have been written to the file.",System.currentTimeMillis());
// start notification
//PendingIntent pIntent = PendingIntent.getService(getApplicationContext(),0,nIntent,PendingIntent.FLAG_UPDATE_CURRENT|Intent.FLAG_FROM_BACKGROUND);
PendingIntent pIntent = PendingIntent.getActivity(this,0,nIntent,0);
msg.flags = Notification.FLAG_AUTO_CANCEL;
msg.setLatestEventInfo(context,
"success",
"All contacts records have been written to the file.",
pIntent);
nManager.notify(NOTIFY_ID,msg);
}
}
#Override
public void onDestroy()
{
nManager.cancel(NOTIFY_ID);
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
// function returns string containing information
// from contacts
public String lookUpContacts()
{
...
}
public boolean saveToSDCard(String fileName, String data)
{
...
}
}
I can only hope that whatever is causing my problem is something fixable and not more of the crazy glitches I've been getting with eclipse (which no one else seems to have ever seen >:U )
If you can help me solve this problem, please share.
If you can't help with this specific problem but feel obligated to say unrelated things about posting, styles, topics, or good practice, then DON'T
Thank you :D
Edit:
You're going to have to add a flag for FLAG_ACTIVITY_NEW_TASK:
nIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
This is because you're launching from outside your app (from the system notification bar).
This is what happens when people overwork themselves. XD
The only reason none of the tutorials I tired worked is because I misspelled my activity name in the manifest.
Thanks for stopping by
Just add following in contactBackup(service class),
Intent nIntent = new Intent(this,contactViewer.class);//Intent nIntent = new Intent(Intent.ACTION_MAIN);
nIntent.setClass(context,contactViewer.class);
nIntent.putExtra("data",data);
nIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Notification msg = new Notification(R.drawable.icon,"All contacts records have been written to the file.",System.currentTimeMillis());
// start notification
//PendingIntent pIntent = PendingIntent.getService(getApplicationContext(),0,nIntent,PendingIntent.FLAG_UPDATE_CURRENT|Intent.FLAG_FROM_BACKGROUND);
PendingIntent pIntent = PendingIntent.getActivity(this,0,nIntent,0);
msg.flags = Notification.FLAG_AUTO_CANCEL;
msg.setLatestEventInfo(context,
"success",
"All contacts records have been written to the file.",
pIntent);
nManager.notify(NOTIFY_ID,msg);
then get value in contactViewer class,
as,
String s=getIntent().getStringExtra("data");