+ * This could probably be replaced by {@link org.fdroid.fdroid.data.InstalledAppProvider}
+ * if that contained all of the info to generate complete {@link App} and
+ * {@link org.fdroid.fdroid.data.Apk} instances.
+ */
+public class CacheSwapAppsService extends IntentService {
+ public static final String TAG = "CacheSwapAppsService";
+
+ private static final String ACTION_PARSE_APP = "org.fdroid.fdroid.localrepo.action.PARSE_APP";
+
+ public CacheSwapAppsService() {
+ super("CacheSwapAppsService");
+ }
+
+ /**
+ * Parse the locally installed APK for {@code packageName} and save its XML
+ * to the APK XML cache.
+ */
+ public static void parseApp(Context context, Intent intent) {
+ intent.setClass(context, CacheSwapAppsService.class);
+ intent.setAction(ACTION_PARSE_APP);
+ context.startService(intent);
+ }
+
+ /**
+ * Parse all of the locally installed APKs into a memory cache, starting
+ * with the currently selected apps. APKs that are already parsed in the
+ * {@code index.jar} file will be read from that file.
+ */
+ public static void startCaching(Context context) {
+ File indexJarFile = LocalRepoManager.get(context).getIndexJar();
+ PackageManager pm = context.getPackageManager();
+ for (ApplicationInfo applicationInfo : pm.getInstalledApplications(0)) {
+ if (applicationInfo.publicSourceDir.startsWith(FDroidApp.SYSTEM_DIR_NAME)) {
+ continue;
+ }
+ if (!indexJarFile.exists()
+ || FileUtils.isFileNewer(new File(applicationInfo.sourceDir), indexJarFile)) {
+ Intent intent = new Intent();
+ intent.setData(Uri.parse("package:" + applicationInfo.packageName));
+ parseApp(context, intent);
+ }
+ }
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
+ if (intent == null || !ACTION_PARSE_APP.equals(intent.getAction())) {
+ Utils.debugLog(TAG, "received bad Intent: " + intent);
+ return;
+ }
+
+ try {
+ PackageManager pm = getPackageManager();
+ String packageName = intent.getData().getSchemeSpecificPart();
+ App app = new App(this, pm, packageName);
+ SwapService.putAppInCache(packageName, app);
+ } catch (CertificateEncodingException | IOException | PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/org/fdroid/fdroid/localrepo/LocalRepoManager.java b/app/src/main/java/org/fdroid/fdroid/localrepo/LocalRepoManager.java
index 32978983b..3cf9b79b3 100644
--- a/app/src/main/java/org/fdroid/fdroid/localrepo/LocalRepoManager.java
+++ b/app/src/main/java/org/fdroid/fdroid/localrepo/LocalRepoManager.java
@@ -257,7 +257,10 @@ public final class LocalRepoManager {
public void addApp(Context context, String packageName) {
App app;
try {
- app = new App(context.getApplicationContext(), pm, packageName);
+ app = SwapService.getAppFromCache(packageName);
+ if (app == null) {
+ app = new App(context.getApplicationContext(), pm, packageName);
+ }
if (!app.isValid()) {
return;
}
diff --git a/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java b/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java
index fcadd5176..8414b05d8 100644
--- a/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java
+++ b/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java
@@ -31,6 +31,7 @@ import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils;
+import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.localrepo.peers.Peer;
@@ -52,6 +53,7 @@ import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
+import java.util.concurrent.ConcurrentHashMap;
import rx.Observable;
import rx.Subscription;
@@ -61,23 +63,6 @@ import rx.schedulers.Schedulers;
/**
* Central service which manages all of the different moving parts of swap which are required
* to enable p2p swapping of apps.
- *
- * The following UI elements don't do anything:
- * + TODO: Be notified of changes to wifi state correctly, particularly from the WiFi AP (https://github.com/mvdan/accesspoint/issues/5)
- * + TODO: The "?" button in the top right of the swap start screen doesn't do anything
- * (This has been commented out for now, but it is still preferable to have a working help mechanism)
- *
- * TODO: Show "Waiting for other device to finish setting up swap" when only F-Droid shown in swap
- * TODO: Handle "not connected to wifi" more gracefully. For example, Bonjour discovery falls over.
- * TODO: When unable to reach the swap repo, but viewing apps to swap, show relevant feedback when attempting to download and install.
- * TODO: Remove peers from list of peers when no longer "visible".
- * TODO: Feedback for "Setting up (wifi|bluetooth)" in start swap view is not as immediate as I had hoped.
- * TODO: Turn off bluetooth after cancelling/timing out if we turned it on.
- * TODO: Disable the Scan QR button unless visible via something. Could equally show relevant feedback.
- *
- * TODO: Starting wifi after cancelling swap and beginning again doesn't work properly
- * TODO: Scan QR hangs when updating repoo. Swapper was 2.3.3 and Swappee was 5.0
- * TODO: Showing the progress bar during install doesn't work when the view is inflated again, or when the adapter is scrolled off screen and back again.
*/
public class SwapService extends Service {
@@ -90,6 +75,19 @@ public class SwapService extends Service {
@NonNull
private final Set