show all installed apps as possibilities for panic uninstall
F-Droid should be able to uninstall any app, in theory, not just the apps that are listed in the index. This lays some groundwork for moving swap's SelectAppsView to the standard AppList elements used everywhere else. It also does a little bit towards getting rid of InstalledApp in favor of just reusing App.
This commit is contained in:
parent
89140d5334
commit
bac0ae8f25
@ -40,8 +40,7 @@ import android.widget.TextView;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.data.AppProvider;
|
||||
import org.fdroid.fdroid.data.Schema;
|
||||
import org.fdroid.fdroid.data.InstalledAppProvider;
|
||||
import org.fdroid.fdroid.views.installed.InstalledAppListAdapter;
|
||||
|
||||
public class SelectInstalledAppsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
@ -89,11 +88,7 @@ public class SelectInstalledAppsActivity extends AppCompatActivity implements Lo
|
||||
@NonNull
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new CursorLoader(
|
||||
this,
|
||||
AppProvider.getInstalledUri(),
|
||||
Schema.AppMetadataTable.Cols.ALL,
|
||||
null, null, null);
|
||||
return new CursorLoader(this, InstalledAppProvider.getAllAppsUri(), null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -15,9 +15,12 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Schema.AppMetadataTable;
|
||||
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
|
||||
import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols;
|
||||
import org.fdroid.fdroid.data.Schema.PackageTable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@ -28,6 +31,23 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
|
||||
public static class Helper {
|
||||
|
||||
public static App[] all(Context context) {
|
||||
ArrayList<App> appList = new ArrayList<>();
|
||||
Cursor cursor = context.getContentResolver().query(InstalledAppProvider.getAllAppsUri(),
|
||||
null, null, null, null);
|
||||
if (cursor != null) {
|
||||
if (cursor.getCount() > 0) {
|
||||
cursor.moveToFirst();
|
||||
while (!cursor.isAfterLast()) {
|
||||
appList.add(new App(cursor));
|
||||
cursor.moveToNext();
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
return appList.toArray(new App[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The keys are the package names, and their corresponding values are
|
||||
* the {@link PackageInfo#lastUpdateTime last update time} in milliseconds.
|
||||
@ -79,6 +99,8 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
|
||||
private static final String PATH_SEARCH = "search";
|
||||
private static final int CODE_SEARCH = CODE_SINGLE + 1;
|
||||
private static final String PATH_ALL_APPS = "allApps";
|
||||
private static final int CODE_ALL_APPS = CODE_SEARCH + 1;
|
||||
|
||||
private static final UriMatcher MATCHER = new UriMatcher(-1);
|
||||
|
||||
@ -99,6 +121,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
static {
|
||||
MATCHER.addURI(getAuthority(), null, CODE_LIST);
|
||||
MATCHER.addURI(getAuthority(), PATH_SEARCH + "/*", CODE_SEARCH);
|
||||
MATCHER.addURI(getAuthority(), PATH_ALL_APPS, CODE_ALL_APPS);
|
||||
MATCHER.addURI(getAuthority(), "*", CODE_SINGLE);
|
||||
}
|
||||
|
||||
@ -106,6 +129,10 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
return Uri.parse("content://" + getAuthority());
|
||||
}
|
||||
|
||||
public static Uri getAllAppsUri() {
|
||||
return getContentUri().buildUpon().appendPath(PATH_ALL_APPS).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Uri} that points to a specific installed app
|
||||
*/
|
||||
@ -228,6 +255,7 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
}
|
||||
|
||||
QuerySelection selection = new QuerySelection(customSelection, selectionArgs);
|
||||
QueryBuilder query = null;
|
||||
switch (MATCHER.match(uri)) {
|
||||
case CODE_LIST:
|
||||
selection = selectNotSystemSignature(selection);
|
||||
@ -241,16 +269,30 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
selection = selection.add(querySearch(uri.getLastPathSegment()));
|
||||
break;
|
||||
|
||||
case CODE_ALL_APPS:
|
||||
selection = selectNotSystemSignature(selection);
|
||||
query = new QueryBuilder();
|
||||
query.addField(Cols._ID);
|
||||
query.appendField(Cols.APPLICATION_LABEL, null, Schema.AppMetadataTable.Cols.NAME);
|
||||
query.appendField(Cols.VERSION_CODE, null, AppMetadataTable.Cols.UPSTREAM_VERSION_CODE);
|
||||
query.appendField(Cols.VERSION_NAME, null, AppMetadataTable.Cols.UPSTREAM_VERSION_NAME);
|
||||
query.appendField(PackageTable.Cols.PACKAGE_NAME, PackageTable.NAME,
|
||||
AppMetadataTable.Cols.Package.PACKAGE_NAME);
|
||||
break;
|
||||
|
||||
default:
|
||||
String message = "Invalid URI for installed app content provider: " + uri;
|
||||
Log.e(TAG, message);
|
||||
throw new UnsupportedOperationException(message);
|
||||
}
|
||||
|
||||
QueryBuilder query = new QueryBuilder();
|
||||
if (projection == null || projection.length == 0) {
|
||||
if (query != null) { // NOPMD
|
||||
// the fields are already setup above
|
||||
} else if (projection == null || projection.length == 0) {
|
||||
query = new QueryBuilder();
|
||||
query.addFields(Cols.ALL);
|
||||
} else {
|
||||
query = new QueryBuilder();
|
||||
query.addFields(projection);
|
||||
}
|
||||
query.addSelection(selection);
|
||||
@ -279,6 +321,12 @@ public class InstalledAppProvider extends FDroidProvider {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Cols.Package#NAME} is not included in the database here, because
|
||||
* it is included only in the {@link PackageTable}, since there are large
|
||||
* cross-table queries needed to handle the complexity of multiple repos
|
||||
* potentially serving the same apps.
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||
|
||||
|
@ -7,6 +7,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Outline;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@ -186,7 +187,15 @@ public abstract class AppListItemController extends RecyclerView.ViewHolder {
|
||||
public void bindModel(@NonNull App app) {
|
||||
currentApp = app;
|
||||
|
||||
ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions());
|
||||
if (app.iconUrl == null) {
|
||||
try {
|
||||
icon.setImageDrawable(activity.getPackageManager().getApplicationIcon(app.packageName));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// ignored
|
||||
}
|
||||
} else {
|
||||
ImageLoader.getInstance().displayImage(app.iconUrl, icon, Utils.getRepoAppDisplayImageOptions());
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -2,6 +2,7 @@ package org.fdroid.fdroid.views.installed;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@ -9,7 +10,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.Schema;
|
||||
|
||||
public class InstalledAppListAdapter extends RecyclerView.Adapter<InstalledAppListItemController> {
|
||||
|
||||
@ -30,7 +30,8 @@ public class InstalledAppListAdapter extends RecyclerView.Adapter<InstalledAppLi
|
||||
}
|
||||
|
||||
cursor.moveToPosition(position);
|
||||
return cursor.getLong(cursor.getColumnIndex(Schema.AppMetadataTable.Cols.ROW_ID));
|
||||
// TODO this should be based on Schema.InstalledAppProvider.Cols._ID
|
||||
return cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -20,6 +20,7 @@ import java.util.Map;
|
||||
import static org.fdroid.fdroid.Assert.assertIsInstalledVersionInDb;
|
||||
import static org.fdroid.fdroid.Assert.assertResultCount;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@ -71,6 +72,39 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelperAll() {
|
||||
final String packageName0 = "com.0";
|
||||
final String packageName1 = "com.1";
|
||||
final String packageName2 = "com.2";
|
||||
|
||||
App[] apps = InstalledAppProvider.Helper.all(context);
|
||||
assertEquals(0, apps.length);
|
||||
|
||||
insertInstalledApp(packageName0, 0, "v0");
|
||||
insertInstalledApp(packageName1, 1, "v1");
|
||||
insertInstalledApp(packageName2, 2, "v2");
|
||||
|
||||
assertResultCount(contentResolver, 3, InstalledAppProvider.getContentUri());
|
||||
assertResultCount(contentResolver, 3, InstalledAppProvider.getAllAppsUri());
|
||||
assertIsInstalledVersionInDb(contentResolver, packageName0, 0, "v0");
|
||||
assertIsInstalledVersionInDb(contentResolver, packageName1, 1, "v1");
|
||||
assertIsInstalledVersionInDb(contentResolver, packageName2, 2, "v2");
|
||||
|
||||
apps = InstalledAppProvider.Helper.all(context);
|
||||
assertEquals(3, apps.length);
|
||||
assertEquals(packageName0, apps[0].packageName);
|
||||
assertEquals("v0", apps[0].upstreamVersionName);
|
||||
assertEquals(0, apps[0].upstreamVersionCode);
|
||||
assertEquals(packageName1, apps[1].packageName);
|
||||
assertEquals("v1", apps[1].upstreamVersionName);
|
||||
assertEquals(1, apps[1].upstreamVersionCode);
|
||||
assertEquals(packageName2, apps[2].packageName);
|
||||
assertEquals("v2", apps[2].upstreamVersionName);
|
||||
assertEquals(2, apps[2].upstreamVersionCode);
|
||||
assertNotEquals(packageName0, apps[2].packageName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsert() {
|
||||
|
||||
@ -84,6 +118,9 @@ public class InstalledAppProviderTest extends FDroidProviderTest {
|
||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com1", 1, "v1");
|
||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com2", 2, "v2");
|
||||
assertIsInstalledVersionInDb(contentResolver, "com.example.com3", 3, "v3");
|
||||
|
||||
App[] apps = InstalledAppProvider.Helper.all(context);
|
||||
assertEquals(3, apps.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user