Make app status updates include parcelized version of status.

This means that we no longer need to receive an APK_URL and then
directly ask the status manager for the relevant status object.
This causes problems when consecutive updates happen in the same event
loop, e.g. download started + download complete. In this case, the
receiver will receive two events for the same app. When it asks for the
associated status object for the first (download started) event, it will
receive a status that says "download complete ready to install". This is
because the status object has already been updated by the second event.

Furthermore, the broadcast manager must receive a copy of the status
object, not the original object. This is because the broadcast manager
doesn't parcel the relevant extras until the end of the event loop. This
means that if the status is changed twice in one frame, then both
parcels will end up looking the same. By sending through a copy instead,
this ensures that any listener receives the statuses in the correct
order, rather than two parceled versions of the same status
notification.
This commit is contained in:
Peter Serwylo 2017-05-26 09:45:36 +10:00
parent ee7055e118
commit 8e2a099e51
2 changed files with 63 additions and 6 deletions

View File

@ -527,13 +527,11 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
private final BroadcastReceiver appStatusReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String apkUrl = intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL);
AppUpdateStatusManager.AppUpdateStatus status = AppUpdateStatusManager.getInstance(context).get(apkUrl);
AppUpdateStatusManager.AppUpdateStatus status = intent.getParcelableExtra(AppUpdateStatusManager.EXTRA_STATUS);
boolean isRemoving = TextUtils.equals(intent.getAction(), AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED);
// !TextUtils.equals(status.apk.packageName, app.packageName)
if (status == null && currentStatus != null && isRemoving && !TextUtils.equals(apkUrl, currentStatus.getUniqueKey())) {
Utils.debugLog(TAG, "Ignoring app status change because it belongs to " + apkUrl + " not " + currentStatus.getUniqueKey());
if (currentStatus != null && isRemoving && !TextUtils.equals(status.getUniqueKey(), currentStatus.getUniqueKey())) {
Utils.debugLog(TAG, "Ignoring app status change because it belongs to " + status.getUniqueKey() + " not " + currentStatus.getUniqueKey());
} else if (status != null && !TextUtils.equals(status.apk.packageName, app.packageName)) {
Utils.debugLog(TAG, "Ignoring app status change because it belongs to " + status.apk.packageName + " not " + app.packageName);
} else {

View File

@ -7,6 +7,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.TaskStackBuilder;
@ -66,6 +68,7 @@ public final class AppUpdateStatusManager {
public static final String BROADCAST_APPSTATUS_REMOVED = "org.fdroid.fdroid.installer.appstatus.appchange.remove";
public static final String EXTRA_APK_URL = "urlstring";
public static final String EXTRA_STATUS = "status";
public static final String EXTRA_REASON_FOR_CHANGE = "reason";
@ -102,7 +105,7 @@ public final class AppUpdateStatusManager {
private static AppUpdateStatusManager instance;
public class AppUpdateStatus {
public static class AppUpdateStatus implements Parcelable {
public final App app;
public final Apk apk;
public Status status;
@ -128,6 +131,59 @@ public final class AppUpdateStatusManager {
public String toString() {
return app.packageName + " [Status: " + status + ", Progress: " + progressCurrent + " / " + progressMax + "]";
}
protected AppUpdateStatus(Parcel in) {
app = in.readParcelable(getClass().getClassLoader());
apk = in.readParcelable(getClass().getClassLoader());
intent = in.readParcelable(getClass().getClassLoader());
status = (Status) in.readSerializable();
progressCurrent = in.readInt();
progressMax = in.readInt();
errorText = in.readString();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(app, 0);
dest.writeParcelable(apk, 0);
dest.writeParcelable(intent, 0);
dest.writeSerializable(status);
dest.writeInt(progressCurrent);
dest.writeInt(progressMax);
dest.writeString(errorText);
}
@Override
public int describeContents() {
return 0;
}
public static final Parcelable.Creator<AppUpdateStatus> CREATOR = new Parcelable.Creator<AppUpdateStatus>() {
@Override
public AppUpdateStatus createFromParcel(Parcel in) {
return new AppUpdateStatus(in);
}
@Override
public AppUpdateStatus[] newArray(int size) {
return new AppUpdateStatus[size];
}
};
/**
* When passing to the broadcast manager, it is important to pass a copy rather than the original object.
* This is because if two status changes are noticed in the same event loop, than they will both refer
* to the same status object. The objects are not parceled until the end of the event loop, and so the first
* parceled event will refer to the updated object (with a different status) rather than the intended
* status (i.e. the one in existence when talking to the broadcast manager).
*/
public AppUpdateStatus copy() {
AppUpdateStatus copy = new AppUpdateStatus(app, apk, status, intent);
copy.errorText = errorText;
copy.progressCurrent = progressCurrent;
copy.progressMax = progressMax;
return copy;
}
}
private final Context context;
@ -209,6 +265,7 @@ public final class AppUpdateStatusManager {
if (!isBatchUpdating) {
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_ADDED);
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
localBroadcastManager.sendBroadcast(broadcastIntent);
}
}
@ -217,6 +274,7 @@ public final class AppUpdateStatusManager {
if (!isBatchUpdating) {
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_CHANGED);
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
broadcastIntent.putExtra(EXTRA_IS_STATUS_UPDATE, isStatusUpdate);
localBroadcastManager.sendBroadcast(broadcastIntent);
}
@ -226,6 +284,7 @@ public final class AppUpdateStatusManager {
if (!isBatchUpdating) {
Intent broadcastIntent = new Intent(BROADCAST_APPSTATUS_REMOVED);
broadcastIntent.putExtra(EXTRA_APK_URL, entry.getUniqueKey());
broadcastIntent.putExtra(EXTRA_STATUS, entry.copy());
localBroadcastManager.sendBroadcast(broadcastIntent);
}
}