diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
index a4af58de4..7a2d6dd9f 100644
--- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
+++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java
@@ -37,7 +37,6 @@ 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.data.AppPrefs;
import org.fdroid.fdroid.installer.ApkCache;
import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.installer.Installer;
@@ -156,7 +155,7 @@ public class AppListItemController extends RecyclerView.ViewHolder {
AppUpdateStatusManager.AppUpdateStatus status = statuses.next();
updateAppStatus(app, status);
} else {
- currentStatus = null;
+ updateAppStatus(app, null);
}
}
@@ -168,223 +167,161 @@ public class AppListItemController extends RecyclerView.ViewHolder {
refreshStatus(app);
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity.getApplicationContext());
- broadcastManager.unregisterReceiver(onInstallAction);
broadcastManager.unregisterReceiver(onStatusChanged);
// broadcastManager.registerReceiver(onInstallAction, Installer.getInstallIntentFilter(Uri.parse(currentAppDownloadUrl)));
-
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_ADDED);
intentFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED);
intentFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED);
broadcastManager.registerReceiver(onStatusChanged, intentFilter);
-
- configureAppName(app);
- configureStatusText(app);
- configureInstalledVersion(app);
- configureIgnoredStatus(app);
- configureInstallButton(app);
- configureActionButton(app);
}
- /**
- * Sets the text/visibility of the {@link R.id#status} {@link TextView} based on whether the app:
- * * Is compatible with the users device
- * * Is installed
- * * Can be updated
- */
- private void configureStatusText(@NonNull App app) {
- if (status == null) {
- return;
- }
-
- if (!app.compatible) {
- status.setText(activity.getString(R.string.app_incompatible));
- status.setVisibility(View.VISIBLE);
- } else if (app.isInstalled()) {
- if (app.canAndWantToUpdate(activity)) {
- String upgradeFromTo = activity.getString(R.string.app_version_x_available, app.getSuggestedVersionName());
- status.setText(upgradeFromTo);
- } else {
- String installed = activity.getString(R.string.app_version_x_installed, app.installedVersionName);
- status.setText(installed);
- }
-
- status.setVisibility(View.VISIBLE);
+ @NonNull
+ private AppListItemState getCurrentViewState(
+ @NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
+ if (appStatus == null) {
+ return getViewStateDefault(app);
} else {
- status.setVisibility(View.INVISIBLE);
- }
+ switch (appStatus.status) {
+ case ReadyToInstall:
+ return getViewStateReadyToInstall(app);
- }
+ case Downloading:
+ return getViewStateDownloading(app, appStatus);
- /**
- * Shows the currently installed version name, and whether or not it is the recommended version.
- * Binds to the {@link R.id#installed_version} {@link TextView}.
- */
- private void configureInstalledVersion(@NonNull App app) {
- if (installedVersion == null) {
- return;
- }
+ case Installed:
+ return getViewStateInstalled(app);
- int res = (app.suggestedVersionCode == app.installedVersionCode)
- ? R.string.app_recommended_version_installed : R.string.app_version_x_installed;
-
- installedVersion.setText(activity.getString(res, app.installedVersionName));
- }
-
- /**
- * Shows whether the user has previously asked to ignore updates for this app entirely, or for a
- * specific version of this app. Binds to the {@link R.id#ignored_status} {@link TextView}.
- */
- private void configureIgnoredStatus(@NonNull App app) {
- if (ignoredStatus == null) {
- return;
- }
-
- AppPrefs prefs = app.getPrefs(activity);
- if (prefs.ignoreAllUpdates) {
- ignoredStatus.setText(activity.getString(R.string.installed_app__updates_ignored));
- ignoredStatus.setVisibility(View.VISIBLE);
- } else if (prefs.ignoreThisUpdate > 0 && prefs.ignoreThisUpdate == app.suggestedVersionCode) {
- ignoredStatus.setText(activity.getString(R.string.installed_app__updates_ignored_for_suggested_version, app.getSuggestedVersionName()));
- ignoredStatus.setVisibility(View.VISIBLE);
- } else {
- ignoredStatus.setVisibility(View.GONE);
- }
- }
-
- /**
- * Queries the {@link AppUpdateStatusManager} to find out if there are any apks corresponding to
- * `app` which are ready to install.
- */
- private boolean isReadyToInstall(@NonNull App app) {
- for (AppUpdateStatusManager.AppUpdateStatus appStatus : AppUpdateStatusManager.getInstance(activity).getByPackageName(app.packageName)) {
- if (appStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
- return true;
+ default:
+ return getViewStateDefault(app);
}
}
- return false;
}
- /**
- * The app name {@link TextView} is used for a few reasons:
- *
Display name + summary of the app (most common).
- * If downloading, mention that it is downloading instead of showing the summary.
- * If downloaded and ready to install, mention that it is ready to update/install.
- */
- private void configureAppName(@NonNull App app) {
+ private void refreshView(@NonNull App app,
+ @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
+
+ AppListItemState viewState = getCurrentViewState(app, appStatus);
+
+ name.setText(viewState.getMainText());
+
if (downloadReady != null) {
- downloadReady.setVisibility(View.GONE);
- }
- if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
- name.setText(app.name);
- if (downloadReady != null) {
- downloadReady.setVisibility(View.VISIBLE);
- }
- } else if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Downloading) {
- name.setText(activity.getString(R.string.app_list__name__downloading_in_progress, app.name));
- } else if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) {
- name.setText(activity.getString(R.string.app_list__name__successfully_installed, app.name));
- } else {
- name.setText(Utils.formatAppNameAndSummary(app.name, app.summary));
- }
- }
-
- /**
- * The action button will either tell the user to "Update" or "Install" the app. Both actually do
- * the same thing (launch the package manager). It depends on whether the app has a previous
- * version installed or not as to the chosen terminology.
- */
- private void configureActionButton(@NonNull App app) {
- if (actionButton == null) {
- return;
+ downloadReady.setVisibility(viewState.shouldShowActionButton() ? View.VISIBLE : View.GONE);
}
- actionButton.setVisibility(View.VISIBLE);
-
- if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) {
- if (activity.getPackageManager().getLaunchIntentForPackage(app.packageName) != null) {
- actionButton.setText(R.string.menu_launch);
+ if (actionButton != null) {
+ if (viewState.shouldShowActionButton()) {
+ actionButton.setVisibility(View.VISIBLE);
+ actionButton.setText(viewState.getActionButtonText());
} else {
actionButton.setVisibility(View.GONE);
}
- } else if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
- if (app.isInstalled()) {
- actionButton.setText(R.string.app__install_downloaded_update);
+ }
+
+ if (progressBar != null) {
+ if (viewState.showProgress()) {
+ progressBar.setVisibility(View.VISIBLE);
+ if (viewState.isProgressIndeterminate()) {
+ progressBar.setIndeterminate(true);
+ } else {
+ progressBar.setIndeterminate(false);
+ progressBar.setMax(viewState.getProgressMax());
+ progressBar.setProgress(viewState.getProgressCurrent());
+ }
} else {
- actionButton.setText(R.string.menu_install);
+ progressBar.setVisibility(View.GONE);
}
- } else {
- actionButton.setVisibility(View.GONE);
- }
- }
-
- /**
- * The install button is shown when an app:
- * * Is compatible with the users device.
- * * Has not been filtered due to anti-features/root/etc.
- * * Is either not installed or installed but can be updated.
- */
- private void configureInstallButton(@NonNull App app) {
- if (installButton == null) {
- return;
}
- if (isReadyToInstall(app)) {
- installButton.setVisibility(View.GONE);
- } else {
- boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled();
- boolean shouldAllow = app.compatible && !app.isFiltered();
+ if (cancelButton != null) {
+ if (viewState.showProgress()) {
+ cancelButton.setVisibility(View.VISIBLE);
+ } else {
+ cancelButton.setVisibility(View.GONE);
+ }
+ }
- if (shouldAllow && installable) {
- installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
+ if (installButton != null) {
+ if (viewState.shouldShowActionButton()) {
+ installButton.setVisibility(View.GONE);
+ } else if (viewState.showProgress()) {
installButton.setVisibility(View.VISIBLE);
+ installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
+ int progressAsDegrees = viewState.getProgressMax() <= 0 ? 0 :
+ (int) (((float) viewState.getProgressCurrent() / viewState.getProgressMax()) * 360);
+ installButton.setImageLevel(progressAsDegrees);
+ } else if (viewState.shouldShowInstall()) {
+ installButton.setVisibility(View.VISIBLE);
+ installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
} else {
installButton.setVisibility(View.GONE);
}
}
- }
- private void onDownloadProgressUpdated(int bytesRead, int totalBytes) {
- if (installButton != null) {
- installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
- int progressAsDegrees = totalBytes <= 0 ? 0 : (int) (((float) bytesRead / totalBytes) * 360);
- installButton.setImageLevel(progressAsDegrees);
+ if (installedVersion != null) {
+ installedVersion.setVisibility(View.VISIBLE);
+ installedVersion.setText(viewState.getInstalledVersionText());
}
- if (progressBar != null) {
- progressBar.setVisibility(View.VISIBLE);
- if (totalBytes <= 0) {
- progressBar.setIndeterminate(true);
+ if (status != null) {
+ CharSequence statusText = viewState.getStatusText();
+ if (statusText == null) {
+ status.setVisibility(View.GONE);
} else {
- progressBar.setIndeterminate(false);
- progressBar.setMax(totalBytes);
- progressBar.setProgress(bytesRead);
+ status.setVisibility(View.VISIBLE);
+ status.setText(statusText);
}
}
- if (cancelButton != null) {
- cancelButton.setVisibility(View.VISIBLE);
+ if (ignoredStatus != null) {
+ CharSequence ignoredStatusText = viewState.getIgnoredStatusText();
+ if (ignoredStatusText == null) {
+ ignoredStatus.setVisibility(View.GONE);
+ } else {
+ ignoredStatus.setVisibility(View.VISIBLE);
+ ignoredStatus.setText(ignoredStatusText);
+ }
}
}
- private void onDownloadComplete() {
- if (installButton != null) {
- installButton.setVisibility(View.GONE);
+ private AppListItemState getViewStateInstalled(@NonNull App app) {
+ CharSequence mainText = activity.getString(
+ R.string.app_list__name__successfully_installed, app.name);
+
+ AppListItemState state = new AppListItemState(activity, app).setMainText(mainText);
+
+ if (activity.getPackageManager().getLaunchIntentForPackage(app.packageName) != null) {
+ state.showActionButton(activity.getString(R.string.menu_launch));
}
- if (progressBar != null) {
- progressBar.setVisibility(View.GONE);
- }
+ return state;
+ }
- if (cancelButton != null) {
- cancelButton.setVisibility(View.GONE);
- }
+ private AppListItemState getViewStateDownloading(
+ @NonNull App app, @NonNull AppUpdateStatusManager.AppUpdateStatus currentStatus) {
+ CharSequence mainText = activity.getString(
+ R.string.app_list__name__downloading_in_progress, app.name);
- if (currentApp != null) {
- configureActionButton(currentApp);
- }
+ return new AppListItemState(activity, app)
+ .setMainText(mainText)
+ .setProgress(currentStatus.progressCurrent, currentStatus.progressMax);
+ }
+
+ private AppListItemState getViewStateReadyToInstall(@NonNull App app) {
+ int actionButtonLabel = app.isInstalled()
+ ? R.string.app__install_downloaded_update
+ : R.string.menu_install;
+
+ return new AppListItemState(activity, app)
+ .setMainText(app.name)
+ .showActionButton(activity.getString(actionButtonLabel))
+ .setShowDownloadReady();
+ }
+
+ private AppListItemState getViewStateDefault(@NonNull App app) {
+ return new AppListItemState(activity, app);
}
@SuppressWarnings("FieldCanBeLocal")
@@ -411,29 +348,9 @@ public class AppListItemController extends RecyclerView.ViewHolder {
* Updates both the progress bar and the circular install button (which shows progress around the outside of the circle).
* Also updates the app label to indicate that the app is being downloaded.
*/
- private void updateAppStatus(@NonNull App app, @NonNull AppUpdateStatusManager.AppUpdateStatus status) {
+ private void updateAppStatus(@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus status) {
currentStatus = status;
-
- configureAppName(app);
- configureActionButton(app);
-
- switch (status.status) {
- case Downloading:
- onDownloadProgressUpdated(status.progressCurrent, status.progressMax);
- break;
-
- case ReadyToInstall:
- onDownloadComplete();
- break;
-
-
- case Installed:
- case Installing:
- case InstallError:
- case UpdateAvailable:
- case DownloadInterrupted:
- break;
- }
+ refreshView(app, status);
}
private final BroadcastReceiver onStatusChanged = new BroadcastReceiver() {
@@ -449,34 +366,6 @@ public class AppListItemController extends RecyclerView.ViewHolder {
}
};
- private final BroadcastReceiver onInstallAction = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
- if (currentApp == null || !TextUtils.equals(apk.packageName, currentApp.packageName)) {
- return;
- }
-
- configureAppName(currentApp);
- configureActionButton(currentApp);
-
- if (installButton == null) {
- 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));
- }
- }
- };
-
@SuppressWarnings("FieldCanBeLocal")
private final View.OnClickListener onActionClicked = new View.OnClickListener() {
@Override
diff --git a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java
new file mode 100644
index 000000000..4e9e5be0a
--- /dev/null
+++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java
@@ -0,0 +1,141 @@
+package org.fdroid.fdroid.views.apps;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.TextView;
+
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.App;
+import org.fdroid.fdroid.data.AppPrefs;
+
+public class AppListItemState {
+ private final Context context;
+ private final App app;
+ private CharSequence mainText = null;
+ private boolean showDownloadReady = false;
+ private CharSequence actionButtonText = null;
+ private int progressCurrent = -1;
+ private int progressMax = -1;
+
+ public AppListItemState(Context context, @NonNull App app) {
+ this.app = app;
+ this.context = context;
+ }
+
+ public AppListItemState setMainText(@NonNull CharSequence mainText) {
+ this.mainText = mainText;
+ return this;
+ }
+
+ public AppListItemState setShowDownloadReady() {
+ this.showDownloadReady = true;
+ return this;
+ }
+
+ public AppListItemState showActionButton(@NonNull CharSequence label) {
+ actionButtonText = label;
+ return this;
+ }
+
+ public AppListItemState setProgress(int progressCurrent, int progressMax) {
+ this.progressCurrent = progressCurrent;
+ this.progressMax = progressMax;
+ return this;
+ }
+
+ @Nullable
+ public CharSequence getMainText() {
+ return mainText != null
+ ? mainText
+ : Utils.formatAppNameAndSummary(app.name, app.summary);
+ }
+
+ public boolean shouldShowInstall() {
+ boolean installable = app.canAndWantToUpdate(context) || !app.isInstalled();
+ boolean shouldAllow = app.compatible && !app.isFiltered();
+
+ return installable && shouldAllow && !shouldShowActionButton() && !showProgress();
+ }
+
+ public boolean shouldShowDownloadReady() {
+ return showDownloadReady;
+ }
+
+ public boolean shouldShowActionButton() {
+ return actionButtonText != null;
+ }
+
+ public CharSequence getActionButtonText() {
+ return actionButtonText;
+ }
+
+ public boolean showProgress() {
+ return progressCurrent >= 0;
+ }
+
+ public boolean isProgressIndeterminate() {
+ return progressMax <= 0;
+ }
+
+ public int getProgressCurrent() {
+ return progressCurrent;
+ }
+
+ public int getProgressMax() {
+ return progressMax;
+ }
+
+ /**
+ * Sets the text/visibility of the {@link R.id#status} {@link TextView} based on whether the app:
+ * * Is compatible with the users device
+ * * Is installed
+ * * Can be updated
+ */
+ @Nullable
+ public CharSequence getStatusText() {
+ String statusText = null;
+ if (!app.compatible) {
+ statusText = context.getString(R.string.app_incompatible);
+ } else if (app.isInstalled()) {
+ if (app.canAndWantToUpdate(context)) {
+ statusText = context.getString(R.string.app_version_x_available, app.getSuggestedVersionName());
+ } else {
+ statusText = context.getString(R.string.app_version_x_installed, app.installedVersionName);
+ }
+ }
+ return statusText;
+ }
+
+ /**
+ * Shows the currently installed version name, and whether or not it is the recommended version.
+ */
+ public CharSequence getInstalledVersionText() {
+ int res = (app.suggestedVersionCode == app.installedVersionCode)
+ ? R.string.app_recommended_version_installed
+ : R.string.app_version_x_installed;
+
+ return context.getString(res, app.installedVersionName);
+ }
+
+ /**
+ * Shows whether the user has previously asked to ignore updates for this app entirely, or for a
+ * specific version of this app. Binds to the {@link R.id#ignored_status} {@link TextView}.
+ */
+ @Nullable
+ public CharSequence getIgnoredStatusText() {
+ AppPrefs prefs = app.getPrefs(context);
+ if (prefs.ignoreAllUpdates) {
+ return context.getString(R.string.installed_app__updates_ignored);
+ } else if (prefs.ignoreThisUpdate > 0 && prefs.ignoreThisUpdate == app.suggestedVersionCode) {
+ return context.getString(
+ R.string.installed_app__updates_ignored_for_suggested_version,
+ app.getSuggestedVersionName());
+ } else {
+ return null;
+ }
+ }
+
+}