Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions src/lib/editorFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,39 +612,55 @@ export default class EditorFile {
const protocol = Url.getProtocol(this.#uri);
const text = this.session.getValue();

// Helper for JS-based comparison (used as fallback)
const jsCompare = async (fileUri) => {
const fs = fsOperation(fileUri);
const oldText = await fs.readFile(this.encoding);
return await system.compareTexts(oldText, text);
};

if (/s?ftp:/.test(protocol)) {
// if file is a ftp or sftp file, get file content from cached file.
// remove ':' from protocol because cache file of remote files are
// stored as ftp102525465N i.e. protocol + id
// Cache files are local file:// URIs, so native file reading works
// FTP/SFTP files use cached local file
const cacheFilename = protocol.slice(0, -1) + this.id;
const cacheFileUri = Url.join(CACHE_STORAGE, cacheFilename);

try {
return await system.compareFileText(cacheFileUri, this.encoding, text);
} catch (error) {
console.error("Native compareFileText failed:", error);
return false;
console.error(
"Native compareFileText failed, using JS fallback:",
error,
);
try {
return await jsCompare(cacheFileUri);
} catch (fallbackError) {
console.error(fallbackError);
return false;
}
}
}

if (/^(file|content):/.test(protocol)) {
// file:// and content:// URIs can be handled by native Android code
// Native reads file AND compares in background thread
// file:// and content:// URIs - try native first, fallback to JS
try {
return await system.compareFileText(this.uri, this.encoding, text);
} catch (error) {
console.error("Native compareFileText failed:", error);
return false;
console.error(
"Native compareFileText failed, using JS fallback:",
error,
);
try {
return await jsCompare(this.uri);
} catch (fallbackError) {
console.error(fallbackError);
return false;
}
}
}

// Other protocols - JS reads file, native compares strings
try {
const fs = fsOperation(this.uri);
const oldText = await fs.readFile(this.encoding);

// Offload string comparison to background thread
return await system.compareTexts(oldText, text);
return await jsCompare(this.uri);
} catch (error) {
console.error(error);
return false;
Expand Down
31 changes: 28 additions & 3 deletions src/plugins/system/android/com/foxdebug/system/System.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.Settings;

import androidx.core.content.ContextCompat;
Expand Down Expand Up @@ -681,11 +682,32 @@ private void compareFileText(
Path path = file.toPath();
fileContent = new String(Files.readAllBytes(path), charset);

} else {
// Handle content:// URIs
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Handle content:// URIs (including SAF tree URIs)
InputStream inputStream = null;
try {
inputStream = context.getContentResolver().openInputStream(uri);
String uriString = fileUri;
Uri resolvedUri = uri;

// Check if this is a SAF tree URI with :: separator
if (uriString.contains("::")) {
try {
// Split into tree URI and document ID
String[] parts = uriString.split("::", 2);
String treeUriStr = parts[0];
String docId = parts[1];

// Build document URI directly from tree URI and document ID
Uri treeUri = Uri.parse(treeUriStr);
resolvedUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, docId);
} catch (Exception e) {
callback.error("SAF_FALLBACK: Invalid SAF URI format - " + e.getMessage());
return;
}
}

// Try to open the resolved URI
inputStream = context.getContentResolver().openInputStream(resolvedUri);

if (inputStream == null) {
callback.error("Cannot open file");
Expand All @@ -710,6 +732,9 @@ private void compareFileText(
} catch (IOException ignored) {}
}
}
} else {
callback.error("Unsupported URI scheme: " + uri.getScheme());
return;
}

// check length first
Expand Down