Change and apply theme at runtime in Android [duplicate] - android

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to change current Theme at runtime in Android
I have an Android application where I allow users to switch between themes at runtime. Switching a theme is easy but the theme isn't applied until the activity is recreated. I found a way to apply the theme to current activity but if the user presses back button previous screens still have the old theme. How can I change theme for those activities? Example of app that supports it: Tasks Free

Just a hint I suppose:
Before finish(); Call
setResult(AnIntegerThatNotifiesThePreviousActivitiesToChangeTheme);
Now in all your Activities, implement onActivityResult
protected void onActivityResult(int request, int result, Intent data) {
if(result == AnIntegerThatNotifiesThePreviousActivitiesToChangeTheme)
{
//update the current theme
}
}
Another solution (Better):
Implement a class that saves the theme:
public class CurrentThemeHolder {
private CurrentThemeHolder() {
}
private static instance;
public static getInstance() {
if(instance == null)
return new CurrentThemeHolder();
else
return instance;
}
private int mTheme; //identifier of the theme
public getTheme() {
return mTheme;
}
public setTheme(int newTheme){
mTheme = newTheme;
}
}
Now let all ur activities extend this ThemeActivity:
public class ThemeActivity extends Activity {
private int mTheme;
protected void onResume() {
if(mTheme != CurrentThemeHolder.getInstance().getTheme()) {
//do what you should do to set the theme
mTheme = CurrentThemeHolder.getInstance().getTheme();
//everytime you set the theme save it
//this maybe should be done in onCreate()
}
}
}

Related

React to change in settings activity in android

Ok, so the thing is that i'm trying to get my android application to have a settings activity for the user to be able to change some of the application's features (language, theme, ...). My problem comes when trying to get the app to react to the change on the value of one of those preferences. For instance the theme one; my idea would be to have a "Switch preference". When it would be on, the app's theme would be Material.Light and when off, Material. For this I though to have some "onValueChanged" method that would react when the switch changed its position. The problem here is that I'm unable to properly get an instance of the SwitchPreference in my SettingsActivity, both because the "findPreference(key)" method is deprecated and I don't really know how to make it take the value of the needed key.
There is any way to do this, or should I change the way of thinking for this problem?
Instead of setting the theme in onValue change listener, you can also do like below:
1) Create ThemeUtils class and do switch case for theme selection and set a theme for activity. I created own style, you can select your stlye name here
public class ThemeUtils {
public final static int THEME_Professional = 1;
public final static int THEME_Default = 2;
public static void onActivityCreateSetTheme(Activity activity, int sTheme) {
switch (sTheme)
{
default:
case THEME_Professional:
activity.setTheme(R.style.ProfessionalTheme);
break;
case THEME_Default:
activity.setTheme(R.style.DefaultTheme);
break;
}
}
}
2)In onCreate method of Activity, call setTheme method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme();
}
3)Gets user selected theme
private void setTheme() {
int themeInt = getThemeValue();
ThemeUtils.onActivityCreateSetTheme(this, themeInt);
}
4) After getting the style id, you can call ThemeUtils class method onActivityCreateSetTheme
private int getThemeValue() {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
Boolean isGrid = pref.getBoolean(getString(R.string.grid_switch), false);
if (isGrid) {
return 2;
} else {
return 1;
}
}
Hope this option helps you !!!

setTheme not changing the theme colors

I know this question was asked few times. but I cant find the problem in my case.
I want to change the theme of the app but my colorPrimary ,colorAccent and etc.. aren't changing.
my MainActivity extends BasicActivity. it looks like this:
public class MainActivity extends BasicActivity {
public static String MY_PREFS = "MY_PREFS";
private SharedPreferences mySharedPreferences;
int prefMode = Activity.MODE_PRIVATE;
private Toolbar toolbar;
private TabLayout tabLayout;
private ViewPager viewPager;
private ViewPagerAdapter adapter;
private TextView tabOne, tabTwo, tabThree;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
this is my BasicActivity(in this case I made it even simpler to show that the theme is taken from R.style):
public class BasicActivity extends AppCompatActivity {
public static String MY_PREFS = "MY_PREFS";
int prefMode = Activity.MODE_PRIVATE;
protected void onCreate(Bundle savedInstanceState) {
JsonParser parser = new JsonParser(getApplicationContext());
int resourceId = this.getResources().getIdentifier(parser.getThemeID(), "style", this.getPackageName());
setTheme(R.style.c_2ecc71_BC6C2B);
if (android.os.Build.VERSION.SDK_INT >= 19) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
super.onCreate(savedInstanceState);
}
}
and my XML:
<style name="c_2ecc71_BC6C2B" parent="#style/Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#2ecc71</item>
<item name="colorPrimaryDark">#1ebc61</item>
<item name="colorAccent">#BC6C2B</item>
</style>
According to the previous questions this code should work but in my case the views that have colorPrimary in their XML still loading the old theme's colors insted of the new one even though i set the theme before calling setContentView(R.layout.activity_main);
Thanks!
If you use Fragments, they will ignore the value you have set in the onCreate(), if you override the getTheme() method, it will be used within fragments as well:
Answered on different question: Change Activity's theme programmatically
#Override
public Resources.Theme getTheme() {
Resources.Theme theme = super.getTheme();
theme.applyStyle(R.style.c_2ecc71_BC6C2B, true);
return theme;
}
Use it in your MainActivity or your BasicActivity depending on where you want it to apply. You will NOT need to change it in the onCreate anymore.
You are trying to extend one of the newer themes of Android (above API 21). In addition to all the answers above , you can put your theme in styles.xml(v21).
More info here https://developer.android.com/training/material/compatibility.html
Not sure if you really want to set it programmatically, but you might try this: How to setTheme to an activity at runtime? It doesn't work call setTheme before onCreate and setContentView
If you're looking to set it for the whole application, it might be easier/cleaner to set it in the AndroidManifest.xml file instead.
<application android:theme="#style/CustomTheme">
Also, I'd highly avoid using a style name that has the values in it. The point of using a style is to avoid hard coding the values and allowing them to be configurable and reusable. What if you want to change the colorPrimary, are you also going to change your style name?
To set theme at runtime you can use following line of code :
setTheme(android.R.style.Theme_Name);
and write it before calling setContentView() and super.onCreate() method inside onCreate() method.
If you want to change that kind of stuff during runtime, you must insert all those "setTheme(android.R.style.Theme_Name);" methods inside runonUiThread, like this:
public class BasicActivity extends AppCompatActivity {
public static String MY_PREFS = "MY_PREFS";
int prefMode = Activity.MODE_PRIVATE;
protected void onCreate(Bundle savedInstanceState) {
JsonParser parser = new JsonParser(getApplicationContext());
int resourceId = this.getResources().getIdentifier(parser.getThemeID(), "style", this.getPackageName());
runOnUiThread(new Runnable() {
#Override
public void run() {
setTheme(R.style.c_2ecc71_BC6C2B);
}
});
recreate();
if (android.os.Build.VERSION.SDK_INT >= 19) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
super.onCreate(savedInstanceState);
}
}
and call recreate() after!
According to Android -
void recreate ()
Cause this Activity to be recreated with a new instance. This results in essentially the same flow as when the Activity is created due to a configuration change -- the current instance will go through its lifecycle to onDestroy() and a new instance then created after it.
Just modify your BasicActivity and MainActivity as shown in below and create appropriate theme. You can use shared preference for checking theme state during app up.
BasicActivity .java
public abstract class BasicActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
if (getLayoutID() != 0) {
setContentView(getLayoutID());
}
} catch (Exception ex) { /* ... */ }
final boolean THEME_DARK = true;// read appropriate value from SP or any other storage
Toolbar toolbar;
if ((toolbar = getToolbar()) != null) {
if (THEME_DARK/* check theme type here*/) {
toolbar.setPopupTheme(R.style.c_2ecc71_BC6C2B);
}
try {
setSupportActionBar(toolbar);
} catch (NoClassDefFoundError e) {
// Toast
finish();
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.setStatusBarColor(getResources().getColor(R.color.colorPrimary));
}
}
public abstract Toolbar getToolbar();
public abstract int getLayoutID();
}
MainActivity.java
public class MainActivity extends BasicActivity {
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public Toolbar getToolbar() {
return toolbar == null ? toolbar = (Toolbar) findViewById(R.id.toolbar) : toolbar;
}
#Override
public int getLayoutID() {
return R.layout.activity_main;
}
}
You have hard-coded the theme in BaseActivity , rather than getting targetted resource id.
You need to put setTheme(value_from_resourceId);
Currently the BaseActivity always calls irrespective of value that you parsed
setTheme(R.style.c_2ecc71_BC6C2B);
than referring the runtime value

How do I display a dialog in android without an Activity context?

This seems like it should be simple, but I'm not finding an answer anywhere. I have an Android application that performs network tasks in the background. If an error comes back, I want to display an error dialog. When the task returns, I don't know which Activity is in the foreground. Based on this post, it looks like we can't use the application context to display a dialog (and indeed I do get the crash if I try).
So how can I get the context of the current activity? Again, the receiver for the network task is running in the Application context, not in a particular Activity. Any other ideas?
Edit: I should clarify. I don't want to display an error dialog if I'm not the foreground application. I'm only interested in the case where our app is in the foreground for now.
If an error comes back, I want to display an error dialog.
Please only do this if you know that the user is actively using your application. The user will be very very annoyed if you interrupt them in the middle of something else (playing a game, watching a movie, reading a book).
So how can I get the context of the current activity?
You don't. At most, you let the current activity know that it needs to do something.
Any other ideas?
One possibility is to use an ordered broadcast, so if you have a foreground activity, it gets control, otherwise you raise a Notification to let the user know about the problem without popping a dialog. The activity that receives the ordered broadcast can display an AlertDialog or otherwise let the user know about the problem. I wrote about the details of how to do this in a blog post (and a book chapter, for that matter), and here is a sample application demonstrating the technique.
Or, have the service call startActivity() to start up a dialog-themed activity.
I've created a helper class that implements CommonsWare's idea. Activities that wish to display alerts just need to call Alerts.register() and Alerts.unregister(). Then anyone can call Alerts.displayError().
Comments welcome.
public class Alerts {
private static class AlertReceiver extends BroadcastReceiver {
private static HashMap<Activity, AlertReceiver> registrations;
private Context activityContext;
static {
registrations = new HashMap<Activity, AlertReceiver>();
}
static void register(Activity activity) {
AlertReceiver receiver = new AlertReceiver(activity);
activity.registerReceiver(receiver, new IntentFilter(MyApplication.INTENT_DISPLAYERROR));
registrations.put(activity, receiver);
}
static void unregister(Activity activity) {
AlertReceiver receiver = registrations.get(activity);
if(receiver != null) {
activity.unregisterReceiver(receiver);
registrations.remove(activity);
}
}
private AlertReceiver(Activity activity) {
activityContext = activity;
}
#Override
public void onReceive(Context context, Intent intent) {
abortBroadcast();
String msg = intent.getStringExtra(Intent.EXTRA_TEXT);
displayErrorInternal(activityContext, msg);
}
}
public static void register(Activity activity) {
AlertReceiver.register(activity);
}
public static void unregister(Activity activity) {
AlertReceiver.unregister(activity);
}
public static void displayError(Context context, String msg) {
Intent intent = new Intent(MyApplication.INTENT_DISPLAYERROR);
intent.putExtra(Intent.EXTRA_TEXT, msg);
context.sendOrderedBroadcast(intent, null);
}
private static void displayErrorInternal(Context context, String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("Error")
.setMessage(msg)
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
final AlertDialog alert = builder.create();
alert.show();
}
}
While this question is quite old, meanwhile there is a nice solution if you want it to work with any active Activity. So you wouldn't need to register individual Activities like it's proposed in other answers.
I want to emphasize that this is usually something that you should try to avoid - creating dialogs w/o knowing the view context, but there might be special circumstances where this can be useful.
With this solution using ActivityLifecycleCallbacks you're always aware of the active Activity on Application level. You can then use this active Activity to open dialogs or other Activities from e.g. network code where you would only have access to the Application class:
class YourApplication : Application() {
private val activeActivityCallbacks = ActiveActivityLifecycleCallbacks()
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(activeActivityCallbacks)
}
override fun onTerminate() {
unregisterActivityLifecycleCallbacks(activeActivityCallbacks)
super.onTerminate()
}
fun getActiveActivity(): Activity? = activeActivityCallbacks.getActiveActivity()
// ...
}
class ActiveActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {
private var activeActivity: Activity? = null
fun getActiveActivity(): Activity? = activeActivity
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activeActivity = activity
}
override fun onActivityDestroyed(activity: Activity) {
if (activity === activeActivity) {
activeActivity = null
}
}
// ...
}
From anywhere:
YourApplication.get().getActiveActivity()?.let { activity ->
activity.runOnUiThread {
AlertDialog.Builder(activity).setMessage("test").show()
}
}
Please check other SO posts how to implement a getter for the Application: e.g. Kotlin singleton application class
I'm using custom created Dialog. It will be called by Service or Handler even in the case if the current Activity will lose focus.
Activity of my custom Dialog:
public class AlertDialogue extends AppCompatActivity {
Button btnOk;
TextView textDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE); //comment this line if you need to show Title.
setContentView(R.layout.activity_alert_dialogue);
textDialog = (TextView)findViewById(R.id.text_dialog) ;
textDialog.setText("Hello, I'm the dialog text!");
btnOk = (Button) findViewById(R.id.button_dialog);
btnOk.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
You can call this dialog using:
Intent intent = new Intent(this, AlertDialogue.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
In the Manifest:
<activity android:name=".AlertDialogue"
android:theme="#style/AlertDialogMy">
</activity>
style:
<resources>
<style name="AlertDialogMy" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowNoTitle">true</item> //delete this line if you need to show Title.
</style>
</resources>
Here is the full code of this example.
Why don't use event bus (https://github.com/greenrobot/EventBus) ?
Define events:
public class ShowErrorMessageEvent {
public String errorMessage;
public ShowErrorMessageEvent(String errorMessage) {
this.errorMessage = errorMessage;
}
public String getErrorMessage() {
return this.errorMessage;
}
}
Prepare subscribers for all activities you needed:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
In all activities, show the dialog if the event received
public void onEvent(ShowErrorMessageEvent event) {
/* Show dialog with event.getErrorMessage() from background thread */
};
In your background thread, post the error event:
EventBus.getDefault().post(new ShowErrorMessageEvent("This is an error message!"));
Here is an implementation that will allow an AlertDialog to be displayed on top of the current active activity (this is an example of a message dialog, but can be used for alerts as well).
public class AlertsDialogue
{
private AlertDialog.Builder alertDialogBuilder;
private AlertDialog alert;
public AlertsDialogue(Context context, String title, String message)
{
alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setTitle(title);
alertDialogBuilder.setIcon(R.drawable.ic_launcher);
alertDialogBuilder.setMessage(message)
.setCancelable(false)
.setPositiveButton(context.getString(R.string.text_ok), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
{
alert.dismiss();
}
});
alert = alertDialogBuilder.create();
Window window = alert.getWindow();
if (window != null)
{
// the important stuff..
window.setType(WindowManager.LayoutParams.TYPE_TOAST);
alert.show();
}
else
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
}
The dialog will be displayed even if the context on which it was instantiated with is no longer active, much like Toast.
Call with new AlertsDialogue(MyActivity.this, "title", "message");
No additional permissions are required in the AndroidManifest file.
What type of network tasks are going on in the background. I would suggest possibly rethinking the design. Perhaps a notification would be better? Or maybe a "results summary" screen. As a user I Would rather a non obtrusive signal of error if I am not actively waiting for the task to complete.
I also face the problem. I find out a simple and effective solution. Usuallay, we have a base activity used to handle some common logic. So have this:
public class BaseActionBarActivity extends ActionBarActivity{ //this BaseActionBarActivity is the Base Act
private static BaseActionBarActivity current;
#Override
protected void onStart() {
super.onStart();
current=this;
}
public static BaseActionBarActivity getCurrentContext() {
return current;
}
}
current field is the current Context Actitvity. And I believe there is not memory leak question. It works well for me! Hope helpful
you need to reset type of Window which dialog attached, as follow:
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
do not forget declaring "android.permission.SYSTEM_ALERT_WINDOW" permission in your manifest.
The best thing is to show dialogs and other view related work in Activity / Fragment, not in some other class that has no idea what context is.
But there are some valid reasons to want to do that outside of Views. For example, I want to show some alert dialog to help testing the app in mock build. To provide activity context you can do several things, I will write about two:
Inject activity context with scope tied to activity. It is the best solution, but requires to have architecture that is correctly implemented, with scoping in mind. I will not describe how to do it here, it can be found in tutorials.
I have created some helper class with activity's extention to get context from activity. It doesn't store context, but function that can provide context, so I think it shouldn't cause memory leaks (? not tested, it is for mocked builds for testers, so didn't spend much time checking that)
object ActivityContextProviderForMockBuilds {
private val contextProvider = MutableSharedFlow<(Context) -> Unit>(extraBufferCapacity = 1)
fun getContext(contextCallback: (Context) -> Unit) = contextProvider.tryEmit(contextCallback)
fun AppCompatActivity.setContextProviderForMockBuild() {
if (BuildConfig.MOCKS_ENABLED) {
contextProvider.onEach {
it.invoke(this)
}.launchIn(lifecycleScope)
}
}
}
Usage:
ActivityContextProviderForMockBuilds.getContext { context -> ... }
But as said previously, you shouldn't use activity context outside of views in most cases and if yes, then by dependency injection :)
Well, I think that the best for you would be an AsyncTask. Here an example and doc:
http://developer.android.com/reference/android/os/AsyncTask.html

Checking if an Android application is running in the background

By background, I mean none of the application's activities are currently visible to the user?
There are few ways to detect whether your application is running in the background, but only one of them is completely reliable:
The right solution (credits go to Dan, CommonsWare and NeTeInStEiN)
Track visibility of your application by yourself using Activity.onPause, Activity.onResume methods. Store "visibility" status in some other class. Good choices are your own implementation of the Application or a Service (there are also a few variations of this solution if you'd like to check activity visibility from the service).
Example
Implement custom Application class (note the isActivityVisible() static method):
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
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 ancestor 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.activityResumed();
}
#Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
Update
ActivityLifecycleCallbacks were added in API level 14 (Android 4.0). You can use them to track whether an activity of your application is currently visible to the user. Check Cornstalks' answer below for the details.
The wrong one
I used to suggest the following solution:
You can detect currently foreground/background application with ActivityManager.getRunningAppProcesses() which returns a list of RunningAppProcessInfo records. To determine if your application is on the foreground check RunningAppProcessInfo.importance field for equality to RunningAppProcessInfo.IMPORTANCE_FOREGROUND while RunningAppProcessInfo.processName is equal to your application package name.
Also if you call ActivityManager.getRunningAppProcesses() from your application UI thread it will return importance IMPORTANCE_FOREGROUND for your task no matter whether it is actually in the foreground or not. Call it in the background thread (for example via AsyncTask) and it will return correct results.
While this solution may work (and it indeed works most of the time) I strongly recommend to refrain from using it. And here's why. As Dianne Hackborn wrote:
These APIs are not there for applications to base their UI flow on, but to do things like show the user the running apps, or a task manager, or such.
Yes there is a list kept in memory for these things. However, it is off in another process, managed by threads running separately from yours, and not something you can count on (a) seeing in time to make the correct decision or (b) have a consistent picture by the time you return. Plus the decision about what the "next" activity to go to is always done at the point where the switch is to happen, and it is not until that exact point (where the activity state is briefly locked down to do the switch) that we actually know for sure what the next thing will be.
And the implementation and global behavior here is not guaranteed to remain the same in the future.
I wish I had read this before I posted an answer on the SO, but hopefully it's not too late to admit my error.
Another wrong solution
Droid-Fu library mentioned in one of the answers uses ActivityManager.getRunningTasks for its isApplicationBroughtToBackground method. See Dianne's comment above and don't use that method either.
GOOGLE SOLUTION - not a hack, like previous solutions. Use ProcessLifecycleOwnerKotlin:
class ArchLifecycleApp : Application(), LifecycleObserver {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onAppBackgrounded() {
//App in background
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onAppForegrounded() {
// App in foreground
}
}
Java:
public class ArchLifecycleApp extends Application implements LifecycleObserver {
#Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded() {
//App in background
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded() {
// App in foreground
}
}
in app.gradle
dependencies {
...
implementation "android.arch.lifecycle:extensions:1.1.0"
//New Android X dependency is this -
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
}
allprojects {
repositories {
...
google()
jcenter()
maven { url 'https://maven.google.com' }
}
}
You can read more about Lifecycle related architecture components here - https://developer.android.com/topic/libraries/architecture/lifecycle
DO NOT USE THIS ANSWER
user1269737's answer is the proper (Google/Android approved) way to do this. Go read their answer and give them a +1.
I'll leave my original answer here for posterity's sake. This was the best available back in 2012, but now Android has proper support for this.
Original answer
The key is using ActivityLifecycleCallbacks (note that this requires Android API level 14 (Android 4.0)). Just check if the number of stopped activities is equal to the number of started activities. If they're equal, your application is being backgrounded. If there are more started activities, your application is still visible. If there are more resumed than paused activities, your application is not only visible, but it's also in the foreground. There are 3 main states that your activity can be in, then: visible and in the foreground, visible but not in the foreground, and not visible and not in the foreground (i.e. in the background).
The really nice thing about this method is that it doesn't have the asynchronous issues getRunningTasks() does, but you also don't have to modify every Activity in your application to set/unset something in onResumed()/onPaused(). It's just a few lines of code that's self contained, and it works throughout your whole application. Plus, there are no funky permissions required either.
MyLifecycleHandler.java:
public class MyLifecycleHandler implements ActivityLifecycleCallbacks {
// I use four separate variables here. You can, of course, just use two and
// increment/decrement them instead of using four and incrementing them all.
private int resumed;
private int paused;
private int started;
private int stopped;
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
++resumed;
}
#Override
public void onActivityPaused(Activity activity) {
++paused;
android.util.Log.w("test", "application is in foreground: " + (resumed > paused));
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityStarted(Activity activity) {
++started;
}
#Override
public void onActivityStopped(Activity activity) {
++stopped;
android.util.Log.w("test", "application is visible: " + (started > stopped));
}
// If you want a static function you can use to check if your application is
// foreground/background, you can use the following:
/*
// Replace the four variables above with these four
private static int resumed;
private static int paused;
private static int started;
private static int stopped;
// And these two public static functions
public static boolean isApplicationVisible() {
return started > stopped;
}
public static boolean isApplicationInForeground() {
return resumed > paused;
}
*/
}
MyApplication.java:
// Don't forget to add it to your manifest by doing
// <application android:name="your.package.MyApplication" ...
public class MyApplication extends Application {
#Override
public void onCreate() {
// Simply add the handler, and that's it! No need to add any code
// to every activity. Everything is contained in MyLifecycleHandler
// with just a few lines of code. Now *that's* nice.
registerActivityLifecycleCallbacks(new MyLifecycleHandler());
}
}
#Mewzer has asked some good questions about this method that I'd like to respond to in this answer for everyone:
onStop() is not called in low memory situations; is that a problem here?
No. The docs for onStop() say:
Note that this method may never be called, in low memory situations where the system does not have enough memory to keep your activity's process running after its onPause() method is called.
The key here is "keep your activity's process running..." If this low memory situation is ever reached, your process is actually killed (not just your activity). This means that this method of checking for backgrounded-ness is still valid because a) you can't check for backgrounding anyway if your process is killed, and b) if your process starts again (because a new activity is created), the member variables (whether static or not) for MyLifecycleHandler will be reset to 0.
Does this work for configuration changes?
By default, no. You have to explicitly set configChanges=orientation|screensize (| with anything else you want) in your manifest file and handle the configuration changes, or else your activity will be destroyed and recreated. If you do not set this, your activity's methods will be called in this order: onCreate -> onStart -> onResume -> (now rotate) -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume. As you can see, there is no overlap (normally, two activities overlap very briefly when switching between the two, which is how this backgrounding-detection method works). In order to get around this, you must set configChanges so that your activity is not destroyed. Fortunately, I've had to set configChanges already in all of my projects because it was undesirable for my entire activity to get destroyed on screen rotate/resize, so I've never found this to be problematic. (thanks to dpimka for refreshing my memory on this and correcting me!)
One note:
When I've said "background" here in this answer, I've meant "your app is no longer visible." Android activities can be visible yet not in the foreground (for example, if there's a transparent notification overlay). That's why I've updated this answer to reflect that.
It's important to know that Android has a weird limbo moment when switching activities where nothing is in the foreground. For this reason, if you check if your application is in the foreground when switching between activities (in the same app), you'll be told you're not in the foreground (even though your app is still the active app and is visible).
You can check if your app is in the foreground in your Activity's onPause() method after super.onPause(). Just remember the weird limbo state I just talked about.
You can check if your app is visible (i.e. if it's not in the background) in your Activity's onStop() method after super.onStop().
Starting support library version 26 you can use ProcessLifecycleOwner, just add it to your dependency like described here, for example:
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}
And then just query ProcessLifecycleOwner whenever you want for app state, examples:
// Check if app is in background
ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED;
// Check if app is in foreground
ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
Since Android API 16 there is a simple way to check if app is in foreground. It may not be foolproof, but no methods on Android are foolproof. This method is good enough to use when your service receives update from server and has to decide whether to show notification, or not (because if UI is foreground, user will notice the update without notification).
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
isInBackground = myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
Idolon's answer is error prone and much more complicated althought repeatead here check android application is in foreground or not? and here Determining the current foreground application from a background task or service
There is a much more simpler approach:
On a BaseActivity that all Activities extend:
protected static boolean isVisible = false;
#Override
public void onResume()
{
super.onResume();
setVisible(true);
}
#Override
public void onPause()
{
super.onPause();
setVisible(false);
}
Whenever you need to check if any of your application activities is in foreground just check isVisible();
To understand this approach check this answer of side-by-side activity lifecycle: Activity side-by-side lifecycle
I tried the recommended solution that uses Application.ActivityLifecycleCallbacks and many others, but they didn't work as expected. Thanks to Sarge, I came up with a pretty easy and straightforward solution that I am describing below.
They key of the solution is the fact of understanding that if we have ActivityA and ActivityB, and we call ActivityB from ActivityA (and not call ActivityA.finish), then ActivityB's onStart() will be called before ActivityA onStop().
That's also the main difference between onStop() and onPause() that none did mention in the articles I read.
So based on this Activity's Lifecycle behavior, you can simply count how many times did onStart() and onPause() got called in your program. Note that for each Activity of your program, you must override onStart() and onStop(), in order to increment/decrement the static variable used for counting. Below is the code implementing this logic. Note that I am using a class that extends Application, so dont forget to declare on Manifest.xml inside Application tag: android:name=".Utilities", although it can be implemented using a simple custom class too.
public class Utilities extends Application
{
private static int stateCounter;
public void onCreate()
{
super.onCreate();
stateCounter = 0;
}
/**
* #return true if application is on background
* */
public static boolean isApplicationOnBackground()
{
return stateCounter == 0;
}
//to be called on each Activity onStart()
public static void activityStarted()
{
stateCounter++;
}
//to be called on each Activity onStop()
public static void activityStopped()
{
stateCounter--;
}
}
Now on each Activity of our program, we should override onStart() and onStop() and increment/decrement as shown below:
#Override
public void onStart()
{
super.onStart();
Utilities.activityStarted();
}
#Override
public void onStop()
{
Utilities.activityStopped();
if(Utilities.isApplicationOnBackground())
{
//you should want to check here if your application is on background
}
super.onStop();
}
With this logic, there are 2 possible cases:
stateCounter = 0 : The number of stopped is equal with the number of started Activities, which means that the application is running on the background.
stateCounter > 0 : The number of started is bigger than the number of stopped, which means that the application is running on the foreground.
Notice: stateCounter < 0 would mean that there are more stopped Activities rather than started, which is impossible. If you encounter this case, then it means that you are not increasing/decreasing the counter as you should.
You are ready to go. You should want to check if your application is on background inside onStop().
There is no way, short of you tracking it yourself, to determine if any of your activities are visible or not. Perhaps you should consider asking a new StackOverflow question, explaining what it is you are trying to achieve from a user experience, so we can perhaps give you alternative implementation ideas.
You can use ComponentCallbacks2 to detect if the app is in background. BTW this callback is only available in API Level 14 (Ice Cream Sandwich) and above.
You will get a call to the method:
public abstract void onTrimMemory (int level)
if the level is ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN then the app is in background.
You can implement this interface to an activity, service, etc.
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
#Override
public void onConfigurationChanged(final Configuration newConfig) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// app is in background
}
}
}
Building on #Cornstalks answer to include a couple of useful features.
Extra features:
introduced singleton pattern so you can do this anywhere in the application: AppLifecycleHandler.isApplicationVisible() and AppLifecycleHandler.isApplicationInForeground()
added handling of duplicate events (see comments // take some action on change of visibility and // take some action on change of in foreground)
App.java
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(AppLifecycleHandler.getInstance());
}
}
AppLifecycleHandler.java
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
private int resumed;
private int started;
private final String DebugName = "AppLifecycleHandler";
private boolean isVisible = false;
private boolean isInForeground = false;
private static AppLifecycleHandler instance;
public static AppLifecycleHandler getInstance() {
if (instance == null) {
instance = new AppLifecycleHandler();
}
return instance;
}
private AppLifecycleHandler() {
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
++resumed;
android.util.Log.w(DebugName, "onActivityResumed -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
#Override
public void onActivityPaused(Activity activity) {
--resumed;
android.util.Log.w(DebugName, "onActivityPaused -> application is in foreground: " + (resumed > 0) + " (" + activity.getClass() + ")");
setForeground((resumed > 0));
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityStarted(Activity activity) {
++started;
android.util.Log.w(DebugName, "onActivityStarted -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
#Override
public void onActivityStopped(Activity activity) {
--started;
android.util.Log.w(DebugName, "onActivityStopped -> application is visible: " + (started > 0) + " (" + activity.getClass() + ")");
setVisible((started > 0));
}
private void setVisible(boolean visible) {
if (isVisible == visible) {
// no change
return;
}
// visibility changed
isVisible = visible;
android.util.Log.w(DebugName, "App Visiblility Changed -> application is visible: " + isVisible);
// take some action on change of visibility
}
private void setForeground(boolean inForeground) {
if (isInForeground == inForeground) {
// no change
return;
}
// in foreground changed
isInForeground = inForeground;
android.util.Log.w(DebugName, "App In Foreground Changed -> application is in foreground: " + isInForeground);
// take some action on change of in foreground
}
public static boolean isApplicationVisible() {
return AppLifecycleHandler.getInstance().started > 0;
}
public static boolean isApplicationInForeground() {
return AppLifecycleHandler.getInstance().resumed > 0;
}
}
The best solution I have come up with uses timers.
You have start a timer in onPause() and cancel the same timer in onResume(), there is 1 instance of the Timer (usually defined in the Application class). The timer itself is set to run a Runnable after 2 seconds (or whatever interval you think is appropriate), when the timer fires you set a flag marking the application as being in the background.
In the onResume() method before you cancel the timer, you can query the background flag to perform any startup operations (e.g. start downloads or enable location services).
This solution allows you to have several activities on the back stack, and doesn't require any permissions to implement.
This solution works well if you use an event bus too, as your timer can simply fire an event and various parts of your app can respond accordingly.
If you turn on developer settings "Don't keep actvities" - check only count of created activites is not enough. You must check also isSaveInstanceState. My custom method isApplicationRunning() check is android app is running:
Here my work code:
public class AppLifecycleService implements Application.ActivityLifecycleCallbacks {
private int created;
private boolean isSaveInstanceState;
private static AppLifecycleService instance;
private final static String TAG = AppLifecycleService.class.getName();
public static AppLifecycleService getInstance() {
if (instance == null) {
instance = new AppLifecycleService();
}
return instance;
}
public static boolean isApplicationRunning() {
boolean isApplicationRunning = true;
if (getCountCreatedActvities() == 0 && !isSaveInstanceState()) {
isApplicationRunning = false;
}
return isApplicationRunning;
}
public static boolean isSaveInstanceState() {
return AppLifecycleService.getInstance().isSaveInstanceState;
}
public static int getCountCreatedActvities() {
return AppLifecycleService.getInstance().created;
}
private AppLifecycleService() {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
this.isSaveInstanceState = true;
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
++created;
}
#Override
public void onActivityDestroyed(Activity activity) {
--created;
}
#Override
public void onActivityResumed(Activity activity) { }
#Override
public void onActivityPaused(Activity activity) { }
#Override
public void onActivityStarted(Activity activity) { }
#Override
public void onActivityStopped(Activity activity) { }
}
To piggyback on what CommonsWare and Key have said, you could perhaps extend the Application class and have all of your activities call that on their onPause/onResume methods. This would allow you to know which Activity(ies) are visible, but this could probably be handled better.
Can you elaborate on what you have in mind exactly? When you say running in the background do you mean simply having your application still in memory even though it is not currently on screen? Have you looked into using Services as a more persistent way to manage your app when it is not in focus?
I did my own implementation of ActivityLifecycleCallbacks. I'm using SherlockActivity, but for normal Activity class might work.
First, I'm creating an interface that have all methods for track the activities lifecycle:
public interface ActivityLifecycleCallbacks{
public void onActivityStopped(Activity activity);
public void onActivityStarted(Activity activity);
public void onActivitySaveInstanceState(Activity activity, Bundle outState);
public void onActivityResumed(Activity activity);
public void onActivityPaused(Activity activity);
public void onActivityDestroyed(Activity activity);
public void onActivityCreated(Activity activity, Bundle savedInstanceState);
}
Second, I implemented this interface in my Application's class:
public class MyApplication extends Application implements my.package.ActivityLifecycleCallbacks{
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
}
#Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
#Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
}
#Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
}
#Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
}
}
Third, I'm creating a class that extends from SherlockActivity:
public class MySherlockActivity extends SherlockActivity {
protected MyApplication nMyApplication;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
nMyApplication = (MyApplication) getApplication();
nMyApplication.onActivityCreated(this, savedInstanceState);
}
protected void onResume() {
// TODO Auto-generated method stub
nMyApplication.onActivityResumed(this);
super.onResume();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
nMyApplication.onActivityPaused(this);
super.onPause();
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
nMyApplication.onActivityDestroyed(this);
super.onDestroy();
}
#Override
protected void onStart() {
nMyApplication.onActivityStarted(this);
super.onStart();
}
#Override
protected void onStop() {
nMyApplication.onActivityStopped(this);
super.onStop();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
nMyApplication.onActivitySaveInstanceState(this, outState);
super.onSaveInstanceState(outState);
}
}
Fourth, all class that extend from SherlockActivity, I replaced for MySherlockActivity:
public class MainActivity extends MySherlockActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
Now, in the logcat you will see the logs programmed in the Interface implementation made in MyApplication.
Offical docs:
The system distinguishes between foreground and background apps. (The definition of background for purposes of service limitations is distinct from the definition used by memory management; an app might be in the background as pertains to memory management, but in the foreground as pertains to its ability to launch services.) An app is considered to be in the foreground if any of the following is true:
It has a visible activity, whether the activity is started or paused.
It has a foreground service.
Another foreground app is connected to the app, either by binding to one of its services or by making use of one of its content providers. For example, the app is in the foreground if another app binds to its:
IME
Wallpaper service
Notification listener
Voice or text service
If none of those conditions is true, the app is considered to be in the background.
The only one correct solution:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
MyApp.mainActivity = this;
super.onCreate(savedInstanceState);
...
}
MyApp.java:
public class MyApp extends Application implements LifecycleObserver {
public static MainActivity mainActivity = null;
#Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
// app in background
if (mainActivity != null) {
...
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
// app in foreground
if (mainActivity != null) {
...
}
}
}
No any solution work for me, however I propose a raw solution. This should work. If isAppBackground return false, then app must be in foreground.
public static boolean isAppBackground(Context context){
boolean isBackground=true;
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH){
List<ActivityManager.RunningAppProcessInfo> runningProcesses =activityManager.getRunningAppProcesses();
for(ActivityManager.RunningAppProcessInfo processInfo:runningProcesses){
if(processInfo.importance==ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
for(String activeProcess:processInfo.pkgList){
if(activeProcess.equals(context.getPackageName())){
isBackground = false;
}
}
}
}
}else{
List<ActivityManager.RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1);
if(taskInfo.size()>0) {
ComponentName componentName = taskInfo.get(0).topActivity;
if(componentName.getPackageName().equals(context.getPackageName())){
isBackground = false;
}
}
}
return isBackground;
}
Activity gets paused when a Dialog comes above it so all the recommended solutions are half-solutions. You need to create hooks for dialogs as well.
I recommend reading through this page: http://developer.android.com/reference/android/app/Activity.html
In short, your activity is no longer visible after onStop() has been called.
Since it isn't already mentioned, I will suggest the readers to explore ProcessLifecycleOwner available through Android Architecture components
This code will check foreground and background in any condition:
Java Code:
private static boolean isApplicationForeground(Context context) {
KeyguardManager keyguardManager =
(KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
if (keyguardManager.isKeyguardLocked()) {
return false;
}
int myPid = Process.myPid();
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> list;
if ((list = activityManager.getRunningAppProcesses()) != null) {
for (ActivityManager.RunningAppProcessInfo aList : list) {
ActivityManager.RunningAppProcessInfo info;
if ((info = aList).pid == myPid) {
return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
}
}
return false;
}
Kotlin Code:
private fun isApplicationForeground(context: Context): Boolean {
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
if (keyguardManager.isKeyguardLocked) {
return false
}
val myPid = Process.myPid()
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
var list: List<ActivityManager.RunningAppProcessInfo>
if (activityManager.runningAppProcesses.also { list = it } != null) {
for (aList in list) {
var info: ActivityManager.RunningAppProcessInfo
if (aList.also { info = it }.pid == myPid) {
return info.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}
}
}
return false
}
Simple and straight-forward answer:
override fun onPause() {
Log.i("APP LIFECYCLE", "App Enter BACKground")
isForeground = false
super.onPause()
}
override fun onResume() {
Log.i("APP LIFECYCLE", "App Enter FOREground")
isForeground = true
super.onResume()
}
Then just use the isForeground property of your activity to check the status.
Another solution for this old post (for those that it might help) :
<application android:name=".BaseApplication" ... >
public class BaseApplication extends Application {
private class Status {
public boolean isVisible = true;
public boolean isFocused = true;
}
private Map<Activity, Status> activities;
#Override
public void onCreate() {
activities = new HashMap<Activity, Status>();
super.onCreate();
}
private boolean hasVisibleActivity() {
for (Status status : activities.values())
if (status.isVisible)
return true;
return false;
}
private boolean hasFocusedActivity() {
for (Status status : activities.values())
if (status.isFocused)
return true;
return false;
}
public void onActivityCreate(Activity activity, boolean isStarting) {
if (isStarting && activities.isEmpty())
onApplicationStart();
activities.put(activity, new Status());
}
public void onActivityStart(Activity activity) {
if (!hasVisibleActivity() && !hasFocusedActivity())
onApplicationForeground();
activities.get(activity).isVisible = true;
}
public void onActivityWindowFocusChanged(Activity activity, boolean hasFocus) {
activities.get(activity).isFocused = hasFocus;
}
public void onActivityStop(Activity activity, boolean isFinishing) {
activities.get(activity).isVisible = false;
if (!isFinishing && !hasVisibleActivity() && !hasFocusedActivity())
onApplicationBackground();
}
public void onActivityDestroy(Activity activity, boolean isFinishing) {
activities.remove(activity);
if(isFinishing && activities.isEmpty())
onApplicationStop();
}
private void onApplicationStart() {Log.i(null, "Start");}
private void onApplicationBackground() {Log.i(null, "Background");}
private void onApplicationForeground() {Log.i(null, "Foreground");}
private void onApplicationStop() {Log.i(null, "Stop");}
}
public class MyActivity extends BaseActivity {...}
public class BaseActivity extends Activity {
private BaseApplication application;
#Override
protected void onCreate(Bundle state) {
application = (BaseApplication) getApplication();
application.onActivityCreate(this, state == null);
super.onCreate(state);
}
#Override
protected void onStart() {
application.onActivityStart(this);
super.onStart();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
application.onActivityWindowFocusChanged(this, hasFocus);
super.onWindowFocusChanged(hasFocus);
}
#Override
protected void onStop() {
application.onActivityStop(this, isFinishing());
super.onStop();
}
#Override
protected void onDestroy() {
application.onActivityDestroy(this, isFinishing());
super.onDestroy();
}
}
See the comment in the onActivityDestroyed function.
Works with SDK target version 14> :
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;
public class AppLifecycleHandler implements Application.ActivityLifecycleCallbacks {
public static int active = 0;
#Override
public void onActivityStopped(Activity activity) {
Log.i("Tracking Activity Stopped", activity.getLocalClassName());
active--;
}
#Override
public void onActivityStarted(Activity activity) {
Log.i("Tracking Activity Started", activity.getLocalClassName());
active++;
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
Log.i("Tracking Activity SaveInstanceState", activity.getLocalClassName());
}
#Override
public void onActivityResumed(Activity activity) {
Log.i("Tracking Activity Resumed", activity.getLocalClassName());
active++;
}
#Override
public void onActivityPaused(Activity activity) {
Log.i("Tracking Activity Paused", activity.getLocalClassName());
active--;
}
#Override
public void onActivityDestroyed(Activity activity) {
Log.i("Tracking Activity Destroyed", activity.getLocalClassName());
active--;
// if active var here ever becomes zero, the app is closed or in background
if(active == 0){
...
}
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Log.i("Tracking Activity Created", activity.getLocalClassName());
active++;
}
}
You should use a shared preference to store the property and act upon it using service binding from your activities. If you use binding only, (that is never use startService), then your service would run only when you bind to it, (bind onResume and unbind onPause) that would make it run on foreground only, and if you do want to work on background you can use the regular start stop service.
I think this question should be more clear. When? Where? What is your specific situation you want to konw if your app is in background?
I just introduce my solution in my way.
I get this done by using the field "importance" of RunningAppProcessInfo class in every activity's onStop method in my app, which can be simply achieved by providing a BaseActivity for other activities to extend which implements the onStop method to check the value of "importance". Here is the code:
public static boolean isAppRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(context.getPackageName())) {
if (appProcess.importance != RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE) {
return true;
}
}
}
return false;
}
What about using getApplicationState().isInForeground() ?
In my opinion, many answers introduce a heavy load of code and bring lots of complexity and non-readability.
When people ask on SO how to communicate between a Service and a Activity, I usually advice to use the LocalBroadcastManager.
Why?
Well, by quoting the docs:
You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
It is more efficient than sending a global broadcast through the system.
Not in the the docs:
It does not require external libraries
The code is minimal
It's fast to implement and understand
No custom self-implemented callbacks / ultra-singleton / intra-process
pattern whatsoever...
No strong references on Activity, Application, ...
Description
So, you want to check if any of the Activity is currently in the foreground. You usually do that in a Service, or your Application class.
This means, your Activity objects become the sender of a signal (I'm on / I'm off). Your Service, on the other hand, becomes the Receiver.
There are two moments in which your Activity tells you if it's going in the foreground or in the background (yes only two... not 6).
When the Activity goes into the foreground, the onResume() method is triggered (also called after onCreate()).
When the Activity goes in the back, onPause() is called.
These are the moments in which your Activity should send the signal to your Service to describe its state.
In case of multiple Activity's, remember the an Activity goes into the background first, then another one comes into the foreground.
So the situation would be:*
Activity1 -- send --> Signal:OFF
Activity2 -- send --> Signal:ON
The Service / Application will simply keep listening for those signals and act accordingly.
Code (TLDR)
Your Service must implement a BroadcastReceiver in order to listen for signals.
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// received data if Activity is on / off
}
}
public static final IntentFilter SIGNAL_FILTER = new IntentFilter("com.you.yourapp.MY_SIGNAL")
Register the Receiver in Service::onCreate()
#Override
protected void onCreate() {
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, SIGNAL_FILTER);
}
Un-register it in Service::onDestroy()
#Override
protected void onDestroy() {
// I'm dead, no need to listen to anything anymore.
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
}
Now your Activity's must communicated their state.
In Activity::onResume()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put ON boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
In Activity::onPause()
Intent intent = new Intent();
intent.setAction(SomeActivity.SIGNAL_FILTER); // put OFF boolean in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
A very, very common situation
Developer: I want to send data from my Service and update the Activity. How do I check if the Activity is in the foreground?
There is usually no need to check if the Activity is in the foreground or not. Just send the data via LocalBroadcastManager from your Service. If the Activity is on, then it will respond and act.
For this very common situation, the Service becomes the sender, and the Activity implements the BroadcastReceiver.
So, create a Receiver in your Activity. Register it in onResume() and un-register it in onPause(). There is no need to use the other life-cycle methods.
Define the Receiver behavior in onReceive() (update ListView, do this, do that, ...).
This way the Activity will listen only if it's in the foreground and nothing will happen if it's in the back or is destroyed.
In case of multiple Activity's, whichever Activity is on will respond (if they also implement the Receiver).
If all are in the background, nobody will respond and the signal will simply get lost.
Send the data from the Service via Intent (see code above) by specifying the signal ID.
Except for Multi-Window Support. It may be tricky (please test it if needed)...
fun isAppInForeground(): Boolean {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager ?: return false
val appProcesses = activityManager.runningAppProcesses ?: return false
val packageName = packageName
for (appProcess in appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
return true
}
}
return false
}
None of the answers quite fitted the specific case if you're looked to know if a specfic activity is in the forground and if you're an SDK without direct access to the Application. For me I was in background thread having just recieved a push notification for a new chat message and only want to display a system notification if the chat screen isn't in the foreground.
Using the ActivityLifecycleCallbacks that as been recommended in other answers I've created a small util class that houses the logic to whether MyActivity is in the Foreground or not.
class MyActivityMonitor(context: Context) : Application.ActivityLifecycleCallbacks {
private var isMyActivityInForeground = false
init {
(context.applicationContext as Application).registerActivityLifecycleCallbacks(this)
}
fun isMyActivityForeground() = isMyActivityInForeground
override fun onActivityPaused(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = false
}
}
override fun onActivityResumed(activity: Activity?) {
if (activity is MyActivity) {
isMyActivityInForeground = true
}
}
}

How to change current Theme at runtime in Android [duplicate]

This question already has answers here:
Implementing user choice of theme
(4 answers)
Closed 5 years ago.
I've created a PreferenceActivity that allows the user to choose the theme he wants to apply to the entire application.
When the user selects a theme, this code is executed:
if (...) {
getApplication().setTheme(R.style.BlackTheme);
} else {
getApplication().setTheme(R.style.LightTheme);
}
But, even though I've checked with the debugger that the code is being executed, I can't see any change in the user interface.
Themes are defined in res/values/styles.xml, and Eclipse does not show any error.
<resources>
<style name="LightTheme" parent="#android:style/Theme.Light">
</style>
<style name="BlackTheme" parent="#android:style/Theme.Black">
</style>
</resources>
Any idea about what could be happening and how to fix it?
Should I call setTheme at any special point in the code? My application consists of several Activities if that helps.
I would like to see the method too, where you set once for all your activities. But as far I know you have to set in each activity before showing any views.
For reference check this:
http://www.anddev.org/applying_a_theme_to_your_application-t817.html
Edit (copied from that forum):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call setTheme before creation of any(!) View.
setTheme(android.R.style.Theme_Dark);
// ...
setContentView(R.layout.main);
}
Edit
If you call setTheme after super.onCreate(savedInstanceState); your activity recreated but if you call setTheme before super.onCreate(savedInstanceState); your theme will set and activity
does not recreate anymore
protected void onCreate(Bundle savedInstanceState) {
setTheme(android.R.style.Theme_Dark);
super.onCreate(savedInstanceState);
// ...
setContentView(R.layout.main);
}
If you want to change theme of an already existing activity, call recreate() after setTheme().
Note: don't call recreate if you change theme in onCreate(), to avoid infinite loop.
recreate() (as mentioned by TPReal) will only restart current activity, but the previous activities will still be in back stack and theme will not be applied to them.
So, another solution for this problem is to recreate the task stack completely, like this:
TaskStackBuilder.create(getActivity())
.addNextIntent(new Intent(getActivity(), MainActivity.class))
.addNextIntent(getActivity().getIntent())
.startActivities();
EDIT:
Just put the code above after you perform changing of theme on the UI or somewhere else. All your activities should have method setTheme() called before onCreate(), probably in some parent activity. It is also a normal approach to store the theme chosen in SharedPreferences, read it and then set using setTheme() method.
i got the same problem but i found the solution.
public class EditTextSmartPhoneActivity extends Activity implements DialogInterface.OnClickListener
{
public final static int CREATE_DIALOG = -1;
public final static int THEME_HOLO_LIGHT = 0;
public final static int THEME_BLACK = 1;
int position;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
position = getIntent().getIntExtra("position", -1);
switch(position)
{
case CREATE_DIALOG:
createDialog();
break;
case THEME_HOLO_LIGHT:
setTheme(android.R.style.Theme_Holo_Light);
break;
case THEME_BLACK:
setTheme(android.R.style.Theme_Black);
break;
default:
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private void createDialog()
{
/** Options for user to select*/
String choose[] = {"Theme_Holo_Light","Theme_Black"};
AlertDialog.Builder b = new AlertDialog.Builder(this);
/** Setting a title for the window */
b.setTitle("Choose your Application Theme");
/** Setting items to the alert dialog */
b.setSingleChoiceItems(choose, 0, null);
/** Setting a positive button and its listener */
b.setPositiveButton("OK",this);
/** Setting a positive button and its listener */
b.setNegativeButton("Cancel", null);
/** Creating the alert dialog window using the builder class */
AlertDialog d = b.create();
/** show dialog*/
d.show();
}
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
AlertDialog alert = (AlertDialog)dialog;
int position = alert.getListView().getCheckedItemPosition();
finish();
Intent intent = new Intent(this, EditTextSmartPhoneActivity.class);
intent.putExtra("position", position);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
We have to set theme before calling 'super.onCreate()' and 'setContentView()' method.
Check out this link for applying new theme to whole application at runtime.
I had a similar problem and I solved in this way..
#Override
public void onCreate(Bundle savedInstanceState) {
if (getIntent().hasExtra("bundle") && savedInstanceState==null){
savedInstanceState = getIntent().getExtras().getBundle("bundle");
}
//add code for theme
switch(theme)
{
case LIGHT:
setTheme(R.style.LightTheme);
break;
case BLACK:
setTheme(R.style.BlackTheme);
break;
default:
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//code
}
this code is for recreate the Activity saving Bundle and changing the theme. You have to write your own onSaveInstanceState(Bundle outState); From API-11 you can use the method recreate() instead
Bundle temp_bundle = new Bundle();
onSaveInstanceState(temp_bundle);
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("bundle", temp_bundle);
startActivity(intent);
finish();
Instead of
getApplication().setTheme(R.style.BlackTheme);
use
setTheme(R.style.BlackTheme);
My code: in onCreate() method:
super.onCreate(savedInstanceState);
if(someExpression) {
setTheme(R.style.OneTheme);
} else {
setTheme(R.style.AnotherTheme);
}
setContentView(R.layout.activity_some_layout);
Somewhere (for example, on a button click):
YourActivity.this.recreate();
You have to recreate activity, otherwise - change won't happen
This is what i have created for Material Design. May it will helpful you.
Have a look for MultipleThemeMaterialDesign
I know that i am late but i would like to post a solution here:
Check the full source code here.
This is the code i used when changing theme using preferences..
SharedPreferences pref = PreferenceManager
.getDefaultSharedPreferences(this);
String themeName = pref.getString("prefSyncFrequency3", "Theme1");
if (themeName.equals("Africa")) {
setTheme(R.style.AppTheme);
} else if (themeName.equals("Colorful Beach")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.beach);
} else if (themeName.equals("Abstract")) {
//Toast.makeText(this, "set theme", Toast.LENGTH_SHORT).show();
setTheme(R.style.abstract2);
} else if (themeName.equals("Default")) {
setTheme(R.style.defaulttheme);
}
This way work for me:
#Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(GApplication.getInstance().getTheme());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Then you want to change a new theme:
GApplication.getInstance().setTheme(R.style.LightTheme);
recreate();
You can finish the Acivity and recreate it afterwards in this way your activity will be created again and all the views will be created with the new theme.
Call SetContentView(Resource.Layout.Main) after setTheme().
This had no effect for me:
public void changeTheme(int newTheme) {
setTheme(newTheme);
recreate();
}
But this worked:
int theme = R.style.default;
protected void onCreate(Bundle savedInstanceState) {
setTheme(this.theme);
super.onCreate(savedInstanceState);
}
public void changeTheme(int newTheme) {
this.theme = newTheme;
recreate();
}

Categories

Resources