Issue
I'm trying to add an auto update functionality to a Xamarin Android app.
When it starts, the app checks whether the current version is allowed and if not retrieves all the packages to update (one or more) from the database.
Then it downloads all the packages and saves them to disk. Once all packages have been downloaded, I prompt android to install them in sequence as such:
foreach (UpdatedApp app in apps)
{
InstallPackage(app);
}
private void InstallPackage(UpdatedApp app)
{
Java.IO.File file = new Java.IO.File(app.Path);
if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
{
Intent intent = new Intent(Intent.ActionInstallPackage);
intent.SetData(FileProvider.GetUriForFile(this, $"{PackageName}.fileprovider", file));
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
StartActivity(intent);
}
else
{
Intent intent = new Intent(Intent.ActionView);
intent.SetDataAndType(Android.Net.Uri.FromFile(file), "application/vnd.android.package-archive");
intent.AddFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
It works great when there is only one package to install, but when there are at least two, I basically prompt android to install the packages simultaneously and only the first package will be installed.
I added a small bit of logic to circumvent this issue:
- Get the current version of the Nth package.
- Prompt install
- Every x seconds for y seconds, get the package version again and compare it to the original
- If different it means the package has successfully been installed and repeat the process for the N+1st package
- Otherwise cancel any further installation
Here's the updated code:
foreach (UpdatedApp app in apps)
{
string versionName = GetPackageVersion(app);
InstallPackage(app);
bool installed = await HasPackageBeenInstalled(app, versionName);
if (!installed)
break;
}
private async Task<bool> HasPackageBeenInstalled(UpdatedApp app, string oldVersion)
{
int count = 0;
do
{
await Task.Delay(1000);
string newVersion = GetPackageVersion(app);
if (newVersion != oldVersion)
return true;
count++;
}
while (count < 20);
return false;
}
private string GetPackageVersion(UpdatedApp app)
{
try
{
PackageInfo packageInfo = PackageManager.GetPackageInfo(app.PackageName, 0);
return packageInfo.VersionName;
}
catch (PackageManager.NameNotFoundException e)
{
return null;
}
}
It ensures I only prompt to install a package after the previous one has been installed.
I was wondering if there was a better way to do that? I had two things in mind:
- Use StartActivityForResult instead of StartActivity. This assumes Android natively returns a result, which is not clear to me.
- Use a BroadcastReceiver. This assumes Android natively sends a broadcast message when any app is successfully installed.
Could any of my ideas work? Or anything else?
Solution
You can use BroadcastReceiver
to detect the PACKAGE_ADDED, PACKAGE_REMOVED
message::
In your manifest:
<receiver android:name=".AppStatusReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
</intent-filter>
</receiver>
And Ccreate a Broadcast Receiver:
[BroadcastReceiver(Enabled = true, Exported = false)]
public class SampleReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
// Do stuff here.
}
}
Please Note that the newly installed package does not receive this broadcast.
Answered By - nevermore
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.