Fixed USB OTG URI forgetting when app pauses

This commit is contained in:
Emmanuel M
2018-06-08 09:46:57 -03:00
parent 607eec485d
commit 51db9450db
6 changed files with 177 additions and 82 deletions
@@ -48,6 +48,7 @@ import android.service.quicksettings.TileService;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
@@ -91,9 +92,10 @@ import com.amaze.filemanager.filesystem.HybridFile;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
import com.amaze.filemanager.filesystem.PasteHelper;
import com.amaze.filemanager.filesystem.RootHelper;
import com.amaze.filemanager.filesystem.SingletonUsbOtg;
import com.amaze.filemanager.filesystem.usb.SingletonUsbOtg;
import com.amaze.filemanager.filesystem.ssh.CustomSshJConfig;
import com.amaze.filemanager.filesystem.ssh.SshConnectionPool;
import com.amaze.filemanager.filesystem.usb.UsbOtgRepresentation;
import com.amaze.filemanager.fragments.AppsListFragment;
import com.amaze.filemanager.fragments.CloudSheetFragment;
import com.amaze.filemanager.fragments.CloudSheetFragment.CloudConnectionCallbacks;
@@ -686,32 +688,13 @@ public class MainActivity extends PermissionsActivity implements SmbConnectionLi
if (usb != null && !rv.contains(usb.getPath())) rv.add(usb.getPath());
if (SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isUsbDeviceConnected()) rv.add(OTGUtil.PREFIX_OTG + "/");
if (SingletonUsbOtg.getInstance().isDeviceConnected()) {
rv.add(OTGUtil.PREFIX_OTG + "/");
}
}
return rv;
}
/**
* Method finds whether a USB device is connected or not
* @return true if device is connected
*/
private boolean isUsbDeviceConnected() {
if (OTGUtil.isMassStorageDeviceConnected(this)) {
if(!SingletonUsbOtg.getInstance().hasRootBeenRequested()) {
SingletonUsbOtg.getInstance().setHasRootBeenRequested(false);
// we need to set this every time as there is no way to know that whether USB device was
// disconnected after closing the app and another one was connected in that case
// the URI will obviously change otherwise we could persist the URI even after
// reopening the app by not writing this preference when it's not null
SingletonUsbOtg.getInstance().setUsbOtgRoot(null);
}
return true;
} else {
SingletonUsbOtg.getInstance().setUsbOtgRoot(null);
return false;
}
}
@Override
public void onBackPressed() {
if (!drawer.isLocked() && drawer.isOpen()) {
@@ -1135,28 +1118,65 @@ public class MainActivity extends PermissionsActivity implements SmbConnectionLi
registerReceiver(receiver2, new IntentFilter(TAG_INTENT_FILTER_GENERAL));
if (SDK_INT >= Build.VERSION_CODES.KITKAT) {
// Registering intent filter for OTG
IntentFilter otgFilter = new IntentFilter();
otgFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
otgFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mOtgReceiver, otgFilter);
updateUsbInformation();
}
}
/**
* Updates everything related to USB devices
* MUST ALWAYS be called after onResume()
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void updateUsbInformation() {
boolean isInformationUpdated = false;
List<UsbOtgRepresentation> connectedDevices = OTGUtil.getMassStorageDevicesConnected(this);
if(!connectedDevices.isEmpty()) {
if (SingletonUsbOtg.getInstance().getUsbOtgRoot() != null && OTGUtil.isUsbUriAccessible(this)) {
for (UsbOtgRepresentation device : connectedDevices) {
if (SingletonUsbOtg.getInstance().checkIfRootIsFromDevice(device)) {
isInformationUpdated = true;
break;
}
}
if(!isInformationUpdated) {
SingletonUsbOtg.getInstance().resetUsbOtgRoot();
}
}
if(!isInformationUpdated) {
SingletonUsbOtg.getInstance().setConnectedDevice(connectedDevices.get(0));
isInformationUpdated = true;
}
}
if(!isInformationUpdated) {
SingletonUsbOtg.getInstance().resetUsbOtgRoot();
drawer.refreshDrawer();
goToMain(null);
}
// Registering intent filter for OTG
IntentFilter otgFilter = new IntentFilter();
otgFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
otgFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(mOtgReceiver, otgFilter);
}
/**
* Receiver to check if a USB device is connected at the runtime of application
* If device is not connected at runtime (i.e. it was connected when the app was closed)
* then {@link #isUsbDeviceConnected()} method handles the connection through
* {@link #getStorageDirectories()}
*/
BroadcastReceiver mOtgReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
SingletonUsbOtg.getInstance().setUsbOtgRoot(null);
SingletonUsbOtg.getInstance().resetUsbOtgRoot();
List<UsbOtgRepresentation> connectedDevices = OTGUtil.getMassStorageDevicesConnected(MainActivity.this);
SingletonUsbOtg.getInstance().setConnectedDevice(connectedDevices.get(0));
drawer.refreshDrawer();
} else if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
SingletonUsbOtg.getInstance().setUsbOtgRoot(null);
SingletonUsbOtg.getInstance().resetUsbOtgRoot();
drawer.refreshDrawer();
goToMain(null);
}
@@ -1594,7 +1614,7 @@ public class MainActivity extends PermissionsActivity implements SmbConnectionLi
if (SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
SingletonUsbOtg.getInstance().setUsbOtgRoot(null);
SingletonUsbOtg.getInstance().resetUsbOtgRoot();
drawer.refreshDrawer();
}
}
@@ -1,38 +0,0 @@
package com.amaze.filemanager.filesystem;
import android.net.Uri;
import android.support.annotation.Nullable;
public class SingletonUsbOtg {
private static SingletonUsbOtg instance = null;
public static SingletonUsbOtg getInstance() {
if(instance == null) instance = new SingletonUsbOtg();
return instance;
}
private Uri usbOtgRoot = null;
/**
* Indicates whether last app exit was for setting {@link #usbOtgRoot} or not
*/
private boolean hasRootBeenRequested = false;
private SingletonUsbOtg() { }
public void setUsbOtgRoot(@Nullable Uri root) {
usbOtgRoot = root;
}
public @Nullable Uri getUsbOtgRoot() {
return usbOtgRoot;
}
public void setHasRootBeenRequested(boolean hasRootBeenRequested) {
this.hasRootBeenRequested = hasRootBeenRequested;
}
public boolean hasRootBeenRequested() {
return hasRootBeenRequested;
}
}
@@ -0,0 +1,47 @@
package com.amaze.filemanager.filesystem.usb;
import android.bluetooth.BluetoothClass;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class SingletonUsbOtg {
private static SingletonUsbOtg instance = null;
public static SingletonUsbOtg getInstance() {
if(instance == null) instance = new SingletonUsbOtg();
return instance;
}
private UsbOtgRepresentation connectedDevice = null;
private @Nullable Uri usbOtgRoot;
private SingletonUsbOtg() { }
public void setConnectedDevice(UsbOtgRepresentation connectedDevice) {
this.connectedDevice = connectedDevice;
}
public boolean isDeviceConnected() {
return connectedDevice != null;
}
public void setUsbOtgRoot(@Nullable Uri root) {
if(connectedDevice == null) throw new IllegalStateException("No device connected!");
usbOtgRoot = root;
}
public void resetUsbOtgRoot() {
connectedDevice = null;
usbOtgRoot = null;
}
public @Nullable Uri getUsbOtgRoot() {
return usbOtgRoot;
}
public boolean checkIfRootIsFromDevice(@NonNull UsbOtgRepresentation device) {
return usbOtgRoot != null && connectedDevice.hashCode() == device.hashCode();
}
}
@@ -0,0 +1,46 @@
package com.amaze.filemanager.filesystem.usb;
import android.hardware.usb.UsbInterface;
import android.support.annotation.Nullable;
import com.amaze.filemanager.utils.Utils;
/**
* This class replesents a usb device.
*
* @see UsbOtgRepresentation#equals(Object)
*/
public class UsbOtgRepresentation {
public final int productID, vendorID;
public final @Nullable String serialNumber;
public UsbOtgRepresentation(int productID, int vendorID, @Nullable String serialNumber) {
this.productID = productID;
this.vendorID = vendorID;
this.serialNumber = serialNumber;
}
/**
* This does not ensure a device is equal to another!
* This tests parameters to know to a certain degree of certanty that a device is "similar enough"
* to another one to be the same one.
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof UsbOtgRepresentation)) return false;
UsbOtgRepresentation other = (UsbOtgRepresentation) obj;
return productID == other.productID && vendorID == other.vendorID
&& ((serialNumber == null && other.serialNumber == null) || serialNumber.equals(other.serialNumber));
}
@Override
public int hashCode() {
int result = productID;
result = 37 * result + vendorID;
result = 37 * result + (serialNumber != null? serialNumber.hashCode():0);
return result;
}
}
@@ -56,7 +56,7 @@ import com.amaze.filemanager.activities.PreferencesActivity;
import com.amaze.filemanager.database.CloudHandler;
import com.amaze.filemanager.filesystem.HybridFile;
import com.amaze.filemanager.filesystem.RootHelper;
import com.amaze.filemanager.filesystem.SingletonUsbOtg;
import com.amaze.filemanager.filesystem.usb.SingletonUsbOtg;
import com.amaze.filemanager.fragments.AppsListFragment;
import com.amaze.filemanager.fragments.CloudSheetFragment;
import com.amaze.filemanager.fragments.FtpServerFragment;
@@ -582,13 +582,10 @@ public class Drawer implements NavigationView.OnNavigationItemSelectedListener {
&& SingletonUsbOtg.getInstance().getUsbOtgRoot() == null) {
MaterialDialog dialog = GeneralDialogCreation.showOtgSafExplanationDialog(mainActivity);
dialog.getActionButton(DialogAction.POSITIVE).setOnClickListener((v) -> {
SingletonUsbOtg.getInstance().setHasRootBeenRequested(true);
Intent safIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
mainActivity.startActivityForResult(safIntent, MainActivity.REQUEST_CODE_SAF);
dialog.dismiss();
});
dialog.show();
} else {
pendingPath = meta.path;
closeIfNotLocked();
@@ -3,18 +3,26 @@ package com.amaze.filemanager.utils;
import android.content.Context;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
import com.amaze.filemanager.filesystem.HybridFileParcelable;
import com.amaze.filemanager.filesystem.RootHelper;
import com.amaze.filemanager.filesystem.SingletonUsbOtg;
import com.amaze.filemanager.filesystem.usb.SingletonUsbOtg;
import com.amaze.filemanager.filesystem.usb.UsbOtgRepresentation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import static android.content.Context.USB_SERVICE;
@@ -108,26 +116,41 @@ public class OTGUtil {
return rootUri;
}
/**
* Check if the usb uri is still accessible
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static boolean isUsbUriAccessible(Context context) {
Uri rootUriString = SingletonUsbOtg.getInstance().getUsbOtgRoot();
return DocumentsContract.isDocumentUri(context, rootUriString);
}
/**
* Checks if there is at least one USB device connected with class MASS STORAGE.
*/
public static boolean isMassStorageDeviceConnected(@NonNull final Context context) {
@NonNull
public static List<UsbOtgRepresentation> getMassStorageDevicesConnected(@NonNull final Context context) {
UsbManager usbManager = (UsbManager) context.getSystemService(USB_SERVICE);
if(usbManager == null) return false;
if(usbManager == null) return Collections.emptyList();
HashMap<String, UsbDevice> devices = usbManager.getDeviceList();
ArrayList<UsbOtgRepresentation> usbOtgRepresentations = new ArrayList<>();
for (String deviceName : devices.keySet()) {
UsbDevice device = devices.get(deviceName);
for (int i = 0; i < device.getInterfaceCount(); i++){
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE){
return true;
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_MASS_STORAGE) {
final @Nullable String serial =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP? device.getSerialNumber():null;
UsbOtgRepresentation usb = new UsbOtgRepresentation(device.getProductId(), device.getVendorId(), serial);
usbOtgRepresentations.add(usb);
}
}
}
return false;
return usbOtgRepresentations;
}
}