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
23 changes: 13 additions & 10 deletions src/handlers/intent.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ const pendingIntents = [];
* @param {Intent} intent
*/
export default async function HandleIntent(intent = {}) {
const type = intent.action.split(".").slice(-1)[0];
const type = intent.action?.split(".").slice(-1)[0];

if (["SEND", "VIEW", "EDIT"].includes(type)) {
/**@type {string} */
const url = intent.fileUri || intent.data;
const url =
intent.fileUri ||
intent.data ||
intent.extras?.["android.intent.extra.STREAM"];
if (!url) return;

if (url.startsWith("acode://")) {
Expand Down Expand Up @@ -72,19 +75,19 @@ export default async function HandleIntent(intent = {}) {
return;
}

const options = {
mode: "single",
render: true,
persistInSession: false,
};

if (sessionStorage.getItem("isfilesRestored") === "true") {
await openFile(url, {
mode: "single",
render: true,
});
await openFile(url, options);
} else {
// Store the intent for later processing when files are restored
pendingIntents.push({
url,
options: {
mode: "single",
render: true,
},
options,
});
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/lib/editorFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ function maybeRecommendLanguageModeExtension(file, modeInfo) {
* @property {string} [paneId] target editor pane id
* @property {object} [pane] target editor pane
* @property {boolean} [isPanePlaceholder] temporary empty tab for an empty pane
* @property {boolean} [persistInSession] restore the tab in a future app session
* @property {boolean} [highlightStyles] adopt static CodeMirror highlight CSS into the custom tab shadow root
*/

Expand Down Expand Up @@ -500,6 +501,7 @@ export default class EditorFile {
diskMtime = null;
hasDiskConflict = false;
isPanePlaceholder = false;
persistInSession = true;

/**
*
Expand All @@ -513,6 +515,7 @@ export default class EditorFile {
this.hideQuickTools = options?.hideQuickTools || false;
this.paneId = options?.paneId || options?.pane?.id || null;
this.isPanePlaceholder = !!options?.isPanePlaceholder;
this.persistInSession = options?.persistInSession !== false;

// if options are passed
if (options) {
Expand Down
13 changes: 13 additions & 0 deletions src/lib/fileSessionPersistence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Promote an open file to durable session persistence when a later open source
* proves that its URI will remain accessible. Less durable sources, such as
* external intents, must not revoke an existing durable grant.
*
* @param {{persistInSession?: boolean}} file
* @param {boolean | undefined} requestedPersistence
*/
export function promoteSessionPersistence(file, requestedPersistence) {
if (requestedPersistence === true) {
file.persistInSession = true;
}
}
15 changes: 14 additions & 1 deletion src/lib/openFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { reopenWithNewEncoding } from "palettes/changeEncoding";
import { decode, detectEncoding } from "utils/encodings";
import helpers from "utils/helpers";
import EditorFile from "./editorFile";
import { promoteSessionPersistence } from "./fileSessionPersistence";
import fileTypeHandler from "./fileTypeHandler";
import recents from "./recents";
import appSettings from "./settings";
Expand All @@ -22,6 +23,7 @@ import appSettings from "./settings";
* @property {string} mode
* @property {string} uri
* @property {string} paneId
* @property {boolean} persistInSession
*/

/**
Expand All @@ -37,9 +39,19 @@ export default async function openFile(file, options = {}) {

/**@type {EditorFile} */
const existingFile = editorManager.getFile(uri, "uri");
const { cursorPos, render, onsave, text, mode, encoding, paneId } = options;
const {
cursorPos,
render,
onsave,
text,
mode,
encoding,
paneId,
persistInSession,
} = options;

if (existingFile) {
promoteSessionPersistence(existingFile, persistInSession);
// If file is already opened and new text is provided
const incomingDoc =
text != null ? Text.of(String(text).split("\n")) : null;
Expand Down Expand Up @@ -120,6 +132,7 @@ export default async function openFile(file, options = {}) {
savedMtime: helpers.getStatMtime(fileInfo),
diskMtime: helpers.getStatMtime(fileInfo),
paneId,
persistInSession,
});
};

Expand Down
2 changes: 1 addition & 1 deletion src/lib/saveState.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default () => {
files.forEach((file) => {
if (file.type !== "editor") return;
if (file.id === config.DEFAULT_FILE_SESSION) return;
if (file.SAFMode === "single") return;
if (file.persistInSession === false) return;

// Selection per file:
// - Active file uses live EditorView selection
Expand Down
64 changes: 42 additions & 22 deletions src/pages/fileBrowser/fileBrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {

const IS_FOLDER_MODE = ["folder", "both"].includes(mode);
const IS_FILE_MODE = ["file", "both"].includes(mode);
const SELECT_DOCUMENT_LABEL = "Select document";
const storedState = helpers.parseJSON(localStorage.fileBrowserState) || [];
/**@type {Array<Location>} */
const state = [];
Expand Down Expand Up @@ -106,6 +107,16 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
);

const $search = <span className="icon search" data-action="search"></span>;
const $selectDocument = (
<span
className="icon folder_open"
data-action="select-document"
title={SELECT_DOCUMENT_LABEL}
aria-label={SELECT_DOCUMENT_LABEL}
role="button"
tabindex="0"
></span>
);
const $lead = <span className="icon clearclose" data-action="close"></span>;
const $page = Page(strings["file browser"].capitalize(), {
lead: $lead,
Expand Down Expand Up @@ -178,8 +189,9 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
$content.addEventListener("click", handleClick);
$content.addEventListener("contextmenu", handleContextMenu, true);
$page.body = $content;
$page.header.append($search);
if (IS_FILE_MODE) $page.header.append($selectDocument);
$page.header.append(
$search,
$pasteToggler,
$selectionModeToggler,
$addMenuToggler,
Expand Down Expand Up @@ -222,6 +234,12 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
};

$pasteToggler.onclick = pasteCopiedItems;
$selectDocument.onclick = selectDocument;
$selectDocument.onkeydown = (event) => {
if (event.key !== "Enter" && event.key !== " ") return;
event.preventDefault();
selectDocument();
};

$fbMenu.onclick = function (e) {
$fbMenu.hide();
Expand Down Expand Up @@ -653,6 +671,25 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
$page.hide();
}

function selectDocument() {
checkFiles.check = false;
sdcard.openDocumentFile(
(res) => {
res.url = res.uri;
resolve({
type: "file",
...res,
name: res.filename,
mode: "single",
});
$page.hide();
},
(err) => {
helpers.error(err);
},
);
}

/**
* @param {string} url
*/
Expand Down Expand Up @@ -872,6 +909,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {

$addMenuToggler.style.display = "none";
$menuToggler.style.display = "none";
$selectDocument.style.display = "none";
$selectionMenuToggler.style.display = "";
updatePasteToggler();

Expand Down Expand Up @@ -899,6 +937,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {

$addMenuToggler.style.display = "";
$menuToggler.style.display = "";
$selectDocument.style.display = "";
$selectionMenuToggler.style.display = "none";
updatePasteToggler();

Expand Down Expand Up @@ -998,7 +1037,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
else if (!$el.hasAttribute("disabled")) file();
break;
case "openDoc":
openDoc();
selectDocument();
break;
}

Expand Down Expand Up @@ -1304,25 +1343,6 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
localStorage.storageList = JSON.stringify(storageList);
reload();
}

function openDoc() {
checkFiles.check = false;
sdcard.openDocumentFile(
(res) => {
res.url = res.uri;
resolve({
type: "file",
...res,
name: res.filename,
mode: "single",
});
$page.hide();
},
(err) => {
helpers.error(err);
},
);
}
}

function handleContextMenu(e) {
Expand Down Expand Up @@ -1431,7 +1451,7 @@ function FileBrowserInclude(mode, info, doesOpenLast = true) {
}

if (IS_FILE_MODE) {
util.pushFolder(allStorages, "Select document", null, {
util.pushFolder(allStorages, SELECT_DOCUMENT_LABEL, null, {
openDoc: true,
notSelectable: true,
});
Expand Down
7 changes: 5 additions & 2 deletions src/pages/fileBrowser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function FileBrowser(mode, info, doesOpenLast, ...args) {
}

FileBrowser.openFile = (res) => {
const { url, name, mode } = res;
const { url, name, mode, persistedUriPermission } = res;
const createOption = {
uri: url,
name,
Expand All @@ -41,8 +41,11 @@ FileBrowser.openFile = (res) => {
if (mode) {
createOption.mode = mode;
}
if (mode === "single") {
createOption.persistInSession = persistedUriPermission !== false;
}

openFile(url, createOption);
return openFile(url, createOption);
};

FileBrowser.openFileError = (err) => {
Expand Down
1 change: 1 addition & 0 deletions src/plugins/sdcard/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ interface DocumentFile {
canWrite: boolean;
filename: string;
length: number;
persistedUriPermission: boolean;
type: string;
uri: string;
}
Expand Down
36 changes: 27 additions & 9 deletions src/plugins/sdcard/src/android/SDcard.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ public void openDocumentFile(String mimeType, CallbackContext callback) {
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(mimeType);
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
);
activityResultCallback = callback;
cordova.startActivityForResult(this, intent, this.OPEN_DOCUMENT);
}
Expand Down Expand Up @@ -409,7 +414,10 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
return;
}

takePermission(uri);
boolean persistedUriPermission = takePermission(
uri,
data.getFlags()
);
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
JSONObject res = new JSONObject();

Expand All @@ -418,6 +426,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
res.put("filename", file.getName());
res.put("canWrite", canWrite(file.getUri()));
res.put("uri", uri.toString());
res.put("persistedUriPermission", persistedUriPermission);
activityResultCallback.success(res);
} catch (JSONException e) {
activityResultCallback.error(e.toString());
Expand Down Expand Up @@ -447,7 +456,7 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
return;
}

takePermission(uri);
takePermission(uri, data.getFlags());
DocumentFile file = DocumentFile.fromTreeUri(context, uri);
if (file != null && file.canWrite()) {
activityResultCallback.success(uri.toString());
Expand Down Expand Up @@ -1152,13 +1161,22 @@ private DocumentFile getFile(String filePath) {
return documentFile;
}

private void takePermission(Uri uri) {
contentResolver = context.getContentResolver();
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION
);
private boolean takePermission(Uri uri, int intentFlags) {
int permissionFlags =
intentFlags &
(Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);

if (permissionFlags == 0) return false;

try {
contentResolver = context.getContentResolver();
contentResolver.takePersistableUriPermission(uri, permissionFlags);
return true;
} catch (SecurityException | IllegalArgumentException error) {
Log.w("SDcard", "Unable to persist URI permission for " + uri, error);
return false;
}
}

public boolean canWrite(Uri uri) {
Expand Down
Loading