The Receiver superclass is not reusing difficult code, but it is hiding the
simple list of UI configuration that it does.
This also eliminates the "error" TextView and just reuses the existing
TextView for error messages.
The most expensive part of this whole process is calculating the hash of the
whole APK. InstalledAppProvider already caches that, and the rest is OK to
query. If any particular part of the query is expensive, it could also be
moved to InstalledAppProviderService.
This moves all logic for setting up the local fdroid repo to its own
IntentService. That makes it much easier to interact with since things can
just use the static helper method to request it to update, and it'll do the
right thing.
Almost all of the nearby/swap view classes could be condensed into a single
base class that is instantiated in the view XML. This is the first step
towards making that happen.
It also lays the groundwork where "steps" are all SwapViews. The
original concept of "steps" put all steps together, whether
F-Droid could control them or not. For example, the Views were
mixed with the system Bluetooth prompts. This is the first step
towards converting the steps to always be SwapViews, which are
always under control of this app.
When coming back to a SwapView/step, it does not seem feasible to handle
automatically restarting things like permissions and Bluetooth prompts. If
there is a way, it should be possible to first load the proper SwapView
instance, then trigger the system prompt. The makes the SwapView a pure
View, without any Controller in it.
java.lang.NullPointerException: println needs a message
at android.util.Log.println_native(Native Method)
at android.util.Log.e(Log.java:232)
at org.fdroid.fdroid.net.DownloaderService.handleIntent(DownloaderService.java:232)
at org.fdroid.fdroid.net.DownloaderService.access$000(DownloaderService.java:88)
at org.fdroid.fdroid.net.DownloaderService$ServiceHandler.handleMessage(DownloaderService.java:108)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.os.HandlerThread.run(HandlerThread.java:61)
java.lang.NullPointerException: Attempt to read from field 'java.lang.String org.fdroid.fdroid.data.App.packageName' on a null object reference
at org.fdroid.fdroid.views.swap.SwapAppsView$AppListAdapter$ViewHolder$2.onChange(SwapAppsView.java:294)
at android.database.ContentObserver.onChange(ContentObserver.java:130)
at android.database.ContentObserver.onChange(ContentObserver.java:145)
at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:216)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:152)
at android.app.ActivityThread.main(ActivityThread.java:5497)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File java.io.File.getCanonicalFile()' on a null object reference
at android.os.storage.StorageManager.getStorageVolume(StorageManager.java:844)
at android.os.storage.StorageManager.getStorageVolume(StorageManager.java:838)
at android.os.Environment.isExternalStorageRemovable(Environment.java:725)
at org.fdroid.fdroid.views.main.NearbyViewBinder.<init>(NearbyViewBinder.java:85)
at org.fdroid.fdroid.views.main.MainViewController.bindSwapView(MainViewController.java:64)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:94)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:47)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6685)
java.lang.IllegalArgumentException: Failed to find storage device at null
at android.os.Environment.isExternalStorageRemovable(Environment.java:859)
at org.fdroid.fdroid.views.main.NearbyViewBinder.<init>(NearbyViewBinder.java:85)
at org.fdroid.fdroid.views.main.MainViewController.bindSwapView(MainViewController.java:64)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:94)
at org.fdroid.fdroid.views.main.MainViewAdapter.onCreateViewHolder(MainViewAdapter.java:47)
at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:6685)
AppUpdateStatusManager and InstallManagerService should be using only the
Canonical URL of the package since that is the global unique ID. The actual
URL used to download it needs to be isolated in DownloaderService, which can
entirely manage the mirror selection process. This is just a bunch of
renaming to make this all clearer.
This method returns the URL that points to the canonical download
source for this package. This is also used as the unique ID for
tracking downloading, progress, and notifications throughout the
whole install process. It is guaranteed to uniquely represent
this file since it points to a file on the file system of the
canonical webserver.
* Rename ids to something meaningful
* Remove inner layouts from constraint layout
* Use same text and button styles
* Make sure the background image doesn't overlap with the text
ACRA E ACRA caught a IllegalStateException for org.fdroid.fdroid.debug
E java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
E at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
E at android.os.Handler.handleCallback(Handler.java:751)
E at android.os.Handler.dispatchMessage(Handler.java:95)
E at android.os.Looper.loop(Looper.java:154)
E at android.app.ActivityThread.main(ActivityThread.java:6128)
E at java.lang.reflect.Method.invoke(Native Method)
E at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
E Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.hashCode()' on a
null object reference
E at org.fdroid.fdroid.localrepo.peers.BonjourPeer.hashCode(BonjourPeer.java:41)
E at sun.misc.Hashing.singleWordWangJenkinsHash(Hashing.java:48)
E at java.util.HashMap.put(HashMap.java:423)
E at java.util.HashSet.add(HashSet.java:217)
E at rx.internal.operators.OperatorDistinct$1.onNext(OperatorDistinct.java:62)
E at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:202)
E at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber$2.call(OperatorObserveOn.java:162)
E at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
E ... 7 more
This adds a new IntentService to pre-process Intents that request a
new repo is added. Right now, this only handles Intents that come
from the new storage scanners.
This also adds a new case to the AddRepo UI logic to cover when an
incoming Intent is for a mirror that is already included in an enabled
repo. In that case, the user is show the Repo Details screen for the
repo that includes that mirror. This is done is a hacky way right now
since the only path through is to click the button. So this clicks
the button in code.
Creates an IntentService subclass for scanning removable "external
storage" for F-Droid package repos, e.g. SD Cards. This is intented to
support sharable package repos, so it ignores non-removable storage,
like the fake emulated sdcard from devices with only built-in storage.
This method will only ever allow for reading repos, never writing. It
also will not work for removeable storage devices plugged in via USB,
since do not show up as "External Storage"
* https://stackoverflow.com/a/40201333
* https://commonsware.com/blog/2017/11/14/storage-situation-external-storage.htmlcloses#1377
This uses the new Storage Access Framework, which was required for
accessing files on the SD Card starting in android-19. But the API
was really limited until android-21, and not really complete until
android-23 or even android-26. So the levels of usability will vary a
lot based on how new the version of Android is.
java.lang.Throwable: Explicit termination method 'end' not called
at dalvik.system.CloseGuard.open(CloseGuard.java:180)
at java.util.zip.Deflater.<init>(Deflater.java:171)
at kellinwood.zipio.ZioEntryOutputStream.<init>(ZioEntryOutputStream.java:35)
at kellinwood.zipio.ZioEntry.getOutputStream(ZioEntry.java:482)
at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:759)
at kellinwood.security.zipsigner.ZipSigner.signZip(ZipSigner.java:664)
at org.fdroid.fdroid.localrepo.LocalRepoKeyStore.signZip(LocalRepoKeyStore.java:213)
at org.fdroid.fdroid.localrepo.LocalRepoManager.writeIndexJar(LocalRepoManager.java:492)
at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:759)
at org.fdroid.fdroid.views.swap.SwapWorkflowActivity$PrepareSwapRepo.doInBackground(SwapWorkflowActivity.java:709)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
E StrictMode: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
NanoHTTPD has issues with HTTP Keep-Alive, especially when other requests
are mixed in, like the /request-swap POST or perhaps the F-Droid HEAD to
fetch the ETag before the GET.
This disables gzip encoding and sets a Content Security Policy while I'm at
it. APKs, PNGs, and JARs are already compressed, so gzip would only ever
cause problems. And the index page is meant to be viewed by browsers, so
having a CSP will limit potential malicious swap activity.