A deep dive to Get the Top Activity Name of Currently Running Application in Android

Amir Ghasemi
4 min readJul 27, 2023

--

In Android later versions, obtaining the name of the currently running activity or package name of the foreground application can be a bit tricky due to the increased emphasis on user privacy and security. In this article, we will explore three different methods to achieve this task.

We will go through each approach step-by-step and provide code examples to help you implement them in your Android application.

Method 1: Via UsageStatsManager

The first method involves using the UsageStatsManager to track the user’s activities. However, this approach requires additional permission, which the user needs to enable manually. We will guide you through adding the necessary permission to your app and implementing the code to get the current activity name.

// Add the required permission to your AndroidManifest.xml
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
// Redirect the user to enable this permission with the following code:
val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
startActivity(intent)
// Get the current activity name using UsageStatsManager
var lastActivityName: String? = null
private fun getCurrentActivityName(): String? {
val usageStatsManager = applicationContext.getSystemService(USAGE_STATS_SERVICE) as UsageStatsManager
try {
val currentTimeMillis = System.currentTimeMillis()
var activityName: String? = null
var packageName: String? = null
val queryEvents = usageStatsManager.queryEvents(
currentTimeMillis - 60000.toLong(),
currentTimeMillis
)
while (queryEvents.hasNextEvent()) {
val event = UsageEvents.Event()
queryEvents.getNextEvent(event)
if (event.eventType == 1) {
packageName = event.packageName
activityName = event.className
} else if (event.eventType == 2) {
if (event.packageName != packageName) {
break
}
packageName = null
}
}
return if (packageName != null && activityName != null) {
lastActivityName = activityName
activityName
} else lastActivityName
} catch (e: Exception) {
e.printStackTrace()
}
return null
}

I added lastActivityName because you may sometimes get null in the same activity, so you can remove it.

Method 2: Via Accessibility (With Special Permission)

The second method utilizes an Accessibility Service to retrieve the current activity name. However, starting from Android 11, this approach requires the special “QUERY_ALL_PACKAGES” permission, which may have implications for app distribution on Google Play. We will show you how to set up the Accessibility Service and request the required permission, along with the code to access the current activity name.

<!-- Add the required permission to your AndroidManifest.xml -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>

<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>
<!-- Define your accessibility service in res/xml/accessibility_setting.xml -->
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault|flagReportViewIds|flagIncludeNotImportantViews"
android:accessibilityEventTypes="typeWindowStateChanged"
android:canRequestFilterKeyEvents="true"
android:canRetrieveWindowContent="true" />
<!-- Add your Accessibility service to the AndroidManifest.xml -->
<service
android:name=".services.MyAccessibilityService"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">

<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>

<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_setting" />
</service>
// Define your MyAccessibilityService class and the function to retrieve the current activity name
class MyAccessibilityService : AccessibilityService() {

var currentActivityName : String? = null

override fun onServiceConnected() {
//Define your initial configs here
}

@Synchronized
override fun onAccessibilityEvent(event: AccessibilityEvent) {
checkForCurrentActivity(event)
}

override fun onInterrupt() {
// define your interruption code here
}
}

Then the checkForCurrentActivity(event) would be:

private fun checkForCurrentActivity(event: AccessibilityEvent) {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (event.packageName != null && event.className != null) {
val componentName = ComponentName(
event.packageName.toString(),
event.className.toString()
)
if (isActivity(componentName)) {
val componentFlattenName = componentName.flattenToShortString()
currentActivityName = componentFlattenName.replace("/.", ".")
if (currentActivityName.contains("/")) {
currentActivityName = currentActivityName.split("/".toRegex())[1]
}
}
}
}
}

private fun isActivity(componentName: ComponentName): Boolean {
return try {
packageManager.getActivityInfo(componentName, 0)
} catch (e: PackageManager.NameNotFoundException) {
null
} != null
}

And finally, you can easily obtain the current activity name by using currentActivityName

Method 3: Via Accessibility (No Extra Permissions)

In the end, we will show you a hack to get the current activity name just with the accessibility permission, without using “QUERY_ALL_PACKAGES”. This solution is based on the second method but with a small difference.

All you need is just to modify the isActivity(componentName).

Follow the code below:

// Modify the isActivity method 
private fun isActivity(componentName: ComponentName):Boolean {
val name = componentName.flattenToShortString()
return name.contains("/") &&
!name.contains("com.android.systemui") &&
!name.contains("Layout") &&
!name.contains(".widget") &&
!name.contains("android.view") &&
!name.contains("android.material") &&
!name.contains("android.inputmethodservice") &&
!name.contains("$") &&
!name.contains("android.view") &&
!name.contains("android.app.dialog")
}

Note: This hack works with almost 99% accuracy, covering the majority of use cases. However, in rare instances, it might not correctly identify the current activity due to variations in app behavior or system configurations. If you encounter any specific cases where this hack fails, feel free to provide feedback in the comments so that we can continuously improve this solution.

Conclusion

Obtaining the name of the currently running activity or package name in Android later versions can be accomplished using different methods, each with its pros and cons. By following the step-by-step instructions and code examples provided in this article, you can choose the approach that best suits your app’s requirements and ensures compliance with the latest privacy and security guidelines.

In this article, we explored three methods to obtain the current activity name:

  1. Via UsageStatsManager: This method involves using the UsageStatsManager to track user activities. However, it requires additional permission that users need to enable manually.
  2. Via Accessibility (With Special Permission): This method utilizes an Accessibility Service, which requires the special “QUERY_ALL_PACKAGES” permission on Android 11 and later versions.
  3. Via Accessibility (No Extra Permissions): The clever hack allows you to get the current activity name using only the accessibility permission without the need for “QUERY_ALL_PACKAGES.” This approach has shown an impressive accuracy rate of nearly 99%, covering the majority of use cases. However, there might be rare instances where it may not function as expected due to variations in app behavior or system configurations. If you encounter such cases, we welcome your feedback in the comments section, enabling us to continuously improve and refine this solution.

Remember to inform your users about the permissions required and respect their privacy while implementing these solutions. Choose the method that best aligns with your app’s needs and consider the implications of permissions when distributing your app on Google Play. Happy coding!

--

--

Amir Ghasemi

Senior Mobile Engineer at MarleySpoon | 📍 Berlin, Germany