Android notification keeps reappearing - android

I am trying to get a better handle on notifications with the simple app shown below (at least the relevant parts). I would like to show a notification icon when the app is not destroyed, and remove the notification icon when it is destroyed; pretty simple right?
My problem is that when the fragment's onDestroyView event is fired the notification icon does disappear, but then reappears and I can't figure out why.
To try to figure things out a bit better I create a stop notification button that calls the mServiceBinder's stop() method, which does remove the notification icon for good even though it is calling the same method called in the onDestroyView event.
Any ideas to what the problem is?
MainActivityFragment
package org.chrisolsen.notificationtest;
import ...
/**
* A placeholder fragment containing a simple view.
*/
public class MainActivityFragment extends Fragment {
private ServiceConnection mServiceConn;
private TestService.TestBinder mServiceBinder;
public MainActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onDestroyView() {
Log.d("Fragment", "onDestroyView");
// *try* to prevent services from restarting
if (mServiceConn != null) {
getActivity().unbindService(mServiceConn);
}
if (mServiceBinder != null) {
mServiceBinder.stop();
}
mServiceConn = null;
mServiceBinder = null;
super.onDestroyView();
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Log.d("Fragment", "onViewCreated");
Button showButton = (Button) view.findViewById(R.id.button);
Button hideButton = (Button) view.findViewById(R.id.btnHide);
showButton.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Fragment", "onCLick");
mServiceConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("serviceConn", "onConnected");
mServiceBinder = (TestService.TestBinder) service;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("serviceConn", "onDisconnected");
}
};
Intent service = new Intent(getActivity(), TestService.class);
getActivity().startService(service);
getActivity().bindService(service, mServiceConn, Context.BIND_ABOVE_CLIENT);
}
});
hideButton.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View v) {
mServiceBinder.stop();
}
});
}
}
TestService
package org.chrisolsen.notificationtest;
import ...
public class TestService extends Service {
public class TestBinder extends Binder {
public void stop() {
TestService.this.stopForeground(true);
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("TestSErvice", "onDestroy");
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return new TestBinder();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("TestSErvice", "onHandleIntent");
Context c = getApplicationContext();
Intent activityIntent = new Intent(c, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
c, 0, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
builder.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("The Content Text")
.setOngoing(false)
.setAutoCancel(true)
.setPriority(Notification.PRIORITY_LOW)
.setContentTitle("The content title");
startForeground(1, builder.build());
return super.onStartCommand(intent, flags, startId);
}
}

The notification reappears because your service onStartCommand called after your activity closed.
It is a bad place to create and show notification. onStartCommand method maybe called multiple times. Place the notification creation to the onCreate for example.
What do you mean when you wrote "app is not destroyed"? If you thought while your service is running, put the creation to the onCreate and remove it in onDestroy.

Returning START_STICKY instead of super.onStartCommand(intent, flags, startId), is what I should've been doing.

Related

How can I launch my Android app by detecting shake using foreground service for API level upper 27?

I am using foreground service in my App to detect shake and launch app from anywhere to MainActivity, After doing on some code now I'm stucked with the problem. I hope someone will come up with the solution or suggest me how to make this work. Yes, I did run the app, after I run whenever I shake my phone the service thus the app gets closed automatically. My service code has been given below
public class ExampleService extends Service {
private SensorManager sensorManager;
private float acelVal;
private float acelLast;
private float shake;
public ExampleService() {
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent=new Intent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,notificationIntent,0);
Notification notification=new NotificationCompat.Builder(this,CHANNEL_ID)
.setContentTitle("Example service")
.setContentText("Example for shaking")
.setSmallIcon(R.drawable.ic_android)
.setContentIntent(pendingIntent)
.build();
sensorManager=(SensorManager)getSystemService(Context.SENSOR_SERVICE);
sensorManager.registerListener(sensorListener,sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),SensorManager.SENSOR_DELAY_NORMAL);
acelVal=SensorManager.GRAVITY_EARTH;
acelLast=SensorManager.GRAVITY_EARTH;
shake=0.00f;
startForeground(1,notification);
return START_NOT_STICKY;
}
public final SensorEventListener sensorListener= new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent sensorEvent) {
float x=sensorEvent.values[0];
float y=sensorEvent.values[1];
float z=sensorEvent.values[2];
acelLast=acelVal;
acelVal=(float)Math.sqrt((double)(x*x+y*y+z*z));
float delta=acelVal-acelLast;
shake=shake*0.9f+delta;
if(shake>12)
{
Intent intent= new Intent(ExampleService.this,MainActivity.class);
startActivity(intent);
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
};
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
And the MainActivity code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent(this,ExampleService.class);
startForegroundService(intent);
}
}
Am I on the right path to achieve this? If not suggest me how to solve it
The onBind method is part of the android service lifecycle. It's not an explicit call, the OS automatically calls it for you. The method is useful in some specific use cases (called bound services). In the most simple cases, you can just return a null binder.
So you should replace:
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
by:
#Override
public IBinder onBind(Intent intent) {
return null;
}

from Service, call Activity method (if it's in the foreground)

From an Android Service, I would like to call an Activity method, but only if the Activity is in the foreground.
I would like nothing to happen in case the Activity is not in the foreground.
Which is the simplest way to achieve that?
From a Service always better to broadcast events if the activity is listening to that broadcast will respond. If the activity is not listening then nothing will happen it will ignore.
This is the better solution than the one which you have asked.
I found a very simple solution, adapted from this previous answer:
On the Service:
Intent intent = new Intent(MainActivity.RECEIVER_INTENT);
intent.putExtra(MainActivity.RECEIVER_MESSAGE, myMessage);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
On the Activity:
public static final String RECEIVER_INTENT = "RECEIVER_INTENT";
public static final String RECEIVER_MESSAGE = "RECEIVER_MESSAGE";
Create a listener on onCreate():
public void onCreate(Bundle savedInstanceState) {
...
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra(RECEIVER_MESSAGE);
// call any method you want here
}
};
}
register it in onStart():
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((mBroadcastReceiver),
new IntentFilter(RECEIVER_INTENT)
);
}
unregister it in onStop():
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
super.onStop();
}
You can do this using a Interface just to check if the activity is in background or in foreground.
I am sharing some code to have some idea.
public interface CheckAppInForeground {
boolean isAppInForGround();
}
In your Activity
public class MainActivity extends AppCompatActivity implements CheckAppInForeground {
Boolean isAppInForeGround;
#Override
protected void onResume() {
super.onResume();
isAppInForeGround = true;
}
#Override
protected void onStop() {
super.onStop();
isAppInForeGround = false;
}
#Override
public boolean isAppInForGround() {
return isAppInForeGround;
}
}
And your service class
public class MyService extends Service {
Activity activity;
public MyService(Activity activity) {
this.activity = activity;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MainActivity mainActivity = (MainActivity) activity;
if (activity != null) {
boolean isActivityInForGround = mainActivity.isAppInForGround();
if (isActivityInForGround) {
// Do what you want to do when activity is in foreground
} else {
// Activity is in background
}
} else {
// Activity is destroyed
}
return super.onStartCommand(intent, flags, startId);
}
}
I think i am clear with the code. If you find something missing or unclear please let me know

Why the body of onStartCommand() is getting executed only once?

To know the difference between IntentService and Service in Android, I created the below posted small test of a Service class. The MainActivity has a Button, when pressed, the
service will be started using startService() as shown below in the code, which will result in a call to onStartCommand(). In onStartCommand(), i run a loop for 10 seconds, and I
expected that, that loop will block the UI "the butoon". Actually that what happened exactly when I first time start the Service, but when I press the button after the 10 seconds
elapsed, it will result in a call to onStartCommand() but the log message inside the onStartCommand() never get displayed plus the UI is never blocked.
can anyone please explain what the body of the onStartCommand() is executed and blocks the UI only when the Service first started and never afterwards?
MainActivity
public class MainActivity extends AppCompatActivity {
private Button mbtnSend = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mbtnSend = (Button) findViewById(R.id.btn_send);
this.mbtnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), MyService.class);
startService(intent);
}
});
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver(this.mBCR_VALUE_SENT, new IntentFilter(MyIntentService.INTENT_ACTION));
this.mbtnSend = (Button) findViewById(R.id.btn_send);
this.mbtnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), MyIntentService.class);
intent.putExtra("intent_key", ++i);
startService(intent);
}
});
}
}
MyIntentService:
public class MyService extends Service{
private final String TAG = this.getClass().getSimpleName();
private long mStartTime;
#Override
public void onCreate() {
super.onCreate();
Log.w(TAG, SubTag.msg("onCreate"));
this.mStartTime = TimeUtils.getTSSec();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, SubTag.msg("onStartCommand"));
while ( (TimeUtils.getTSSec() - this.mStartTime) <=10) {
Log.w(TAG, SubTag.msg("time: " + (TimeUtils.getTSSec() - this.mStartTime)));
SystemClock.sleep(1000);
}
return Service.START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.w(TAG, SubTag.msg("onBind"));
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, SubTag.msg("onDestroy"));
}
}
You are setting mStartTime to TimeUtils.getTSSec() in onCreate(), which means that it will get intialized only once.
Afterwards, onStartCommand() is called, but the mStartTime timestamp is not being updated, so the while loop never runs.
I believe that moving the line where you initialize mStartTime to onStartCommand() before the while loop will make your thread hang again.

The same push notification keeps appearing whenever i open my apps

The same push notification keeps appearing whenever I reopen my apps although i have already cleared the notification in the notification bar. Secondly how do I implement a service so that my apps can receive notification although the apps is closed.
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
SharedPreferences sharedPreferences = getSharedPreferences(Constants.SHARED_PREF, MODE_PRIVATE);
String id = sharedPreferences.getString(Constants.UNIQUE_ID, null);
Firebase firebase = new Firebase(Constants.FIREBASE_APP + id);
firebase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
String msg = snapshot.child("msg").getValue().toString();
if (msg.equals("none"))
return;
showNotification(msg);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("The read failed: ", firebaseError.getMessage());
}
});
return START_STICKY;
}
private void showNotification(String msg){
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
Intent intent = new Intent(NotificationListener.this,ViewRecord.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
builder.setContentIntent(pendingIntent);
Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSound(alarmSound);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentTitle("Notifier");
builder.setContentText(msg);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
builder.setAutoCancel(true);
notificationManager.notify(1, builder.build());
}
my service code as below. and i call the service at onCreate function in the 1st activity..
public class MyService extends Service {
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
Toast.makeText(this, "The new Service was Created", Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startId) {
// For time consuming an long tasks you can launch a new thread here...
Toast.makeText(this, " Service Started", Toast.LENGTH_LONG).show();
}
}
Posting this as answer since code in comment wud make it look unstructured
isServiceStarted
public class MainActivity extends AppCompatActivity {
private SharedPreferences servicePref;
private boolean isServiceStarted;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
servicePref = getSharedPreferences("servicePref", MODE_PRIVATE);
isServiceStarted = servicePref.getBoolean("isServiceStarted", false);
if (!isServiceStarted) {
startService(new Intent(this, MyService.class));
servicePref.edit().putBoolean("isServiceStarted",true).apply();
}
}
and in ur MyService.class inside onStop method do this without fail.
public class MyService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
// save value as false when service gets destroyed so as to start again when u open the app
getSharedPreferences("servicePref", MODE_PRIVATE).edit().putBoolean("isServiceStarted",false).apply();
}
}
override the onStartCommand() method and then return START_STICKY.

Service and Chronometer Synchronization

I need to be able to start chronometer, then close activity, after that through notifications, back to that activity, and see the right time in chronometer.
What I've Done
A part of my Activity:
public void doClick(View target)
{
switch(target.getId())
{
case R.id.buttonStart:
{
Mchronometer.setBase(SystemClock.elapsedRealtime());
Mchronometer.start();
Intent intent = new Intent(RecentActivity.this, ChronometerService.class);
intent.putExtra("task_name",task_name);
intent.putExtra("task_id",task_id);
intent.putExtra("ellapsedTime",Mchronometer.getBase());
Log.d("base",""+Mchronometer.getBase());
startService(intent);
break;
}
case R.id.buttonStop:
{
stopService(new Intent(RecentActivity.this, ChronometerService.class));
Mchronometer.stop();
Mchronometer.setBase(SystemClock.elapsedRealtime());
break;
}
case R.id.button3:
{
break;
}
}
}
A part of my Service:
public class ChronometerService extends Service {
private ThreadGroup myThreads = new ThreadGroup("ServiceWorker");
private NotificationManager notificationMgr;
private int task_id;
private long ellapsedTime;
#Override
public void onCreate() {
super.onCreate();
notificationMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
String task_name =intent.getExtras().getString("task_name");
task_id =intent.getExtras().getInt("task_id");
ellapsedTime = intent.getExtras().getLong("ellapsedTime");
Log.d("servicebase",""+ellapsedTime);
displayNotificationMessage(task_name);
new Thread(myThreads, new ServiceWorker(),"ChronometerService").start();
return START_STICKY;
}
private class ServiceWorker implements Runnable {
public void run() {
}
}
#Override
public void onDestroy()
{
myThreads.interrupt();
notificationMgr.cancelAll();
super.onDestroy();
}
public IBinder onBind(Intent intent) {
return null;
}
public void displayNotificationMessage(String message){
Notification notification = new Notification(R.drawable.emo_im_winking,message,System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR;
Intent intent = new Intent(this, RecentActivity.class);
intent.putExtra("task_id", task_id);
intent.putExtra("ellapsedTime",ellapsedTime);
Log.d("servicebase1",""+Long.toString(ellapsedTime));
PendingIntent contentintent = PendingIntent.getActivity(this,0,intent,0);
notification.setLatestEventInfo(this,"ChronometerService",message,contentintent);
notificationMgr.notify(0, notification);
}
}
I tried to send a message from activity to a service, which contains elapsed information.
If I started it first on my device (after system load) it's works right, but when I launch it again. The activity receives wrong message. It receives the time of the first service launched on the device.
As you can see I also send one more variable, and activity reads it correctly.
I've found a solution to my question.
It's simple.
It's needed to use flag(PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent contentintent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
And it's work fine.

Categories

Resources