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 7801ff82e..67be68ead 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 @@ -26,7 +26,9 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; + import com.nostra13.universalimageloader.core.ImageLoader; + import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus; import org.fdroid.fdroid.R; @@ -39,7 +41,7 @@ import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.InstallerFactory; import org.fdroid.fdroid.views.AppDetailsActivity; -import org.fdroid.fdroid.views.updates.DismissResult; +import org.fdroid.fdroid.views.updates.UpdatesAdapter; import java.io.File; import java.util.Iterator; @@ -206,29 +208,24 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder { } /** - * If able, forwards the request onto {@link #onDismissApp(App)}. + * If able, forwards the request onto {@link #onDismissApp(App, UpdatesAdapter)}. * This mainly exists to keep the API consistent, in that the {@link App} is threaded through to the relevant * method with a guarantee that it is not null, rather than every method having to check if it is null or not. */ - @NonNull - public final DismissResult onDismiss() { + public final void onDismiss(UpdatesAdapter adapter) { if (currentApp != null && canDismiss()) { - return onDismissApp(currentApp); + onDismissApp(currentApp, adapter); } - - return new DismissResult(); } /** * Override to respond to the user swiping an app to dismiss it from the list. + * @param app The app that was swiped away + * @param updatesAdapter The adapter. Can be used for refreshing the adapter with adapter.refreshStatuses(). * - * @return Optionally return a description of what you did if it is not obvious to the user. It will be shown as - * a {@link android.widget.Toast} for a {@link android.widget.Toast#LENGTH_SHORT} time. * @see #canDismiss() This must also be overriden and should return true. */ - @NonNull - protected DismissResult onDismissApp(@NonNull App app) { - return new DismissResult(); + protected void onDismissApp(@NonNull App app, UpdatesAdapter updatesAdapter) { } /** diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/DismissResult.java b/app/src/main/java/org/fdroid/fdroid/views/updates/DismissResult.java deleted file mode 100644 index f37d58fb4..000000000 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/DismissResult.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.fdroid.fdroid.views.updates; - -import android.support.annotation.Nullable; - -/** - * When dismissing an item from the Updates tab, there is two different things we need to return. - * This is a dumb data object to represent these things, because a method is only allowed to return one thing. - */ -public class DismissResult { - - @Nullable - public final CharSequence message; - - public final boolean requiresAdapterRefresh; - - public DismissResult() { - this(null, false); - } - - public DismissResult(@Nullable CharSequence message, boolean requiresAdapterRefresh) { - this.message = message; - this.requiresAdapterRefresh = requiresAdapterRefresh; - } - -} diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesItemTouchCallback.java b/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesItemTouchCallback.java index 9d7f40296..cde7ae3ac 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesItemTouchCallback.java +++ b/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesItemTouchCallback.java @@ -1,9 +1,7 @@ package org.fdroid.fdroid.views.updates; -import android.content.Context; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; -import android.widget.Toast; import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.updates.items.AppStatusListItemController; @@ -33,11 +31,9 @@ import org.fdroid.fdroid.views.updates.items.UpdateableAppListItemController; */ public class UpdatesItemTouchCallback extends ItemTouchHelper.Callback { - private final Context context; private final UpdatesAdapter adapter; - public UpdatesItemTouchCallback(Context context, UpdatesAdapter adapter) { - this.context = context; + public UpdatesItemTouchCallback(UpdatesAdapter adapter) { this.adapter = adapter; } @@ -62,15 +58,7 @@ public class UpdatesItemTouchCallback extends ItemTouchHelper.Callback { @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { AppListItemController controller = (AppListItemController) viewHolder; - DismissResult result = controller.onDismiss(); - - if (result.message != null) { - Toast.makeText(context, result.message, Toast.LENGTH_SHORT).show(); - } - - if (result.requiresAdapterRefresh) { - adapter.refreshStatuses(); - } + controller.onDismiss(adapter); } @Override diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesViewBinder.java b/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesViewBinder.java index 4e763741b..1a8bf2a56 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesViewBinder.java +++ b/app/src/main/java/org/fdroid/fdroid/views/updates/UpdatesViewBinder.java @@ -32,7 +32,7 @@ public class UpdatesViewBinder { list.setLayoutManager(new LinearLayoutManager(activity)); list.setAdapter(adapter); - ItemTouchHelper touchHelper = new ItemTouchHelper(new UpdatesItemTouchCallback(activity, adapter)); + ItemTouchHelper touchHelper = new ItemTouchHelper(new UpdatesItemTouchCallback(adapter)); touchHelper.attachToRecyclerView(list); emptyState = (TextView) view.findViewById(R.id.empty_state); diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/items/AppStatusListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/updates/items/AppStatusListItemController.java index 2f612ca16..34ee0d502 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/items/AppStatusListItemController.java +++ b/app/src/main/java/org/fdroid/fdroid/views/updates/items/AppStatusListItemController.java @@ -3,6 +3,7 @@ package org.fdroid.fdroid.views.updates.items; import android.app.Activity; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import android.view.View; import org.fdroid.fdroid.AppUpdateStatusManager; @@ -11,7 +12,7 @@ import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.apps.AppListItemState; -import org.fdroid.fdroid.views.updates.DismissResult; +import org.fdroid.fdroid.views.updates.UpdatesAdapter; /** * Shows apps which are: @@ -52,22 +53,41 @@ public class AppStatusListItemController extends AppListItemController { return true; } - @NonNull @Override - protected DismissResult onDismissApp(@NonNull App app) { + protected void onDismissApp(@NonNull final App app, final UpdatesAdapter adapter) { AppUpdateStatus status = getCurrentStatus(); - CharSequence message = null; if (status != null) { - AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(activity); + final AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(activity); + final AppUpdateStatus appUpdateStatus = manager.get(status.getCanonicalUrl()); manager.removeApk(status.getCanonicalUrl()); + + switch (status.status) { + case Downloading: cancelDownload(); - message = activity.getString(R.string.app_list__dismiss_downloading_app); + Snackbar.make(itemView, R.string.app_list__dismiss_downloading_app, Snackbar.LENGTH_SHORT).show(); break; + + case ReadyToInstall: + if (appUpdateStatus != null) { + Snackbar.make( + itemView, + R.string.app_list__dismiss_installing_app, + Snackbar.LENGTH_LONG + ).setAction(R.string.undo, new View.OnClickListener() { + @Override + public void onClick(View view) { + manager.addApk(appUpdateStatus.apk, appUpdateStatus.status, appUpdateStatus.intent); + adapter.refreshStatuses(); + } + }).show(); + break; + } } } - return new DismissResult(message, true); + adapter.refreshStatuses(); } + } diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/items/KnownVulnAppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/updates/items/KnownVulnAppListItemController.java index 0d1ac37dd..451f0118a 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/items/KnownVulnAppListItemController.java +++ b/app/src/main/java/org/fdroid/fdroid/views/updates/items/KnownVulnAppListItemController.java @@ -7,8 +7,10 @@ import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import android.support.v4.content.LocalBroadcastManager; import android.view.View; + import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.R; import org.fdroid.fdroid.data.Apk; @@ -22,7 +24,7 @@ import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.apps.AppListItemState; -import org.fdroid.fdroid.views.updates.DismissResult; +import org.fdroid.fdroid.views.updates.UpdatesAdapter; /** * Tell the user that an app they have installed has a known vulnerability. @@ -86,11 +88,9 @@ public class KnownVulnAppListItemController extends AppListItemController { return true; } - @NonNull @Override - protected DismissResult onDismissApp(@NonNull App app) { + protected void onDismissApp(@NonNull final App app, UpdatesAdapter adapter) { this.ignoreVulnerableApp(app); - return new DismissResult(activity.getString(R.string.app_list__dismiss_vulnerable_app), false); } @Override @@ -98,9 +98,26 @@ public class KnownVulnAppListItemController extends AppListItemController { this.ignoreVulnerableApp(app); } - private void ignoreVulnerableApp(@NonNull App app) { + private void ignoreVulnerableApp(@NonNull final App app) { + setIgnoreVulnerableApp(app, true); + + Snackbar.make( + itemView, + R.string.app_list__dismiss_vulnerable_app, + Snackbar.LENGTH_LONG + ) + .setAction(R.string.undo, new View.OnClickListener() { + @Override + public void onClick(View view) { + setIgnoreVulnerableApp(app, false); + } + }) + .show(); + } + + private void setIgnoreVulnerableApp(@NonNull App app, boolean ignore) { AppPrefs prefs = app.getPrefs(activity); - prefs.ignoreVulnerabilities = true; + prefs.ignoreVulnerabilities = ignore; AppPrefsProvider.Helper.update(activity, app, prefs); refreshUpdatesList(); } diff --git a/app/src/main/java/org/fdroid/fdroid/views/updates/items/UpdateableAppListItemController.java b/app/src/main/java/org/fdroid/fdroid/views/updates/items/UpdateableAppListItemController.java index 5baa5f89e..34c3bc6df 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/updates/items/UpdateableAppListItemController.java +++ b/app/src/main/java/org/fdroid/fdroid/views/updates/items/UpdateableAppListItemController.java @@ -3,6 +3,7 @@ package org.fdroid.fdroid.views.updates.items; import android.app.Activity; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; import android.view.View; import org.fdroid.fdroid.AppUpdateStatusManager; @@ -12,7 +13,7 @@ import org.fdroid.fdroid.data.AppPrefs; import org.fdroid.fdroid.data.AppPrefsProvider; import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.apps.AppListItemState; -import org.fdroid.fdroid.views.updates.DismissResult; +import org.fdroid.fdroid.views.updates.UpdatesAdapter; /** * Very trimmed down list item. Only displays the app icon, name, and a download button. @@ -39,14 +40,27 @@ public class UpdateableAppListItemController extends AppListItemController { } @Override - @NonNull - protected DismissResult onDismissApp(@NonNull App app) { - AppPrefs prefs = app.getPrefs(activity); + protected void onDismissApp(@NonNull final App app, UpdatesAdapter adapter) { + final AppPrefs prefs = app.getPrefs(activity); prefs.ignoreThisUpdate = app.suggestedVersionCode; + Snackbar.make( + itemView, + R.string.app_list__dismiss_app_update, + Snackbar.LENGTH_LONG + ) + .setAction(R.string.undo, new View.OnClickListener() { + @Override + public void onClick(View view) { + prefs.ignoreThisUpdate = 0; + AppPrefsProvider.Helper.update(activity, app, prefs); + } + }) + .show(); + + // The act of updating here will trigger a re-query of the "can update" apps, so no need to do anything else // to update the UI in response to this. AppPrefsProvider.Helper.update(activity, app, prefs); - return new DismissResult(activity.getString(R.string.app_list__dismiss_app_update), false); } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 06104328a..2137c088b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -619,7 +619,9 @@ This often occurs with apps installed via Google Play or other sources, if they <string name="details_last_updated_today">Updated today</string> <string name="warning_scaning_qr_code">Your camera doesn\'t seem to have an autofocus. It might be difficult to scan the code.</string> - <plurals name="details_last_update_days"> + <string name="undo">Undo</string> + <string name="app_list__dismiss_installing_app">Installation cancelled</string> + <plurals name="details_last_update_days"> <item quantity="one">Updated %1$d day ago</item> <item quantity="other">Updated %1$d days ago</item> </plurals>