I've created an application where you press "start logging" which starts a service that periodically logs data. The button changes to show "stop logging". You then minimize the application and allow it to log in the background whilst you go about your business. However, I have had to implement "startForeground" as the application would close/be destroyed, and with "startForeground" a notification is required.
Now, when I run the application and minimize, then re-open the app, it is fine, but if i attempt to reopen via the notification it resets the screen so that the button now says "start logging" again and when clicked, starts logging again (takes over the current service). The service was still logging even when it said "start logging" so it has something to do with the screen not saving the button state I assume?
I have tried this, but to no avail:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("message", (String) but1.getText());
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.logorcheck);
System.out.println("goes in oncreate");
if (savedInstanceState != null) {
but1.setText(savedInstanceState.getString("message"));
Here are a few snippets:
LoggingService.java
NotificationManager nm = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
Resources res = this.getResources();
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.arrow_up_float)
.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.arrow_down_float))
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContentTitle("App")
.setContentText("Service running OK");
Notification n = builder.getNotification();
// !
startForeground(100, n);
return Service.START_STICKY;
LogOrCheck.java
if (isClicked) {
Context context = getApplicationContext();
Toast toast = Toast.makeText(context, "Please press 'MINIMIZE' at the top to continue logging.", Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.RIGHT, 10, 55);
toast.show();
System.out.println("Start logging");
but1.setText("Stop Logging");
but1.setTextColor(Color.RED);
// START SERVICE, DONE. (STOP IS BELOW).
startService(myIntent);
isClicked = false;
}
else if (!isClicked) {
System.out.println("Stop Logging");
but1.setText("Start Logging");
// STOP SERVICE, DONE.
stopService(myIntent);
but1.setTextColor(Color.BLACK);
isClicked = true;
}
Thanks for any help, it seems an easy fix that I can't figure out how to do it, especially with notificationBuilder. Sorry for the long message! Thanks.
The Simplest thing you could do would be to create a static boolean variable in your LoggingService.java something like isLogging and set it accordingly in the service when changes occur.
In the onResume() of the activity access that variable and set the button state accordingly.
Related
My app has a Notification that holds a Button.
When the user clicks the Button, the app should open (if it was closed) and show a custom dialog, which I've created by a bunch of views.
Showing the dialog means setting its Visibility to View.VISIBLE.
I use a BroadcastReceiver to receive the Notifications Button click, and it works (I've used Toast messages to check it, and it does - the Receiver is getting called when the Button is clicked, and it reaches to the function that changes the dialogs Visibility to View.VISIBLE)
When I click the Button, the app does open, but the dialog won't show.
Also, I've noticed that when I click the Button, the Notification panel does not hide (unlike when clicking the Notification itself), and I thought maybe it has something to do with it.
So I found this answer, but it didn't work for me (below there's another answer, much newer, but I didn't understand what he did there or how can I do it).
I also checked using Toast messages whether the Visibility parameter of the dialog is changing or not, and it is changing to View.VISIBLE (0), and still, the dialog is not shown.
If I'm in the app and trying to show the dialog, it works, the dialog shows on an in-app button click, the issue occurs when trying to show the dialog as soon as opening the app using the Notifications Button click
Is the Notification Panel the problem?
If it is, why is it happening and how can I hide the notification panel?
If it's not, what is it, and how can I solve this?
Edit
I was asked for the code that inits and shows the notification, so here it is:
#RequiresApi(api = Build.VERSION_CODES.M)
private void updateNotification() {
isBatteryOptimizationActive = App.isBatteryOptimizationActive();
clearAllTimerNotificationActionButtons();
if (isBatteryOptimizationActive) {
initBatteryOptimizationActiveNotification();
} else {
if (notificationJustStarted) {
initNotificationBase();
}
initAndStartPomodoroTimerNotification();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
NotificationManager manager = getSystemService(NotificationManager.class);
manager.notify(TIMER_NOTIFICATION_ID, pomodoroNotificationBuilder.build());
}
}
private void initNotificationBase() {
pomodoroNotificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
pomodoroNotificationBuilder
.setContentTitle(getString(R.string.timer_notification_background_allowed_title))
.setOngoing(true)
.setAutoCancel(false)
.setSmallIcon(R.drawable.tomato)
.setContentIntent(pendingIntent)
.setNotificationSilent()
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
notificationJustStarted = false;
}
#RequiresApi(api = Build.VERSION_CODES.M)
private void initAndStartTimerNotification() {
String enableScreenOverlayText = "",
actionButtonTitle;
PendingIntent pendingIntent;
if (!Settings.canDrawOverlays(this)) {
/** Initialize the notification in case "draw over other apps" setting is disabled */
Intent notificationIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
actionButtonTitle = "Enable";
enableScreenOverlayText = String.format("To be able stopping the timer from the notification:\nClick Enable -> Search for %s -> Allow Display over other apps", getString(R.string.app_name));
} else {
/** Initialize the notification in case "draw over other apps" setting is enabled */
Intent stopActionIntent = new Intent(this, PomodoroService.class);
stopActionIntent.setAction(ACTION_STOP_SERVICE);
stopActionIntent.putExtra(POMODORO_SERVICE_STOP_FOR_REAL_EXTRA_NAME, false);
pendingIntent = PendingIntent.getService(this, 1, stopActionIntent, PendingIntent.FLAG_IMMUTABLE);
actionButtonTitle = "Stop";
}
pomodoroNotificationBuilder
.setContentText(String.format("%s is in progress\nThis session remains: %s until finished", goalName, PublicMethods.formatStopWatchTime(millisRemaining)))
.setStyle(new NotificationCompat.BigTextStyle().bigText(enableScreenOverlayText))
.addAction(R.drawable.stop, actionButtonTitle, pendingIntent);
}
How can I hide the notification panel?
I see that you are receiving the action button click in the Receiver. So, then it becomes an easy task to dismiss it. In the receiver, add this line:
NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
manager.cancel(TIMER_NOTIFICATION_ID);
When I click the Button, the app does open, but the dialog won't show.
You need to pass an intent extra that you are wanting to show the dialog. Then, you can, in the onCreate() get that extra and then show the dialog accordingly.
From the stackoverflow and many blogs, i surely understand that foreground service never run without notification in API>25. But still i confuse that Is notification mandory while app is running on screen or visible.
For eg. no need of notification when user stand within app. So is this possible to remove notification while app running ?
In service class
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
......
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
}
return START_NOT_STICKY;
}
In activity
Intent myService = new Intent(this, MyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(myService);
} else {
startService(myService);
}
It's not possible to remove the notification while the foreground service is running, but it is possible to change your foreground service back into a "regular" service. This removes the need for a notification. In fact, the function to use,
stopForeground(boolean removeNotification)
...includes a removeNotification parameter just for that purpose. You service can switch from being "foreground" to "regular" on demand, by alternating calls to startForeground() and stopForeground().
In case it's not clear, you'd probably want to call stopForeground() whenever you have at least one Activity in a "started" state. This is something you'd have to track manually. Then, when the number of "started" activities reaches 0, you'd call startForeground().
EDIT
One approach is to use a bound service. Then, it's easy to call stopForeground() on it when you want.
Assume you have a single Activity. You can bind it to the service (see this doc or use one of these examples). Then your onServiceConnected() function could look like this (adapted from the Google example):
//MyActivity.java:
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mService.stopForeground(true); //This makes the notification go away
bound = true;
}
...
#Override
protected void onStart() {
super.onStart();
// Bind to the service
bindService(new Intent(this, MyService.class), this, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (bound) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText(text)
.setAutoCancel(true);
Notification notification = builder.build();
mService.startForeground(1, notification); //This brings the notification back! Service is already running, and continues to run.
unbindService(this);
bound = false;
}
}
No, it is mandatory even your app is running in foreground your foreground service need a notification.
You won't able to hide it.
Why :
You can use any other background task handler like intent service, job sclr but things is designed defferent for foreground service your user understand that event i will close this one of it's progress is going to keep running but things is defferent with background service your know it will do something in background but when system decide it's best time to do it not when your app want (as like in foreground service).
One more case ex :
Suppose your app in foreground battery level is lower than expected by user or system your foreground service will execute instantly no matter what so it's important for your user to know this it's running and take my resources (battery, data, etc)
Hopefully you got my mean 🙂
public void showNotification(Context context,String pnrNumber){
Intent intent=new Intent(context,HomeActivity.class);
intent.putExtra("PNR", pnrNumber);
//To Clear the Activity Stack
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(context, uniqueNumber,intent, Intent.FLAG_ACTIVITY_CLEAR_TASK);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("TravelKhana")
.setContentText("Get food in train for the PNR:" +pnrNumber);
mBuilder.setContentIntent(contentIntent);
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(uniqueNumber, mBuilder.build());
uniqueNumber++;
}
and in oncreate of the HomeActivity i am getting this string extra
if(getIntent().hasExtra("PNR")){
mPnrSearch.setTag(getIntent().getStringExtra("PNR"));
onClick(mPnrSearch);
}
And then in onClick(mPnrSearch);
public void onClick(View v) {
switch (v.getId()) {
case R.id.pnrSearch:
if(NetworkChecker.isConnected(getApplicationContext())) {
easyTracker.send(MapBuilder.createEvent("Home Activity","click", "PNR", null).build());
}
Intent pnrIntent = new Intent(HomeActivity.this, PnrSearch.class);
//If the user came from notification
if(v.getTag() != null){
pnrIntent.putExtra("PNR", v.getTag().toString());
v.setTag(null);
getIntent().removeExtra("PNR");
}
startActivity(pnrIntent);
break;
}
I removed the extra and then I pressed the back button to destroy the app and reopened it by long pressing the home button in my phone and then after the extra is still there and onClick(mPnrSearch) is called again, but i have removed the extra why is it so?? and what do i need to do to resolve this out.
This is either an Android bug or a feature, depending on whether you want it to happen or not ;-) This case isn't clearly documented, but it is clear that it behaves the way you describe.
I recently answered a similar question and made some suggestions about how to deal with this problem. See my answer for more information.
i think after removing you should update new intent, just implement this function in your activity
protected void onNewIntent(Intent intent) {
// TODO Auto-generated method stub
super.onNewIntent(intent);
setIntent(intent);
}
in my case i passed the value from notification to my MainActivity from onMessageReceived. why because i wanted to count the opening app by notification click for specific notification id which is the value i passed by exteras( notification opens the app mainactivity ) for analytics reasons. what ever, to find out if main activity is opened by notification or not i checked both exteras value and intent flag. i observed that when ever app is opened by notification click the intent flag is 0 so i added the below to my mainactivity.
if(getIntent().getFlags() == 0 && getIntent().getExtras() != null){
Log.i("opened by notification only",getIntent().getStringExtra("myID"));
//rest of process...............
}
I have a button that when pressed, starts a service. This service starts logging information from the device. However, when I press the run, the screen temporarily freezes for about 8 seconds whilst it logs, and then once it is finished the screen unfreezes, a toast message shows (that's meant to show as soon as you press the button) and then I can do whatever. If i go on the application screen again, when the application is logging (note: the service is always running so that doesnt freeze it, just the logging) then the activity is frozen again and doesnt let me do anything until the logging is complete.
I have tried asynctasks and running on a new runnable/new thread but none of these have seemed to work for me. Whether i am implementing them correctly or not I'm unsure, but I'd like to fix the problem.
Here is my onStartCommand in the service class:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// THIS WOULD BE THE METHOD THAT RUNS WHATEVER YOU WANT TO DO EVERY SO OFTEN SO FOR ME THIS IS GETTING ALL DATA ETC
getProcessInfo();
// LOG DELAY = SECONDS BETWEEN RUNNING SERVICE/METHOD. SET AT THE TOP
myPendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Alarmmgr.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+LOG_DELAY*1000, myPendingIntent);
Intent notificationIntent = new Intent(this, LogOrCheck.class);
PendingIntent contentIntent = PendingIntent.getActivity(this,
101, notificationIntent,
PendingIntent.FLAG_NO_CREATE);
NotificationManager nm = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
Resources res = this.getResources();
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.arrow_up_float)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.arrow_down_float))
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContentTitle("Androigenius")
.setContentText("Service running OK");
Notification n = builder.getNotification();
startForeground(100, n);
return Service.START_STICKY;
}
And here is where I call startService:
but1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (isClicked) {
Context context = getApplicationContext();
Toast toast = Toast.makeText(context, "Please press 'MINIMIZE' at the top to continue logging.", Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.RIGHT, 10, 55);
toast.show();
System.out.println("Start logging");
but1.setText("Stop Logging");
but1.setTextColor(Color.RED);
// START SERVICE, DONE. (STOP IS BELOW).
startService(myIntent);
isClicked = false;
}
else if (!isClicked) {
System.out.println("Stop Logging");
but1.setText("Start Logging");
// STOP SERVICE, DONE.
stopService(myIntent);
but1.setTextColor(getResources().getColor(color.cornblue));
isClicked = true;
}
Services run on the UI thread which is causing the 'freeze'. Using an IntentService will create a service that automatically runs in the background, not on the UI thread. Here is some details of the IntentService class:
IntentService Class
By default, the Service also run in the main thread of your applicaiton, in which your UI operations occur. Therefore, if you perform a long task in your onStartCommand() method, it will block the main thread.
To avoid the problem, you have to migration the logging task into a separate thread. You may use AsycTask class to do so. Please refer to http://developer.android.com/reference/android/os/AsyncTask.html for how to use the class.
I want to implement the following scenario:
When a user presses the Home key a Notification is displayed in the Status bar and the App is normally hidden. When user taps the Notification App is normally restored.
Here is my code for this:
private int NOTIFICATION = R.string.local_service_started;
private NotificationManager mNM;
private void showNotification() {
CharSequence text = getText(R.string.local_service_started);
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, JingleCartoucheActivity.class), 0);
notification.setLatestEventInfo(this, getText(R.string.local_service_label), text, contentIntent);
mNM.notify(NOTIFICATION, notification);
}
#Override
public void onPause() {
super.onPause();
if (!isFinishing()) {
showNotification();
}
}
#Override
public void onResume() {
super.onResume();
mNM.cancel(NOTIFICATION);
}
All of this is working well except for one thing:
When the App is run and the user rotates the phone, the notification is created and immediately destroyed. Is there a way to detect the phone orientation change in onPause() and not show this Notification?
You can get the current orientation using the getResources().getConfiguration().orientation. At the onCreate you store the orientation value and then you can compare it later. Be sure to store the value into a static variable, since when you rotate the phone, the activity is destroyed and so are it's values.
When the user rotate the screen the activity is destroyed and recreated , If you want to display the notification after orientation change make a static flag in onDestroy method and check it in the onCreate method to display the notification like
public void onDestroy() {
Flag = 1;
}
and
public static int Flag = 0;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(Flag == 1) {
showNotification();
}
else {
.......//
}
}