how to detect if a activity is on top - android

In the app having background service running, periodical it will trigger some event that app needs to show an activity.
The issue is if that activity is already on top how to not open another one.
The following snippet whenever the event is triggered, the showAlertActivity() will try to open the activity (it is not just a alert).
Any way to check if there is one has been opened? Thanks!
public void showAlertActivity(String title, String msg……) {
// this does not work, throws a exception
//“ava.lang.SecurityException: Permission Denial: getTasks() from pid=24139, uid=10048 requires android.permission.GET_TASKS”
ActivityManager mngr = (ActivityManager) mContext.getSystemService( Activity.ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
for (ActivityManager.RunningTaskInfo info: taskList) {
Log.d(“+++ info.numActivities:" + info.numActivities +", info.topActivity.getClassName():"+info.topActivity.getClassName()+", info.baseActivity:"+info.baseActivity.getClassName());
}
Intent intent = new Intent(mContext, TheAlertActivity.class);
dialogIntent.putExtra(Constants.LAYOUT_RESOURCE_ID, R.layout.alert_form);
dialogIntent.putExtra(Constants.TILTLE, title);
dialogIntent.putExtra(Constants.TEXT, msg);
……
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}

Add something along the lines of this to your Activity you want to check and isOpen will tell you if it is currently on top.
public static boolean isOpen = false;
private static int countOpen = 0;
public void onStart() {
super.onStart();
countOpen++;
isOpen = countOpen > 0;
}
public void onStop() {
super.onStop();
countOpen--;
isOpen = countOpen > 0;
}
The count is added to secure the boolean being correct even if you open the activity for some reason in front of itself and the system sometimes calling the new activities onStart() before the old activities onStop().

Related

Check if service is running not behaving right

I created a boolean to check if a service is running, if it is running I want it to show linearlayoutA and if it is not running I want it to show another linearlayoutB.
The problem is this, it shows the right linear layout when I start the activity, if the service is running it shows linearlayoutA if its not running it shows linearlayoutB but when I start the service in the activity it shows linearlayoutB and does not change to A even when the service as stopped until I close the application and open it. method for checking
public boolean isRunning(Class<? extends Service> serviceClass) {
final Intent intent = new Intent(TimerActivity.this, serviceClass);
return (PendingIntent.getService(TimerActivity.this, CODE, intent, PendingIntent.FLAG_NO_CREATE) != null);
}
This is how I call it in onCreate of the activity
if(isRunning(TimerLocationService.class)){
setWaitScreen(true);
}else{
setWaitScreen(false);
}
try this.
private Boolean isServiceRunning(String serviceName) {
ActivityManager activityManager = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo runningServiceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceName.equals(runningServiceInfo.service.getClassName())) {
return true;
}
}
return false;
}
I hope this is helpful for you.

How to find whether activity is last activity in stack

I am opening a link in my app and once back is pressed I want to show HomePage to retain user for some more time. I have been trying to acheive this but unable to do so. I get homeLauncher activity as my top as well as baseActivity.
DeepLink Tap > Open desired Activity > user presses back button > Check if its last activity but not homeActivity > If yes, Navigate user to homeActivity.
Tried following code:
#Override
public void onBackPressed() {
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 && taskList.get(0).topActivity.getClassName().equals(this.getClass().getName())){
//// This is last activity
}
else{
//// There are more activities in stack
}
super.onBackPressed();
}
I have also tried isTaskRoot but result is same. It doesn't give right answer.Please help
Use isTaskRoot() method. (From a h9kdroid comment - here)
#Override
public void onBackPressed() {
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(isTaskRoot()){
//// This is last activity
} else{
//// There are more activities in stack
}
super.onBackPressed();
}
You could simply use the ActivityManager it keeps track of which activity is and is not here is a piece of code I stumbled on that I use always:
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 &&
taskList.get(0).topActivity.getClassName().equals(this.getClass().getName())) {
Log.i(TAG, "This is last activity in the stack");
}
Quoting From ActivityManager.RunningTaskInfo
Information you can retrieve about a particular task that is currently
"running" in the system. Note that a running task does not mean the
given task actually has a process it is actively running in; it simply
means that the user has gone to it and never closed it, but currently
the system may have killed its process and is only holding on to its
last state in order to restart it when the user returns.
String getLastOpenClass ;// Global
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
componentInfo.getPackageName();
getLastOpenClass=taskInfo.get(0).topActivity.getClassName();
if(getLastOpenClass.equals("Your_Class_Name"))
{
}else{
}
Give permission
<uses-permission android:name="android.permission.GET_TASKS" />
The ActivityManager keeps a record of the runnings task and the topmost task .
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 && taskList.get(0).topActivity.getClassName().equals(this.getClass().getName()))
{
Log.i(TAG, "This is Last activity in the stack");
}
Courtesy How to check if an activity is the last one in the activity stack for an application?
Please Check How can I get the current foreground activity package name
http://developer.android.com/intl/es/reference/android/app/ActivityManager.html#getRunningTasks%28int%29
Please note that the below solution will only work on API14+.
Create a custom application class;
public class App extends Application {
private int created;
#Override
public void onCreate() {
registerActivityLifecycleCallbacks(new Callbacks());
}
public int getCreated() {
return created;
}
private class Callbacks implements ActivityLifecycleCallbacks {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
created++;
}
#Override
public void onActivityDestroyed(Activity activity) {
created--;
}
}
}
Register it in your AndroidManifest.xml in the application element;
<application name=".App"/>
And in your activity opened through a deep link use the following piece of code;
#Override
public void onBackPressed() {
if (((App) getApplicationContext()).getCreated() == 1) {
// start your home activity
}
super.onBackPressed();
}
I wrote this off the top of my head so I haven't had a chance to test it, but theoretically it should work.
Specify parent activity for desired Activity, like :
<activity
android:name=".DesiredActivity"
android:parentActivityName="com.packagename.HomePage" >
<!-- The meta-data element is needed for versions lower than 4.1 -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.packagename.HomePage" />
</activity>
With the parent activity declared, you can use the NavUtils APIs to
synthesize a new back stack by identifying which activity is the
appropriate parent for each activity.
override onBackPressed as :
#Override
public void onBackPressed() {
Intent intent = NavUtils.getParentActivityIntent(this);
startActivity(intent);
super.onBackPressed();
}
Android developer's site has a very good resource for same problem. Please refer to link http://developer.android.com/training/implementing-navigation/temporal.html#SynthesizeBackStack

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

Push Notification when app is running

I am implementing push notification and so far it works fine. I manage to get push notification and when I click on that able to start activity.
But I don't want to notify user about notification if app is already running. This how I am planning to do this...but not sure is this correct way
Intent actIntent = new Intent(this, MainActivity.class);
actIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, actIntent, 0);
if (!isActivityRunning())
mNotificationManager.notify(0, notification);
public boolean isActivityRunning(Context ctx) {
ActivityManager activityManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (RunningTaskInfo task : tasks) {
if (ctx.getPackageName().equalsIgnoreCase(task.baseActivity.getPackageName()))
return true;
}
return false;
}
isActivityRunning function will basically checks whether MainActivity is running or not. If it is in running state then won't show notification and will pass information to activity itself to update UI. If activity is not running on click of notification MainActivity will open.
Is this right way of achieving this?
Using the activity manager doesn't exactly work as expected. The activity manager keeps a track of all the running apps on the phone. It doesn't really tell you whether the app is in foreground or background. To check whether the activity is running, set a boolean value in the onResume and onPause method of the activity.
Example:
public void onResume()
{
super.onResume();
isActivityRunning = true;
}
public void onPause()
{
super.onPause();
isActivityRunning = false;
}
You can then use the isActivityRunning to see if you want to throw the notification or not.
Also see this: Checking if an Android application is running in the background
I use this code to know if app is running:
private boolean isActivityRunning() {
if (MainActivity.getInstance() != null) {
return true;
} else {
return false;
}
}
And in MainActivity
public static MainActivity getInstance() {
return mInstance;
}
in onCreate I assign mInstance:
mInstance = this;
in onDestroy:
mInstance = null;
I use checking of mInstance because I use it in different activities for checking or for using some methods from MainActivity, so for me no need to create new boolean var.

How to define that android app is in background?

I have a broadcast receiver, which launches a service, and in this service I should check, if my main application is active, or in background. Are there any methods how to do this?
You can extend the Application class and store the current state of your application. You will need to update it from every Activity's onPause() and onResume() methods.
public class MyApplication extends Application {
public static boolean isAppVisible() {
return visible;
}
public static void inForeground() {
visible = true;
}
public static void inBackground() {
visible = false;
}
private static boolean visible;
}
Register your application class in AndroidManifest.xml:
<application
android:name="your.app.package.MyApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
Add onPause and onResume to every Activity in the project (you may create a common superclass for your Activities if you'd like to, but if your activity is already extended from MapActivity/ListActivity etc. you still need to write the following by hand):
#Override
protected void onResume() {
super.onResume();
MyApplication.inBackground();
}
#Override
protected void onPause() {
super.onPause();
MyApplication.inForeground();
}
Context ctx = context.getApplicationContext();
ActivityManager am = (ActivityManager) context
.getSystemService(ACTIVITY_SERVICE);
// get the info from the currently running task
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
PackageManager pm = this.getPackageManager();
boolean flag = false;
try {
/**
* take fore ground activity name
*/
ComponentName componentInfo = taskInfo.get(0).topActivity;
if (printLog) {
Log.d("Tag", "CURRENT Activity ::"
+ taskInfo.get(0).topActivity.getClassName());
Log.d("Tag", "Number Of Activities : "
+ taskInfo.get(0).numRunning);
Log.d("Tag",
"Componenet Info : " + componentInfo.getPackageName());
Log.d("Tag",
"Componenet Info : " + componentInfo.getClassName());
}
/**
* All activities name of a package to compare with fore ground
* activity. if match found, no notification displayed.
*/
PackageInfo info = pm.getPackageInfo(
"com.package_name",
PackageManager.GET_ACTIVITIES);
ActivityInfo[] list = info.activities;
for (int i = 0; i < list.length; i++) {
Log.d("Tag","Activity : "+list[i].name);
if (list[i].name.equals(componentInfo.getClassName())) {
flag = true;
}
}
if(flag)
{
//activity is running
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
And take <uses-permission android:name="android.permission.GET_TASKS" /> permission in manifest.
Considering implementing an application that keep track if the app goes front or back using onPause() and onResume().
Have a look at this solution.
Hope it helps.

Categories

Resources