Ensure `getContentResolver()` is not requested from `null` object.
Should fix#554, however I couldn't reproduce this. At the very least, I fail to see how it is possible with this new change for a `null` to get passed to `AppProvider.Helper.categories`, whereas it could've before.
In the process, I also made the database query run on the background thread, then update the UI on the UI thread.
See merge request !224
Previously, it was not explicit that the `onCreate` happened to be invoked
in the UI thread. Now it is, due to passing `new Handler(Looper.getMainLooper())`.
Also, the categories are now loaded in a background task, and then the UI is
updated on the UI thread.
Translators:
ageru French
Danial Behzadi Persian
enolp Asturian
ezjerry liao Chinese (Taiwan)
Fr Translation French
Hsiu-Ming Chang Chinese (Taiwan)
Jonatan Swedish
relan Russian
riotism Chinese (Hong Kong)
Translators:
Adrià García-Alzórriz Catalan
Adrià García-Alzórriz Spanish
Ajeje Brazorf Sardinian
Alberto Moshpirit Spanish
Alex Kalles Greek
jetamkadlec Czech
Ldm Public French
Licaon Kter Romanian
Mladen Pejaković Serbian
naofum Japanese
Olexandr Nesterenko Ukrainian
Verdulo Esperanto
Verdulo Polish
Cleanup swap code when returning to start swap view.
This change cleans up some code around how the `StartSwapView` attaches and detaches listeners/broadcast receivers/etc. There are two things that occured which were undesirable, one causing observable bugs, the other having the potential to.
The first is that when the swap process is stopped (e.g. by hitting the cross in the top left of the action bar) then it would trigger an event to be broadcast. This would hit the `StartSwapView`, and then that view would change the UI widgets to match the state of the swap process (i.e. "Stopping" == Disabled and unchecked switch, "Stopped" == Enabled and unchecked switch). When the UI was updated, it was inadvertantly sending further requests to stop swap, because `Switch` widgets _always_ notify their listeners, even if they are explicitly set via `setChecked()`. Now it temporarily removes listeners while brining the UI in line with what the swap service is doing, then reattaches them afterwards.
The later is due to `BroadcastReceivers` being added each time the `StartSwapView` is shown, and never unregistered. Now they are unregistered when the view is detached. Note that because we are not using the convoluted but well documented `Fragment` API, I'm not 100% certain this is the right time to detach listeners, but it seems suitable.
FYI, here is a logcat of me starting hitting the "Cancel swap" X button on master:
```
SwapManager I Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.
D Moving SwapService to background so that it can be GC'ed if required.
SwapType D Sending broadcast STOPPING from WifiSwap
WifiSwap D Sending message to swap webserver to stop it.
BonjourBroadcast D Unregistering MDNS service...
WifiSwap I we've been asked to stop the webserver: Thread-622 says stop
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap IS connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
D Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.
AvailableAppsFragment D Category 'What's New' selected.
OpenGLRenderer D endAllStagingAnimators on 0xb8c007e8 (RippleDrawable) with handle 0xb8cef7e8
BluetoothFinder D Stopping bluetooth discovery.
BluetoothAdapter D 235158966: getState() : mService = null. Returning STATE_OFF
BonjourFinder D Cancelling BonjourFinder, releasing multicast lock, removing jmdns service listeners
art I WaitForGcToComplete blocked for 7.385ms for cause DisableMovingGc
Manager.CallbackHandler D CM callback handler got msg 524290
D CM callback handler got msg 524290
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
D Sending broadcast STOPPING from WifiSwap
SwapManager D Moving SwapService to background so that it can be GC'ed if required.
WifiSwap D Sending message to swap webserver to stop it.
MessageQueue W Handler (org.fdroid.fdroid.localrepo.type.WifiSwap$5$1$1) {29eeaaee} sending message to a Handler on a dead thread
W java.lang.IllegalStateException: Handler (org.fdroid.fdroid.localrepo.type.WifiSwap$5$1$1) {29eeaaee} sending message to a Handler on a dead
thread
W at android.os.MessageQueue.enqueueMessage(MessageQueue.java:325)
W at android.os.Handler.enqueueMessage(Handler.java:631)
W at android.os.Handler.sendMessageAtTime(Handler.java:600)
W at android.os.Handler.sendMessageDelayed(Handler.java:570)
W at android.os.Handler.sendMessage(Handler.java:507)
W at org.fdroid.fdroid.localrepo.type.WifiSwap.stop(WifiSwap.java:167)
W at org.fdroid.fdroid.localrepo.type.SwapType$3.doInBackground(SwapType.java:103)
W at org.fdroid.fdroid.localrepo.type.SwapType$3.doInBackground(SwapType.java:100)
W at android.os.AsyncTask$2.call(AsyncTask.java:288)
W at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
W at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
W at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
W at java.lang.Thread.run(Thread.java:818)
BonjourBroadcast D Unregistering MDNS service...
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
SwapManager D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
SwapManager D Moving SwapService to background so that it can be GC'ed if required.
D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
```
And also on this branch:
```
SwapService I Asked to stop swapping, will stop bluetooth, wifi, and move service to BG for GC.
D Moving SwapService to background so that it can be GC'ed if required.
SwapType D Sending broadcast STOPPING from WifiSwap
WifiSwap D Sending message to swap webserver to stop it.
BonjourBroadcast D Unregistering MDNS service...
WifiSwap I we've been asked to stop the webserver: Thread-646 says stop
SwapService D Remembering that Bluetooth swap is NOT connected and WiFi swap IS connected.
StartSwapView D WiFi service is stopping (setting toggle to unchecked and disabled).
AvailableAppsFragment D Category 'What's New' selected.
OpenGLRenderer D endAllStagingAnimators on 0xb8b1bb70 (RippleDrawable) with handle 0xb8cec7c0
BonjourFinder D Cancelling BonjourFinder, releasing multicast lock, removing jmdns service listeners
BluetoothFinder D Stopping bluetooth discovery.
BluetoothAdapter D 93224770: getState() : mService = null. Returning STATE_OFF
Manager.CallbackHandler D CM callback handler got msg 524290
D CM callback handler got msg 524290
SwapType D Sending broadcast STOPPED from BonjourBroadcast
D Sending broadcast STOPPED from WifiSwap
SwapService D Moving SwapService to background so that it can be GC'ed if required.
D Remembering that Bluetooth swap is NOT connected and WiFi swap is NOT connected.
StartSwapView D WiFi service has stopped (setting toggle to not-visible).
Manager.CallbackHandler D CM callback handler got msg 524290
```
One of the most notable things that can be seen is the lack of the following message in the second (fixed) logcat.
> Received onCheckChanged(false) for WiFi swap, disabling WiFi swap in background thread.
See merge request !218
Previously, they were registered, then forgotten. This means that each time
the start swap view was run, another receiver was registered. As a result,
they were being invoked multiple times.
It doesn't appear that this had any specific side effects which were terrible,
but they definitely have the potential to going forward.
Note that because we are not using `Fragments` with their convoluted, but at
least well documented API, I'm not 100% certain that I've unregistered the
receivers at the right location.
Previously, something like this would happen:
* Swap service is cancelled
* WiFi swap is asked to stop
* Event is broadcast when done
* UI listens to this event
* Upon receiving the event, it updates the UI
* Updating the UI triggers an event, causing the process to happen again
An alternative solution to this would have been for the UI to stop listening
to listeners before WiFi swap is shut down, but that is then only specific
to the case when the swap view is being destroyed/removed. This could also
happen in other situations however, such as when the swap service times out.
When the view is detached, then the listeners will be unregistered.
This will also help in the future so that they can be temporarily
unregistered when manually changing the state of the switches.
Smooth Tor setup
This is a big reworking of the Tor support to make it really easy to setup, and to make .onion addresses work automatically even when "Use Tor" is off.
I needed to make the NetCipher v1.2.1 release to support this, hence it was originally WIP:.
See merge request !216
Thread runs at normal priority by default. AsyncTasks are integrated into
Android for handling things running in the background while keeping the UI
responsive.
This reverts most of commit 828cc272ee5235f868104b009349cc7e835e144f.