5

Accessing App Usage History In Android

 2 years ago
source link: https://proandroiddev.com/accessing-app-usage-history-in-android-79c3af861ccf
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

📌 Why not use ‘ActivityManager.getRunningTasks’?

As in most accepted answers on SO, you are most likely to use the ActivityManager.getRunningTasks method to determine which application is in the foreground state. For those who are unaware of the foreground and background states of an Android app, here’s a short description,

For our use-case, the foreground state would only mean that the app is not visible to the user ( maybe the home button is pressed; in such a case, the app moves to the Recents page ).

Getting back to the point, your very first observation in the officials docs of ActivityManager.getRunningTasks will tell that the method is deprecated from Android Lollipop ( API level 21 ). And the reason Android suggests is quite convincing,

As of Build.VERSION_CODES.LOLLIPOP, this method is no longer available to third party applications: the introduction of document-centric recents means it can leak person information to the caller.

You’ll also bump into this observation by looking at the high-voted comments below the answers on SO, which suggested the use of ActivityManager.getRunningTasks .

Accessing the recent activity of a users without consent, could force users to believe that your app is some sort of spyware installed on their device. Also, developers could study app-usage patterns robbed from your device to enhance their own apps, all from your private data. So, the decision of deprecating ActivityManager.getRunningTasks from Android was a obvious step for the security of user-data.

📌 Alternative: Use ‘ActivityManager.getRunningAppProcesses’

If you’re searching for a replacement of the deprecated ActivityManager.getRunningTasks you’ll probably find a solution using the ActivityManager.getRunningAppProcesses method. But, due to some reason, the method returns only the package name of the current process i.e. the app which called this method. No other apps, which were used by the user, are returned through the method.

It seems that Android has restricted this method as well, in order to provide enhanced privacy to the user. Moreover, we’re looking for a way which is more official and safe from future deprecation by Android.

📌 The Solution: Using `UsageStatsManager.queryEvents`

The UsageStatsManager class provides access to the device usage history and statistics. Considering our use-case, we’re going to use the specific UsageStatsManager.queryUsageStats method to check the user’s recent activity or the apps which were used recently, with the consent of the user.

Step 1: 🔐 Requesting the Permissions

The official documentation for the UsageStatsManager states,

Most methods on this API require the permission android.permission.PACKAGE_USAGE_STATS. However, declaring the permission implies intention to use the API and the user of the device still needs to grant permission through the Settings application.

So, its good to add this permission in our AndroidManifest.xml ,

Snippet 1: Adding the PACKAGE_USAGE_STATS permission to AndroidManifest.xml

Note, this is a special permission and not a runtime permission like CAMERA or WRITE_EXTERNAL_STORAGE which can be requested by the app. This is evident, as this is a sensitive permission, accessing user’s personal data, regarding which the user needs to be aware.

Step 2: 🔑 Checking the status of the permission and requesting it

In the app’s logic, we first need to determine whether this is permission is granted by the user and if not, we need to request it. Unfortunately, we can’t check the status of this permission using ContextCompat.checkSelfPermission as it is a special permission. Special permissions are rather handled by the AppOpsManager , which is the class used for managing all app-ops and access control.

Snippet 2: Checking the status of the PACKAGE_USAGE_STATS permission using AppOpsManager

In order to request the PACKAGE_USAGE_STATS permission, we can’t use the ActivityCompat.requestPermissions method or the ActivityResultContracts.RequestPermission() as this is not a runtime permission, as we discussed earlier. Instead, we need to navigate the user to the Settings page where the user enables this permission for the app,

Snippet 3: Requesting the PACKAGE_USAGE_STATS permission after checking the status the permission
1*0ZjVaoPDbEix0Ux3K42C3A.png?q=20
accessing-app-usage-history-in-android-79c3af861ccf
Usage Data Settings as observed in a Android 9 device ( Samsung J7 ).

Step 3: đŸ“±Retrieving Usage Events

Once the user has granted the permission, we can now access user’s device history and check which apps were used in the past with the user’s consent. We can now use the UsageStatsManager.queryEvents method to retrieve the usage events.

Snippet 4: Requesting the usage events using the `usageStatsManager.queryEvents` method.
  • As observed in the code, the queryEvents method takes in two arguments, beginTime and endTime . beginTime denotes the time ( in the past ) from which the events need to be retrieved, whereas endTime denotes the time upto we need the events. Note, both these arguments are Unix Time values, which is evident as we’re using System.currentTimeMillis() in the code snippet above.
  • The usageEvents.getNextEvent will return the events in a chronological order, so you need to sort them.

If you’re targeting devices from Android R, make sure you have a look at Step 5.

Check the output below, each line shows the package name and the timestamp at which the package was accessed by the user.

1*91LUc6Ji7y8d4AjJFk9ZKA.png?q=20
accessing-app-usage-history-in-android-79c3af861ccf
The output of code snippet 4. The last line in the output above shows the name of the calling package.

Step 4: Filtering user-installed apps ( Optional )

As you may observe, the above output also contains some system applications like com.sec.android.app.launcher which is the default launcher application on Samsung devices. This is because the user navigates to the home screen to open some other app or to change a device setting. You may wish to filter those system apps, such that only user-installed apps are visible in the output. We can create a method which returns a Map containing user-installed apps with their package names and labels ( the name of the app, visible to the user ). We can filter the user-installed apps and also get their label ( in case you wish to process it further in your app’s logic )

Snippet 5: Getting a `Map` containing package names and labels of user-installed apps

Step 5: Check if user is unlocked ( from Android R )

If you’ve checked the documentation for the UsageStatsManager.queryEvent method, you’ll discover,

Note: Starting from Android R, if the user's device is not in an unlocked state (as defined by UserManager#isUserUnlocked()), then null will be returned.

So, from Android R we can’t run this method when the device is locked. So, we need to check if the user is unlocked,

Snippet 6: Checking if the user is unlocked with UserManager

Also, note that the UserManager.isUserUnlocked method is available for API 24 and above, and hence we’ll secured it in a if statement.

This marks the end of our implementation. Connect to the a physical device and the app running!

📍 We’re done!

Hope you loved the blog! For any suggestions & queries, feel free to leave a comment. Keep reading, keep learning and have a nice day ahead!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK