I have an activity where I load different fragment with navigation drawer options.
Those fragments have different asynctasks (e.g. one for downloading image, one for importing for database etc.). Every time an asynctask is initiated, I use NotificationManager to show the progress.
What I am looking for is, if anybody click on any notification, it will cancel corresponding AsyncTask. I read about PendingIntent method, but I am not sure whether I need to open an intent to do that.
Also, I am confused how to get reference of the AsyncTasks from my MainActivity as they are initiated inside fragment (and those fragments get destroyed time to time).
I can put some code here if you want, but the code is very basic AsyncTask and Fragment based Navigation Drawer with single Activity.
Thanks,
public class MyFragment1 extends Fragment {
private DownloadFile asynctaskhandler;
public method(){
asynctaskhandler = new DownloadFile();
asynctaskhandler.execute();
}
private class DownloadFile extends AsyncTask<Void, String, Void> {
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
int mId;
protected Void doInBackground(Void... args) {
while(){
//DON SOMETHING, GET PROGRESS progress
mBuilder.setProgress(mId, progress, true);
mNotifyManager.notify(mId, mBuilder.build());
}
}
}
}
To many questions in this one... I'll try to answer the one about notification.
notification:
Intent intent2 = new Intent();
intent2.setAction("com.app.example.MyServiceClass.STOP");
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, intent2, 0);
noti = new NotificationCompat.Builder(this)
.setContentTitle("Recorder")
.setContentText("running")
.setSmallIcon(R.drawable.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.addAction(R.drawable.ic_launcher, "Stop", pIntent)
.build();
startForeground(12345, noti);
Service (receiver of the pendingIntent):
// registering BroadcastReceiver
IntentFilter filter2 = new IntentFilter();
filter2.addAction("com.app.example.MyServiceClass.STOP"); //further more
registerReceiver(receiver, filter2);
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals("com.app.example.MyServiceClass.STOP")){
isCancelled = true; // this is a class variable
sendMessage("hide");
}
}
};
doInBackground:
protected Boolean doInBackground(String... StringUrls) {
// some loop
if (myServiceClass.this.isCancelled) {
myServiceClass.this.stopSelf();
}
// some loop
}
Related
I'm trying to create a Service to checking data and push notification. I have a problem with call service again after stop it. It like 2 instance or more of the service is running(// my code here running twice and call notification twice after stop and start, I comment it cuz it is a long code xD). How can I prevent it?
From some answer i found, i try to put my thread in onCreate() but no effect.
Here is my code:
public class CheckDataAndPushNotificationService extends Service {
public CheckDataAndPushNotificationService() {
}
private String token;
ArrayList<UsefulRequestData> oldData;
ArrayList<UsefulRequestData> newData;
LocalBroadcastManager localBroadcastManager;
ApiUtil apiUtil;
String notificationNewRequest = "Có yêu cầu mới.";
String notificationCancelRequest = "Có yêu cầu đã bị hủy.";
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
newData = new ArrayList<>();
localBroadcastManager = LocalBroadcastManager.getInstance(this);
apiUtil = new ApiUtil(this);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
// my code here
}, 0, 5000);
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
public void createNotification(String message) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("RequestNotification", "RequestNotification", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("Use to notify if you have new or cancel request.");
notificationManager.createNotificationChannel(channel);
}
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext(), "RequestNotification")
.setSmallIcon(R.mipmap.ic_app_icon) // notification icon
.setContentTitle("XCaller") // title for notification
.setContentText(message)// message for notification
.setAutoCancel(true)// clear notification after click
.setSound(defaultSound);
Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pi);
notificationManager.notify(0, mBuilder.build());
}
}
You need to use IntentService. It is perfect for your needs. It automatically creates separated thread for the work that you send to it.
From Documentation:
IntentService is a base class for Services that handle asynchronous
requests (expressed as Intents) on demand. Clients send requests
through Context.startService(Intent) calls; the service is started as
needed, handles each Intent in turn using a worker thread, and stops
itself when it runs out of work.
This "work queue processor" pattern is commonly used to offload tasks
from an application's main thread. The IntentService class exists to
simplify this pattern and take care of the mechanics. To use it,
extend IntentService and implement
onHandleIntent(android.content.Intent). IntentService will receive the
Intents, launch a worker thread, and stop the service as appropriate.
All requests are handled on a single worker thread -- they may take as
long as necessary (and will not block the application's main loop),
but only one request will be processed at a time.
I solved it by override method onDestroy() and cancel the timer. When you call stopService(), onDestroy() will run.
#Override
public void onDestroy() {
timer.cancel();
super.onDestroy();
}
I'm getting notification from fcm when app is in foreground or background.
I don't want to show the notification When app in a specific activity how to disable notification when app is in specific activity.
I'm using datapaylod to for notification
You can check current top activity by:
public static Activity getActivity() {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
activitiesField.setAccessible(true);
Map<Object, Object> activities = (Map<Object, Object>) activitiesField.get(activityThread);
if (activities == null)
return null;
for (Object activityRecord : activities.values()) {
Class activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(activityRecord);
return activity;
}
}
return null;
}
you can check if top activity is the activity in which you don't want to show notification and then return the call from service.
Try this,
private void sendNotification(String msg) {
Intent notificationIntent = null;
notificationIntent = new Intent(currentactivity.this, Main2Activity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(GCMNotificationIntentService.this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationManager nm = (NotificationManager) GCMNotificationIntentService.this.getSystemService(Context.NOTIFICATION_SERVICE);
Resources res = GCMNotificationIntentService.this.getResources();
NotificationCompat.Builder builder = new NotificationCompat.Builder(GCMNotificationIntentService.this);
builder.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.app_logo_small_trans)
.setColor(getResources().getColor(R.color.app_logo_color))
.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.app_logo))
.setTicker(msg)
.setWhen(System.currentTimeMillis())
.setAutoCancel(true)
.setContentTitle("Propreka")
.setLights(0xffff0000, 100, 2000)
.setPriority(Notification.DEFAULT_SOUND)
.setContentText(msg);
Notification n = builder.getNotification();
n.defaults |= Notification.DEFAULT_ALL;
nm.notify(0, n);
}
So you want to know which (if any) activity is in foreground at the moment. And depending on the class type, want to limit the showing of notifications.
You need to maintain this yourself. Easiest way is to write a new class and have public static functions in it to mark activity as active / inactive
public static Activity mCurrentActivity = null;
public static void SetActivity(Activtiy activity) { mCurrentActivity = activity };
public static void ClearActivity(Activtiy activity)
{
if (mCurrentActivity.equals(activity))
mCurrentActivity = null;
}
public static Activity GetCurrentActivity() { return mCurrentActivity; }
Once you have such an infra, you can call the SetActivity and ClearActivity functions in all of your onResume and onPause overrides respectively.
Finally, in your notification builder class, you will get the instance of current activity and compare the .class to check if your desired activity is the one in focus. If the Current activity returns null, it means your app is not in foreground.
There are better, more efficient and cleaner way to do this. E.g. Dependency Injection with Daggr.
I know that you can launch Activities from the action buttons using PendingIntents. How do you make it so that the a method gets called when the user clicks the notification action button?
public static void createNotif(Context context){
...
drivingNotifBldr = (NotificationCompat.Builder) new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.steeringwheel)
.setContentTitle("NoTextZone")
.setContentText("Driving mode it ON!")
//Using this action button I would like to call logTest
.addAction(R.drawable.smallmanwalking, "Turn OFF driving mode", null)
.setOngoing(true);
...
}
public static void logTest(){
Log.d("Action Button", "Action Button Worked!");
}
You can't directly call methods when you click action buttons.
You have to use PendingIntent with BroadcastReceiver or Service to perform this. Here is an example of PendingIntent with BroadcastReciever.
First lets build a Notification
public static void createNotif(Context context){
...
//This is the intent of PendingIntent
Intent intentAction = new Intent(context,ActionReceiver.class);
//This is optional if you have more than one buttons and want to differentiate between two
intentAction.putExtra("action","actionName");
pIntentlogin = PendingIntent.getBroadcast(context,1,intentAction,PendingIntent.FLAG_UPDATE_CURRENT);
drivingNotifBldr = (NotificationCompat.Builder) new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.steeringwheel)
.setContentTitle("NoTextZone")
.setContentText("Driving mode it ON!")
//Using this action button I would like to call logTest
.addAction(R.drawable.smallmanwalking, "Turn OFF driving mode", pIntentlogin)
.setOngoing(true);
...
}
Now the receiver which will receive this Intent
public class ActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Toast.makeText(context,"recieved",Toast.LENGTH_SHORT).show();
String action=intent.getStringExtra("action");
if(action.equals("action1")){
performAction1();
}
else if(action.equals("action2")){
performAction2();
}
//This is used to close the notification tray
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.sendBroadcast(it);
}
public void performAction1(){
}
public void performAction2(){
}
}
Declare Broadcast Receiver in Manifest
<receiver android:name=".ActionReceiver" />
Hope it helps.
I have followed tutorial in http://developer.android.com/training/notify-user/index.html
It works well. But what I want is : when I click ping, the old service will we stopped, and then create the service again. So if I clicked id multiple time, It will notify me only once.
Problem: If I set time 10, then I click "Ping" button. Then after 5 second, I click it again. It will notify me twice.
What I want : If I set time 10, then I click "Ping" button. Then after 5 second, I click it it will notify only once, 10 secondds after the last time I click the button.
public class MainActivity extends Activity {
private Intent mServiceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Creates an explicit Intent to start the service that constructs and
// issues the notification.
mServiceIntent = new Intent(getApplicationContext(), PingService.class);
}
/*
* Gets the values the user entered and adds them to the intent that will be
* used to launch the IntentService that runs the timer and issues the
* notification.
*/
public void onPingClick(View v) {
stopCurrentService();
int seconds;
// Gets the reminder text the user entered.
EditText msgText = (EditText) findViewById(R.id.edit_reminder);
String message = msgText.getText().toString();
mServiceIntent.putExtra(CommonConstants.EXTRA_MESSAGE, message);
mServiceIntent.setAction(CommonConstants.ACTION_PING);
Toast.makeText(this, R.string.timer_start, Toast.LENGTH_SHORT).show();
// The number of seconds the timer should run.
EditText editText = (EditText) findViewById(R.id.edit_seconds);
String input = editText.getText().toString();
if (input == null || input.trim().equals("")) {
// If user didn't enter a value, sets to default.
seconds = R.string.seconds_default;
} else {
seconds = Integer.parseInt(input);
}
int milliseconds = (seconds * 1000);
mServiceIntent.putExtra(CommonConstants.EXTRA_TIMER, milliseconds);
// Launches IntentService "PingService" to set timer.
startService(mServiceIntent);
}
private void stopCurrentService() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList = activityManager
.getRunningServices(Integer.MAX_VALUE);
if (serviceList.size() <= 0) { }
int size = serviceList.size();
for (int i = 0; i < size; i++) {
RunningServiceInfo serviceInfo = serviceList.get(i);
ComponentName serviceName = serviceInfo.service;
if (serviceName.getClassName().equals(PingService.class.getName())) {
try {
Intent intentstop = new Intent();
intentstop.setComponent(serviceName);
getApplicationContext().stopService(intentstop);
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
}
}
PingService creates a notification that includes 2 buttons: one to snooze the
notification, and one to dismiss it.
public class PingService extends IntentService {
private NotificationManager mNotificationManager;
private String mMessage;
private int mMillis;
NotificationCompat.Builder builder;
private boolean status;
public PingService() {
// The super call is required. The background thread that IntentService
// starts is labeled with the string argument you pass.
super("com.example.android.pingme");
}
#Override
protected void onHandleIntent(Intent intent) {
// The reminder message the user set.
mMessage = intent.getStringExtra(CommonConstants.EXTRA_MESSAGE);
// The timer duration the user set. The default is 10 seconds.
mMillis = intent.getIntExtra(CommonConstants.EXTRA_TIMER,
CommonConstants.DEFAULT_TIMER_DURATION);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
String action = intent.getAction();
// This section handles the 3 possible actions:
// ping, snooze, and dismiss.
if (action.equals(CommonConstants.ACTION_PING)) {
issueNotification(intent, mMessage);
} else if (action.equals(CommonConstants.ACTION_SNOOZE)) {
nm.cancel(CommonConstants.NOTIFICATION_ID);
Log.d(CommonConstants.DEBUG_TAG, getString(R.string.snoozing));
// Sets a snooze-specific "done snoozing" message.
issueNotification(intent, getString(R.string.done_snoozing));
} else if (action.equals(CommonConstants.ACTION_DISMISS)) {
nm.cancel(CommonConstants.NOTIFICATION_ID);
}
}
private void issueNotification(Intent intent, String msg) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Sets up the Snooze and Dismiss action buttons that will appear in the
// expanded view of the notification.
Intent dismissIntent = new Intent(this, PingService.class);
dismissIntent.setAction(CommonConstants.ACTION_DISMISS);
PendingIntent piDismiss = PendingIntent.getService(this, 0,
dismissIntent, 0);
Intent snoozeIntent = new Intent(this, PingService.class);
snoozeIntent.setAction(CommonConstants.ACTION_SNOOZE);
PendingIntent piSnooze = PendingIntent.getService(this, 0,
snoozeIntent, 0);
// Constructs the Builder object.
builder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_notification)
.setTicker("Ping ! ping ! PIng!")
.setContentTitle(getString(R.string.notification))
.setContentText(getString(R.string.ping))
.setDefaults(Notification.DEFAULT_ALL)
// requires VIBRATE permission
/*
* Sets the big view "big text" style and supplies the text (the
* user's reminder message) that will be displayed in the detail
* area of the expanded notification. These calls are ignored by
* the support library for pre-4.1 devices.
*/
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.addAction(R.drawable.ic_stat_dismiss,
getString(R.string.dismiss), piDismiss)
.addAction(R.drawable.ic_stat_snooze,
getString(R.string.snooze), piSnooze);
/*
* Clicking the notification itself displays ResultActivity, which
* provides UI for snoozing or dismissing the notification. This is
* available through either the normal view or big view.
*/
Intent resultIntent = new Intent(this, ResultActivity.class);
resultIntent.putExtra(CommonConstants.EXTRA_MESSAGE, msg);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Because clicking the notification opens a new ("special") activity,
// there's
// no need to create an artificial back stack.
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(resultPendingIntent);
startTimer(mMillis);
}
// Starts the timer according to the number of seconds the user specified.
private void startTimer(int millis) {
Log.d(CommonConstants.DEBUG_TAG, getString(R.string.timer_start));
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Log.d(CommonConstants.DEBUG_TAG, getString(R.string.sleep_error));
}
Log.d(CommonConstants.DEBUG_TAG, getString(R.string.timer_finished));
issueNotification(builder);
}
private void issueNotification(NotificationCompat.Builder builder) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Including the notification ID allows you to update the notification
// later on.
mNotificationManager.notify(CommonConstants.NOTIFICATION_ID,
builder.build());
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
I have called stopService(), but the old notification shows up again.
What I want is it will notify me once, 10 seconds after the latest click.
You can use handler in order to stop/start your service.
Please look at my code. It's not exactly related to your code but you can get the idea.
Click this link
You can do checking in Run method of Runnable.
Currently I am working on GCM (Google Cloud message), it allow user to push the message to user device. And I would like achieve the following requirement :
if the user has already enter app , ignore it
if the user has not enter the app , click on notification to enter the app
And the work flow of my app is:
WelcomePage (download json and create data set from it) => MainPage (Display base on the data set)
The code to handle notification
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
String notifyMsg = "";
JSONTokener tokener = new JSONTokener(msg);
if (tokener != null) {
try {
notifyMsg = new JSONObject(tokener).getString("msg");
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Intent myintent = new Intent(this, WelcomePageActivity.class);
myintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, myintent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getResources().getString(R.string.notification_title))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(notifyMsg))
.setContentText(notifyMsg)
.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
The problem is if I use WelcomePageActivity class , it will create a new activity if I am at the main page, how can I adjust the code to fit my requirement ?
Thanks
For
1. if the user has already enter app , ignore it:
in the onReceive() , check if your app is running, do not notify.
It can be checked with something like:
ActivityManager activityManager =(ActivityManager)gpsService.this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList= activityManager.getRunningServices(Integer.MAX_VALUE);
if((serviceList.size() > 0)) {
boolean found = false;
for(int i = 0; i < serviceList.size(); i++) {
RunningServiceInfo serviceInfo = serviceList.get(i);
ComponentName serviceName = serviceInfo.service;
if(serviceName.getClassName().equals("Packagename.ActivityOrServiceName")) {
//Your service or activity is running
break;
}
}
if the user has not enter the app , click on notification to enter the app
from the code above, you'l know if you would like to resume the app or launch - call Splash Screen or in your case WelcomeActivity.
About the workflow of your app, i'd suggest check whether you need to download the data every time or not. Can save it maybe or update/download only when required, and rest of flow works as it is.
In your AndroidManifest.xml, define your WelcomePageActivity with the flag android:launchMode="singleTop". From the definition of this flag:
A new instance of a "singleTop" activity may also be created to handle
a new intent. However, if the target task already has an existing
instance of the activity at the top of its stack, that instance will
receive the new intent (in an onNewIntent() call); a new instance is
not created.
So with this flag, your activity will not be created again, rather it will receive a call in the onNewIntent() function with the Intent you used to create the PendingIntent for the notification. You could override this function, and use the intent to pass the activity new information.
You will not able to receive any notification click event so,
try this code :
Intent myintent = new Intent(this, TestActivity.class);
myintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, myintent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getResources().getString(R.string.notification_title))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(notifyMsg))
.setContentText(notifyMsg)
.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
public class TestActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// check for your app state is running or not
if(appRunning == false) {
// start your WelcomePage activity.
}
}
}
1.Create an object in GcmIntentService
public static final Object CURRENTACTIVIYLOCK = new Object();
//for storing current activity
public static Activity currentActivity;
2.Update this object value in onPause and onResume of MainActivity to recognize Activity is running or not.
#Override
public void onResume() {
super.onResume();
System.out.println("onResume Home page");
synchronized (GcmIntentService.CURRENTACTIVIYLOCK) {
GcmIntentService.currentActivity = this;
}
}
#Override
public void onPause() {
super.onPause();
synchronized (GcmIntentService.CURRENTACTIVIYLOCK) {
GcmIntentService.currentActivity = null;
}
}
3.In GcmIntentService class, check for the current activity in onHandleIntent method.
synchronized (CURRENTACTIVIYLOCK) {
if (currentActivity != null) {
if (currentActivity.getClass() == HomePageActivity.class) {
} else {
sendNotification(extras.getString("message"));
}
} else {
sendNotification(extras.getString("message"));
}
I'm sure this will help you.