Merge branch 'swap-crash-serving-fdroid' into 'master'

Fix crash when trying to swap.

See merge request !563
This commit is contained in:
Hans-Christoph Steiner 2017-09-04 20:08:53 +00:00
commit 312bc9f503
8 changed files with 107 additions and 28 deletions

View File

@ -250,8 +250,10 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
private void checkRepoAddress() { private void checkRepoAddress() {
if (repoAddress == null || apkName == null) { if (repoAddress == null || apkName == null) {
throw new IllegalStateException("Apk needs to have both Schema.ApkTable.Cols.REPO_ADDRESS and " throw new IllegalStateException(
+ "Schema.ApkTable.Cols.NAME set in order to calculate URL."); "Apk needs to have both Schema.ApkTable.Cols.REPO_ADDRESS and " +
"Schema.ApkTable.Cols.NAME set in order to calculate URL " +
"[package: " + packageName + ", versionCode: " + versionCode + ", repoId: " + repoId + "]");
} }
} }

View File

@ -114,14 +114,10 @@ public class ApkProvider extends FDroidProvider {
} }
public static List<Apk> findByPackageName(Context context, String packageName) { public static List<Apk> findByPackageName(Context context, String packageName) {
return findByPackageName(context, packageName, Cols.ALL);
}
public static List<Apk> findByPackageName(Context context, String packageName, String[] projection) {
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
final Uri uri = getAppUri(packageName); final Uri uri = getAppUri(packageName);
final String sort = "apk." + Cols.VERSION_CODE + " DESC"; final String sort = "apk." + Cols.VERSION_CODE + " DESC";
Cursor cursor = resolver.query(uri, projection, null, null, sort); Cursor cursor = resolver.query(uri, Cols.ALL, null, null, sort);
return cursorToList(cursor); return cursorToList(cursor);
} }
@ -132,13 +128,15 @@ public class ApkProvider extends FDroidProvider {
return cursorToList(cursor); return cursorToList(cursor);
} }
public static Apk get(Context context, Uri uri) { @NonNull
return get(context, uri, Cols.ALL); public static List<Apk> findAppVersionsByRepo(Context context, App app, Repo repo) {
ContentResolver resolver = context.getContentResolver();
final Uri uri = getRepoUri(repo.getId(), app.packageName);
Cursor cursor = resolver.query(uri, Cols.ALL, null, null, null);
return cursorToList(cursor);
} }
public static Apk get(Context context, Uri uri, String[] fields) { private static Apk cursorToApk(Cursor cursor) {
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, fields, null, null, null);
Apk apk = null; Apk apk = null;
if (cursor != null) { if (cursor != null) {
if (cursor.getCount() > 0) { if (cursor.getCount() > 0) {
@ -150,6 +148,12 @@ public class ApkProvider extends FDroidProvider {
return apk; return apk;
} }
public static Apk get(Context context, Uri uri) {
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, Cols.ALL, null, null, null);
return cursorToApk(cursor);
}
@NonNull @NonNull
public static List<Apk> findApksByHash(Context context, String apkHash) { public static List<Apk> findApksByHash(Context context, String apkHash) {
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
@ -167,10 +171,12 @@ public class ApkProvider extends FDroidProvider {
private static final int CODE_APK_ROW_ID = CODE_APKS + 1; private static final int CODE_APK_ROW_ID = CODE_APKS + 1;
static final int CODE_APK_FROM_ANY_REPO = CODE_APK_ROW_ID + 1; static final int CODE_APK_FROM_ANY_REPO = CODE_APK_ROW_ID + 1;
static final int CODE_APK_FROM_REPO = CODE_APK_FROM_ANY_REPO + 1; static final int CODE_APK_FROM_REPO = CODE_APK_FROM_ANY_REPO + 1;
private static final int CODE_REPO_APP = CODE_APK_FROM_REPO + 1;
private static final String PROVIDER_NAME = "ApkProvider"; private static final String PROVIDER_NAME = "ApkProvider";
protected static final String PATH_APK_FROM_ANY_REPO = "apk-any-repo"; protected static final String PATH_APK_FROM_ANY_REPO = "apk-any-repo";
protected static final String PATH_APK_FROM_REPO = "apk-from-repo"; protected static final String PATH_APK_FROM_REPO = "apk-from-repo";
protected static final String PATH_REPO_APP = "repo-app";
private static final String PATH_APKS = "apks"; private static final String PATH_APKS = "apks";
private static final String PATH_APP = "app"; private static final String PATH_APP = "app";
private static final String PATH_REPO = "repo"; private static final String PATH_REPO = "repo";
@ -187,6 +193,7 @@ public class ApkProvider extends FDroidProvider {
PACKAGE_FIELDS.put(Cols.Package.PACKAGE_NAME, PackageTable.Cols.PACKAGE_NAME); PACKAGE_FIELDS.put(Cols.Package.PACKAGE_NAME, PackageTable.Cols.PACKAGE_NAME);
MATCHER.addURI(getAuthority(), PATH_REPO + "/#", CODE_REPO); MATCHER.addURI(getAuthority(), PATH_REPO + "/#", CODE_REPO);
MATCHER.addURI(getAuthority(), PATH_REPO_APP + "/#/*", CODE_REPO_APP);
MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*/*", CODE_APK_FROM_ANY_REPO); MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*/*", CODE_APK_FROM_ANY_REPO);
MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*", CODE_APK_FROM_ANY_REPO); MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*", CODE_APK_FROM_ANY_REPO);
MATCHER.addURI(getAuthority(), PATH_APK_FROM_REPO + "/#/#", CODE_APK_FROM_REPO); MATCHER.addURI(getAuthority(), PATH_APK_FROM_REPO + "/#/#", CODE_APK_FROM_REPO);
@ -227,6 +234,15 @@ public class ApkProvider extends FDroidProvider {
.build(); .build();
} }
public static Uri getRepoUri(long repoId, String packageName) {
return getContentUri()
.buildUpon()
.appendPath(PATH_REPO_APP)
.appendPath(Long.toString(repoId))
.appendPath(packageName)
.build();
}
public static Uri getApkFromAnyRepoUri(Apk apk) { public static Uri getApkFromAnyRepoUri(Apk apk) {
return getApkFromAnyRepoUri(apk.packageName, apk.versionCode, null); return getApkFromAnyRepoUri(apk.packageName, apk.versionCode, null);
} }
@ -427,6 +443,13 @@ public class ApkProvider extends FDroidProvider {
QuerySelection query = new QuerySelection(selection, selectionArgs); QuerySelection query = new QuerySelection(selection, selectionArgs);
switch (MATCHER.match(uri)) { switch (MATCHER.match(uri)) {
case CODE_REPO_APP:
List<String> uriSegments = uri.getPathSegments();
Long repoId = Long.parseLong(uriSegments.get(1));
String packageName = uriSegments.get(2);
query = query.add(queryRepo(repoId)).add(queryPackage(packageName));
break;
case CODE_LIST: case CODE_LIST:
break; break;

View File

@ -53,6 +53,7 @@ import org.fdroid.fdroid.localrepo.SwapService;
import org.fdroid.fdroid.net.Downloader; import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService; import org.fdroid.fdroid.net.DownloaderService;
import java.util.List;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -233,8 +234,13 @@ public class SwapAppsView extends ListView implements
private class ViewHolder { private class ViewHolder {
private final LocalBroadcastManager localBroadcastManager; private final LocalBroadcastManager localBroadcastManager;
@Nullable
private App app; private App app;
@Nullable
private Apk apk;
ProgressBar progressView; ProgressBar progressView;
TextView nameView; TextView nameView;
ImageView iconView; ImageView iconView;
@ -290,8 +296,11 @@ public class SwapAppsView extends ListView implements
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
Activity activity = getActivity(); Activity activity = getActivity();
if (activity != null) { if (activity != null) {
app = AppProvider.Helper.findSpecificApp(getActivity().getContentResolver(), app = AppProvider.Helper.findSpecificApp(
app.packageName, app.repoId, AppMetadataTable.Cols.ALL); getActivity().getContentResolver(),
app.packageName,
app.repoId,
AppMetadataTable.Cols.ALL);
resetView(); resetView();
} }
} }
@ -305,14 +314,19 @@ public class SwapAppsView extends ListView implements
if (this.app == null || !this.app.packageName.equals(app.packageName)) { if (this.app == null || !this.app.packageName.equals(app.packageName)) {
this.app = app; this.app = app;
Context context = getContext(); List<Apk> availableApks = ApkProvider.Helper.findAppVersionsByRepo(getActivity(), app, repo);
Apk apk = ApkProvider.Helper.findApkFromAnyRepo(context, if (availableApks.size() > 0) {
app.packageName, app.suggestedVersionCode); // Swap repos only add one version of an app, so we will just ask for the first apk.
String urlString = apk.getUrl(); this.apk = availableApks.get(0);
}
// TODO unregister receivers? or will they just die with this instance if (apk != null) {
localBroadcastManager.registerReceiver(downloadReceiver, String urlString = apk.getUrl();
DownloaderService.getIntentFilter(urlString));
// TODO unregister receivers? or will they just die with this instance
IntentFilter downloadFilter = DownloaderService.getIntentFilter(urlString);
localBroadcastManager.registerReceiver(downloadReceiver, downloadFilter);
}
// NOTE: Instead of continually unregistering and re-registering the observer // NOTE: Instead of continually unregistering and re-registering the observer
// (with a different URI), this could equally be done by only having one // (with a different URI), this could equally be done by only having one
@ -364,8 +378,8 @@ public class SwapAppsView extends ListView implements
OnClickListener installListener = new OnClickListener() { OnClickListener installListener = new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (app.hasUpdates() || app.compatible) { if (apk != null && (app.hasUpdates() || app.compatible)) {
getActivity().install(app); getActivity().install(app, apk);
showProgress(); showProgress();
} }
} }

View File

@ -39,7 +39,6 @@ import org.fdroid.fdroid.Preferences;
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;
import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.NewRepoConfig; import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.installer.InstallManagerService;
@ -766,8 +765,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
} }
public void install(@NonNull final App app) { public void install(@NonNull final App app, @NonNull final Apk apk) {
final Apk apk = ApkProvider.Helper.findApkFromAnyRepo(this, app.packageName, app.suggestedVersionCode);
Uri downloadUri = Uri.parse(apk.getUrl()); Uri downloadUri = Uri.parse(apk.getUrl());
localBroadcastManager.registerReceiver(installReceiver, localBroadcastManager.registerReceiver(installReceiver,
Installer.getInstallIntentFilter(downloadUri)); Installer.getInstallIntentFilter(downloadUri));

View File

@ -9,6 +9,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.InstalledAppProvider; import org.fdroid.fdroid.data.InstalledAppProvider;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.Schema.ApkTable; import org.fdroid.fdroid.data.Schema.ApkTable;
import org.fdroid.fdroid.data.Schema.AppMetadataTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable;
import org.fdroid.fdroid.data.Schema.InstalledAppTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable;
@ -181,6 +182,12 @@ public class Assert {
return insertApp(context, packageName, name, new ContentValues()); return insertApp(context, packageName, name, new ContentValues());
} }
public static App insertApp(Context context, String packageName, String name, Repo repo) {
ContentValues values = new ContentValues();
values.put(AppMetadataTable.Cols.REPO_ID, repo.getId());
return insertApp(context, packageName, name, values);
}
public static App insertApp(Context context, String packageName, String name, ContentValues additionalValues) { public static App insertApp(Context context, String packageName, String name, ContentValues additionalValues) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();

View File

@ -6,7 +6,10 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo;
import android.net.Uri;
import org.fdroid.fdroid.data.Apk;
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.Repo; import org.fdroid.fdroid.data.Repo;
@ -81,10 +84,14 @@ public class TestUtils {
assertEquals(message, formatSigForDebugging(expected), formatSigForDebugging(actual)); assertEquals(message, formatSigForDebugging(expected), formatSigForDebugging(actual));
} }
public static void insertApk(Context context, App app, int versionCode, String signature) { public static Apk insertApk(Context context, App app, int versionCode, String signature) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Schema.ApkTable.Cols.SIGNATURE, signature); values.put(Schema.ApkTable.Cols.SIGNATURE, signature);
Assert.insertApk(context, app, versionCode, values);
long repoId = app.repoId > 0 ? app.repoId : 1;
values.put(Schema.ApkTable.Cols.REPO_ID, repoId);
Uri uri = Assert.insertApk(context, app, versionCode, values);
return ApkProvider.Helper.findByUri(context, uri, Schema.ApkTable.Cols.ALL);
} }
public static App insertApp(Context context, String packageName, String appName, int upstreamVersionCode, public static App insertApp(Context context, String packageName, String appName, int upstreamVersionCode,

View File

@ -6,6 +6,7 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import org.fdroid.fdroid.Assert; import org.fdroid.fdroid.Assert;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.TestUtils;
import org.fdroid.fdroid.data.Schema.ApkTable.Cols; import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
import org.fdroid.fdroid.data.Schema.RepoTable; import org.fdroid.fdroid.data.Schema.RepoTable;
import org.fdroid.fdroid.mock.MockApk; import org.fdroid.fdroid.mock.MockApk;
@ -271,6 +272,27 @@ public class ApkProviderTest extends FDroidProviderTest {
assertBelongsToApp(thingoApks, "com.apk.thingo"); assertBelongsToApp(thingoApks, "com.apk.thingo");
} }
@Test
public void findApksForAppInSpecificRepo() {
Repo fdroidRepo = RepoProvider.Helper.findByAddress(context, "https://f-droid.org/repo");
Repo swapRepo = RepoProviderTest.insertRepo(context, "http://192.168.1.3/fdroid/repo", "", "22", "", true);
App officialFDroid = insertApp(context, "org.fdroid.fdroid", "F-Droid (Official)", fdroidRepo);
TestUtils.insertApk(context, officialFDroid, 4, TestUtils.FDROID_SIG);
TestUtils.insertApk(context, officialFDroid, 5, TestUtils.FDROID_SIG);
App debugSwapFDroid = insertApp(context, "org.fdroid.fdroid", "F-Droid (Debug)", swapRepo);
TestUtils.insertApk(context, debugSwapFDroid, 6, TestUtils.THIRD_PARTY_SIG);
List<Apk> foundOfficialApks = ApkProvider.Helper.findAppVersionsByRepo(context, officialFDroid, fdroidRepo);
assertEquals(2, foundOfficialApks.size());
List<Apk> debugSwapApks = ApkProvider.Helper.findAppVersionsByRepo(context, officialFDroid, swapRepo);
assertEquals(1, debugSwapApks.size());
assertEquals(debugSwapFDroid.getId(), debugSwapApks.get(0).appId);
assertEquals(6, debugSwapApks.get(0).versionCode);
}
@Test @Test
public void testUpdate() { public void testUpdate() {

View File

@ -256,11 +256,17 @@ public class RepoProviderTest extends FDroidProviderTest {
public static Repo insertRepo(Context context, String address, String description, public static Repo insertRepo(Context context, String address, String description,
String fingerprint, @Nullable String name) { String fingerprint, @Nullable String name) {
return insertRepo(context, address, description, fingerprint, name, false);
}
public static Repo insertRepo(Context context, String address, String description,
String fingerprint, @Nullable String name, boolean isSwap) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(RepoTable.Cols.ADDRESS, address); values.put(RepoTable.Cols.ADDRESS, address);
values.put(RepoTable.Cols.DESCRIPTION, description); values.put(RepoTable.Cols.DESCRIPTION, description);
values.put(RepoTable.Cols.FINGERPRINT, fingerprint); values.put(RepoTable.Cols.FINGERPRINT, fingerprint);
values.put(RepoTable.Cols.NAME, name); values.put(RepoTable.Cols.NAME, name);
values.put(RepoTable.Cols.IS_SWAP, isSwap);
RepoProvider.Helper.insert(context, values); RepoProvider.Helper.insert(context, values);
return RepoProvider.Helper.findByAddress(context, address); return RepoProvider.Helper.findByAddress(context, address);