20

I'm writing an app updater for my app. After I make sure I have my apk on the device, this is what I do from within the app I'm trying to update:

Intent promptInstall = new Intent(Intent.ACTION_VIEW);
File f = new File(apkLocation);    
promptInstall.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive");
_context.startActivity(promptInstall);

This launches my installer which displays the app permissions and I am able to click "Install". But from here the app simply closes, I get no message whatsoever (I would've expected the dialog telling me the install was successful giving me the option to press "Close" or "Open"). It just goes to the main screen of the device without further notice.

On a side note, the app is indeed updated when I manually open it back. How can I make the installer go all the way as expected? Is there anything to set on the intent?

While writing this, I'm wondering if the reason this happens is that the current app is simply overwritten on the device thus closing it and by extent not getting the result of the intent because it's source was killed?

Florin Bombeanu
  • 850
  • 2
  • 11
  • 23
  • 5
    You should take into account that google has now specifically stated in their terms and conditions that you cannot do this for apps downloaded from the play store(probably fine with amazon etc. but something to take into account) – jcw May 15 '13 at 16:17
  • Thanks, I knew that, read it here on SW. It is not my case. – Florin Bombeanu May 15 '13 at 18:53
  • If you're updating via a command (`pm install`) you also need the `android.permission.INSTALL_PACKAGES` permission, which is signature level. – Squeazer Apr 03 '19 at 15:20

3 Answers3

18

All you can do is register a receiver with the intent filters like android.intent.action.PACKAGE_INSTALL or android.intent.action.PACKAGE_REPLACED from which you can restart your application back again.

<receiver android:enabled="true" android:exported="true" android:label="BootService" android:name="com.project.services.BootService">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <data android:scheme="package"/>
        </intent-filter>
         <intent-filter>
            <action android:name="android.intent.action.PACKAGE_ADDED"/>
            <data android:scheme="package"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.PACKAGE_INSTALL"/>
            <data android:scheme="package"/>
        </intent-filter>
         <intent-filter>
            <action android:name="android.intent.action.PACKAGE_CHANGED"/>
            <data android:scheme="package"/>
        </intent-filter>
         <intent-filter>
            <action android:name="android.intent.action.PACKAGE_REPLACED"/>
            <data android:scheme="package"/>
        </intent-filter>
    </receiver>
</application>

And

public class BootService extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {

    if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
        Intent serviceIntent = new Intent();
        serviceIntent.setClass(context,Controller.class);
        serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(serviceIntent);
    } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
        Intent serviceIntent = new Intent();
        serviceIntent.setClass(context, Controller.class);
        serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(serviceIntent);
    }
  }
}
zachjs
  • 1,738
  • 1
  • 11
  • 22
Bishwash
  • 196
  • 3
  • 5
    I tried it and it works perfectly. I just added `if(context.getApplicationInfo().packageName.equals((intent.getData()).getSchemeSpecificPart()))` check to make sure I've caught the correct broadcast. Without it, my app would start after performing any app update within the store. – Florin Bombeanu May 16 '13 at 10:09
  • I'd suggest to add the part filter to your Manifest next to scheme, that way you avoid having your app constantly start on every app update/install/etc... – 3c71 May 16 '13 at 21:29
  • 1
    I tried using the filter next to scheme but that didn't stop the app starting whenever any other app was updated. I had to manually check the packageName of the context against the intent.getData(). – Florin Bombeanu May 17 '13 at 13:52
  • 3
    two body condition are same as , why you write `if` `if else` statements ?! - am i wrong ? – java acm Feb 23 '17 at 14:38
  • @Bishwash Hey may I check with you on how you execute the onReceive? As in where you called that method –  Jun 08 '18 at 01:18
3

To successfully update you need to launch intent with URI indicating to your update app as new task.

 final Intent intent = new Intent(Intent.ACTION_VIEW);
 intent.setDataAndType(Uri.fromFile(new File(PATH_TO_APK));
 "application/vnd.android.package-archive");
 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 startActivity(intent);

My post below:

Android application update issue

Community
  • 1
  • 1
dawid gdanski
  • 2,432
  • 3
  • 21
  • 29
2

First off, you can't install without prompt, unless you are rooted or have system privileges. I don't think you were asking that, but one of your paragraphs isn't clear.

Secondly, if installing an update version of a running app, the behavior you're seeing is expected: The app is force-closed and updated. You can't update in-place. You can detect when the installation was aborted, because the activity invoking the installer will be resumed.

In order to update a running app AND keep it running, you'll need a separate process (app) to monitor the installation and restart your app.

323go
  • 14,143
  • 6
  • 33
  • 41
  • Yeah, that kind of answers my question just as i thought while formulating my question. I've been reading about Activity life cycle but couldn't find the exact mentioning of this behavior although it makes perfect sense to get killed with the app. What are my alternatives? I'm thinking I need to start the install activity from a different scope than my initial app's. Any ideas on how I could do this? – Florin Bombeanu May 15 '13 at 19:00
  • I just tried @Bishwashs' code and it works great so I'm thinking it's actually possible to receive the PACKAGE_REPLACED broadcast. Thanks for your help. – Florin Bombeanu May 16 '13 at 10:07
  • Good deal, that's great news. – 323go May 16 '13 at 13:24