64

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.

Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
Petre Popescu
  • 1,950
  • 3
  • 24
  • 38

5 Answers5

114

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: 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.

Louis CAD
  • 10,965
  • 2
  • 39
  • 58
Huang
  • 4,812
  • 3
  • 21
  • 20
  • 9
    just adding intentFilter.addDataScheme("package"); to my code solved everything. Thanks. – Petre Popescu Sep 19 '11 at 18:15
  • 2
    this is for only install new application. but how should i get the uninstall my applcation event in broadcast receiver? – Hiren Dabhi Feb 07 '12 at 06:52
  • 8
    @HirenDabhi, Just simply add the corresponding intent filter's action: ``. – Huang Feb 07 '12 at 07:24
  • @Huang, is it possible to handle uninsallation of my application in my application. means broadcast receiver is within my application and watch uninstallation of my application? – Hiren Dabhi Feb 07 '12 at 09:37
  • @Huang, i tried using broadcast receiver in other application and it watch the uninstallation process of my application , it works fine but if broadcast receiver is within my application then it doesn't fire any action on uninstallaion. – Hiren Dabhi Feb 07 '12 at 09:40
  • I think I can explain why you failed later,when I get back home. – Huang Feb 07 '12 at 10:03
  • 3
    @HirenDabhi Yes, I also found that. I think that happens because when you register in your own app and when the app is uninstalled, the registered BroadcastReceiver has been uninstalled before the app gets uninstalled,so its own uninstallation event won't be received by that BroadcastReceiver. – Huang Feb 07 '12 at 11:20
  • 4
    I think the "ACTION_PACKAGE_INSTALL" is not needed, as the documentation says (here: http://developer.android.com/reference/android/content/Intent.html#ACTION_PACKAGE_INSTALL ) : "This constant was deprecated in API level 14. This constant has never been used." – android developer Apr 19 '14 at 09:27
  • 4
    By the way, the package URI (`intent.getData().toString()`) received is like this - `package:com.rovio.angrybirds` – rishabhmhjn Aug 19 '14 at 02:38
  • Also ```android.intent.action.PACKAGE_REPLACED``` action should be provided. This action allows to listen apps' package changes so we can notice app icon and name changes. – ismailarilik Nov 24 '16 at 15:05
  • Where will I write your above code so that I could run some code after successful installation? I meant, Do I need to create a activity and write somewhere that this will run first on successful installation? – Pankaj Nov 05 '19 at 05:21
34

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+.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    Thanks. Why would they do such a change? – kar Apr 16 '18 at 14:53
  • 7
    @KarsenGauss: I have [a blog post](https://commonsware.com/blog/2017/04/11/android-o-implicit-broadcast-ban.html) that covers that. – CommonsWare Apr 16 '18 at 19:46
  • 1
    You always pop up in the answers and comments and you have helped me a lot while I was working on my first app. So thanks for that! But you got me confused with this one. In your blogpost you are suggesting the use of `LocalBroadcastManager`. But according to [this answer](https://stackoverflow.com/a/54264538/3356425) `LocalBroadcastManager` is being depricated in Version 1.1.0. I basically want to force an update to a newer version. Either I just kill the app, if the installation is canelled or I run the installation intent again. How would I do this (by also supporitng 8.0+) today? Thanks. – Alex Apr 01 '19 at 10:06
  • @Alex: IMHO, these broadcasts would not be all that useful for what you are trying to do. When your app starts, see if your version is the latest. If it is not, lead the user to install the latest version and refuse to do anything else. – CommonsWare Apr 01 '19 at 10:36
  • @CommonsWare wow, thanks for the quick response. That's basically what I'm doing now. I'm already leading the user to the install but the user can just cancel the installer. Sorry if it wasn't clear. That's why I was thinking about handling the "cancel"-button of the package installer. Should I just loop the check for a new version until the newest one is installed? Or is there something like handling the cancel-button in Android 8.0+? What would you suggest? My main target device is API 24 now. But I guess better make a future-proof solution now than changing it every few years. Thanks! – Alex Apr 01 '19 at 11:17
  • @Alex: "Should I just loop the check for a new version until the newest one is installed?" -- well, your process will be terminated if the user installs an upgrade. So, just lock down your UI in your current process (e.g., disable all widgets) and throw up an "upgrade me dammit" dialog. Do that as part of each activity `onCreate()`, and you should be fairly well covered -- a loop shouldn't be needed. – CommonsWare Apr 01 '19 at 11:23
  • @CommonsWare Oh yeah, you're right haha. So just check the version periodically (in the `onCreate()` of each activity) rather than trying to react to a cancelled package installer? Not what I was hoping for but probably easier/better than using `BroadcastReceiver`s or a mix of solutions for different Android versions. Thanks for the fast support. I really appreciate it. Have a good one! – Alex Apr 01 '19 at 12:07
  • @CommonsWare Hey, I ended up just using `startActivityForResult` to start the Package Installer and show the "update me dammit"-dialog again if the `resultCode` in the overriden `onActivityResult`-method was `RESULT_CANCELED`. I don't know why I didn't find this much simpler solution before. Doesn't really have to do much with your answer here but just wanted to let you know. And maybe someone else ends up looking for that here. Thanks anyways, lead me in the right direction. – Alex Apr 04 '19 at 14:23
  • @Alex: "I don't know why I didn't find this much simpler solution before" -- make sure you are using `EXTRA_RETURN_RESULT`. This approach is fine for casual stuff, but given device manufacturer tweaks, I would not rely upon the result code, even with `EXTRA_RETURN_RESULT`. – CommonsWare Apr 04 '19 at 14:42
  • @CommonsWare I don't really get what `EXTRA_RETURN_RESULTS` is doing here. I did a `putExtra` with it before starting my intent. But then what? Is it supposed to give me more detailed info in `onActivityResult`? Fortunately I only have to support this one device. It's the only model that the customer will be able to get from us with the app. The only not-so-nice thing now is I don't get the "Installation finished" screen with the buttons "Done" or "Open". But I suppose that's because I'm saving the update.apk in the cache of the app I'm updating. But this way I don't need to get SD permission. – Alex Apr 05 '19 at 14:00
  • @Alex: "Is it supposed to give me more detailed info in onActivityResult? " -- it is supposed to actually give you a result. `startActivityForResult()` requires cooperation on the part of the activity being started. "It's the only model that the customer will be able to get from us with the app" -- oh, well, that helps. – CommonsWare Apr 05 '19 at 15:07
25

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();
    }
}
THANN Phearum
  • 1,969
  • 22
  • 19
2

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);
Sana Ebadi
  • 6,656
  • 2
  • 44
  • 44
0

This solution does not use any kind of Receiver or Intent just Kotlin and coroutines, so it is not limited by any android version.

  1. 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
        }
    }
  1. 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
    }
  1. 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
}
manoellribeiro
  • 187
  • 2
  • 5