diff --git a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java index 3d222cff9..73ba4db50 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java +++ b/app/src/full/java/org/fdroid/fdroid/views/panic/SelectInstalledAppsActivity.java @@ -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 { @@ -89,11 +88,7 @@ public class SelectInstalledAppsActivity extends AppCompatActivity implements Lo @NonNull @Override public Loader 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 diff --git a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java index 548ad2fdb..5790a3117 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/InstalledAppProvider.java @@ -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 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) { 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 e8825bbbf..0b87fc5c7 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 @@ -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. diff --git a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java index b5ca5d692..57976a62b 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/installed/InstalledAppListAdapter.java @@ -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 { @@ -30,7 +30,8 @@ public class InstalledAppListAdapter extends RecyclerView.Adapter