Receiving package install and uninstall events
AndroidBroadcastreceiverAndroid Problem Overview
I am trying to detect when a new App is being installed but only if my app is running. I managed to detect the installation of the app by making a BroadcastReceiver and activating it inside the AndroidManifest file but this will detect even if my app is closed. So that is why I need to manually activate and deactivate the broadcastreveiver. To do this I have this code:
br = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.i("Enter", "Enters here");
Toast.makeText(context, "App Installed!!!!.", Toast.LENGTH_LONG).show();
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL);
registerReceiver(br, intentFilter);
This should make a toast when a new app is installed. But sadly it does not. It does not enter in the onReceive method. Any help is appreciated.
Android Solutions
Solution 1 - Android
I tried to register the BroadcastReceiver
in either manifest file or java code. But both of these two methods failed to trigger the onReceive()
method.
After googling this problem, I found a solution for both methods from another Thread in SO:
https://stackoverflow.com/questions/5499025/android-notification-app
In the manifest file (this approach no longer applies since API 26 (Android 8), it was causing performance issues on earlier Android versions):
<receiver android:name=".YourReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_INSTALL" />
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
In java code:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL);
intentFilter.addDataScheme("package");
registerReceiver(br, intentFilter);
This should work for you.
Solution 2 - Android
Other answers point out listening for ACTION_PACKAGE_ADDED
and ACTION_PACKAGE_REPLACED
broadcasts. That is fine for Android 7.1 and lower. On Android 8.0+, you cannot register for those broadcasts in the manifest.
Instead, you need to call getChangedPackages()
on PackageManager
periodically, such as via a periodic JobScheduler
job. This will not give you real-time results, but real-time results are no longer an option on Android 8.0+.
Solution 3 - Android
Just to add to Huang's answer above, here is how to get the package name of the newly installed application:
public class YourReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String packageName = intent.getData().getEncodedSchemeSpecificPart();
}
}
Solution 4 - Android
This code is for REMOVED_APPLICATION .
With below code, U did not need to Use manifest.Just in ur Java Class Write This code .
BroadcastReceiver uninstallApplication = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
String packageName = Objects.requireNonNull(intent.getData()).getEncodedSchemeSpecificPart();
Toast.makeText(context, "USER UNINSTALL : " + packageName, Toast.LENGTH_SHORT).show();
}
}
};
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
registerReceiver(uninstallApplication, intentFilter);
Solution 5 - Android
This solution does not use any kind of Receiver
or Intent
just Kotlin and coroutines, so it is not limited by any android version.
- I created a method to return if my app is installed or not using
PackageManager
.
private fun isAppInstalled(): Boolean {
return try {
val appPackageInfo = packageManager.getPackageInfo(packageName, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
- Then I created another method that returns a Deferred with a Boolean as value to finish only when my app is installed or when it achieves the timeout limit.
override suspend fun returnWhenAppIsInstalledAsync(): Deferred<Boolean> = ioScope.async {
while (isActive) {
var delayCount = 0
if(isAppInstalled()) {
return@async true
} else {
delay(1000)
delayCount++
if(delayCount == 30) return@async false
}
}
return@async false
}
- So I start to await for it to returns after the session commit
...
session.commit(intentSender)
val wasAppInstalled = viewModel.returnWhenAppIsInstalledAsync().await()
if(wasAppInstalled) {
// Here you can handle any logic
}