Merge branch 'issue-1131--swipe-to-dismiss-updates' into 'master'

Swipe to dismiss updates

Closes #1131

See merge request fdroid/fdroidclient!584
This commit is contained in:
Hans-Christoph Steiner 2017-09-27 20:59:09 +00:00
commit 695394fe71
10 changed files with 276 additions and 33 deletions

View File

@ -32,6 +32,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
@ -41,6 +42,7 @@ import org.fdroid.fdroid.installer.ApkCache;
import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.installer.Installer; import org.fdroid.fdroid.installer.Installer;
import org.fdroid.fdroid.installer.InstallerFactory; import org.fdroid.fdroid.installer.InstallerFactory;
import org.fdroid.fdroid.views.updates.DismissResult;
import java.io.File; import java.io.File;
import java.util.Iterator; import java.util.Iterator;
@ -57,7 +59,7 @@ import java.util.Iterator;
* </ul> * </ul>
* *
* The state of the UI is defined in a dumb {@link AppListItemState} class, then applied to the UI * The state of the UI is defined in a dumb {@link AppListItemState} class, then applied to the UI
* in the {@link #refreshView(App, AppUpdateStatusManager.AppUpdateStatus)} method. * in the {@link #refreshView(App, AppUpdateStatus)} method.
*/ */
public abstract class AppListItemController extends RecyclerView.ViewHolder { public abstract class AppListItemController extends RecyclerView.ViewHolder {
@ -102,7 +104,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
private App currentApp; private App currentApp;
@Nullable @Nullable
private AppUpdateStatusManager.AppUpdateStatus currentStatus; private AppUpdateStatus currentStatus;
@TargetApi(21) @TargetApi(21)
public AppListItemController(final Activity activity, View itemView) { public AppListItemController(final Activity activity, View itemView) {
@ -159,6 +161,11 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
itemView.setOnClickListener(onAppClicked); itemView.setOnClickListener(onAppClicked);
} }
@Nullable
protected final AppUpdateStatus getCurrentStatus() {
return currentStatus;
}
public void bindModel(@NonNull App app) { public void bindModel(@NonNull App app) {
currentApp = app; currentApp = app;
@ -166,10 +173,10 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
// Figures out the current install/update/download/etc status for the app we are viewing. // Figures out the current install/update/download/etc status for the app we are viewing.
// Then, asks the view to update itself to reflect this status. // Then, asks the view to update itself to reflect this status.
Iterator<AppUpdateStatusManager.AppUpdateStatus> statuses = Iterator<AppUpdateStatus> statuses =
AppUpdateStatusManager.getInstance(activity).getByPackageName(app.packageName).iterator(); AppUpdateStatusManager.getInstance(activity).getByPackageName(app.packageName).iterator();
if (statuses.hasNext()) { if (statuses.hasNext()) {
AppUpdateStatusManager.AppUpdateStatus status = statuses.next(); AppUpdateStatus status = statuses.next();
updateAppStatus(app, status); updateAppStatus(app, status);
} else { } else {
updateAppStatus(app, null); updateAppStatus(app, null);
@ -186,27 +193,56 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
broadcastManager.registerReceiver(onStatusChanged, intentFilter); broadcastManager.registerReceiver(onStatusChanged, intentFilter);
} }
/** To be overridden if required */
public boolean canDismiss() {
return false;
}
/**
* If able, forwards the request onto {@link #onDismissApp(App)}.
* 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() {
if (currentApp != null && canDismiss()) {
return onDismissApp(currentApp);
}
return new DismissResult();
}
/**
* Override to respond to the user swiping an app to dismiss it from the list.
* @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();
}
/** /**
* Updates both the progress bar and the circular install button (which shows progress around the outside of * 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. * the circle). Also updates the app label to indicate that the app is being downloaded.
*/ */
private void updateAppStatus(@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus status) { private void updateAppStatus(@NonNull App app, @Nullable AppUpdateStatus status) {
currentStatus = status; currentStatus = status;
refreshView(app, status); refreshView(app, status);
} }
/** /**
* Queries the current state via {@link #getCurrentViewState(App, AppUpdateStatusManager.AppUpdateStatus)} * Queries the current state via {@link #getCurrentViewState(App, AppUpdateStatus)}
* and then updates the relevant widgets depending on that state. * and then updates the relevant widgets depending on that state.
* *
* Should contain little to no business logic, this all belongs to * Should contain little to no business logic, this all belongs to
* {@link #getCurrentViewState(App, AppUpdateStatusManager.AppUpdateStatus)}. * {@link #getCurrentViewState(App, AppUpdateStatus)}.
* *
* @see AppListItemState * @see AppListItemState
* @see #getCurrentViewState(App, AppUpdateStatusManager.AppUpdateStatus) * @see #getCurrentViewState(App, AppUpdateStatus)
*/ */
private void refreshView(@NonNull App app, private void refreshView(@NonNull App app, @Nullable AppUpdateStatus appStatus) {
@Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
AppListItemState viewState = getCurrentViewState(app, appStatus); AppListItemState viewState = getCurrentViewState(app, appStatus);
@ -292,8 +328,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
} }
@NonNull @NonNull
protected AppListItemState getCurrentViewState( protected AppListItemState getCurrentViewState(@NonNull App app, @Nullable AppUpdateStatus appStatus) {
@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
if (appStatus == null) { if (appStatus == null) {
return getViewStateDefault(app); return getViewStateDefault(app);
} else { } else {
@ -328,8 +363,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
return state; return state;
} }
protected AppListItemState getViewStateDownloading( protected AppListItemState getViewStateDownloading(@NonNull App app, @NonNull AppUpdateStatus currentStatus) {
@NonNull App app, @NonNull AppUpdateStatusManager.AppUpdateStatus currentStatus) {
CharSequence mainText = activity.getString( CharSequence mainText = activity.getString(
R.string.app_list__name__downloading_in_progress, app.name); R.string.app_list__name__downloading_in_progress, app.name);
@ -384,8 +418,7 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
private final BroadcastReceiver onStatusChanged = new BroadcastReceiver() { private final BroadcastReceiver onStatusChanged = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
AppUpdateStatusManager.AppUpdateStatus newStatus = AppUpdateStatus newStatus = intent.getParcelableExtra(AppUpdateStatusManager.EXTRA_STATUS);
intent.getParcelableExtra(AppUpdateStatusManager.EXTRA_STATUS);
if (currentApp == null if (currentApp == null
|| !TextUtils.equals(newStatus.app.packageName, currentApp.packageName) || !TextUtils.equals(newStatus.app.packageName, currentApp.packageName)
@ -477,11 +510,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
private final View.OnClickListener onCancelDownload = new View.OnClickListener() { private final View.OnClickListener onCancelDownload = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (currentStatus == null || currentStatus.status != AppUpdateStatusManager.Status.Downloading) { cancelDownload();
return;
}
InstallManagerService.cancel(activity, currentStatus.getUniqueKey());
} }
}; };
protected final void cancelDownload() {
if (currentStatus == null || currentStatus.status != AppUpdateStatusManager.Status.Downloading) {
return;
}
InstallManagerService.cancel(activity, currentStatus.getUniqueKey());
}
} }

View File

@ -23,6 +23,7 @@ import com.ashokvarma.bottomnavigation.BottomNavigationItem;
import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.NfcHelper; import org.fdroid.fdroid.NfcHelper;
import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Preferences;
@ -106,9 +107,9 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
.addItem(new BottomNavigationItem(R.drawable.ic_settings, R.string.menu_settings)) .addItem(new BottomNavigationItem(R.drawable.ic_settings, R.string.menu_settings))
.initialise(); .initialise();
IntentFilter updateableAppsFilter = new IntentFilter( IntentFilter updateableAppsFilter = new IntentFilter(AppUpdateStatusManager.BROADCAST_APPSTATUS_LIST_CHANGED);
AppUpdateStatusManager.BROADCAST_APPSTATUS_LIST_CHANGED);
updateableAppsFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED); updateableAppsFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_CHANGED);
updateableAppsFilter.addAction(AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED);
LocalBroadcastManager.getInstance(this).registerReceiver(onUpdateableAppsChanged, updateableAppsFilter); LocalBroadcastManager.getInstance(this).registerReceiver(onUpdateableAppsChanged, updateableAppsFilter);
if (savedInstanceState != null) { if (savedInstanceState != null) {
@ -378,15 +379,28 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(context); AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(context);
String reason = intent.getStringExtra(AppUpdateStatusManager.EXTRA_REASON_FOR_CHANGE); String reason = intent.getStringExtra(AppUpdateStatusManager.EXTRA_REASON_FOR_CHANGE);
if (AppUpdateStatusManager.BROADCAST_APPSTATUS_LIST_CHANGED.equals(intent.getAction()) && switch (intent.getAction()) {
(AppUpdateStatusManager.REASON_READY_TO_INSTALL.equals(reason) || // Apps which are added/removed from the list due to becoming ready to install or a repo being
AppUpdateStatusManager.REASON_REPO_DISABLED.equals(reason))) { // disabled both cause us to increase/decrease our badge count respectively.
updateBadge = true; case AppUpdateStatusManager.BROADCAST_APPSTATUS_LIST_CHANGED:
if (AppUpdateStatusManager.REASON_READY_TO_INSTALL.equals(reason) ||
AppUpdateStatusManager.REASON_REPO_DISABLED.equals(reason)) {
updateBadge = true;
}
break;
// Apps which were previously "Ready to install" but have been removed. We need to lower our badge
// count in response to this.
case AppUpdateStatusManager.BROADCAST_APPSTATUS_REMOVED:
AppUpdateStatus status = intent.getParcelableExtra(AppUpdateStatusManager.EXTRA_STATUS);
if (status != null && status.status == AppUpdateStatusManager.Status.ReadyToInstall) {
updateBadge = true;
}
break;
} }
// Check if we have moved into the ReadyToInstall or Installed state. // Check if we have moved into the ReadyToInstall or Installed state.
AppUpdateStatusManager.AppUpdateStatus status = manager.get( AppUpdateStatus status = manager.get(intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL));
intent.getStringExtra(AppUpdateStatusManager.EXTRA_APK_URL));
boolean isStatusChange = intent.getBooleanExtra(AppUpdateStatusManager.EXTRA_IS_STATUS_UPDATE, false); boolean isStatusChange = intent.getBooleanExtra(AppUpdateStatusManager.EXTRA_IS_STATUS_UPDATE, false);
if (isStatusChange if (isStatusChange
&& status != null && status != null
@ -396,7 +410,7 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB
if (updateBadge) { if (updateBadge) {
int count = 0; int count = 0;
for (AppUpdateStatusManager.AppUpdateStatus s : manager.getAll()) { for (AppUpdateStatus s : manager.getAll()) {
if (s.status == AppUpdateStatusManager.Status.ReadyToInstall) { if (s.status == AppUpdateStatusManager.Status.ReadyToInstall) {
count++; count++;
} }

View File

@ -0,0 +1,29 @@
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(boolean requiresAdapterRefresh) {
this(null, requiresAdapterRefresh);
}
public DismissResult(@Nullable CharSequence message, boolean requiresAdapterRefresh) {
this.message = message;
this.requiresAdapterRefresh = requiresAdapterRefresh;
}
}

View File

@ -341,4 +341,11 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
} }
}; };
/**
* If an item representing an {@link org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus} is dismissed,
* then we should rebuild the list of app statuses and update the adapter.
*/
public void refreshStatuses() {
onAppStatusRemoved();
}
} }

View File

@ -0,0 +1,81 @@
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;
import org.fdroid.fdroid.views.updates.items.KnownVulnAppListItemController;
import org.fdroid.fdroid.views.updates.items.UpdateableAppListItemController;
/**
* Certain views within the {@link UpdatesAdapter} can be swiped to dismiss. Depending on which item is swiped, there
* is a different behaviour, but all of it revolves around dismissing the item.
*
* <ul>
* <li>
* {@link KnownVulnAppListItemController}: Will be marked as "Ignored" and wont warn the user in the future.
* </li>
* <li>
* {@link UpdateableAppListItemController}: Will get marked as "Ignore this update".
* </li>
* <li>
* {@link AppStatusListItemController}:
* <ul>
* <li>If downloading or queued to download, cancel the download.</li>
* <li>If downloaded waiting to install, forget that we downloaded it.</li>
* <li>If installed ready to run, stop prompting the user to run the app.</li>
* </ul>
* <li>
* </ul>
*/
public class UpdatesItemTouchCallback extends ItemTouchHelper.Callback {
private final Context context;
private final UpdatesAdapter adapter;
public UpdatesItemTouchCallback(Context context, UpdatesAdapter adapter) {
this.context = context;
this.adapter = adapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int swipeFlags = 0;
if (viewHolder instanceof AppListItemController) {
AppListItemController controller = (AppListItemController) viewHolder;
if (controller.canDismiss()) {
swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
}
}
return makeMovementFlags(0, swipeFlags);
}
@Override
public boolean onMove(
RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@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();
}
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
}

View File

@ -4,6 +4,7 @@ import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
@ -30,6 +31,9 @@ public class UpdatesViewBinder {
list.setLayoutManager(new LinearLayoutManager(activity)); list.setLayoutManager(new LinearLayoutManager(activity));
list.setAdapter(adapter); list.setAdapter(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(new UpdatesItemTouchCallback(activity, adapter));
touchHelper.attachToRecyclerView(list);
emptyState = (TextView) view.findViewById(R.id.empty_state); emptyState = (TextView) view.findViewById(R.id.empty_state);
emptyImage = (ImageView) view.findViewById(R.id.image); emptyImage = (ImageView) view.findViewById(R.id.image);

View File

@ -6,10 +6,12 @@ import android.support.annotation.Nullable;
import android.view.View; import android.view.View;
import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.AppUpdateStatusManager.AppUpdateStatus;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.apps.AppListItemController;
import org.fdroid.fdroid.views.apps.AppListItemState; import org.fdroid.fdroid.views.apps.AppListItemState;
import org.fdroid.fdroid.views.updates.DismissResult;
/** /**
* Shows apps which are: * Shows apps which are:
@ -24,15 +26,14 @@ public class AppStatusListItemController extends AppListItemController {
@NonNull @NonNull
@Override @Override
protected AppListItemState getCurrentViewState( protected AppListItemState getCurrentViewState(@NonNull App app, @Nullable AppUpdateStatus appStatus) {
@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
return super.getCurrentViewState(app, appStatus) return super.getCurrentViewState(app, appStatus)
.setStatusText(getStatusText(appStatus)); .setStatusText(getStatusText(appStatus));
} }
@Nullable @Nullable
private CharSequence getStatusText(@Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) { private CharSequence getStatusText(@Nullable AppUpdateStatus appStatus) {
if (appStatus != null) { if (appStatus != null) {
switch (appStatus.status) { switch (appStatus.status) {
case ReadyToInstall: case ReadyToInstall:
@ -45,4 +46,34 @@ public class AppStatusListItemController extends AppListItemController {
return null; return null;
} }
@Override
public boolean canDismiss() {
return true;
}
@NonNull
@Override
protected DismissResult onDismissApp(@NonNull App app) {
AppUpdateStatus status = getCurrentStatus();
CharSequence message = null;
if (status != null) {
AppUpdateStatusManager manager = AppUpdateStatusManager.getInstance(activity);
manager.removeApk(status.getUniqueKey());
switch (status.status) {
case ReadyToInstall:
manager.markAsNoLongerPendingInstall(status);
// Do this silently, because it should be pretty obvious based on the context
// of a "Ready to install" app being dismissed.
break;
case Downloading:
cancelDownload();
message = activity.getString(R.string.app_list__dismiss_downloading_app);
break;
}
}
return new DismissResult(message, true);
}
} }

View File

@ -24,6 +24,7 @@ import org.fdroid.fdroid.installer.Installer;
import org.fdroid.fdroid.installer.InstallerService; import org.fdroid.fdroid.installer.InstallerService;
import org.fdroid.fdroid.views.apps.AppListItemController; import org.fdroid.fdroid.views.apps.AppListItemController;
import org.fdroid.fdroid.views.apps.AppListItemState; import org.fdroid.fdroid.views.apps.AppListItemState;
import org.fdroid.fdroid.views.updates.DismissResult;
/** /**
* Tell the user that an app they have installed has a known vulnerability. * Tell the user that an app they have installed has a known vulnerability.
@ -82,8 +83,23 @@ public class KnownVulnAppListItemController extends AppListItemController {
} }
} }
@Override
public boolean canDismiss() {
return true;
}
@Override
protected DismissResult onDismissApp(@NonNull App app) {
this.ignoreVulnerableApp(app);
return new DismissResult(activity.getString(R.string.app_list__dismiss_vulnerable_app), false);
}
@Override @Override
protected void onSecondaryButtonPressed(@NonNull App app) { protected void onSecondaryButtonPressed(@NonNull App app) {
this.ignoreVulnerableApp(app);
}
private void ignoreVulnerableApp(@NonNull App app) {
AppPrefs prefs = app.getPrefs(activity); AppPrefs prefs = app.getPrefs(activity);
prefs.ignoreVulnerabilities = true; prefs.ignoreVulnerabilities = true;
AppPrefsProvider.Helper.update(activity, app, prefs); AppPrefsProvider.Helper.update(activity, app, prefs);

View File

@ -6,9 +6,13 @@ import android.support.annotation.Nullable;
import android.view.View; import android.view.View;
import org.fdroid.fdroid.AppUpdateStatusManager; import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
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.AppListItemController;
import org.fdroid.fdroid.views.apps.AppListItemState; import org.fdroid.fdroid.views.apps.AppListItemState;
import org.fdroid.fdroid.views.updates.DismissResult;
/** /**
* Very trimmed down list item. Only displays the app icon, name, and a download button. * Very trimmed down list item. Only displays the app icon, name, and a download button.
@ -28,4 +32,21 @@ public class UpdateableAppListItemController extends AppListItemController {
return new AppListItemState(app) return new AppListItemState(app)
.setShowInstallButton(true); .setShowInstallButton(true);
} }
@Override
public boolean canDismiss() {
return true;
}
@Override
@NonNull
protected DismissResult onDismissApp(@NonNull App app) {
AppPrefs prefs = app.getPrefs(activity);
prefs.ignoreThisUpdate = app.suggestedVersionCode;
// 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);
}
} }

View File

@ -87,6 +87,9 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="app_list__name__downloading_in_progress">Downloading %1$s</string> <string name="app_list__name__downloading_in_progress">Downloading %1$s</string>
<string name="app_list__name__successfully_installed">%1$s installed</string> <string name="app_list__name__successfully_installed">%1$s installed</string>
<string name="app_list_download_ready">Downloaded, ready to install</string> <string name="app_list_download_ready">Downloaded, ready to install</string>
<string name="app_list__dismiss_app_update">Update ignored</string>
<string name="app_list__dismiss_vulnerable_app">Vulnerability ignored</string>
<string name="app_list__dismiss_downloading_app">Download canceled</string>
<string name="installed_apps__activity_title">Installed Apps</string> <string name="installed_apps__activity_title">Installed Apps</string>
<string name="installed_app__updates_ignored">Updates ignored</string> <string name="installed_app__updates_ignored">Updates ignored</string>