
In this article, you will learn how to get the app install and uninstall events in Android.
To get the install and uninstall events, we can use intents like ACTION_PACKAGE_ADDED with the broadcast receivers. But, starting from Oreo (Android 8, API level 26), Google imposed restrictions on the receivers. They introduced getChangedPackages() API in Android 8. So, we should use it for the devices running Android 8 and above, and broadcast receivers for the earlier versions.
App Install and Uninstall Events in Android 8 and Later:
Let’s look at the getChangedPackages() from the PackageManager class:
public abstract ChangedPackages getChangedPackages (int sequenceNumber)
It takes an integer called sequenceNumber and returns the ChangedPackages object. If no packages (apps) have been modified, it returns null.
The ChangedPackages object contains the packages that have been changed (added, removed, or updated) since the given sequenceNumber.
Android sequenceNumber:
The sequence number starts at 0. Every time the user installs a new app, or updates/deletes an existing app, the number will be incremented. It is also reset every boot.
We can get the last known sequence number from the getSequenceNumber() method.
We should save this number in a shared preference file and get its value from the file whenever we query the changed apps.
Here is the complete code:
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity() {
private val tagLog = javaClass.simpleName as String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
// call the method from a background thread
checkChangedPackages(this.applicationContext)
}
}
private fun checkChangedPackages(context: Context) {
val packageManagerApps = context.packageManager
val sequenceNumber = getSequenceNumber(context)
Log.d(tagLog, "sequenceNumber = $sequenceNumber")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val changedPackages = packageManagerApps.getChangedPackages(sequenceNumber)
if (changedPackages != null) {
// Packages are changed
// Get the list of changed packages
// the list includes new, updated and deleted apps
val changedPackagesNames = changedPackages.packageNames
var appName: CharSequence
for (packageName in changedPackagesNames) {
try {
appName = packageManagerApps.getApplicationLabel(
packageManagerApps.getApplicationInfo(
packageName, 0,
)
)
// Either a new or an updated app
Log.d(
tagLog,
"New Or Updated App: $packageName , appName = ${appName.toString()}"
)
} catch (e: PackageManager.NameNotFoundException) {
// The app is deleted
Log.d(tagLog, "Deleted App: $packageName")
}
}
saveSequenceNumber(context, changedPackages.sequenceNumber)
} else {
// packages not changed
}
}
}
private fun getSequenceNumber(context: Context): Int {
val sharedPrefFile = context.getSharedPreferences("your_file_name", MODE_PRIVATE)
return sharedPrefFile.getInt("sequence_number", 0)
}
private fun saveSequenceNumber(context: Context, newSequenceNumber: Int) {
val sharedPrefFile = context.getSharedPreferences("your_file_name", MODE_PRIVATE)
val editor = sharedPrefFile.edit()
editor.putInt("sequence_number", newSequenceNumber)
editor.apply()
}
}
Note: Sometimes, getChangedPackages() returns the packages that are not changed. Also, the sequence number is incremented even if there is no change in the apps list.
Broadcast Receiver for App Install and Uninstall Events (Below Android 8):
It is easy to implement a broadcast receiver. Create a class with the name AppEventsBroadcastReceiver.
class AppEventsBroadcastReceiver: BroadcastReceiver() {
private val tagLog = javaClass.simpleName as String
override fun onReceive(context: Context?, intent: Intent?) {
}
}
Every time an app is changed, the onReceive() method gets called. In the manifest file, register the receiver (inside the application tag).
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.semicolonspace.examples">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round">
<receiver
android:name=".AppEventsBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /
</intent-filter>
</activity>
</application>
</manifest>
Add the following code in the AppEventsBroadcastReceiver class:
class AppEventsBroadcastReceiver : BroadcastReceiver() {
private val tagLog = javaClass.simpleName as String
override fun onReceive(context: Context?, intent: Intent?) {
if (intent != null && context != null) {
// Check this condition because the broadcast receiver
// is getting triggered on some devices running above Oreo
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
val packageManager = context.packageManager
val appUid = intent.getIntExtra(Intent.EXTRA_UID, 0)
if (intent.action == "android.intent.action.PACKAGE_FULLY_REMOVED") {
Log.d(tagLog, "PACKAGE_FULLY_REMOVED $appUid")
} else {
val applicationInfo = packageManager?.getApplicationInfo(
packageManager.getNameForUid(appUid)!!, PackageManager.GET_META_DATA
)!!
val appName = packageManager.getApplicationLabel(applicationInfo).toString()
val appPackageName = applicationInfo.packageName
if (intent.action == "android.intent.action.PACKAGE_ADDED") {
Log.d(tagLog, "PACKAGE_ADDED $appPackageName , $appName")
} else if (intent.action == "android.intent.action.PACKAGE_REPLACED") {
Log.d(tagLog, "PACKAGE_REPLACED $appPackageName , $appName")
}
}
}
}
}
}
Note: When the user updates an app, both PACKAGE_ADDED and PACKAGE_REPLACED are getting triggered.
This is how you get the app install and uninstall events in Android. If you have any doubts, comment below.
Related:
installs are not configured
What do you mean?
if we install new apps and when we log it does not show the package and app name , it only showing deleted apps.
Log.d(tagLog,”New Or Updated App: $packageName ,
appName = ${appName.toString()}”)
this part is not working for new installed apps . Kindly check and inform asap, Thank you.
I mean to say that when we install new packages/apps and run our app its not getting logged in our logcat .Only deleted apps/packages are getting logged in logcat.
Log.d(tagLog,”New Or Updated App: $packageName ,
appName = ${appName.toString()}”)
Basically the above part is not working properly. Kindly check and resolve asap. Thank You!!
I tested the code. It is working. Can you share your code?
This is the github link for the code
https://github.com/prasadankitt/CheckPackage.git
Kindly read Readme.md
Do you know how delete an folder in downloads folder when user unnistall an app?
See data storage options: https://developer.android.com/training/data-storage
I will try to do It, thank you very much, blessings.