From ca5996c5b3ecfa8294b777b6f1d6d55b742fd934 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 17 Jun 2019 14:44:30 +0200 Subject: [PATCH] detect when USB OTG device is plugged in and show Nearby tab --- .../fdroid/views/main/NearbyViewBinder.java | 3 +- app/src/full/AndroidManifest.xml | 14 +++ .../nearby/UsbDeviceAttachedActivity.java | 112 ++++++++++++++++++ .../fdroid/views/main/NearbyViewBinder.java | 55 +++++++-- app/src/full/res/xml/device_filter.xml | 26 ++++ .../fdroid/views/main/MainActivity.java | 9 ++ 6 files changed, 206 insertions(+), 13 deletions(-) create mode 100644 app/src/full/java/org/fdroid/fdroid/nearby/UsbDeviceAttachedActivity.java create mode 100644 app/src/full/res/xml/device_filter.xml diff --git a/app/src/basic/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java b/app/src/basic/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java index f3e21451a..d53d0b937 100644 --- a/app/src/basic/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java +++ b/app/src/basic/java/org/fdroid/fdroid/views/main/NearbyViewBinder.java @@ -1,10 +1,9 @@ package org.fdroid.fdroid.views.main; import android.app.Activity; -import android.content.Intent; class NearbyViewBinder { - static void onActivityResult(Activity activity, Intent data) { + public static void updateUsbOtg(final Activity activity) { throw new IllegalStateException("unimplemented"); } } diff --git a/app/src/full/AndroidManifest.xml b/app/src/full/AndroidManifest.xml index 74779b98b..41fb74cfe 100644 --- a/app/src/full/AndroidManifest.xml +++ b/app/src/full/AndroidManifest.xml @@ -28,6 +28,7 @@ + @@ -42,6 +43,8 @@ + @@ -88,6 +91,17 @@ android:name=".nearby.SDCardScannerService" android:exported="false"/> + + + + + + + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +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 android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.text.TextUtils; +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} + * events then open up the right screen in {@link MainActivity}. + */ +public class UsbDeviceAttachedActivity extends Activity { + public static final String TAG = "UsbDeviceAttachedActivi"; + + private static final HashMap contentObservers = new HashMap<>(); + + @RequiresApi(api = 19) + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT < 19) { + finish(); + return; + } + + Intent intent = getIntent(); + if (intent == null || TextUtils.isEmpty(intent.getAction()) + || !UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + Log.i(TAG, "ignoring irrelevant intent: " + intent); + finish(); + return; + } + Log.i(TAG, "handling intent: " + intent); + + final ContentResolver contentResolver = getContentResolver(); + BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { + return; + } + NearbyViewBinder.updateUsbOtg(UsbDeviceAttachedActivity.this); + unregisterReceiver(this); + for (ContentObserver contentObserver : contentObservers.values()) { + contentResolver.unregisterContentObserver(contentObserver); + } + } + }; + registerReceiver(receiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED)); + + for (final UriPermission uriPermission : contentResolver.getPersistedUriPermissions()) { + Uri uri = uriPermission.getUri(); + final ContentObserver contentObserver = new ContentObserver(new Handler()) { + + @Override + public void onChange(boolean selfChange, Uri uri) { + NearbyViewBinder.updateUsbOtg(UsbDeviceAttachedActivity.this); + } + }; + contentResolver.registerContentObserver(uri, true, contentObserver); + } + intent.setComponent(new ComponentName(this, MainActivity.class)); + intent.putExtra(MainActivity.EXTRA_VIEW_NEARBY, true); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); + } + + @Override + public void finish() { + setResult(RESULT_OK); + super.finish(); + } +} 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 74996bfe0..f6e20dac9 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 @@ -2,11 +2,12 @@ package org.fdroid.fdroid.views.main; import android.Manifest; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.UriPermission; import android.content.pm.PackageManager; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -15,7 +16,7 @@ import android.os.storage.StorageVolume; import android.support.annotation.RequiresApi; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.support.v4.provider.DocumentFile; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; @@ -25,12 +26,12 @@ import android.widget.TextView; import android.widget.Toast; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; -import org.fdroid.fdroid.nearby.TreeUriUtils; import org.fdroid.fdroid.nearby.SDCardScannerService; import org.fdroid.fdroid.nearby.SwapService; import org.fdroid.fdroid.nearby.TreeUriScannerIntentService; import java.io.File; +import java.util.List; /** * A splash screen encouraging people to start the swap process. The swap @@ -59,13 +60,14 @@ import java.io.File; * @see TreeUriScannerIntentService * @see org.fdroid.fdroid.nearby.SDCardScannerService */ -class NearbyViewBinder { +public class NearbyViewBinder { public static final String TAG = "NearbyViewBinder"; - static File externalStorage = null; + private static File externalStorage = null; + private static View swapView; NearbyViewBinder(final Activity activity, FrameLayout parent) { - View swapView = activity.getLayoutInflater().inflate(R.layout.main_tab_swap, parent, true); + swapView = activity.getLayoutInflater().inflate(R.layout.main_tab_swap, parent, true); TextView subtext = swapView.findViewById(R.id.both_parties_need_fdroid_text); subtext.setText(activity.getString(R.string.nearby_splash__both_parties_need_fdroid, @@ -141,11 +143,24 @@ class NearbyViewBinder { }); } + updateUsbOtg(activity); + } + + public static void updateUsbOtg(final Activity activity) { if (Build.VERSION.SDK_INT < 24) { return; } - StorageManager storageManager = (StorageManager) activity.getSystemService(Context.STORAGE_SERVICE); - for (StorageVolume storageVolume : storageManager.getStorageVolumes()) { + if (swapView == null) { + Utils.debugLog(TAG, "swapView == null"); + return; + } + TextView storageVolumeText = swapView.findViewById(R.id.storage_volume_text); + Button requestStorageVolume = swapView.findViewById(R.id.request_storage_volume_button); + storageVolumeText.setVisibility(View.GONE); + requestStorageVolume.setVisibility(View.GONE); + + final StorageManager storageManager = (StorageManager) activity.getSystemService(Context.STORAGE_SERVICE); + for (final StorageVolume storageVolume : storageManager.getStorageVolumes()) { if (storageVolume.isRemovable() && !storageVolume.isPrimary()) { Log.i(TAG, "StorageVolume: " + storageVolume); final Intent intent = storageVolume.createAccessIntent(null); @@ -153,14 +168,32 @@ class NearbyViewBinder { Utils.debugLog(TAG, "Got null Storage Volume access Intent"); return; } - TextView storageVolumeText = swapView.findViewById(R.id.storage_volume_text); storageVolumeText.setVisibility(View.VISIBLE); - Button requestStorageVolume = swapView.findViewById(R.id.request_storage_volume_button); - requestStorageVolume.setText(storageVolume.getDescription(activity)); + + String text = storageVolume.getDescription(activity); + if (!TextUtils.isEmpty(text)) { + requestStorageVolume.setText(text); + UsbDevice usb = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + if (usb != null) { + text = String.format("%s (%s %s)", text, usb.getManufacturerName(), usb.getProductName()); + Toast.makeText(activity, text, Toast.LENGTH_LONG).show(); + } + } + requestStorageVolume.setVisibility(View.VISIBLE); requestStorageVolume.setOnClickListener(new View.OnClickListener() { @Override + @RequiresApi(api = 24) public void onClick(View v) { + List list = activity.getContentResolver().getPersistedUriPermissions(); + if (list != null) for (UriPermission uriPermission : list) { + Uri uri = uriPermission.getUri(); + if (uri.getPath().equals(String.format("/tree/%s:", storageVolume.getUuid()))) { + intent.setData(uri); + TreeUriScannerIntentService.onActivityResult(activity, intent); + return; + } + } activity.startActivityForResult(intent, MainActivity.REQUEST_STORAGE_ACCESS); } }); diff --git a/app/src/full/res/xml/device_filter.xml b/app/src/full/res/xml/device_filter.xml new file mode 100644 index 000000000..3ddae426d --- /dev/null +++ b/app/src/full/res/xml/device_filter.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java index 48a787156..82e9ce64b 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/main/MainActivity.java @@ -136,6 +136,15 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationB bottomNavigation .addItem(new BottomNavigationItem(R.drawable.ic_categories, R.string.main_menu__categories)) .addItem(new BottomNavigationItem(R.drawable.ic_nearby, R.string.main_menu__swap_nearby)); + + bottomNavigation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (bottomNavigation.getCurrentSelectedPosition() == 2) { + NearbyViewBinder.updateUsbOtg(MainActivity.this); + } + } + }); } bottomNavigation.setTabSelectedListener(this) .setBarBackgroundColor(getBottomNavigationBackgroundColorResId())