Send a notification only when my app is not running - android

I want to send a notification when an alarm starts the broadcastreceiver and my App is closed. On the Receive method, Works fine detecting when the app is running. But the else part is not working. The "send_notification" method only works if it is not inside the else. Even the System.Out is not working when app is closed.
public void onReceive(Context context, Intent intent) {
if (App_status.isAppRunning(context, "com.example.app")) {
System.out.println(TAG + " APP IS RUNNING");
} else {
System.out.println(TAG + " APP IS NOT RUNNING");
send_notification(context);
}
}
public static boolean isAppRunning(final Context context, final String packageName) {
final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.getRunningAppProcesses();
if (procInfos != null) {
for (final ActivityManager.RunningAppProcessInfo processInfo : procInfos) {
if (processInfo.processName.equals(packageName)) {
return true;
}
}
}
return false;
}
}
Why doesn't work the else part?
I tried also to create a String variable. Being null at the begining the activity and changing to "no" if app is running. Inside the if(App_status.isAppRunning(context, "com.example.app")).
So at the end asking if the variable is diferent of no send the notification. But also doesn't work.

Use alarm manager, for this :
AlarmManager alarmManager =
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent =
PendingIntent.getService(context, requestId, intent,
PendingIntent.FLAG_NO_CREATE);
if (pendingIntent != null && alarmManager != null) {
alarmManager.cancel(pendingIntent);
}
For full guidance visit this developer android page

Related

Alarm Manager not working when app is closed on a real device but works on an emulator- Xamarin Forms(Android)

I have been on this for about 3 days and I have not found anything to resolve my issue. What I seek to achieve is to receive notifications when the app is closed. I have tested it on my emulators and it works fine but does not work on real devices. The notifications work fine when the app is running in the foreground and when minimized. I use the alarm manager to trigger a receiver to then send the notification.
This is how I prep the alarm manager:
alarmHandlerIntent.PutExtra(TitleKey, title);
alarmHandlerIntent.PutExtra(MessageKey, message);
alarmHandlerIntent.PutExtra(DataKey, todo);
alarmHandlerIntent.AddFlags(ActivityFlags.ReceiverForeground);
alarmHandlerIntent.AddFlags(ActivityFlags.IncludeStoppedPackages);
PendingIntent pendingIntent = PendingIntent.GetBroadcast(AndroidApp.Context, pendingIntentId++, alarmHandlerIntent, GetPendingIntentFlags());
long triggerTime = GetNotifyTime(notifyTime.Value);
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
alarmManager.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, triggerTime, pendingIntent);
}
else
{
alarmManager.SetExact(AlarmType.RtcWakeup, triggerTime, pendingIntent);
}
and this is the GetPendingIntentFlags():
var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S)
? PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable
: PendingIntentFlags.UpdateCurrent;
return pendingIntentFlags;
This is the Receiver as well:
[BroadcastReceiver(Enabled = true, Exported = true, Label = "ToDo Local Notifications Broadcast Receiver")]
[IntentFilter(new[] { Intent.ActionBootCompleted},
Priority = (int)IntentFilterPriority.HighPriority)]
public class AlarmHandler : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent?.Extras != null)
{
PowerManager pm = (PowerManager)context.GetSystemService(Context.PowerService);
PowerManager.WakeLock wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BackgroundReceiver");
wakeLock.Acquire();
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
string data = intent.GetStringExtra(AndroidNotificationManager.DataKey);
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
var status = new Todo
{
Id = data
};
MessagingCenter.Send(status, "TodoNotified", status);
manager.Show(title, message, data);
wakeLock.Release();
}
}
}

Starting app only if its not currently running

I am sending push notification to users which when clicking on it opens the app.
My problem is that when the app is already open, clicking on the notification start the app again.
I only want it to start the app if its not already running.
I am using Pending Intent in the notification:
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Splash.class), 0);
I saw posts which say use:
<activity
android:name=".Splash"
android:launchMode="singleTask"
but the thing is that my running app is running other activity then the splash which is finished after 7 seconds from app start, so when the app is running Splash is not the current activity
Use a "launch Intent" for your app, like this:
PackageManager pm = getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage("your.package.name");
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launchIntent, 0);
Replace "your.package.name" with the name of your package from the Android manifest.
Also, you should remove the special launchMode="singleTask" from your manifest. Standard Android behaviour will do what you want.
String appPackageName = "";
private void isApplicationInForeground() throws Exception {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
final List<ActivityManager.RunningAppProcessInfo> processInfos = am
.getRunningAppProcesses();
ActivityManager.RunningAppProcessInfo processInfo = processInfos
.get(0);
// for (ActivityManager.RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
// getting process at 0th index means our application is on top on all apps or currently open
appPackageName = (Arrays.asList(processInfo.pkgList).get(0));
}
// }
}
else {
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = null;
componentInfo = taskInfo.get(0).topActivity;
appPackageName = componentInfo.getPackageName();
}
}
private void notifyMessage(String text) {
if (appPackageName.contains("com.example.test")) {
// do not notify
}
else {
// create notification and notify user
}
}
For those who use Xamarin.Android.
The Xamarin version of David Wasser's answer is below:
//Create notification
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
Intent uiIntent = PackageManager.GetLaunchIntentForPackage("com.company.app");
//Create the notification
var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title);
//Auto-cancel will remove the notification once the user touches it
notification.Flags = NotificationFlags.AutoCancel;
//Set the notification info
//we use the pending intent, passing our ui intent over, which will get called
//when the notification is tapped.
notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, PendingIntentFlags.OneShot));
//Show the notification
notificationManager.Notify(0, notification);
Instead of showing the Splash activity on notification click, show your MainActivity because your splash activity will closed after some time but MainActivity will be remain open and
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
Use Splash as Fragment instead of Activity. Keep Splash fragment(7 seconds), replace the same with the desired one(landing page).
Add launchMode="singleTask" to the manifest.
As already stated by Rahul, onNewIntent() get called if application is already running else onCreate()
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
}
OR
Go with David's answer, seems promising.
when notification clicked and your code that redirect to your desire screen just replace that code by calling this method and redirect to particular screen on "true/false" result basis.
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
And maybe don't start the Splash Activity and reopen (bring to front) the MainActivity and update the UI with a listener that tells you, that you have a new notification (with a flag - boolean or with an Interface to make a listener).
You can use an ordered broadcast to accomplish this.
1) Change your PendingIntent to start a BroadcastReceiver which will decide whether to start the activity or do nothing:
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, DecisionReceiver.class), 0);
2) Create the decision BroadcastReceiver:
public class DecisionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.sendOrderedBroadcast(new Intent(MainActivity.NOTIFICATION_ACTION), null, new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (getResultCode() == MainActivity.IS_ALIVE) {
// Activity is in the foreground
}
else {
// Activity is not in the foreground
}
}
}, null, 0, null, null);
}
}
3) Create a BroadcastReceiver in your activity that will signal that it is alive:
public static final String NOTIFICATION_ACTION = "com.mypackage.myapplication.NOTIFICATION";
public static final int IS_ALIVE = 1;
private BroadcastReceiver mAliveReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
setResultCode(IS_ALIVE);
}
};
// Register onResume, unregister onPause
// Essentially receiver only responds if the activity is the foreground activity
#Override
protected void onResume() {
super.onResume();
registerReceiver(mAliveReceiver, new IntentFilter(NOTIFICATION_ACTION));
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mAliveReceiver);
}
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_NEW_TASK );
notificationIntent.putExtras(bundle);
PendingIntent pintent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
try adding this to your intent to bring activity to front if it is running in the background
Intent intent = new Intent(this, Splash.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
first of all set a default Task android:taskAffinity="com.example.testp.yourPreferredName" in your Application element in the Manifest file. Maintain your android:launchMode="singleTask" on your SplashActivity. Now since your SplashActivity is your main entry add this code to both onResume(), onNewIntent() and onCreate() (on a second thought onResume() is not recomended) -follow the comments in the code
//Note these following lines of code will work like magic only if its UPVOTED.
//so upvote before you try it.-or it will crash with SecurityException
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1000);
for(int i =0; i< taskInfo.size(); i++){
String PackageName = taskInfo.get(i).baseActivity.getPackageName();
if(PackageName.equals("packagename.appname")){// suppose stackoverflow.answerer.Elltz
//if the current runing actiivity is not the splash activity. it will be 1
//only if this is the first time your <taskAffinity> is be called as a task
if(taskInfo.get(i).numActivities >1){
//other activities are running, so kill this splash dead!! reload!!
finish();
// i am dying in onCreate..(the user didnt see nothing, that's the good part)
//about this code. its a silent assassin
}
//Operation kill the Splash is done so retreat to base.
break;
}
}
This code will not work on api 21+; to make it work you need to use AppTask, this will save you extra lines of code as you will not be in a Loop to find your Task.
Hope it helps

Improve BroadcastReceiver + AlarmManager

I am experiencing some lag/black screen in my application since I start using AlarmManager + BroadcastReceiver. I have 2 BroadcastReceivers, one to when the phone gets restarted and another that AlarmManager call in the given period of time to send data to the server.
This is the code for BootReceiver to start the alarmManager once the cellphone is rebooted (it is working so far):
private final String BOOT_COMPLETED_ACTION = "android.intent.action.BOOT_COMPLETED";
#Override
public void onReceive(Context context, Intent intent) {
// when the boot is completed, restart the alarm manager
if(intent.getAction().equals(BOOT_COMPLETED_ACTION)){
SharedPreferences mPrefs = context.getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
if (mPrefs.getBoolean("hasGeolocation", false) &&
!mPrefs.getBoolean("isThreadOn", false)){
EngineerTracker tracker = new EngineerTracker(context);
try {
tracker.startEngineerTrackingLocation();
} catch (ApplicationException e) {
e.printStackTrace();
}
}
}
}
The method to start and stop the alarm manager is this:
public void startEngineerTrackingLocation() throws ApplicationException{
PendingIntent pendingIntent = null;
AlarmManager manager = null;
ProjectGeospatialConfig geospatialConfig;
// check if the intent is running, if it is not, start it
if (PendingIntent.getBroadcast(context, 0,
new Intent(context, EngineerGeospatialTrackingReceiver.class),
PendingIntent.FLAG_NO_CREATE) == null){
// fetch the geospatial configuration, it may come null, so verify before using
geospatialConfig = getFirstFoundGeospatialConfiguration();
// if not null and use gps
if (geospatialConfig != null && geospatialConfig.isUseGps()){
// session information
SessionInformationDTO sessionInformation = dao.getObjectForKey(SqlLiteStorageKey.USER_INFORMATION);
Integer currentResourceId = sessionInformation.getSecurityHandler().getCurrentUser().getId();
// Retrieve a PendingIntent that will perform a broadcast and add resource id as extra
Intent alarmIntent = new Intent(context, EngineerGeospatialTrackingReceiver.class);
alarmIntent.putExtra("resourceId", currentResourceId.toString());
// set pending intent
if (pendingIntent == null){
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
}
// set manager
if (manager == null){
manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
// set interval between alarms
int interval = (geospatialConfig.getGpsTrackingInterval() *1000) * 60;
// set alarm repetition
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
interval, pendingIntent);
// set variables for gps tracking
SharedPreferences mPrefs = getApplicationContext().getSharedPreferences("GPS_TRACKING", Context.MODE_PRIVATE);
Editor editor = mPrefs.edit();
// these variables will be measured once db is set
editor.putBoolean("hasExecuted", false);
editor.commit();
}
}
}
both are also working so far, the flag is meant to know when the service has been executed once and will not attempt again at the basic activity (template for all activitied)
The broadcast that is invoked in the alarm manager to send the information in the defined interval is this:
public class EngineerGeospatialTrackingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String resourceId = intent.getStringExtra("id");
sendLocation(context, resourceId);
}
private void sendLocation(final Context context, final String resourceId){
new RemoteRequestTask<Void>(null, false, null) {
#Override
public Void executeTask() throws ApplicationException {
// working code
}
#Override
public void completed(Void refreshed) {
}
#Override
public void onException(final ApplicationException ex) {
}
}.start();
}}
Both receivers were added to the AndroidManifest. Beside the slowness, i also get a black screen when transitioning from an activity to another.
Use Traceview to determine where you are spending your time, and consider enabling StrictMode to point out where you are doing unfortunate things on the main application thread.
You want onReceive() to be very quick, ideally under 1ms, as. However, it looks like you might be doing database I/O in there (e.g., references to dao), which means that work should be handled off the main application thread, perhaps by an IntentService that you start from onReceive().

Wake Lock not working properly

I apologies in advance if I'm not good in writing English.
I'm writing a simple task app that remind me with alarm in specific time.
Below I set alarm with AlarmManager :
private static void setAlarm(Context context, Calendar calendar,
PendingIntent pIntent) {
AlarmManager alarmManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
if (android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), pIntent);
}
}
and then AlarmManagerHelper :
public class AlarmManagerHelper extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String title = intent.getStringExtra("Title");
int hour = intent.getIntExtra("Hour", 0);
int min = intent.getIntExtra("Minute", 0);
String alarmTone = intent.getStringExtra("AlarmTone");
Intent i = new Intent();
i.setClassName("com.example.tasks",
"com.example.tasks.AlarmScreenActivity");
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.putExtra("Title", title);
i.putExtra("Hour", hour);
i.putExtra("Minute", min);
i.putExtra("AlarmTone", alarmTone);
context.startActivity(i);
}
}
and AlarmScreenActivity is:
public class AlarmScreenActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get intent
pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
mPlayer = new MediaPlayer();
try {
if (task_Tone != null && !task_Tone.equals("")) {
android.net.Uri toneUri = android.net.Uri.parse(task_Tone);
if (toneUri != null) {
mPlayer.setDataSource(this, toneUri);
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
mPlayer.setLooping(true);
mPlayer.prepare();
mPlayer.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// some code
public void onClickDissmis(View view) {
mPlayer.stop();
finish();
}
protected void onDestroy() {
super.onDestroy();
wl.release();
}
}
then with AlarmManagerHelper and AlarmScreenActivity displaying it.
my problem is:
in the specific time that should wake up and ringing not do int, so when I press power button an turn screen on that is work???!
(when is in debug mode and the device , connected to system work properly)
I hope that describe my problem perfectly.
I don't understand your problem, exactly. I can say, though, that Android only guarantees that it is holding a wakelock while it delivers the Broadcast. Your code leave considerable time between the reception of the Broadcast, by the Receiver, and the time you seize the wakelock. There is nothing to prevent the device from going back to sleep, in that interval.
While AlarmManagerHelper.onReceive runs the system holds a lock (because of the Alarm manager) that will not fail. But between the context.startActivity(i); and the starting of the activity the system falls asleep again. You need to either use a WakefulBroadcastReceiver (see BroadcastReceiver Vs WakefulBroadcastReceiver) or (that's what I use) a WakefulIntentService.
In any case you need to start a service and start your activity from there. For the WakefulIntentService pattern see my answer PowerManager.PARTIAL_WAKE_LOCK android and links there.

Service gets killed in Jelly Bean

I'm trying to fetch application's name when it hits foreground. I've accomplished this by running a background service in which I've implemented Runnable for running this service every second. This is working fine in all OS except in Jelly Bean. My service gets killed. I know that if an app consumes more RAM then it kills available background services to manage the required space. For example high resolution games. But I'll be loosing data. I implemented Foreground Services. Its working fine. My doubt is, does this effect any way in the matter efficiency or battery consumption?? Is there any other method other than foreground??
This is the service which I have made foreground.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ReceiverRunning = true;
context = this;
Intent intent1 = new Intent(this, ComponentSelector.class);
PendingIntent pendingIndent = PendingIntent.getActivity(this, 1, intent1, 0);
Notification mNotification = new Notification(R.drawable.ic_launcher, "BackgroundApp", System.currentTimeMillis());
mNotification.setLatestEventInfo(this, "BatteryUSage", "Click to open to app", pendingIndent);
mNotification.flags = mNotification.flags|Notification.FLAG_ONGOING_EVENT;
startForeground(1000, mNotification);
// Start service if not started.
if (!ForegroundApp.isRunning == true) {
context.startService(new Intent(context, Brightness.class));
}
boolean has_tele = getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY);
if (has_tele == true) {
TelephonyManager teleman = (TelephonyManager) getBaseContext()
.getSystemService(Context.TELEPHONY_SERVICE);
if (teleman != null)
deviceId = teleman.getDeviceId();
}
uId = deviceInfo();
try {
NetworkInfo info = (NetworkInfo) ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE))
.getActiveNetworkInfo();
if (info != null) {
Log.d("wifiRun", "Network available");
ConnectivityManager conMan = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo.State wifi = null;
if (conMan.getNetworkInfo(1).isAvailable())
wifi = conMan.getNetworkInfo(1).getState();
if (wifi == NetworkInfo.State.CONNECTED
|| wifi == NetworkInfo.State.CONNECTING) {
wifiCheck = true;
context.startService(new Intent(context, WiFi.class));
Log.d("wifiRun","wifiCheck: " +wifiCheck);
} else {
Log.d("wifiRun","wifiCheck: " +wifiCheck);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// Register for Screen On and Screen Off.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
sReceiver = new ServiceDefinition();
registerReceiver(sReceiver, intentFilter);
//return super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
I think, no other way. You can also make your service sticky (http://developer.android.com/reference/android/app/Service.html#START_STICKY) but making service foreground is more right way in this situation. Of course, your service will drain battery, so try do not use "heavy" operations in your service.
If you will provide source code of your service we will check that you implement all optimal.

Categories

Resources