Make AppSnippet parcelable

AppSnippet is used in Pia in 4 activities. Each time, it is expensive to
parse the app's logo and label from PackageManager and the APK file of
the app being installed. Thus, lets compute the AppSnippet once and pass
it as an intent extra to whichever activity needs it.

This also solves the bug where InstallSuccess showed an activity's name
in place of the app label since the temporary directory used to create
AppSnippet is already destroyed after app installation.

Bug: 290862169
Test: Manually install an APK and observe the app install / update
dialog

Change-Id: I2095b092e1a7b3186ff4cdc4c81effce51377b19
This commit is contained in:
Sumedh Sen
2023-07-18 15:00:59 -07:00
parent 8a77a8462c
commit 719d00a979
5 changed files with 59 additions and 26 deletions

View File

@@ -33,8 +33,6 @@ import android.view.View;
import androidx.annotation.Nullable;
import java.io.File;
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
@@ -101,14 +99,8 @@ public class InstallFailed extends AlertActivity {
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
if ("package".equals(packageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
pm.getApplicationIcon(appInfo));
} else {
final File sourceFile = new File(packageURI.getPath());
as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
}
as = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
PackageUtil.AppSnippet.class);
// Store label for dialog
mLabel = as.label;

View File

@@ -16,6 +16,7 @@
package com.android.packageinstaller;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.PendingIntent;
@@ -86,7 +87,8 @@ public class InstallInstalling extends AlertActivity {
// ContentResolver.SCHEME_FILE
// STAGED_SESSION_ID extra contains an ID of a previously staged install session.
final File sourceFile = new File(mPackageURI.getPath());
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
PackageUtil.AppSnippet as = getIntent()
.getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);

View File

@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -30,7 +29,6 @@ import android.widget.Button;
import androidx.annotation.Nullable;
import java.io.File;
import java.util.List;
/**
@@ -65,18 +63,8 @@ public class InstallSuccess extends AlertActivity {
ApplicationInfo appInfo =
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAppPackageName = appInfo.packageName;
Uri packageURI = intent.getData();
// Set header icon and title
PackageManager pm = getPackageManager();
if ("package".equals(packageURI.getScheme())) {
mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
pm.getApplicationIcon(appInfo));
} else {
File sourceFile = new File(packageURI.getPath());
mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
}
mAppSnippet = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
PackageUtil.AppSnippet.class);
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);

View File

@@ -84,6 +84,7 @@ public class PackageInstallerActivity extends AlertActivity {
static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
static final String EXTRA_APP_SNIPPET = "EXTRA_APP_SNIPPET";
private static final String ALLOW_UNKNOWN_SOURCES_KEY =
PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
@@ -708,6 +709,9 @@ public class PackageInstallerActivity extends AlertActivity {
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
if (mAppSnippet != null) {
newIntent.putExtra(EXTRA_APP_SNIPPET, mAppSnippet);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);

View File

@@ -28,8 +28,13 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -117,7 +122,7 @@ public class PackageUtil {
icon);
}
static final class AppSnippet {
static final class AppSnippet implements Parcelable {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
@@ -125,10 +130,52 @@ public class PackageUtil {
this.icon = icon;
}
private AppSnippet(Parcel in) {
label = in.readString();
Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
icon = new BitmapDrawable(Resources.getSystem(), bmp);
}
@Override
public String toString() {
return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(label.toString());
Bitmap bmp = getBitmapFromDrawable(icon);
dest.writeParcelable(bmp, 0);
}
private Bitmap getBitmapFromDrawable(Drawable drawable) {
// Create an empty bitmap with the dimensions of our drawable
final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
// Associate it with a canvas. This canvas will draw the icon on the bitmap
final Canvas canvas = new Canvas(bmp);
// Draw the drawable in the canvas. The canvas will ultimately paint the drawable in the
// bitmap held within
drawable.draw(canvas);
return bmp;
}
public static final Parcelable.Creator<AppSnippet> CREATOR = new Parcelable.Creator<>() {
public AppSnippet createFromParcel(Parcel in) {
return new AppSnippet(in);
}
public AppSnippet[] newArray(int size) {
return new AppSnippet[size];
}
};
}
/**