From 6fd7970ca58ee6a97efbb34ea83a2962908ff59c Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 11 Nov 2020 13:52:22 +0100 Subject: [PATCH 1/5] update USB-OTG status every time the user switches to Nearby The USB-OTG device can be plugged and unplugged anytime, so the Nearby view should be updated each time the user switches to this screen. Registered callbacks should handle updating the USB-OTG status while the Nearby view is active. --- .../org/fdroid/fdroid/views/main/MainViewAdapter.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/views/main/MainViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/main/MainViewAdapter.java index eb7319f0a..5fdf45e74 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/main/MainViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/main/MainViewAdapter.java @@ -21,14 +21,14 @@ package org.fdroid.fdroid.views.main; -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.RecyclerView; import android.util.SparseIntArray; import android.view.Menu; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.PopupMenu; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; import org.fdroid.fdroid.R; /** @@ -76,6 +76,8 @@ class MainViewAdapter extends RecyclerView.Adapter { long viewType = getItemId(holder.getAdapterPosition()); if (viewType == R.id.updates) { holder.bindUpdates(); + } else if (viewType == R.id.nearby) { + NearbyViewBinder.updateUsbOtg(activity); } } From 6af66abf54db01d5679075209fe222bdd534897c Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 11 Nov 2020 15:08:49 +0100 Subject: [PATCH 2/5] use MEDIA_* events to update USB-OTG detection This means that sometimes the NearbyView is updated from a BroadcastReceiver's Context, which is not an Activity. So this has to try a little harder to fetch the Activity instance needed for the prompt to request permissions to a folder on the USB. This adds a failsafe to fallback to the file:/// scanning in SDCardScannerService. --- app/src/full/AndroidManifest.xml | 12 +++++++++- .../nearby/UsbDeviceMediaMountedReceiver.java | 23 +++++++++++++++++++ .../fdroid/views/main/NearbyViewBinder.java | 19 +++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceMediaMountedReceiver.java diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index abfb948e4..ffc489a44 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -100,7 +100,6 @@ android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter"/> - @@ -110,6 +109,17 @@ android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" android:resource="@xml/device_filter"/> + + + + + + + + + + + Date: Wed, 11 Nov 2020 15:15:24 +0100 Subject: [PATCH 3/5] allow SDCard/TreeUri scans to be manually triggered by user button press Before, the preference blocked all scans. That lead to confusing UX since the "Try it" and USB-OTG buttons would do nothing. --- .../fdroid/fdroid/nearby/SDCardScannerService.java | 13 +++++-------- .../fdroid/nearby/TreeUriScannerIntentService.java | 14 +++++--------- app/src/main/java/org/fdroid/fdroid/FDroidApp.java | 4 +++- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/SDCardScannerService.java b/app/src/full/java/org/fdroid/fdroid/nearby/SDCardScannerService.java index 733038613..9de7e7408 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/SDCardScannerService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/SDCardScannerService.java @@ -28,11 +28,10 @@ import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Process; -import androidx.core.content.ContextCompat; import android.util.Log; +import androidx.core.content.ContextCompat; import org.fdroid.fdroid.IndexUpdater; import org.fdroid.fdroid.IndexV1Updater; -import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; import java.io.File; @@ -55,7 +54,7 @@ import java.util.List; * "External Storage" *

* Scanning the removable storage requires that the user allowed it. This - * requires both the {@link Preferences#isScanRemovableStorageEnabled()} + * requires both the {@link org.fdroid.fdroid.Preferences#isScanRemovableStorageEnabled()} * and the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} * permission to be enabled. * @@ -75,11 +74,9 @@ public class SDCardScannerService extends IntentService { } public static void scan(Context context) { - if (Preferences.get().isScanRemovableStorageEnabled()) { - Intent intent = new Intent(context, SDCardScannerService.class); - intent.setAction(ACTION_SCAN); - context.startService(intent); - } + Intent intent = new Intent(context, SDCardScannerService.class); + intent.setAction(ACTION_SCAN); + context.startService(intent); } @Override diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java b/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java index 54d8033dc..a0750c838 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java @@ -20,7 +20,6 @@ package org.fdroid.fdroid.nearby; import android.annotation.TargetApi; -import android.app.Activity; import android.app.IntentService; import android.content.ContentResolver; import android.content.Context; @@ -28,15 +27,14 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Process; -import androidx.documentfile.provider.DocumentFile; import android.util.Log; import android.widget.Toast; +import androidx.documentfile.provider.DocumentFile; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.fdroid.fdroid.AddRepoIntentService; import org.fdroid.fdroid.IndexUpdater; import org.fdroid.fdroid.IndexV1Updater; -import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Repo; @@ -82,12 +80,10 @@ public class TreeUriScannerIntentService extends IntentService { } public static void scan(Context context, Uri data) { - if (Preferences.get().isScanRemovableStorageEnabled()) { - Intent intent = new Intent(context, TreeUriScannerIntentService.class); - intent.setAction(ACTION_SCAN_TREE_URI); - intent.setData(data); - context.startService(intent); - } + Intent intent = new Intent(context, TreeUriScannerIntentService.class); + intent.setAction(ACTION_SCAN_TREE_URI); + intent.setData(data); + context.startService(intent); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 442ef01fa..459ff948e 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -539,7 +539,9 @@ public class FDroidApp extends Application { atStartTime.edit().remove(queryStringKey).apply(); } - SDCardScannerService.scan(this); + if (Preferences.get().isScanRemovableStorageEnabled()) { + SDCardScannerService.scan(this); + } } /** From 6887e09d88d37ccb3a6ddd744a4c52d9a1560853 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 11 Nov 2020 15:17:36 +0100 Subject: [PATCH 4/5] properly track ContentObservers so they can be unregistered --- .../fdroid/nearby/UsbDeviceAttachedReceiver.java | 3 +-- .../fdroid/nearby/UsbDeviceDetachedReceiver.java | 12 ++---------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java index f9ed28cd1..b253f7f87 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java @@ -40,7 +40,6 @@ import android.util.Log; import org.fdroid.fdroid.views.main.MainActivity; import org.fdroid.fdroid.views.main.NearbyViewBinder; -import java.util.HashMap; /** * This is just a shim to receive {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} @@ -49,7 +48,6 @@ import java.util.HashMap; public class UsbDeviceAttachedReceiver extends BroadcastReceiver { public static final String TAG = "UsbDeviceAttachedReceiv"; - private static final HashMap contentObservers = new HashMap<>(); @RequiresApi(api = 19) @Override @@ -77,6 +75,7 @@ public class UsbDeviceAttachedReceiver extends BroadcastReceiver { } }; contentResolver.registerContentObserver(uri, true, contentObserver); + UsbDeviceDetachedReceiver.contentObservers.put(uri, contentObserver); } } } diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceDetachedReceiver.java b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceDetachedReceiver.java index bb902b705..00b71da99 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceDetachedReceiver.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceDetachedReceiver.java @@ -19,25 +19,17 @@ package org.fdroid.fdroid.nearby; -import android.app.Activity; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.UriPermission; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.net.Uri; import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.Log; -import org.fdroid.fdroid.views.main.MainActivity; +import androidx.annotation.RequiresApi; import org.fdroid.fdroid.views.main.NearbyViewBinder; import java.util.HashMap; @@ -49,7 +41,7 @@ import java.util.HashMap; public class UsbDeviceDetachedReceiver extends BroadcastReceiver { public static final String TAG = "UsbDeviceDetachedReceiv"; - private static final HashMap contentObservers = new HashMap<>(); + static final HashMap contentObservers = new HashMap<>(); @RequiresApi(api = 19) @Override From d00108ba68a980ca98c7ed7ab3be33e1f274596b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 11 Nov 2020 16:24:55 +0100 Subject: [PATCH 5/5] support opening USB-OTG on android-29+ Here's another gem: they added this nice new API in android-24 and told everyone this was THE WAY. Then they made it a no-op in android-29 https://developer.android.com/reference/android/os/storage/StorageVolume#createAccessIntent(java.lang.String) --- .../nearby/TreeUriScannerIntentService.java | 5 ++++ .../nearby/UsbDeviceAttachedReceiver.java | 8 +------ .../fdroid/views/main/NearbyViewBinder.java | 24 +++++++++++++++---- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java b/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java index a0750c838..441fc10e2 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/TreeUriScannerIntentService.java @@ -74,6 +74,11 @@ public class TreeUriScannerIntentService extends IntentService { public static final String TAG = "TreeUriScannerIntentSer"; private static final String ACTION_SCAN_TREE_URI = "org.fdroid.fdroid.nearby.action.SCAN_TREE_URI"; + /** + * @see DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY + * @see ExternalStorageProvider.AUTHORITY + */ + public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"; public TreeUriScannerIntentService() { super("TreeUriScannerIntentService"); diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java index b253f7f87..1eccc2d10 100644 --- a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java +++ b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java @@ -19,25 +19,19 @@ package org.fdroid.fdroid.nearby; -import android.app.Activity; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.UriPermission; import android.database.ContentObserver; import android.hardware.usb.UsbManager; import android.net.Uri; import android.os.Build; -import android.os.Bundle; import android.os.Handler; -import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import android.text.TextUtils; import android.util.Log; -import org.fdroid.fdroid.views.main.MainActivity; +import androidx.annotation.RequiresApi; import org.fdroid.fdroid.views.main.NearbyViewBinder; diff --git a/app/src/full/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java b/app/src/full/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java index 4bc2ab5bf..5d29e0c2f 100644 --- a/app/src/full/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java +++ b/app/src/full/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java @@ -13,6 +13,7 @@ import android.os.Build; import android.os.Environment; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; +import android.provider.DocumentsContract; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -21,11 +22,9 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; - import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.nearby.SDCardScannerService; @@ -61,6 +60,8 @@ import java.util.List; * * @see TreeUriScannerIntentService * @see org.fdroid.fdroid.nearby.SDCardScannerService + *

+ * TODO use {@link StorageManager#registerStorageVolumeCallback(Executor, StorageManager.StorageVolumeCallback)} */ public class NearbyViewBinder { public static final String TAG = "NearbyViewBinder"; @@ -165,11 +166,26 @@ public class NearbyViewBinder { for (final StorageVolume storageVolume : storageManager.getStorageVolumes()) { if (storageVolume.isRemovable() && !storageVolume.isPrimary()) { Log.i(TAG, "StorageVolume: " + storageVolume); - final Intent intent = storageVolume.createAccessIntent(null); - if (intent == null) { + Intent tmpIntent = null; + if (Build.VERSION.SDK_INT < 29) { + tmpIntent = storageVolume.createAccessIntent(null); + } else { + tmpIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + tmpIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, + Uri.parse("content://" + + TreeUriScannerIntentService.EXTERNAL_STORAGE_PROVIDER_AUTHORITY + + "/tree/" + + storageVolume.getUuid() + + "%3A/document/" + + storageVolume.getUuid() + + "%3A")); + } + if (tmpIntent == null) { Utils.debugLog(TAG, "Got null Storage Volume access Intent"); return; } + final Intent intent = tmpIntent; + storageVolumeText.setVisibility(View.VISIBLE); String text = storageVolume.getDescription(context);