From d0cf6213143c79b26c9dc5c5cce7f1cbbae13726 Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 28 Jun 2017 16:30:42 +1000 Subject: [PATCH 1/4] Refactor the AppListItemControler to be more predictable. Previously, there were different pieces of business logic, invoked at different times, which would touch subsets of the UI. This change rips that out, and replaces it with a single place where the UI is setup. This can always be called safely, and it will render the correct data for the current state of the app (e.g. downloading, waiting for install, etc). The AppListItemState class is a dumb object which keeps track of what is supposed to be displayed in the UI. The AppListItemController now creates a different AppListItemState depending on what state the list item is in. This AppListItemState is then used to bind the values of each UI widget. All of the binding code is now in the single `resetView()` method, but all of the business logic for what the view should look like is separated into different `getViewState*()` methods. This separation should make it easier to make sense of the UI code, and hopefully should be testable should somebody choose to write tests for it in the future. --- .../views/apps/AppListItemController.java | 329 ++++++------------ .../fdroid/views/apps/AppListItemState.java | 141 ++++++++ 2 files changed, 250 insertions(+), 220 deletions(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java 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; + } + } + +} From ef230f749ca186bbb5f85aca8ac18ceda4df75ee Mon Sep 17 00:00:00 2001 From: Peter Serwylo Date: Wed, 28 Jun 2017 17:28:32 +1000 Subject: [PATCH 2/4] Added "Successfully Installed" status text to updates tab. Also: * Extracted common styles into `@Style/AppListItemStatusText`. * Fixed left margin on older devices (where `layout_marginStart` doesn't exist). --- .../views/apps/AppListItemController.java | 19 +++++++++++----- .../fdroid/views/apps/AppListItemState.java | 22 +++++++++++-------- app/src/main/res/layout/app_list_item.xml | 7 +----- .../res/layout/updateable_app_status_item.xml | 12 +++++----- app/src/main/res/values-v16/styles.xml | 3 +++ app/src/main/res/values/styles.xml | 11 ++++++++++ 6 files changed, 46 insertions(+), 28 deletions(-) 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 7a2d6dd9f..8c9ce9acb 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 @@ -66,7 +66,7 @@ public class AppListItemController extends RecyclerView.ViewHolder { private final TextView status; @Nullable - private final TextView downloadReady; + private final TextView appInstallStatus; @Nullable private final TextView installedVersion; @@ -125,7 +125,7 @@ public class AppListItemController extends RecyclerView.ViewHolder { icon = (ImageView) itemView.findViewById(R.id.icon); name = (TextView) itemView.findViewById(R.id.app_name); status = (TextView) itemView.findViewById(R.id.status); - downloadReady = (TextView) itemView.findViewById(R.id.download_ready); + appInstallStatus = (TextView) itemView.findViewById(R.id.app_install_status); installedVersion = (TextView) itemView.findViewById(R.id.installed_version); ignoredStatus = (TextView) itemView.findViewById(R.id.ignored_status); progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar); @@ -207,8 +207,13 @@ public class AppListItemController extends RecyclerView.ViewHolder { name.setText(viewState.getMainText()); - if (downloadReady != null) { - downloadReady.setVisibility(viewState.shouldShowActionButton() ? View.VISIBLE : View.GONE); + if (appInstallStatus != null) { + if (viewState.shouldShowAppInstallStatusText()) { + appInstallStatus.setVisibility(View.VISIBLE); + appInstallStatus.setText(viewState.getAppInstallStatusText()); + } else { + appInstallStatus.setVisibility(View.GONE); + } } if (actionButton != null) { @@ -290,7 +295,9 @@ public class AppListItemController extends RecyclerView.ViewHolder { CharSequence mainText = activity.getString( R.string.app_list__name__successfully_installed, app.name); - AppListItemState state = new AppListItemState(activity, app).setMainText(mainText); + AppListItemState state = new AppListItemState(activity, app) + .setMainText(mainText) + .setAppInstallStatusText(activity.getString(R.string.notification_content_single_installed)); if (activity.getPackageManager().getLaunchIntentForPackage(app.packageName) != null) { state.showActionButton(activity.getString(R.string.menu_launch)); @@ -317,7 +324,7 @@ public class AppListItemController extends RecyclerView.ViewHolder { return new AppListItemState(activity, app) .setMainText(app.name) .showActionButton(activity.getString(actionButtonLabel)) - .setShowDownloadReady(); + .setAppInstallStatusText(activity.getString(R.string.app_list_download_ready)); } private AppListItemState getViewStateDefault(@NonNull App app) { 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 index 4e9e5be0a..89b79a807 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java +++ b/app/src/main/java/org/fdroid/fdroid/views/apps/AppListItemState.java @@ -15,7 +15,7 @@ public class AppListItemState { private final Context context; private final App app; private CharSequence mainText = null; - private boolean showDownloadReady = false; + private CharSequence appInstallStatusText = null; private CharSequence actionButtonText = null; private int progressCurrent = -1; private int progressMax = -1; @@ -30,13 +30,13 @@ public class AppListItemState { return this; } - public AppListItemState setShowDownloadReady() { - this.showDownloadReady = true; + public AppListItemState showActionButton(@NonNull CharSequence label) { + actionButtonText = label; return this; } - public AppListItemState showActionButton(@NonNull CharSequence label) { - actionButtonText = label; + public AppListItemState setAppInstallStatusText(@NonNull CharSequence text) { + appInstallStatusText = text; return this; } @@ -60,10 +60,6 @@ public class AppListItemState { return installable && shouldAllow && !shouldShowActionButton() && !showProgress(); } - public boolean shouldShowDownloadReady() { - return showDownloadReady; - } - public boolean shouldShowActionButton() { return actionButtonText != null; } @@ -88,6 +84,14 @@ public class AppListItemState { return progressMax; } + public boolean shouldShowAppInstallStatusText() { + return appInstallStatusText != null; + } + + public CharSequence getAppInstallStatusText() { + return appInstallStatusText; + } + /** * Sets the text/visibility of the {@link R.id#status} {@link TextView} based on whether the app: * * Is compatible with the users device diff --git a/app/src/main/res/layout/app_list_item.xml b/app/src/main/res/layout/app_list_item.xml index a88464c45..76f60c7c2 100644 --- a/app/src/main/res/layout/app_list_item.xml +++ b/app/src/main/res/layout/app_list_item.xml @@ -46,12 +46,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" tools:text="Installed" - android:textStyle="italic" - android:textSize="14sp" - android:textColor="#424242" - android:maxLines="1" - android:ellipsize="end" - android:fontFamily="sans-serif-light" + style="@style/AppListItemStatusText" app:layout_constraintTop_toBottomOf="@+id/app_name" app:layout_constraintStart_toEndOf="@+id/icon" android:layout_marginStart="8dp" diff --git a/app/src/main/res/layout/updateable_app_status_item.xml b/app/src/main/res/layout/updateable_app_status_item.xml index d4136d7de..e67e47bec 100644 --- a/app/src/main/res/layout/updateable_app_status_item.xml +++ b/app/src/main/res/layout/updateable_app_status_item.xml @@ -36,20 +36,18 @@ app:layout_constraintBottom_toBottomOf="parent" android:layout_marginBottom="8dp" android:layout_marginStart="8dp" + android:layout_marginLeft="8dp" android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" android:layout_marginTop="8dp" app:layout_constraintVertical_bias="0.333" /> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index bb040bf38..56e10ffd1 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -121,6 +121,17 @@ ?attr/lightGrayTextColor + + +