Initial work to support inline Download+Install button in app lists
Shows progress and download complete now, but left a few TODOs lying around. These are mainly around the ability to then deal with installing an app once download is complete.
This commit is contained in:
parent
405279c0ff
commit
952024768a
@ -1,15 +1,23 @@
|
||||
package org.fdroid.fdroid.views.apps;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Outline;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -18,31 +26,57 @@ import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
|
||||
import org.fdroid.fdroid.AppDetails;
|
||||
import org.fdroid.fdroid.AppDetails2;
|
||||
import org.fdroid.fdroid.AppUpdateStatusManager;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.ApkProvider;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.installer.InstallManagerService;
|
||||
import org.fdroid.fdroid.installer.Installer;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.fdroid.fdroid.net.DownloaderService;
|
||||
|
||||
// TODO: Support cancelling of downloads by tapping the install button a second time.
|
||||
// TODO: Support installing of an app once downloaded by tapping the install button a second time.
|
||||
public class AppListItemController extends RecyclerView.ViewHolder {
|
||||
|
||||
private final Activity activity;
|
||||
|
||||
private final Button installButton;
|
||||
private final ImageView installButton;
|
||||
private final ImageView icon;
|
||||
private final TextView name;
|
||||
private final TextView status;
|
||||
private final DisplayImageOptions displayImageOptions;
|
||||
|
||||
private App currentApp;
|
||||
private String currentAppDownloadUrl;
|
||||
|
||||
public AppListItemController(Activity activity, View itemView) {
|
||||
@TargetApi(21)
|
||||
public AppListItemController(final Activity activity, View itemView) {
|
||||
super(itemView);
|
||||
this.activity = activity;
|
||||
|
||||
installButton = (Button) itemView.findViewById(R.id.install);
|
||||
installButton = (ImageView) itemView.findViewById(R.id.install);
|
||||
installButton.setOnClickListener(onInstallClicked);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
installButton.setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
float density = activity.getResources().getDisplayMetrics().density;
|
||||
|
||||
// TODO: This is a bit hacky/hardcoded/too-specific to the particular icons we're using.
|
||||
// This is because the default "download & install" and "downloaded & ready to install"
|
||||
// icons are smaller than the "downloading progress" button. Hence, we can't just use
|
||||
// the width/height of the view to calculate the outline size.
|
||||
int xPadding = (int) (8 * density);
|
||||
int yPadding = (int) (9 * density);
|
||||
outline.setOval(xPadding, yPadding, installButton.getWidth() - xPadding, installButton.getHeight() - yPadding);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
icon = (ImageView) itemView.findViewById(R.id.icon);
|
||||
name = (TextView) itemView.findViewById(R.id.app_name);
|
||||
status = (TextView) itemView.findViewById(R.id.status);
|
||||
@ -58,6 +92,16 @@ public class AppListItemController extends RecyclerView.ViewHolder {
|
||||
|
||||
ImageLoader.getInstance().displayImage(app.iconUrl, icon, displayImageOptions);
|
||||
|
||||
Apk apkToInstall = ApkProvider.Helper.findApkFromAnyRepo(activity, app.packageName, app.suggestedVersionCode);
|
||||
currentAppDownloadUrl = apkToInstall.getUrl();
|
||||
|
||||
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity.getApplicationContext());
|
||||
broadcastManager.unregisterReceiver(onDownloadProgress);
|
||||
broadcastManager.unregisterReceiver(onInstallAction);
|
||||
|
||||
broadcastManager.registerReceiver(onDownloadProgress, DownloaderService.getIntentFilter(currentAppDownloadUrl));
|
||||
broadcastManager.registerReceiver(onInstallAction, Installer.getInstallIntentFilter(Uri.parse(currentAppDownloadUrl)));
|
||||
|
||||
configureStatusText(app);
|
||||
configureInstallButton(app);
|
||||
}
|
||||
@ -107,15 +151,29 @@ public class AppListItemController extends RecyclerView.ViewHolder {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean readyToInstall = false;
|
||||
for (AppUpdateStatusManager.AppUpdateStatus appStatus : AppUpdateStatusManager.getInstance(activity).getByPackageName(app.packageName)) {
|
||||
if (appStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
|
||||
readyToInstall = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (readyToInstall) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_complete));
|
||||
// TODO: If in the downloading phase, then need to reflect that instead of this "download complete" icon.
|
||||
} else {
|
||||
boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled();
|
||||
boolean shouldAllow = app.compatible && !app.isFiltered();
|
||||
|
||||
if (shouldAllow && installable) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
|
||||
installButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
installButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final View.OnClickListener onAppClicked = new View.OnClickListener() {
|
||||
@Override
|
||||
@ -136,6 +194,51 @@ public class AppListItemController extends RecyclerView.ViewHolder {
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver onDownloadProgress = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (currentApp == null || !TextUtils.equals(currentAppDownloadUrl, intent.getDataString())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Downloader.ACTION_PROGRESS.equals(intent.getAction())) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
|
||||
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
|
||||
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 100);
|
||||
|
||||
int progressAsDegrees = (int) (((float) bytesRead / totalBytes) * 360);
|
||||
installButton.setImageLevel(progressAsDegrees);
|
||||
} else if (Downloader.ACTION_COMPLETE.equals(intent.getAction())) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_complete));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver onInstallAction = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (currentApp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
|
||||
if (!TextUtils.equals(apk.packageName, currentApp.packageName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Installer.ACTION_INSTALL_STARTED.equals(intent.getAction())) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
|
||||
installButton.setImageLevel(0);
|
||||
} else if (Installer.ACTION_INSTALL_COMPLETE.equals(intent.getAction())) {
|
||||
installButton.setVisibility(View.GONE);
|
||||
// TODO: It could've been a different version other than the current suggested version.
|
||||
// In these cases, don't hide the button but rather set it back to the default install image.
|
||||
} else if (Installer.ACTION_INSTALL_INTERRUPTED.equals(intent.getAction())) {
|
||||
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final View.OnClickListener onInstallClicked = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -53,11 +53,14 @@
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp" />
|
||||
|
||||
<Button
|
||||
<ImageView
|
||||
android:id="@+id/install"
|
||||
android:background="@drawable/download_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
tools:src="@drawable/ic_download"
|
||||
android:scaleType="fitXY"
|
||||
android:contentDescription="@string/menu_install"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:elevation="2dp"
|
||||
android:padding="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
|
Loading…
x
Reference in New Issue
Block a user