334 lines
13 KiB
Java
334 lines
13 KiB
Java
package com.dcx.ruiyiweiux;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.provider.DocumentsContract;
|
|
import android.util.Log;
|
|
import android.webkit.MimeTypeMap;
|
|
|
|
import java.io.OutputStream;
|
|
import java.nio.charset.StandardCharsets;
|
|
|
|
public class UsbStorageAccess {
|
|
private static final String TAG = "DCXUsbStorageAccess";
|
|
private static final String PREFS_NAME = "dcx_usb_storage_access";
|
|
private static final String PREF_TREE_URI = "tree_uri";
|
|
private static final int REQUEST_OPEN_TREE = 42001;
|
|
|
|
public static boolean hasPersistedUsbDirectory(Activity activity) {
|
|
String treeUri = getPersistedTreeUri(activity);
|
|
return treeUri != null && treeUri.length() > 0;
|
|
}
|
|
|
|
public static String getPersistedUsbDirectoryUri(Activity activity) {
|
|
String treeUri = getPersistedTreeUri(activity);
|
|
return treeUri == null ? "" : treeUri;
|
|
}
|
|
|
|
public static void requestUsbDirectoryPermission(Activity activity) {
|
|
Intent intent = new Intent(activity, ActivityBridge.class);
|
|
activity.startActivity(intent);
|
|
}
|
|
|
|
public static void clearPersistedUsbDirectory(Activity activity) {
|
|
String treeUriText = getPersistedTreeUri(activity);
|
|
if (treeUriText != null && treeUriText.length() > 0) {
|
|
try {
|
|
Uri uri = Uri.parse(treeUriText);
|
|
activity.getContentResolver().releasePersistableUriPermission(
|
|
uri,
|
|
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "release persisted tree uri failed", ex);
|
|
}
|
|
}
|
|
|
|
activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
.edit()
|
|
.remove(PREF_TREE_URI)
|
|
.apply();
|
|
}
|
|
|
|
public static boolean writeTextFile(Activity activity, String fileName, String content) {
|
|
byte[] bytes = content == null ? new byte[0] : content.getBytes(StandardCharsets.UTF_8);
|
|
return writeBytesFile(activity, fileName, bytes);
|
|
}
|
|
|
|
public static boolean writeBytesFile(Activity activity, String fileName, byte[] bytes) {
|
|
try {
|
|
String treeUriText = getPersistedTreeUri(activity);
|
|
if (treeUriText == null || treeUriText.length() == 0) {
|
|
return false;
|
|
}
|
|
|
|
Uri treeUri = Uri.parse(treeUriText);
|
|
Uri exportDir = ensureDirectory(activity, treeUri, "DCX_Export");
|
|
if (exportDir == null) {
|
|
return false;
|
|
}
|
|
|
|
Uri fileUri = createOrReplaceFile(activity, exportDir, fileName);
|
|
if (fileUri == null) {
|
|
return false;
|
|
}
|
|
|
|
OutputStream stream = activity.getContentResolver().openOutputStream(fileUri, "wt");
|
|
if (stream == null) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
stream.write(bytes == null ? new byte[0] : bytes);
|
|
stream.flush();
|
|
} finally {
|
|
stream.close();
|
|
}
|
|
|
|
return true;
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "write usb file failed: " + fileName, ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static Uri ensureDirectory(Activity activity, Uri treeUri, String displayName) {
|
|
try {
|
|
String treeDocumentId = DocumentsContract.getTreeDocumentId(treeUri);
|
|
if (isSelectedTreeDirectory(treeDocumentId, displayName)) {
|
|
return DocumentsContract.buildDocumentUriUsingTree(treeUri, treeDocumentId);
|
|
}
|
|
|
|
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
|
|
treeUri,
|
|
treeDocumentId);
|
|
|
|
android.database.Cursor cursor = activity.getContentResolver().query(
|
|
childrenUri,
|
|
new String[]{
|
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
|
},
|
|
null,
|
|
null,
|
|
null);
|
|
|
|
if (cursor != null) {
|
|
try {
|
|
while (cursor.moveToNext()) {
|
|
String documentId = cursor.getString(0);
|
|
String name = cursor.getString(1);
|
|
String mimeType = cursor.getString(2);
|
|
if (displayName.equals(name) &&
|
|
DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) {
|
|
return DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId);
|
|
}
|
|
}
|
|
} finally {
|
|
cursor.close();
|
|
}
|
|
}
|
|
|
|
return DocumentsContract.createDocument(
|
|
activity.getContentResolver(),
|
|
DocumentsContract.buildDocumentUriUsingTree(
|
|
treeUri,
|
|
treeDocumentId),
|
|
DocumentsContract.Document.MIME_TYPE_DIR,
|
|
displayName);
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "ensure usb export directory failed", ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static boolean isSelectedTreeDirectory(String treeDocumentId, String displayName) {
|
|
if (treeDocumentId == null || displayName == null) {
|
|
return false;
|
|
}
|
|
|
|
int colonIndex = treeDocumentId.indexOf(':');
|
|
String pathPart = colonIndex >= 0 ? treeDocumentId.substring(colonIndex + 1) : treeDocumentId;
|
|
while (pathPart.endsWith("/") && pathPart.length() > 0) {
|
|
pathPart = pathPart.substring(0, pathPart.length() - 1);
|
|
}
|
|
|
|
int slashIndex = Math.max(pathPart.lastIndexOf('/'), pathPart.lastIndexOf('\\'));
|
|
String selectedName = slashIndex >= 0 ? pathPart.substring(slashIndex + 1) : pathPart;
|
|
return displayName.equals(selectedName);
|
|
}
|
|
|
|
private static Uri createOrReplaceFile(Activity activity, Uri parentUri, String fileName) {
|
|
try {
|
|
String safeName = sanitizeFileName(fileName);
|
|
String mimeType = getMimeType(safeName);
|
|
|
|
Uri existingFile = findChild(activity, parentUri, safeName, null);
|
|
if (existingFile != null) {
|
|
DocumentsContract.deleteDocument(activity.getContentResolver(), existingFile);
|
|
}
|
|
|
|
return DocumentsContract.createDocument(
|
|
activity.getContentResolver(),
|
|
parentUri,
|
|
mimeType,
|
|
safeName);
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "create usb export file failed: " + fileName, ex);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static Uri findChild(Activity activity, Uri parentUri, String displayName, String requiredMimeType) {
|
|
android.database.Cursor cursor = null;
|
|
try {
|
|
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
|
|
parentUri,
|
|
DocumentsContract.getDocumentId(parentUri));
|
|
cursor = activity.getContentResolver().query(
|
|
childrenUri,
|
|
new String[]{
|
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
|
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
|
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
|
},
|
|
null,
|
|
null,
|
|
null);
|
|
|
|
if (cursor == null) {
|
|
return null;
|
|
}
|
|
|
|
while (cursor.moveToNext()) {
|
|
String documentId = cursor.getString(0);
|
|
String name = cursor.getString(1);
|
|
String mimeType = cursor.getString(2);
|
|
if (displayName.equals(name) &&
|
|
(requiredMimeType == null || requiredMimeType.equals(mimeType))) {
|
|
return DocumentsContract.buildDocumentUriUsingTree(parentUri, documentId);
|
|
}
|
|
}
|
|
} catch (Exception ignored) {
|
|
} finally {
|
|
if (cursor != null) {
|
|
cursor.close();
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static String getPersistedTreeUri(Context context) {
|
|
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
return prefs.getString(PREF_TREE_URI, "");
|
|
}
|
|
|
|
private static void savePersistedTreeUri(Context context, Uri uri) {
|
|
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
.edit()
|
|
.putString(PREF_TREE_URI, uri.toString())
|
|
.apply();
|
|
}
|
|
|
|
private static boolean isLikelyUsbTree(Uri uri) {
|
|
try {
|
|
String documentId = DocumentsContract.getTreeDocumentId(uri);
|
|
if (documentId == null || documentId.length() == 0) {
|
|
return false;
|
|
}
|
|
|
|
String lowerDocumentId = documentId.toLowerCase();
|
|
return !lowerDocumentId.startsWith("primary:") &&
|
|
!lowerDocumentId.startsWith("home:");
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "check selected tree uri failed", ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static String sanitizeFileName(String fileName) {
|
|
if (fileName == null || fileName.trim().length() == 0) {
|
|
return "BFI_Export.txt";
|
|
}
|
|
|
|
return fileName.replaceAll("[\\\\/:*?\"<>|]", "_");
|
|
}
|
|
|
|
private static String getMimeType(String fileName) {
|
|
String extension = MimeTypeMap.getFileExtensionFromUrl(fileName);
|
|
if (extension != null && extension.length() > 0) {
|
|
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
|
|
if (mimeType != null && mimeType.length() > 0) {
|
|
return mimeType;
|
|
}
|
|
}
|
|
|
|
if (fileName.endsWith(".csv")) {
|
|
return "text/csv";
|
|
}
|
|
|
|
if (fileName.endsWith(".pdf")) {
|
|
return "application/pdf";
|
|
}
|
|
|
|
return "text/plain";
|
|
}
|
|
|
|
public static class ActivityBridge extends Activity {
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
|
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
|
|
startActivityForResult(intent, REQUEST_OPEN_TREE);
|
|
}
|
|
|
|
@Override
|
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
|
|
if (requestCode == REQUEST_OPEN_TREE && resultCode == RESULT_OK && data != null) {
|
|
Uri uri = data.getData();
|
|
if (uri != null) {
|
|
if (!isLikelyUsbTree(uri)) {
|
|
Log.w(TAG, "selected tree is internal storage, ignore: " + uri);
|
|
finish();
|
|
return;
|
|
}
|
|
|
|
int flags = data.getFlags() &
|
|
(Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
|
|
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION |
|
|
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
|
boolean persisted = false;
|
|
try {
|
|
getContentResolver().takePersistableUriPermission(
|
|
uri,
|
|
flags & (Intent.FLAG_GRANT_READ_URI_PERMISSION |
|
|
Intent.FLAG_GRANT_WRITE_URI_PERMISSION));
|
|
persisted = true;
|
|
} catch (Exception ex) {
|
|
Log.w(TAG, "persist selected usb tree uri failed", ex);
|
|
}
|
|
|
|
if (persisted) {
|
|
savePersistedTreeUri(this, uri);
|
|
}
|
|
}
|
|
}
|
|
|
|
finish();
|
|
}
|
|
}
|
|
}
|