Add apps with known vulnerabilities to updates tab.
Currently only supports "Uninstall", but will soon also support "Upgrade to a newer version".
This commit is contained in:
parent
9b20142fd9
commit
7424220c02
@ -388,55 +388,59 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the button says "Run", then launch the app.
|
onActionButtonPressed(currentApp);
|
||||||
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) {
|
|
||||||
Intent intent = activity.getPackageManager().getLaunchIntentForPackage(currentApp.packageName);
|
|
||||||
if (intent != null) {
|
|
||||||
activity.startActivity(intent);
|
|
||||||
|
|
||||||
// Once it is explicitly launched by the user, then we can pretty much forget about
|
|
||||||
// any sort of notification that the app was successfully installed. It should be
|
|
||||||
// apparent to the user because they just launched it.
|
|
||||||
AppUpdateStatusManager.getInstance(activity).removeApk(currentStatus.getUniqueKey());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
|
|
||||||
Uri apkDownloadUri = Uri.parse(currentStatus.apk.getUrl());
|
|
||||||
File apkFilePath = ApkCache.getApkDownloadPath(activity, apkDownloadUri);
|
|
||||||
Utils.debugLog(TAG, "skip download, we have already downloaded " + currentStatus.apk.getUrl() +
|
|
||||||
" to " + apkFilePath);
|
|
||||||
|
|
||||||
// TODO: This seems like a bit of a hack. Is there a better way to do this by changing
|
|
||||||
// the Installer API so that we can ask it to install without having to get it to fire
|
|
||||||
// off an intent which we then listen for and action?
|
|
||||||
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity);
|
|
||||||
final BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
broadcastManager.unregisterReceiver(this);
|
|
||||||
|
|
||||||
if (Installer.ACTION_INSTALL_USER_INTERACTION.equals(intent.getAction())) {
|
|
||||||
PendingIntent pendingIntent =
|
|
||||||
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
|
||||||
try {
|
|
||||||
pendingIntent.send();
|
|
||||||
} catch (PendingIntent.CanceledException ignored) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
broadcastManager.registerReceiver(receiver, Installer.getInstallIntentFilter(apkDownloadUri));
|
|
||||||
Installer installer = InstallerFactory.create(activity, currentStatus.apk);
|
|
||||||
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), apkDownloadUri);
|
|
||||||
} else {
|
|
||||||
final Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, currentApp);
|
|
||||||
InstallManagerService.queue(activity, currentApp, suggestedApk);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected void onActionButtonPressed(@NonNull App app) {
|
||||||
|
// When the button says "Run", then launch the app.
|
||||||
|
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.Installed) {
|
||||||
|
Intent intent = activity.getPackageManager().getLaunchIntentForPackage(app.packageName);
|
||||||
|
if (intent != null) {
|
||||||
|
activity.startActivity(intent);
|
||||||
|
|
||||||
|
// Once it is explicitly launched by the user, then we can pretty much forget about
|
||||||
|
// any sort of notification that the app was successfully installed. It should be
|
||||||
|
// apparent to the user because they just launched it.
|
||||||
|
AppUpdateStatusManager.getInstance(activity).removeApk(currentStatus.getUniqueKey());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStatus != null && currentStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
|
||||||
|
Uri apkDownloadUri = Uri.parse(currentStatus.apk.getUrl());
|
||||||
|
File apkFilePath = ApkCache.getApkDownloadPath(activity, apkDownloadUri);
|
||||||
|
Utils.debugLog(TAG, "skip download, we have already downloaded " + currentStatus.apk.getUrl() +
|
||||||
|
" to " + apkFilePath);
|
||||||
|
|
||||||
|
// TODO: This seems like a bit of a hack. Is there a better way to do this by changing
|
||||||
|
// the Installer API so that we can ask it to install without having to get it to fire
|
||||||
|
// off an intent which we then listen for and action?
|
||||||
|
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity);
|
||||||
|
final BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
broadcastManager.unregisterReceiver(this);
|
||||||
|
|
||||||
|
if (Installer.ACTION_INSTALL_USER_INTERACTION.equals(intent.getAction())) {
|
||||||
|
PendingIntent pendingIntent =
|
||||||
|
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
||||||
|
try {
|
||||||
|
pendingIntent.send();
|
||||||
|
} catch (PendingIntent.CanceledException ignored) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
broadcastManager.registerReceiver(receiver, Installer.getInstallIntentFilter(apkDownloadUri));
|
||||||
|
Installer installer = InstallerFactory.create(activity, currentStatus.apk);
|
||||||
|
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), apkDownloadUri);
|
||||||
|
} else {
|
||||||
|
final Apk suggestedApk = ApkProvider.Helper.findSuggestedApk(activity, app);
|
||||||
|
InstallManagerService.queue(activity, app, suggestedApk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
private final View.OnClickListener onCancelDownload = new View.OnClickListener() {
|
private final View.OnClickListener onCancelDownload = new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.LoaderManager;
|
import android.support.v4.app.LoaderManager;
|
||||||
import android.support.v4.content.CursorLoader;
|
import android.support.v4.content.CursorLoader;
|
||||||
@ -20,6 +21,7 @@ import org.fdroid.fdroid.data.AppProvider;
|
|||||||
import org.fdroid.fdroid.data.Schema;
|
import org.fdroid.fdroid.data.Schema;
|
||||||
import org.fdroid.fdroid.views.updates.items.AppStatus;
|
import org.fdroid.fdroid.views.updates.items.AppStatus;
|
||||||
import org.fdroid.fdroid.views.updates.items.AppUpdateData;
|
import org.fdroid.fdroid.views.updates.items.AppUpdateData;
|
||||||
|
import org.fdroid.fdroid.views.updates.items.KnownVulnApp;
|
||||||
import org.fdroid.fdroid.views.updates.items.UpdateableApp;
|
import org.fdroid.fdroid.views.updates.items.UpdateableApp;
|
||||||
import org.fdroid.fdroid.views.updates.items.UpdateableAppsHeader;
|
import org.fdroid.fdroid.views.updates.items.UpdateableAppsHeader;
|
||||||
|
|
||||||
@ -65,6 +67,9 @@ import java.util.Set;
|
|||||||
public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||||
implements LoaderManager.LoaderCallbacks<Cursor> {
|
implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
|
private static final int LOADER_CAN_UPDATE = 289753982;
|
||||||
|
private static final int LOADER_KNOWN_VULN = 520389740;
|
||||||
|
|
||||||
private final AdapterDelegatesManager<List<AppUpdateData>> delegatesManager = new AdapterDelegatesManager<>();
|
private final AdapterDelegatesManager<List<AppUpdateData>> delegatesManager = new AdapterDelegatesManager<>();
|
||||||
private final List<AppUpdateData> items = new ArrayList<>();
|
private final List<AppUpdateData> items = new ArrayList<>();
|
||||||
|
|
||||||
@ -72,6 +77,7 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
|
|
||||||
private final List<AppStatus> appsToShowStatus = new ArrayList<>();
|
private final List<AppStatus> appsToShowStatus = new ArrayList<>();
|
||||||
private final List<UpdateableApp> updateableApps = new ArrayList<>();
|
private final List<UpdateableApp> updateableApps = new ArrayList<>();
|
||||||
|
private final List<KnownVulnApp> knownVulnApps = new ArrayList<>();
|
||||||
|
|
||||||
private boolean showAllUpdateableApps = false;
|
private boolean showAllUpdateableApps = false;
|
||||||
|
|
||||||
@ -80,9 +86,11 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
|
|
||||||
delegatesManager.addDelegate(new AppStatus.Delegate(activity))
|
delegatesManager.addDelegate(new AppStatus.Delegate(activity))
|
||||||
.addDelegate(new UpdateableApp.Delegate(activity))
|
.addDelegate(new UpdateableApp.Delegate(activity))
|
||||||
.addDelegate(new UpdateableAppsHeader.Delegate(activity));
|
.addDelegate(new UpdateableAppsHeader.Delegate(activity))
|
||||||
|
.addDelegate(new KnownVulnApp.Delegate(activity));
|
||||||
|
|
||||||
activity.getSupportLoaderManager().initLoader(0, null, this);
|
activity.getSupportLoaderManager().initLoader(LOADER_CAN_UPDATE, null, this);
|
||||||
|
activity.getSupportLoaderManager().initLoader(LOADER_KNOWN_VULN, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -162,6 +170,10 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (KnownVulnApp app : knownVulnApps) {
|
||||||
|
items.add(app);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -186,33 +198,41 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||||
|
Uri uri;
|
||||||
|
switch (id) {
|
||||||
|
case LOADER_CAN_UPDATE:
|
||||||
|
uri = AppProvider.getCanUpdateUri();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOADER_KNOWN_VULN:
|
||||||
|
uri = AppProvider.getInstalledWithKnownVulnsUri();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown loader requested: " + id);
|
||||||
|
}
|
||||||
|
|
||||||
return new CursorLoader(
|
return new CursorLoader(
|
||||||
activity,
|
activity, uri, Schema.AppMetadataTable.Cols.ALL, null, null, Schema.AppMetadataTable.Cols.NAME);
|
||||||
AppProvider.getCanUpdateUri(),
|
|
||||||
new String[]{
|
|
||||||
Schema.AppMetadataTable.Cols._ID, // Required for cursor loader to work.
|
|
||||||
Schema.AppMetadataTable.Cols.Package.PACKAGE_NAME,
|
|
||||||
Schema.AppMetadataTable.Cols.NAME,
|
|
||||||
Schema.AppMetadataTable.Cols.SUMMARY,
|
|
||||||
Schema.AppMetadataTable.Cols.IS_COMPATIBLE,
|
|
||||||
Schema.AppMetadataTable.Cols.LICENSE,
|
|
||||||
Schema.AppMetadataTable.Cols.ICON,
|
|
||||||
Schema.AppMetadataTable.Cols.ICON_URL,
|
|
||||||
Schema.AppMetadataTable.Cols.InstalledApp.VERSION_CODE,
|
|
||||||
Schema.AppMetadataTable.Cols.InstalledApp.VERSION_NAME,
|
|
||||||
Schema.AppMetadataTable.Cols.SuggestedApk.VERSION_NAME,
|
|
||||||
Schema.AppMetadataTable.Cols.SUGGESTED_VERSION_CODE,
|
|
||||||
Schema.AppMetadataTable.Cols.REQUIREMENTS, // Needed for filtering apps that require root.
|
|
||||||
Schema.AppMetadataTable.Cols.ANTI_FEATURES, // Needed for filtering apps that require anti-features.
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
Schema.AppMetadataTable.Cols.NAME
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||||
|
switch (loader.getId()) {
|
||||||
|
case LOADER_CAN_UPDATE:
|
||||||
|
onCanUpdateLoadFinished(cursor);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOADER_KNOWN_VULN:
|
||||||
|
onKnownVulnLoadFinished(cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
populateItems();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCanUpdateLoadFinished(Cursor cursor) {
|
||||||
updateableApps.clear();
|
updateableApps.clear();
|
||||||
|
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
@ -220,9 +240,16 @@ public class UpdatesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
updateableApps.add(new UpdateableApp(activity, new App(cursor)));
|
updateableApps.add(new UpdateableApp(activity, new App(cursor)));
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
populateItems();
|
private void onKnownVulnLoadFinished(Cursor cursor) {
|
||||||
notifyDataSetChanged();
|
knownVulnApps.clear();
|
||||||
|
|
||||||
|
cursor.moveToFirst();
|
||||||
|
while (!cursor.isAfterLast()) {
|
||||||
|
knownVulnApps.add(new KnownVulnApp(activity, new App(cursor)));
|
||||||
|
cursor.moveToNext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.fdroid.fdroid.views.updates.items;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.hannesdorfmann.adapterdelegates3.AdapterDelegate;
|
||||||
|
|
||||||
|
import org.fdroid.fdroid.R;
|
||||||
|
import org.fdroid.fdroid.data.App;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all apps which can be updated, but have not yet been downloaded.
|
||||||
|
*
|
||||||
|
* @see KnownVulnApp The data that is bound to this view.
|
||||||
|
* @see R.layout#known_vuln_app_list_item The view that this binds to.
|
||||||
|
* @see KnownVulnAppListItemController Used for binding the {@link App} to
|
||||||
|
* the {@link R.layout#known_vuln_app_list_item}
|
||||||
|
*/
|
||||||
|
public class KnownVulnApp extends AppUpdateData {
|
||||||
|
|
||||||
|
public final App app;
|
||||||
|
|
||||||
|
public KnownVulnApp(Activity activity, App app) {
|
||||||
|
super(activity);
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Delegate extends AdapterDelegate<List<AppUpdateData>> {
|
||||||
|
|
||||||
|
private final Activity activity;
|
||||||
|
|
||||||
|
public Delegate(Activity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isForViewType(@NonNull List<AppUpdateData> items, int position) {
|
||||||
|
return items.get(position) instanceof KnownVulnApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
|
||||||
|
return new KnownVulnAppListItemController(activity, activity.getLayoutInflater()
|
||||||
|
.inflate(R.layout.known_vuln_app_list_item, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindViewHolder(@NonNull List<AppUpdateData> items, int position,
|
||||||
|
@NonNull RecyclerView.ViewHolder holder, @NonNull List<Object> payloads) {
|
||||||
|
KnownVulnApp app = (KnownVulnApp) items.get(position);
|
||||||
|
((KnownVulnAppListItemController) holder).bindModel(app.app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.fdroid.fdroid.views.updates.items;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
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.App;
|
||||||
|
import org.fdroid.fdroid.data.AppProvider;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tell the user that an app they have installed has a known vulnerability.
|
||||||
|
* The role of this controller is to prompt the user what it is that should be done in response to this
|
||||||
|
* (e.g. uninstall, update, disable).
|
||||||
|
*/
|
||||||
|
public class KnownVulnAppListItemController extends AppListItemController {
|
||||||
|
public KnownVulnAppListItemController(Activity activity, View itemView) {
|
||||||
|
super(activity, itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected AppListItemState getCurrentViewState(
|
||||||
|
@NonNull App app, @Nullable AppUpdateStatusManager.AppUpdateStatus appStatus) {
|
||||||
|
return new AppListItemState(app)
|
||||||
|
.setMainText(activity.getString(R.string.updates__app_with_known_vulnerability__uninstall, app.name))
|
||||||
|
.showActionButton(activity.getString(R.string.menu_uninstall));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActionButtonPressed(@NonNull App app) {
|
||||||
|
LocalBroadcastManager.getInstance(activity).registerReceiver(uninstallReceiver,
|
||||||
|
Installer.getUninstallIntentFilter(app.packageName));
|
||||||
|
InstallerService.uninstall(activity, app.getInstalledApk(activity));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterUninstallReceiver() {
|
||||||
|
LocalBroadcastManager.getInstance(activity).unregisterReceiver(uninstallReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver uninstallReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
switch (intent.getAction()) {
|
||||||
|
case Installer.ACTION_UNINSTALL_COMPLETE:
|
||||||
|
// This will cause the LoaderManager in UpdatesAdapter to automatically requery for the list of
|
||||||
|
// apps with known vulnerabilities (i.e. this app should no longer be in that list).
|
||||||
|
activity.getContentResolver().notifyChange(AppProvider.getInstalledWithKnownVulnsUri(), null);
|
||||||
|
unregisterUninstallReceiver();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Installer.ACTION_UNINSTALL_INTERRUPTED:
|
||||||
|
unregisterUninstallReceiver();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Installer.ACTION_UNINSTALL_USER_INTERACTION:
|
||||||
|
PendingIntent uninstallPendingIntent =
|
||||||
|
intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
|
||||||
|
|
||||||
|
try {
|
||||||
|
uninstallPendingIntent.send();
|
||||||
|
} catch (PendingIntent.CanceledException ignored) { }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
19
app/src/main/res/drawable/ic_known_vuln_overlay.xml
Normal file
19
app/src/main/res/drawable/ic_known_vuln_overlay.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<vector android:height="24dp" android:viewportHeight="12.7"
|
||||||
|
android:viewportWidth="12.7" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff"
|
||||||
|
android:pathData="M6.35,6.35m-6.35,0a6.35,6.35 0,1 1,12.7 0a6.35,6.35 0,1 1,-12.7 0"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#00000000"
|
||||||
|
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0.52916664"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ff3600"
|
||||||
|
android:pathData="M6.35,6.35m-5.33,0a5.33,5.33 0,1 1,10.661 0a5.33,5.33 0,1 1,-10.661 0"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#00000000"
|
||||||
|
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0.52916664"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff"
|
||||||
|
android:pathData="M5.863,3.778h0.904v3.082h-0.904z"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#00000000"
|
||||||
|
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0.52916664"/>
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff"
|
||||||
|
android:pathData="M5.863,8.899l0.904,0l0,-0.973l-0.904,0z"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#00000000"
|
||||||
|
android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0.52916664"/>
|
||||||
|
</vector>
|
66
app/src/main/res/layout/known_vuln_app_list_item.xml
Normal file
66
app/src/main/res/layout/known_vuln_app_list_item.xml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.constraint.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
|
||||||
|
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
|
||||||
|
when it will inevitably read out the name of the app straight after. -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
tools:src="@drawable/ic_launcher"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="48dp"
|
||||||
|
android:layout_marginLeft="48dp"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:src="@drawable/ic_known_vuln_overlay"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/app_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="We found a vulnerability with VulnApp. We recommend uninstalling this app immediately."
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="?attr/installedApps"
|
||||||
|
android:ellipsize="end"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/icon"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginRight="8dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/action_button"
|
||||||
|
style="@style/DetailsPrimaryButtonStyleSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/app_name"
|
||||||
|
tools:text="Uninstall"/>
|
||||||
|
|
||||||
|
</android.support.constraint.ConstraintLayout>
|
@ -97,9 +97,10 @@ This often occurs with apps installed via Google Play or other sources, if they
|
|||||||
<string name="updates__tts__download_app">Download</string>
|
<string name="updates__tts__download_app">Download</string>
|
||||||
<string name="updates__tts__download_updates_for_all_apps">Download all updates</string>
|
<string name="updates__tts__download_updates_for_all_apps">Download all updates</string>
|
||||||
|
|
||||||
|
<string name="updates__app_with_known_vulnerability__uninstall">We found a vulnerability with %1$s. We recommend uninstalling this app immediately.</string>
|
||||||
|
|
||||||
<string name="updates__hide_updateable_apps">Hide apps</string>
|
<string name="updates__hide_updateable_apps">Hide apps</string>
|
||||||
<string name="updates__show_updateable_apps">Show apps</string>
|
<string name="updates__show_updateable_apps">Show apps</string>
|
||||||
|
|
||||||
<plurals name="updates__download_updates_for_apps">
|
<plurals name="updates__download_updates_for_apps">
|
||||||
<item quantity="one">Download update for %1$d app.</item>
|
<item quantity="one">Download update for %1$d app.</item>
|
||||||
<item quantity="other">Download updates for %1$d apps.</item>
|
<item quantity="other">Download updates for %1$d apps.</item>
|
||||||
|
@ -21,6 +21,7 @@ import org.fdroid.fdroid.data.ApkProvider;
|
|||||||
import org.fdroid.fdroid.data.App;
|
import org.fdroid.fdroid.data.App;
|
||||||
import org.fdroid.fdroid.data.AppProvider;
|
import org.fdroid.fdroid.data.AppProvider;
|
||||||
import org.fdroid.fdroid.data.FDroidProviderTest;
|
import org.fdroid.fdroid.data.FDroidProviderTest;
|
||||||
|
import org.fdroid.fdroid.data.InstalledAppTestUtils;
|
||||||
import org.fdroid.fdroid.data.Repo;
|
import org.fdroid.fdroid.data.Repo;
|
||||||
import org.fdroid.fdroid.data.RepoProvider;
|
import org.fdroid.fdroid.data.RepoProvider;
|
||||||
import org.fdroid.fdroid.data.RepoPushRequest;
|
import org.fdroid.fdroid.data.RepoPushRequest;
|
||||||
@ -121,6 +122,12 @@ public class IndexV1UpdaterTest extends FDroidProviderTest {
|
|||||||
assertEquals("repo.mirrors should have items", 2, repo.mirrors.length);
|
assertEquals("repo.mirrors should have items", 2, repo.mirrors.length);
|
||||||
assertEquals("repo.mirrors first URL", "http://frkcchxlcvnb4m5a.onion/fdroid/repo", repo.mirrors[0]);
|
assertEquals("repo.mirrors first URL", "http://frkcchxlcvnb4m5a.onion/fdroid/repo", repo.mirrors[0]);
|
||||||
assertEquals("repo.mirrors second URL", "http://testy.at.or.at/fdroid/repo", repo.mirrors[1]);
|
assertEquals("repo.mirrors second URL", "http://testy.at.or.at/fdroid/repo", repo.mirrors[1]);
|
||||||
|
|
||||||
|
// Make sure the per-apk anti features which are new in index v1 get added correctly.
|
||||||
|
assertEquals(0, AppProvider.Helper.findInstalledAppsWithKnownVulns(context).size());
|
||||||
|
InstalledAppTestUtils.install(context, "com.waze", 1019841, "v3.9.5.4", "362488e7be5ea0689b4e97d989ae1404",
|
||||||
|
"cbbdb8c5dafeccd7dd7b642dde0477d3489e18ac366e3c8473d5c07e5f735a95");
|
||||||
|
assertEquals(1, AppProvider.Helper.findInstalledAppsWithKnownVulns(context).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RepoUpdater.SigningException.class)
|
@Test(expected = RepoUpdater.SigningException.class)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user