I am creating a custom launcher in android application and , I want to track actual time which application is opened.
suppose i open Facebook for 10 minute and I went ideal for 5 minute how to calculate 5 minute which is used by user.
Thanks
You can define two buttons to start and stop your service. Fire an intent on Start button something like this:
Intent startServiceIntent = new Intent();
startServiceIntent.setClass(this, MainService.class);
startServiceIntent.setComponent(new ComponentName(this, MainService.class));
bindService(startServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
In the service, you can define broadcast receivers to detect screen on-off events.
private BroadcastReceiver screenWakeUp = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if (intent.getAction().equals("android.intent.action.SCREEN_ON")) {
isRunningForegroundAppsThread = true;
isScreenOn = true;
startThread();
}
}
};
From here, you can start a thread which constantly runs inside a loop and checks whether the application being used by the user has changed meanwhile. If it has, you may update time for that app in the map else you can just continue. At the SCREEN_OFF event, you may stop the thread as user is no more using the phone.
You can retrieve the application name using this:
ActivityManager am = (ActivityManager) mContext.getSystemService(ACTIVITY_SERVICE);
PackageManager pm = mContext.getPackageManager();
ApplicationInfo af = null;
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
// String activity = taskInfo.get(0).topActivity.getClassName();
ComponentName c = taskInfo.get(0).topActivity;
String packageName = c.getPackageName();
try {
af = pm.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String app_Name_current = (String) pm.getApplicationLabel(af);
It solely depends on your requirement, and if needed the time can be stored in a DB. Also, this records just the time for apps which require screen to be ON, so you'll have to separately maintain time for background apps and store that in a separate map maybe. At the end, when you press the stop button, you can display the results in a listview or whatever UI element you consider suitable for this.
I know this is not complete code, but I guess it gives you an idea of how to go about it. Also, I'd say that though this might solve the problem, but it's not a good way of achieving the same, as PackageManager objects are heavyweight and dealing with them continuously is expensive operation.
The above approach only works on pre-L devices as ActivityManager has been deprecated in API level 21. So you have to use UsageStats class for Lollypop devices.
Related
i have an app with service that works completely background, but sometimes i need to change the content in the foreground activity , for now let's say i want to show Toast.
first i am checking if the app is opened as foreground
public boolean isForeground(String myPackage) {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> runningTaskInfo = manager.getRunningTasks(1);
ComponentName componentInfo = runningTaskInfo.get(0).topActivity;
return componentInfo.getPackageName().equals(myPackage);
}
then i want to know what is the running activity
String PKG_Name = componentInfo.getPackageName();
this will give me the name , but what i want is to is get the object Activity ,to do something like this:
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
how can i do it? if not possible, is there's a way to is to send a signal or a message to the service to tell the it?
For that you have two options, if you are fine showing just a Toast and not interact with other elements, you can implement a broadcast reciever and make your custom intent with custom action,
Intent int= new Intent(); int.setAction("Your custom string here");
and then post a toast in your braodcast reciever,
OR
you can use a custom handler to interact with all the objects in foreground and activity, here's some study material for it,
https://developer.android.com/training/multiple-threads/communicate-ui
i set a service that checks if a specific process is running in time intervals by using:
appsList = am.getRunningAppProcesses();
i saved its name and id with:
s = pross.processName;
i=pross.pid;
i launch the default launcher with:
Intent intent = null;
final PackageManager pManager = context.getPackageManager();
for (final ResolveInfo resolveInfo:pManager.queryIntentActivities(new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),pManager.MATCH_DEFAULT_ONLY ))
{
if(!context.getPackageName().equals(resolveInfo.activityInfo.packageName))
{
intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
break;
}
}
context.startActivity(intent);
than kill the process with :
mActivityManager.killBackgroundProcesses(s);
it all works fine but the problem is that its doing the whole calling the launcher and closing the process twice.
it's like the process is still running the second time the service checks if its running.
any idea how to solve this?
I think we can not kill any other process. System not allow to do that without root access.
Is there a way for an Activity to find out who (i.e. class name) has sent an Intent? I'm looking for a generic way for my Activity to respond to a received intent by sending one back to the sender, whoever that may be.
There may be another way, but the only solution I know of is having Activity A invoke Activity B via startActivityForResult(). Then Activity B can use getCallingActivity() to retrieve Activity A's identity.
Is it an external app you receive the intent from? You could use the getReferrer() method of the activity class
A simple example: I opened google map app to share some location with my app by using the share option of google maps. Then my app opens and this method call in the Activity:
this.getReferrer().getHost()
will return:
com.google.android.apps.maps
see documentation here: https://developer.android.com/reference/android/app/Activity.html#getReferrer()
Note that this requires API 22. For older Android versions see answer from ajwillliams
A technique I use is to require the application sending the relevant Intent to add a PendingIntent as a Parcelable extra; the PendingIntent can be of any type (service, broadcast, etc.). The only thing my service does is call PendingIntent.getCreatorUid() and getCreatorPackage(); this information is populated when the PendingIntent is created and cannot be forged by the app so I can get the info about an Intent's sender.
Only caveat is that solution only works from Jellybean and later which is my case.
Hope this helps,
This isn't incredibly direct but you can get a list of the recent tasks from ActivityManager. So the caller would essentially be the task before yours and you can fetch info on that task.
Example usage:
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);
The above will return a list of all the tasks from most recent (yours) to the limit specified. See docs here for the type of info you can get from a RecentTaskInfo object.
Generally you don't need to know this. If the calling activity uses startActivityForResult(Intent, int), the callee can use setResult(int, Intent) to specify an Intent to send back to the caller. The caller will receive this Intent in its onActivityResult(int, int, Intent) method.
Based on your question, since you want to send an intent back to the sender startActivityForResult is a better choice than what I am going to suggest. But I needed to start activity B when a notification is clicked by the user and execute some code in activity B only if the sender activity is activity A. This is how I did it quite simply.
Inside Activity A:
String senderName = this.getClass().getSimpleName();
Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
clickIntent.putExtra("SENDER_CLASS_NAME", senderName);
//I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);
Inside Activity B:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if(bundle.containsKey("SENDER_CLASS_NAME")){
String senderName = bundle.getString("SENDER_CLASS_NAME");
//Execute some code
Log.d("GCM", "Notifications clicked");
}
}
}
}
In my case, neither the accepted here and another most voted answer works perfectly.
Activity.getCallerActivity() works only for the sender which starts your activity by startActivityForResult, meaning that if the sender is also in your app and you have full control, it works, but not every external app starts others in that way.
Another most voted answer provides the solution for external app, but it too has issue. First I would prefer getAuthority() instead of getHost(), secondly, if the sender is a browser kind of app, like Chrome, both host and authority will give you the browsing web page's address host, such as www.google.com, instead of the app itself. So it depends on how you define 'sender', if you need to find out which web page starts you, the authority/host is good enough, but if you need to find out which app starts you, I am afraid authority/host can be trusted only when getScheme() gives you android-app instead of http.
Use UsageStatsManager and the old RecentTaskInfo to get the intent sender for OnCreate or onNewIntent:
public static String getTopMostThirdPartyPackage(Context context) {
String thisPak = null, tmp, top = null;
try {
thisPak = context.getPackageName();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long now = System.currentTimeMillis();
UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
UsageEvents.Event e = new UsageEvents.Event();
while (uEvts.getNextEvent(e)){
tmp = e.getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
} else {
ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
for(ActivityManager.RecentTaskInfo info:tasks) {
tmp = info.baseIntent.getComponent().getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
}
} catch (Exception e) {
}
return top;
}
permissions :
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.GET_TASKS" />
intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);
There are a class of Android applications that enable password protection on certain user-specified apps; for example, Android Protector. I need to approach this problem from a different direction.
Is it possible to create an application that blocks all activity launches unless they are on a predefined whitelist? Will there be unintended consequences with this approach? I am familiar with Android basics and have written a few reasonably simple apps, but I'm still trying to figure out how these "Protector" apps intercept the launch intents correctly. Would someone mind giving me a brief overview on the correct way to do this?
The basic problem is that we have a generic Android phone that needs to be locked down so that our clients (internal only) can access our custom applications without being able to play "Need for Speed", etc. I would like to remove the carrier bloatware, but rooting the device seems like it would be a maintenance headache. We want the setup for each phone to be as simple as installing a few custom applications.
Edited to elaborate on the solution
Overview
My simple solution was to add a new service and activity to my application. The service uses Handler and postDelayed to continuously schedule the monitoring task. The monitoring task checks that the current activity is on the whitelist. Getting the currently running activity involves ActivityManager and a call to getRunningTasks. After finishing the check, the monitoring task schedules itself to run again after X seconds (1, in my case).
If the activity on top is not on the whitelist, we launch the blocking activity which pops up over whatever is currently running. The key part of the blocking activity is that it overrides onBackPressed, preventing the user from simply going back to the "bad" activity. Pressing the Home key is the only way (to my knowledge) to leave this screen.
Tips
Build a backdoor into the lock screen. For example, my solution prompts for a password on a long-press of the back key. After entering the correct password, the monitor service goes to sleep for 5 minutes so I can do my administrative work
Display the name of the blocked activity
Gather a good whitelist before turning this on! Activities you should definitely whitelist: the launcher, package installer, your own app (obviously), the browser, if your app has a web-based component
I don't like that my service is constantly looping in the background; it seems wasteful. I'd like to find some way to be notified when a new task is being launched, but I couldn't find a way to do that. The battery usage for my particular value of the monitor period and my particular phone is acceptable; though you should definitely test before adopting this yourself.
an efective solution,and here is the code from author's opinion
public class MonitorService extends Service {
private Handler handler;
Runnable runnable;
#Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
new Thread(new Runnable() {
#Override
public void run() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am
.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
String currentActivityName=componentInfo.getClassName();
String packageName=componentInfo.getPackageName();
if(whitelist.contains(currentActivityName)){
Intent launchIntent = new Intent();
launchIntent.setComponent(new ComponentName(blockActivityPackageName,
blockActivityName));
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launchIntent);
}
}
}).start();
handler.postDelayed(this, 1000);
}
};
handler.postDelayed(runnable, 1000);
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Intent intent = new Intent(this, MonitorService.class);
startService(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
As you suggest, writing a custom launcher is probably would be cleaner; check out this open source launcher for reference http://code.google.com/p/adw-launcher-android/
Is there a way for an Activity to find out who (i.e. class name) has sent an Intent? I'm looking for a generic way for my Activity to respond to a received intent by sending one back to the sender, whoever that may be.
There may be another way, but the only solution I know of is having Activity A invoke Activity B via startActivityForResult(). Then Activity B can use getCallingActivity() to retrieve Activity A's identity.
Is it an external app you receive the intent from? You could use the getReferrer() method of the activity class
A simple example: I opened google map app to share some location with my app by using the share option of google maps. Then my app opens and this method call in the Activity:
this.getReferrer().getHost()
will return:
com.google.android.apps.maps
see documentation here: https://developer.android.com/reference/android/app/Activity.html#getReferrer()
Note that this requires API 22. For older Android versions see answer from ajwillliams
A technique I use is to require the application sending the relevant Intent to add a PendingIntent as a Parcelable extra; the PendingIntent can be of any type (service, broadcast, etc.). The only thing my service does is call PendingIntent.getCreatorUid() and getCreatorPackage(); this information is populated when the PendingIntent is created and cannot be forged by the app so I can get the info about an Intent's sender.
Only caveat is that solution only works from Jellybean and later which is my case.
Hope this helps,
This isn't incredibly direct but you can get a list of the recent tasks from ActivityManager. So the caller would essentially be the task before yours and you can fetch info on that task.
Example usage:
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(10000,ActivityManager.RECENT_WITH_EXCLUDED);
The above will return a list of all the tasks from most recent (yours) to the limit specified. See docs here for the type of info you can get from a RecentTaskInfo object.
Generally you don't need to know this. If the calling activity uses startActivityForResult(Intent, int), the callee can use setResult(int, Intent) to specify an Intent to send back to the caller. The caller will receive this Intent in its onActivityResult(int, int, Intent) method.
Based on your question, since you want to send an intent back to the sender startActivityForResult is a better choice than what I am going to suggest. But I needed to start activity B when a notification is clicked by the user and execute some code in activity B only if the sender activity is activity A. This is how I did it quite simply.
Inside Activity A:
String senderName = this.getClass().getSimpleName();
Intent clickIntent = new Intent(ActivityA.this, ActivityB.class);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
clickIntent.putExtra("SENDER_CLASS_NAME", senderName);
//I use PendingIntent to start Activity B but you can use what you like such as this.startActivity(clickIntent);
PendingIntent.getActivity(ActivityA.this, NOTIFICATION_ID, clickIntent, PendingIntent.FLAG_ONE_SHOT);
Inside Activity B:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
if(bundle.containsKey("SENDER_CLASS_NAME")){
String senderName = bundle.getString("SENDER_CLASS_NAME");
//Execute some code
Log.d("GCM", "Notifications clicked");
}
}
}
}
In my case, neither the accepted here and another most voted answer works perfectly.
Activity.getCallerActivity() works only for the sender which starts your activity by startActivityForResult, meaning that if the sender is also in your app and you have full control, it works, but not every external app starts others in that way.
Another most voted answer provides the solution for external app, but it too has issue. First I would prefer getAuthority() instead of getHost(), secondly, if the sender is a browser kind of app, like Chrome, both host and authority will give you the browsing web page's address host, such as www.google.com, instead of the app itself. So it depends on how you define 'sender', if you need to find out which web page starts you, the authority/host is good enough, but if you need to find out which app starts you, I am afraid authority/host can be trusted only when getScheme() gives you android-app instead of http.
Use UsageStatsManager and the old RecentTaskInfo to get the intent sender for OnCreate or onNewIntent:
public static String getTopMostThirdPartyPackage(Context context) {
String thisPak = null, tmp, top = null;
try {
thisPak = context.getPackageName();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
UsageStatsManager man = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
long now = System.currentTimeMillis();
UsageEvents uEvts = man.queryEvents(now - 5000,now); // query in 5 sec
UsageEvents.Event e = new UsageEvents.Event();
while (uEvts.getNextEvent(e)){
tmp = e.getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
} else {
ActivityManager man = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RecentTaskInfo> tasks = man.getRecentTasks(3, 0);
for(ActivityManager.RecentTaskInfo info:tasks) {
tmp = info.baseIntent.getComponent().getPackageName();
if (!thisPak.equals(tmp)) {
top = tmp;
break;
}
}
}
} catch (Exception e) {
}
return top;
}
permissions :
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.GET_TASKS" />
intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
startActivity(intent);