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"/> </receiver> - <receiver android:name=".nearby.UsbDeviceDetachedReceiver"> <intent-filter> @@ -110,6 +109,17 @@ android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" android:resource="@xml/device_filter"/> </receiver> + <receiver android:name=".nearby.UsbDeviceMediaMountedReceiver"> + <intent-filter> + <action android:name="android.intent.action.MEDIA_EJECT" /> + <action android:name="android.intent.action.MEDIA_REMOVED" /> + <action android:name="android.intent.action.MEDIA_MOUNTED" /> + <action android:name="android.intent.action.MEDIA_BAD_REMOVAL" /> + + <data android:scheme="content" /> + <data android:scheme="file" /> + </intent-filter> + </receiver> <activity android:name=".panic.PanicPreferencesActivity" 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" * <p> * 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..441fc10e2 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; @@ -76,18 +74,21 @@ 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 <a href="https://android.googlesource.com/platform/frameworks/base/+/android-10.0.0_r38/core/java/android/provider/DocumentsContract.java#238">DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY</a> + * @see <a href="https://android.googlesource.com/platform/frameworks/base/+/android-10.0.0_r38/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java#70">ExternalStorageProvider.AUTHORITY</a> + */ + public static final String EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"; public TreeUriScannerIntentService() { super("TreeUriScannerIntentService"); } 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/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedReceiver.java index f9ed28cd1..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,28 +19,21 @@ 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; /** * This is just a shim to receive {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} @@ -49,7 +42,6 @@ import java.util.HashMap; public class UsbDeviceAttachedReceiver extends BroadcastReceiver { public static final String TAG = "UsbDeviceAttachedReceiv"; - private static final HashMap<Uri, ContentObserver> contentObservers = new HashMap<>(); @RequiresApi(api = 19) @Override @@ -77,6 +69,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<Uri, ContentObserver> contentObservers = new HashMap<>(); + static final HashMap<Uri, ContentObserver> contentObservers = new HashMap<>(); @RequiresApi(api = 19) @Override diff --git a/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceMediaMountedReceiver.java b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceMediaMountedReceiver.java new file mode 100644 index 000000000..c07370a29 --- /dev/null +++ b/app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceMediaMountedReceiver.java @@ -0,0 +1,23 @@ +package org.fdroid.fdroid.nearby; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Environment; +import org.fdroid.fdroid.views.main.NearbyViewBinder; + +public class UsbDeviceMediaMountedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null || intent.getAction() == null) { + return; + } + String action = intent.getAction(); + if (Environment.MEDIA_BAD_REMOVAL.equals(action) + || Environment.MEDIA_MOUNTED.equals(action) + || Environment.MEDIA_REMOVED.equals(action) + || Environment.MEDIA_EJECTING.equals(action)) { + NearbyViewBinder.updateUsbOtg(context); + } + } +} 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 21c5b7bb5..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 + * <p> + * 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); @@ -196,8 +212,23 @@ public class NearbyViewBinder { return; } } - ((Activity) context).startActivityForResult(intent, - MainActivity.REQUEST_STORAGE_ACCESS); + + Activity activity = null; + if (context instanceof Activity) { + activity = (Activity) context; + } else if (swapView != null && swapView.getContext() instanceof Activity) { + activity = (Activity) swapView.getContext(); + } + + if (activity != null) { + activity.startActivityForResult(intent, MainActivity.REQUEST_STORAGE_ACCESS); + } else { + // scan in the background without requesting permissions + Toast.makeText(context.getApplicationContext(), + context.getString(R.string.scan_removable_storage_toast, externalStorage), + Toast.LENGTH_SHORT).show(); + SDCardScannerService.scan(context); + } } }); } 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); + } } /** 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<MainViewController> { long viewType = getItemId(holder.getAdapterPosition()); if (viewType == R.id.updates) { holder.bindUpdates(); + } else if (viewType == R.id.nearby) { + NearbyViewBinder.updateUsbOtg(activity); } }