How to see that is Activity running or not? - android

I have a BroadCastReceiver that is listen for incoming short messages. In SmsReceiver I want to start an Activity to process the sms. In many situation the Activity is running while getting message and I don't want to start it again.
In fact I want to see that if that Activity is already running (visible or not killed yet) just take it to front and otherwise start it with new task.
Any idea?

I mixed some ideas from here and other places and finally solved the problem. I write the solution here for other people that may have similar situations:
In amy Activity:
static boolean isRunning = false;
#Override
public void onStart() {
super.onStart();
isRunning = true;
}
public void onStop() {
isRunning = false;
super.onStop();
}
public static boolean isRuuning() {
return isRunning;
}
#Override
public void onCreate(Bundle savedInstanceState) {
smsReceiver = new SmsReceiver();
smsReceiver.setActivity(this);
// ...
}
Inside my SmsReceiver:
static MyActivity myActivity;
public void setActivity(MyActivity MyActivity) {
SmsReceiver.myActivity = myActivity;
}
if( MyActivity.isRuuning() ) {
SmsReceiver.myActivity.receiveNewMessage(smsBody);
} else {
// Create an intent and start it
}
This works fine for me.

Related

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

How to know if a certain amount of time has passed while user is using my app?

I want to send an event to firebase if user spend a minimum of 20sec on my app. This looks simple enough as I only have to create a timer. Here is what I've done so far:
Subscription sessionEvent = Observable.timer(20, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> Log.v("tag", "hello"));
I am subscribing inside the onResume of my activity while unsubscribing inside onPause.
This is however a problem since if my activity started another activity, onPause will also be called. That means that the timer will stop even though the user is still on my app.
What I'm looking for is when the user closed the app, that's when the timer will stop. I already tried using the onUserLeaveHint, unfortunately, starting another activity will also call that method.
You can use ActivityLifecycleCallbacks to monitor the state of all activities in your app. You can register one with Application.registerActivityLifecycleCallbacks() and it will be called for every Activity that goes through its lifecycle methods. When you have a total of 0 started activities, you can be sure that the user is not looking at your app, but it's also entirely possible that they might switch back in at any time from the task switcher.
I dont think that there is a direct way to detect if your app is on foreground or background if you have multiple activities. However you can use OnResume and OnPause to track the state of each activity. Use boolean Shared Preferences to track the state of each activity and periodically run a background service using repeating alert to simply check if there is at least on activity on screen. StartTime-StartTime = duration.
Here is an example Application implementation that uses the ActivityLifeCycleCallbacks mentioned in Doug Stevenson's answer. The onTrimMemory() method is used to detect when the app is backgrounded:
public class MyApplication extends Application
implements Application.ActivityLifecycleCallbacks {
private static final String TAG = "MyApplication";
private Handler handler;
private final Runnable generateEvent = new Runnable() {
#Override
public void run() {
Log.d(TAG, "User engagement is 20 secs");
// Generate analytics event
}
};
private boolean mAppInForeground;
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
handler = new Handler(getMainLooper());
mAppInForeground = false;
}
#Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
Log.i(TAG, "onTrimMemory: App in background");
handler.removeCallbacks(generateEvent);
mAppInForeground = false;
}
}
#Override
public void onActivityStarted(Activity activity) {
// Called when any activity is started
Log.d(TAG, "onActivityStarted: Was foreground=" + mAppInForeground);
if (!mAppInForeground) {
mAppInForeground = true;
handler.postDelayed(generateEvent, 20 * 1000);
}
}
// Remaining ActivityLifecycleCallbacks are not used
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
#Override
public void onActivityResumed(Activity activity) {}
#Override
public void onActivityPaused(Activity activity) {}
#Override
public void onActivityStopped(Activity activity) {}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
#Override
public void onActivityDestroyed(Activity activity) {}
}
I ended up using <uses-permission android:name="android.permission.GET_TASKS"/> to check if the app is on foreground. here is the method that I used:
public boolean isAppForeground(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) return false;
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
if (processInfos != null && !processInfos.isEmpty()) {
ActivityManager.RunningAppProcessInfo info = processInfos.get(0);
if (info == null || info.processName == null || !info.processName.contentEquals(context.getPackageName()))
return false;
} else {
return false;
}
}
else{
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
if (tasks != null && !tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (topActivity == null || topActivity.getPackageName() == null || !topActivity.getPackageName().equals(context.getPackageName())) {
return false;
}
} else {
return false;
}
}
return true;
}
The method is located in my custom Application class. I used registerActivityLifecycleCallbacks to check inside the onActivityPaused if the app is on foreground, I will not stop the timer, but if it's not, I will stop the timer.

do something inside mainActivity whenever a push notification is received

In my app, whenever I receive a push notification, I will perform a check if my mainActivity is visible to the user to do something...
I have a static boolean value that is set true inside onResume of mainActivity, and false inside it's onPause.
What should I do inside the onMessage
#Override
protected void onMessage(Context context, Intent intent)
{
if(mainActivity == visible)
//do something inside mainactivity.. change text inside edittext
else
//do something else
}
any insights ?
I'm not a fan of keeping static references to activities. I think they're a can of worms ready to explode on you. So you'll suggest an alternative to #TeRRo answer:
on your global BroadcastReceiver onMessage you'll send a LocalBroadcast that your activity will be listening to. Like this:
private static final String ACTION_PUSH_RECEIVED = "com.myapp.mypackage.action.pushReceived";
public static final IntentFilter BROADCAST_INTENT_FILTER = new IntentFilter(ACTION_PUSH_RECEIVED);
#Override
protected void onMessage(Context context, Intent intent) {
Intent i = new Intent(ACTION_PUSH_RECEIVED);
i.putExtra( ... add any extra data you want... )
LocalBroadcastManager.getInstance(context).sendBroadcast(i);
}
and now we make the activity listen to it:
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(context)
.registerReceiver(mBroadcastReceiver, BroadcastReceiverClass.BROADCAST_INTENT_FILTER);
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(context)
.unregisterReceiver(mBroadcastReceiver);
super.onPause();
}
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent){
// read any data you might need from intent and do your action here
}
}
To avoid this, you should manage activities references. Add the name of the application in the manifest file:
<application
android:name=".MyApp"
....
</application>
Your application class :
public class MyApp extends Application {
public void onCreate() {
super.onCreate();
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity(){
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.mCurrentActivity = mCurrentActivity;
}
}
Create a new Activity :
public class MyBaseActivity extends Activity {
protected MyApp mMyApp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyApp = (MyApp)this.getApplicationContext();
}
protected void onResume() {
super.onResume();
mMyApp.setCurrentActivity(this);
}
protected void onPause() {
clearReferences();
super.onPause();
}
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = mMyApp.getCurrentActivity();
if (currActivity != null && currActivity.equals(this))
mMyApp.setCurrentActivity(null);
}
}
So, now instead of extending Activity class for your activities, just extend MyBaseActivity. Now, you can get your current activity from application or Activity context like that :
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
Or why don't you use the Local broadcasts when you receive the push notification, and receive it in your activity, and do respective changes or actions.
And if they are UI intensive tasks, bind your activity to a service, and receive the push notification and perform the action in this service and use the result in the activity.

Close Activity Using BroadcastReceiver

i have one activity Main.java is open in my application, i want to close the activity using broadcast receiver , how to close the activity?
Firstly your Main.java needs to be registered as a receiver. You could register it in Main.java's onResume():
#Override
public void onResume() {
registerReceiver(broadcastReceiver, new IntentFilter(BroadcasterClassName.NAME_OF_ACTION));
}
Then handle the broadcast and finish your activity:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(BroadcasterClassName.NAME_OF_ACTION)) {
finish();
}
}
}
You could send a message to the activity which implements Handler.Callback, and handle it there to close the activity.
Quick example:
class Act1 extends Activity implements Handler.Callback {
public static final int CLOSE_ACTIVITY = 54212;
public boolean handleMessage(Message msg) {
if(msg.what == CLOSE_ACTIVITY) {
finish();
}
}
}
And then, since you BroadcastReceiver runs on main thread, in most of the cases. Just send the message via Handler.
new Handler().sendMessage(MessageFactory.createShutdownMsg()).
you could do this:
in your main have:
private static Main mInstance;
onCreate()
{
...
mInstance = this;
}
public static boolean closeActivity()
{
if (mInstance != null)
{
mInstance.finish();
return true;
}
return false;
}
although, this implies only one Main exists at any one time. I think you can achieve that by adding android:noHistory="true" or something similar in the manifest.

IntentService onHandleIntent() still running after onDestroy() fired

In my preference screen, I want to start a service to download files from the internet when one of the preference is being clicked. If the service is already running (downloading files), then the service should be stopped (cancel download).
public class Setting extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
downloadPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference pref) {
if (DownloadService.isRunning) {
Setting.this.stopService(new Intent(Setting.this,
DownloadService.class));
} else {
Setting.this.startService(new Intent(Setting.this,
DownloadService.class));
}
return false;
}
});
}
}
The service class:
public class DownloadService extends IntentService {
public static final int DOWNLOAD_SUCCESS = 0;
public static final int DOWNLOAD_FAIL = 1;
public static final int DOWNLOAD_CANCELLED = 2;
public static final int SERVER_FAIL = 3;
public static boolean isRunning = false;
private int result;
public DownloadService() {
super("DownloadService");
}
#Override
public void onCreate() {
super.onCreate();
isRunning = true;
}
#Override
protected void onHandleIntent(Intent intent) {
if (NetworkStateUtils.isInternetConnected(getApplicationContext()))
result = downloadFiles(getApplicationContext());
}
#Override
public void onDestroy() {
super.onDestroy();
switch (result) {
case DOWNLOAD_SUCCESS:
Toast.makeText(getApplicationContext(), R.string.download_finished,
Toast.LENGTH_SHORT).show();
break;
case DOWNLOAD_CANCELLED:
Toast.makeText(getApplicationContext(), R.string.download_canceled,
Toast.LENGTH_SHORT).show();
break;
case DOWNLOAD_FAIL:
Toast.makeText(getApplicationContext(), R.string.download_failed,
Toast.LENGTH_SHORT).show();
break;
}
isRunning = false;
}
}
This service is meant to run until download has finished. The function downloadFiles() uses no AsyncTask. It saves the HttpURLConnection with an FileOutputStream directly.
The service started correctly when I click the preference. Now the problem is, when I click to stop the service with stopService(), DownloadService triggered onDestroy() immediately; however according to logs, onHandleIntent() is still running becasue I can still see HTTP requests continuously. Is this because Service runs in a thread itself, or am I doing something wrong? How can I ensure that everything in onHandleIntent() stops immediately (or at least able to stop) when stopService() is being called?
Finally figured out how to make it work.
As I stated in my question, somehow onHandleIntent() will create a thread to do the job. So even when the service itself is destoryed, the thread is still running. I achieved my goal by adding a global variable
private static boolean isStopped = false;
to DownloadService class.
In order to cancel my service, instead of calling
Setting.this.stopService(new Intent(Setting.this, DownloadService.class));
just set DownloadService.isStopped = true.
Finally, while doing things in onHandleIntent(), check this boolean periodically to see if it should stop downloading. If isStopped = true, return immediately and the service will stop itself.
Hope this helps someone who come across this problem too. And thanks for your time reading this question.
It has a separate thread to do work, and depending on what it is doing it might not be possible to stop it immediately. If it is blocking on I/O, interrupting it will likely have no effect.

Categories

Resources