I was wondering how to keep a record of launched activites for logging purposes. what broadcast receiver I have to subscribe to intercept this intent? or what intent-filter to use? I figure that I must use some type of long-running service in the background.
My first objetive is to track main-focus applications, some sort of history.
Want to get finally some similar to:
- Launched app com.android.xxx
- Launched app xx.yy.zz
- App xx.yy.zz lost focus
Thanks in advance
EDIT - Just see that app MyAppRank , that does exactly what i mean
What i'm able to figure out from your question is that you want to keep track of all the activities when they are launched in your application. If that is correct, the solution may work for you:
Crate a BaseActivity which all of your Activities should extend
public class BaseActivity extends Activity
{
private Activity activity;
public static final String INTENTFILTER_TRACK_MY_ACTIVITIES="INTENTFILTER_TRACK_MY_ACTIVITIES";
public static final String INTENTFILTER_REMOVE_MY_ACTIVITIES="INTENTFILTER_REMOVE_MY_ACTIVITIES";
public void setActivity(Activity act)
{
activity = act;
}
public Activity getActivity()
{
return activity;
}
#Override protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setAction(INTENTFILTER_TRACK_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
}
#Override protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
Intent intent = new Intent();
intent.setAction(INTENTFILTER_REMOVE_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
setActivity(null);
}
}
Now extend above BaseActivity for all your activities. i.e instead of extending your Activities should extend BaseActivity and call setActivity(this); in onCreate like below:
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setActivity(this);
//write your other code form here
}
}
3.Then write a BroadcastReceiver like below:
class TrackActivitiesReceiver extends BroadcastReceiver
{
private static final Object SEPERATOR = ",";// use , as seperator
String sb="";
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_TRACK_MY_ACTIVITIES))
{
sb+=intent.getStringExtra("activityName");
sb+=SEPERATOR;
}
else if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_REMOVE_MY_ACTIVITIES))
{
sb=sb.replace(intent.getStringExtra("activityName")+SEPERATOR, "");
}
}}
4Finally, Register above Receiver in your AndroidManifest.xml
<receiver
android:name="TrackActivitiesReceiver"
android:exported="false" >
<intent-filter>
<action android:name="INTENTFILTER_TRACK_MY_ACTIVITIES" />
</intent-filter>
</receiver>
Hope this solves your problem. cheers!
There are no Intents broadcast when applications are started or when applications come to the foreground. There isn't anything that you can hook into as a listener to get these events.
The way you can do this (which is the way apps like MyAppRank do it) is to use the methods of the ActivityManager:
getRunningTasks()
getRunningAppProcesses()
getRecentTasks()
You create a Service which runs all the time and at regular intervals calls methods of the ActvityManager to determine which task is in the foreground and you can "infer" what the user has done (or is doing). It isn't an exact science.
Note: You will need android.permission.GET_TASKS and none of this works anymore as of API 21 (Android 5, Lollipop). As of API 21 the security has been tightened and an application can only get information about its own tasks, not other tasks in the system.
Related
Problem: I need to run some code at every start before my app is ready to be used.
At first, I tried doing it in a dedicated activity.
AndroidManifest.xml
<activity android:name=".MainActivity" />
<activity android:name=".StarterActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
AppLoader.java
public class AppLoader {
private static Object someInstance;
public static void load(Runnable onCompleteCallback) {
try {
someInstance = new Object();
//potentially long operation to initialize the app
Thread.sleep(5000);
onCompleteCallback.run();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void checkInitialized() {
if (someInstance == null) {
throw new RuntimeException("Not initialized");
}
}
}
StarterActivity.java
public class StarterActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppLoader.load(() -> {
MainActivity.start(this);
finish();
});
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static void start(Context context) {
Intent starter = new Intent(context, MainActivity.class);
context.startActivity(starter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppLoader.checkInitialized();
}
}
This works fine if the app is cold started via the launcher icon but crashes in all other cases. Simple way to reproduce the issue:
Go to developer settings on your device and set "Background process limit" to "No background process"
Open the app
Open some other app
Open the app again. Result: it crashes.
Here's an article describing a similar problem: Android process death — and the (big) implications for your app
Possible solutions:
Lazy loading/reactive approach. I try to use it as much as possible but there is always some code I need to run in a blocking way before user can interact with the app so this is not enough.
Putting all of that code in App.onCreate(). This would probably work for small apps but I've seen large apps that take 5-10 seconds to initialize, and I doubt they use onCreate() for that. Possible downsides: ANR and/or excessive startup time in Android Vitals?
Checking if the app is initialized in a BaseActivity, but that would require either blocking onCreate or managing lifecycle callbacks manually which doesn't sound like a good idea.
So, what's the proper way to run some code every time the app is launched?
Note: Normally StarterActivity would be a splash screen, AppLoader would be injected, etc, but I left that out for simplicity.
AndroidManifest.xml
<application
android:name=".AppLoader"
AppLoader.java
public class AppLoader extends Application {
private static Object someInstance;
#Override
public void onCreate() {
super.onCreate();
// DO YOUR STUFF
}
}
Update
- Use Handler with splash screen.
public class StarterActivity extends AppCompatActivity {
private Handler handler;
private Runnable myStuffRunnable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Handler();
myStuffRunnable = new Runnable(){
public void run(){
// DO MY STUFF
MainActivity.start(this);
}
};
}
#Override
protected void onPause() {
handler.removeCallbacks(myStuffRunnable);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
handler.post(myStuffRunnable);
}
#Override
protected void onDestroy() {
handler.removeCallbacks(myStuffRunnable);
super.onDestroy();
}
}
Your app is throwing the RuntimeException you set in AppLoader.checkInitialized() method, because your someInstance object is losing it's state when the app goes to background and gets killed by the system ('cause you have set your device to hold zero background threads). So, when you try to reopen the app, the system launches MainActivity directly (and not StarterActivity) because it is trying to restore it's previous state. But variables are not restored, not even static variables.
So, if you need the Object someInstance on your MainActivity, you should integrate it's instantiation into MainActivitie's lifecycle, overriding methods like onSavedInstanceState, onRestoreInstanceState, etc, to properly handle and reaload this object if your app gets killed by the system.
Take a look on this https://developer.android.com/guide/components/activities/activity-lifecycle
If anyone's interested, I ended up just redirecting the user to StarterActivity if needed to make sure the necessary code is executed at every start.
public abstract class BaseActivity extends AppCompatActivity {
private boolean isCreated;
#Override
protected final void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!appLoader.isLoaded()) {
StarterActivity.start(this);
finish();
return;
}
onCreateActivity(savedInstanceState);
isCreated = true;
}
protected void onCreateActivity(#Nullable Bundle savedInstanceState) {
}
#Override
protected final void onDestroy() {
super.onDestroy();
if (isCreated) {
onDestroyActivity();
}
}
protected void onDestroyActivity() {
}
}
All activities extend BaseActivity (except StarterActivity) and override onCreateActivity/onDestroyActivity instead of onCreate/onDestroy.
I am using AlarmManager to periodically check for new content at some endpoint, validate if the results coming from the endpoint are the same as the ones I already have on my app, and if its not the same create a notification for each item.
What i need to know is how should i make the alarms to start only when the application is paused or stopped and cancel the alarms when de application is started or resumed.
where should i start the alarms and where should i cancel them?
In Android Notifications Guideline it says (on chapter: When not to display a notification):
Don't create a notification if the relevant new information is currently on screen. Instead, use the UI of the application itself to notify the user of new information directly in context. For instance, a chat application should not create system notifications while the user is actively chatting with another user.
If I have the application open i just want to disable alarms, when the application is closed/paused i want to cancel everything.
You need to create a Custom Application with global state and implement your own onPause and onResume at Application Level.
Create your own subclass of Application like this:
public class MyApplication extends Application {
private static MyApplication sInstance;
public MyApplication getInstance(){
return sInstance;
}
#Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
public void onStart() {
// TODO: Stop your notification.
}
public void onStop() {
// TODO: Start your notification.
}
}
Specify its name in your AndroidManifest.xml's tag:
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:name="MyApplication">
Create a class to hold the counts of activities:
public class ActiveActivitiesTracker {
private static int sActiveActivities = 0;
public static void activityStarted()
{
if (sActiveActivities == 0) {
// TODO: Here is presumably "application level" resume
MyApplication.getInstance().onStart();
}
sActiveActivities++;
}
public static void activityStopped()
{
sActiveActivities--;
if (sActiveActivities == 0) {
// TODO: Here is presumably "application level" pause
MyApplication.getInstance().onStop();
}
}
}
Then create a base activity (or do that in every activity), simply call the activityStarted() and activityStopped() methods:
#Override
public void onStart() {
super.onStart();
ActiveActivitiesTracker.activityStarted();
}
#Override
public void onStop() {
super.onStop();
ActiveActivitiesTracker.activityStopped();
}
For more details about Custom Application, see this.
For more details about Android Application Level Pause and Resume, see this.
Hope this helps.
you can try using a service and override in it , the onTrimMemory method and show the notificaton when "level" is equal to TRIM_MEMORY_UI_HIDDEN
#Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
break;
}
}
check the documentation for more info
http://developer.android.com/reference/android/content/ComponentCallbacks2.html#TRIM_MEMORY_UI_HIDDEN
I am not sure that this will be feasible within your project or if it will achieve what you hope to, however you could extend all of your activities from one base activity. In that base activity's onPause/onStop/onDestroy methods start the alarms and in that base activities onCreate/onStart methods cancel the alarms with the pending intent.
This will provide you with a set location from which you can handle your alarms if you have multiple activities from which the app may close.
You can learn more about the life cycle of activities here.
I want to clear the HistoryStack once I have started my MainAcitivty that is inside its oncreate() method of Main.
Due to some issues I cant use android:noHistory="true" because it creates problem for my gPlus signing in, also cant use finish() FLAG_ACTIVITY_NO_HISTORY for similar reasons.
I only want it to be removes in particular case that is when inside Main, otherwise it should be there on History stack.
Once in Main all history stack should be clear and over pressing back the app should exit. Is it possible, if yes how, please explain.
For no recents activities use android:excludeFromRecents="true" in desire activity.
You could add a BroadcastReceiver in all activities you want to finish.
public class MyActivity extends Activity {
private FinishReceiver finishReceiver;
private static final String ACTION_FINISH =
"com.mypackage.MyActivity.ACTION_FINISH";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
finishReceiver = new FinishReceiver();
registerReceiver(finishReceiver, new IntentFilter(ACTION_FINISH));
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(finishReceiver);
}
private final class FinishReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_FINISH))
finish();
}
}
}
You can close those Activitys by calling
sendBroadcast(new Intent(ACTION_FINISH));
Check this example for details:
http://www.hrupin.com/2011/10/how-to-finish-all-activities-in-your-android-application-through-simple-call
I have tried almost all the solutions from SO but no success :(.
I have a simple myJavaClass.java with a couple of functions.
One of the functions in myJavaClass : startActivity() starts MyCustomActivity
public startActivity(Context context)
{
Intent intent = new Intent(context, MyCustomActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
This launches MyCustomActivity() as expected.
Now I have another function in myJavaClass.java to close/finish MyCustomActivity but it is not able to do so!
I have tried
Making MyCustomActivity SingleTop in manifest and creating the activity via an intent as above
Passing an activity instance to "this" in onCreate() of MyCustomActivity and calling MyCustomActivity.activity.finish() from myJava.class but that doesnt work as well
Please help me. I have been stuck here for hours now. I know the solution is very simple and conceptual but I am a newbie. Just building Java/Android concepts!
EDIT
MyCustomActivity
public Activity activity;
OnCreate()
{
...
this = activity;
}
MyJavaClass
public closeActivity(Context context)
{
Activity customActivity = MyCustomActivity.activity;
customActivity.finish();
}
I think that what you are trying to do is fundamentally bad. For a start, outside of the Activity code, there are no guarantees that the activity still exists - the memory manager may have cleaned it up, the user may have pressed Back etc. Think of Activities as independent entities - you can start them, and you can optionally get a result back when they finish what they're doing, but that's it.
Think about whether you really have to programmatically close the activity from outside it - I'd say this is an unusual design, but there are circumstances where it may be appropriate.
If so, what I think you want is a publish/subscribe system whereby MyCustomActivity can register a listener with MyJavaClass, and then receive a callback whereupon it can 'finish' itself.
public Activity activity implements FinishListener
{
public void onCreate(...)
{
//where does MyJavaClass come from? see in a minute
MyJavaClass myjava = getMyJavaclass();
myJava.addFinishListener( this );
}
public void onFinishCallback()
{
this.finish();
}
}
and
public class MyJavaClass
{
private List<FinishListener> finishListeners = ...;
public void addFinishListener( FinishListener fl )
{
this.finishListeners.add(fl);
}
public closeActivity(Context context)
{
for ( FinishListener fl : finishListeners )
{
fl.onFinishCallback();
}
}
}
and
public interface FinishListener
{
void onFinishCallback();
}
Now the only remaining issue is how to get MyJavaClass from the Activity. That's up to you - you may already know how, you may be able to put it in your Application implementation, it could be a singleton (bad), the listeners could be static (bad) or various other options.
Oh, and don't forget to remove the listener again in the Activity's onDestroy() method!
Just try this....
public closeActivity(Activity _activity)
{
_activity.finish();
}
you can't finish activity from other class until you have the reference of instance of Activity in that class, give the reference in that class and call finish() method to stop the activity.
activity.finish();
I want to have an activity that can only be launched from certain other activites in my app, and not from others
At the moment, this works:
public abstract class Launcher extends Activity {
protected static boolean flag = true;
protected abstract void Launch();
public static class myPrivateActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(flag)
finish();
setContentView(R.layout.main);
}
}
}
public class SpecialActivity extends Launcher {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.newlayout);
}
#Override
protected void Launch {
flag = false;
Intent i = new Intent(Intent.ACTION_VIEW);
String name = myPrivateActivity.class.getName();
i.setClassName(this, name );
startActivity(i);
//finish();
}
}
and in android manifest
<activity android:name=".Launcher$myPrivateActivity"
android:label="Private?">
However, I'd like to know if there is a better solution than just using a flag?
That's a creative approach. I don't think I've ever seen something like that before.
However, I don't think it accomplishes what you're trying to accomplish. If an approach like this were taken, it would probably make sense to "harden" it further by making the abstract base class and its inner static class package private, and any concrete public subclasses final. With these changes, then I don't think the flag would any longer provide any sort of protection.
However (again), do be aware that Android's permissions model provides for custom permissions, and may be used to achieve what I think you want to do. In fact, the document at http://developer.android.com/guide/topics/security/security.html describes how to "enforce your own permissions" to configure "an application that wants to control who can start one of its activities" in the "Declaring and Enforcing Permissions" section.
Does that get you closer to your goal?
I think you can use this:
android:permission
The name of a permission that clients must have to launch the activity or otherwise get it to respond to an intent. If a caller of startActivity() or startActivityForResult() has not been granted the specified permission, its intent will not be delivered to the activity.
If this attribute is not set, the permission set by the element's permission attribute applies to the activity. If neither attribute is set, the activity is not protected by a permission.
http://developer.android.com/guide/topics/manifest/activity-element.html#prmsn