diff --git a/android/app/build.gradle b/android/app/build.gradle index 06689b63c..b43bcda91 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,6 +1,8 @@ apply plugin: 'com.android.application' android { + + namespace 'com.twohundredok.organice' compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId "com.twohundredok.organice" @@ -21,6 +23,11 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + testOptions { + unitTests { + includeAndroidResources = true + } + } } repositories { @@ -34,8 +41,11 @@ dependencies { implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation 'androidx.documentfile:documentfile:1.0.1' implementation project(':capacitor-android') testImplementation "junit:junit:$junitVersion" + testImplementation 'org.robolectric:robolectric:4.9' + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" implementation project(':capacitor-cordova-android-plugins') diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 5f94affb4..ec71b6492 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -10,7 +10,6 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-app') - implementation project(':send-intent') } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8b95ab90e..4d143bf0c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - - - - - - - - - - - - - + * Will save permissions to the directory. + * + * @param call + * @param result + */ + @SuppressLint("WrongConstant") + @ActivityCallback + public void pickDirectoryResult(PluginCall call, ActivityResult result) { + if (call == null) { + return; + } + + Intent intent = result.getData(); + + if (intent != null) { + Uri uri = intent.getData(); + JSObject ret = new JSObject(); + + // Persist permissions + // https://developer.android.com/training/data-storage/shared/documents-files#grant-access-directory + final int takeFlags = intent.getFlags() + & (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + // Check for the freshest data. + getActivity().getContentResolver().takePersistableUriPermission(uri, takeFlags); + + var d = DocumentFile.fromTreeUri(getContext(), uri); + + ret.put("uri", uri); + ret.put("path", d.getUri().getEncodedPath()); + call.resolve(ret); + } else { + JSObject ret = new JSObject(); + ret.put("intent", result.getData()); + ret.put("resultCode", result.getResultCode()); + ret.put("uri", null); + call.reject("Failed with intent code " + result.getResultCode(), ret); + } + } + + /** + * List files in a directory. + * + * @param call Expect a "uri" string parameter. + */ + @PluginMethod + public void listFiles(PluginCall call) { + String uriStr = call.getString("uri"); + String path = call.getString("path"); + if (uriStr != null && path != null) { + try { + final Uri base = Uri.parse(uriStr); + final Uri uri = pathToUri(base, path); + var d = DocumentFile.fromTreeUri(getContext(), uri); + if (d.exists() && d.isDirectory()) { + JSObject ret = new JSObject(); + var files = d.listFiles(); + // convert DocumentFile to a json object structure for use in organice + var listing = Arrays.stream(files) + .map(OrganiceSync::asFileMetaData) + .collect(Collectors.toList()); + ret.put("files", new JSArray(listing)); + call.resolve(ret); + } else { + JSObject ret = new JSObject(); + ret.put("error", true); + ret.put("uri", base); + ret.put("errorMessage", "Usi is not a directory "); + call.reject("Usi is not a directory " + uri); + } + } catch (Exception e) { + JSObject o = new JSObject(); + o.put("error", true); + o.put("uriStr", uriStr); +// o.put("uri", uri.toString()); + o.put("path", path); + o.put("errorMessage", e.getLocalizedMessage()); + call.reject("Exception writing uri" + uriStr, o); + } + } else { + call.reject("Uri or path is null"); + } + } + + public void writeFile(ContentResolver resolver, Uri uri, String data) throws Exception { + try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w"); + FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor())) { + fileOutputStream.write(data.getBytes(StandardCharsets.UTF_8)); + } + } + + /** + * Update / write to a file. + * Perform a full file write. + * + * @param call Expect a "uri" string parameter. + */ + @PluginMethod + public void putFileContents(PluginCall call) { + String uriStr = call.getString("uri"); + String data = call.getString("contents"); + String path = call.getString("path"); + if (uriStr != null && path != null) { + Uri base = Uri.parse(uriStr); + Uri uri = pathToUri(base, path); + try { + ContentResolver resolver = getActivity().getContentResolver(); + writeFile(resolver, uri, data); + } catch (Exception e) { + JSObject o = new JSObject(); + o.put("error", true); + o.put("uri", uri); + o.put("errorMessage", e.getLocalizedMessage()); + call.reject("Exception writing uri" + uri, o); + } + } else { + call.reject("Uri is null"); + } + } + + /** + * @param call + */ + @PluginMethod + public void getFileContentsAndMetadata(PluginCall call) { + String uriStr = call.getString("uri"); + String path = call.getString("path"); + if (uriStr != null && path != null) { + var uri = Uri.parse(uriStr).buildUpon().encodedPath(path).build(); + try { + ContentResolver resolver = getContext().getContentResolver(); + var contents = readTextFromUri(resolver, uri); + var d = DocumentFile.fromSingleUri(getContext(), uri); + LocalDateTime date = toLocalDateTime(d.lastModified()); + JSObject r = asFileMetaData(d); + r.put("contents", contents); + r.put("lastModifiedAt", date.format(DateTimeFormatter.ISO_DATE_TIME)); + call.resolve(r); + } catch (Exception e) { + JSObject o = new JSObject(); + o.put("error", true); + o.put("uriStr", uriStr); + o.put("uri", uri.toString()); + o.put("path", path); + o.put("errorMessage", e.getLocalizedMessage()); + call.reject("Exception writing uri" + uriStr, o); + } + } else { + JSObject o = new JSObject(); + o.put("error", true); + o.put("message", "Uri or path is null"); + call.reject("Uri or path is null", o); + } + } + + /** + * https://developer.android.com/training/data-storage/shared/documents-files#create-file + * + * @param call + */ + @PluginMethod + public void createFile(PluginCall call) { + String uriStr = call.getString("uri"); + String path = call.getString("path"); + String content = call.getString("content"); + if (uriStr == null || path == null) { + JSObject o = new JSObject(); + o.put("error", true); + o.put("message", "Uri or path is null"); + call.reject("Uri or path is null", o); + } + + var base = Uri.parse(uriStr); + var uri = pathToUri(base, path); + var fileParent = removeLastPathSegment(uri); + try { + // https://www.reddit.com/r/androiddev/comments/mz2j9s/comment/gw1uddt/?utm_source=share&utm_medium=web2x&context=3 + var dir = DocumentFile.fromTreeUri(getContext(), fileParent); + ContentResolver resolver = getContext().getContentResolver(); + Uri document = DocumentsContract.createDocument(resolver, uri, "application/octet-stream", + uri.getLastPathSegment()); + var file = DocumentFile.fromSingleUri(getContext(), document); + writeFile(resolver, file.getUri(), content); + JSObject r = asFileMetaData(dir); + call.resolve(r); + } catch (Exception e) { + JSObject o = new JSObject(); + o.put("error", true); + o.put("uriStr", uriStr); + o.put("uri", uri.toString()); + o.put("path", path); + o.put("parentFile", fileParent); + o.put("errorMessage", e.getLocalizedMessage()); + call.reject("Exception writing uri" + uriStr, o); + } + } + + private Uri removeLastPathSegment(Uri uri) { + var segments = uri.getPathSegments(); + segments = segments.subList(0, segments.size() - 1); + var u = uri.buildUpon(); + segments.forEach(s -> u.appendPath(s)); + return u.build(); + } + + /** + * @param call + */ + @PluginMethod + public void deleteFile(PluginCall call) { + String uriStr = call.getString("uri"); + String path = call.getString("path"); + if (uriStr != null && path != null) { + Uri base = Uri.parse(uriStr); + Uri uri = pathToUri(base, path); + var f = DocumentFile.fromSingleUri(getContext(), uri); + var deleted = f.delete(); + if (deleted) { + call.resolve(); + } else { + call.reject("File was not deleted"); + } + } else { + call.reject("Uri is null"); + } + } + +} diff --git a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index c7bd21dbd..000000000 --- a/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml index d5fccc538..e55387b67 100644 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -2,169 +2,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:viewportWidth="108" + android:viewportHeight="108"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09bc5..c4a603d4c 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc5..c4a603d4c 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index c023e5059..c7eb273a7 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 2127973b2..48557d23a 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index b441f37d6..9dafa41b1 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 72905b854..f750f94c3 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index 8ed0605c2..019356997 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 9502e47a2..e933b6271 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 4d1e07710..9371d9885 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index df0f15880..9519ef60f 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 853db043d..e1884070d 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 6cdf97c11..bec298f48 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 2960cbb61..17e272b9d 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 8e3093a86..3c3ae9c3a 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 46de6e255..1f12870ef 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index d2ea9abed..000ff8012 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index a40d73e9c..dff68140f 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/test/java/com/twohundredok/organice/OrganiceSyncTest.java b/android/app/src/test/java/com/twohundredok/organice/OrganiceSyncTest.java new file mode 100644 index 000000000..c383a65b4 --- /dev/null +++ b/android/app/src/test/java/com/twohundredok/organice/OrganiceSyncTest.java @@ -0,0 +1,38 @@ +package com.twohundredok.organice; + +import static org.junit.Assert.*; + +import android.net.Uri; + +import androidx.documentfile.provider.DocumentFile; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import java.io.File; + +@RunWith(RobolectricTestRunner.class) +public class OrganiceSyncTest { + + + @Test + public void uriTest(){ + var base = Uri.parse("content://com.android.externalstorage.documents/tree/1413-3A04%3Aorg"); + var document = Uri.parse("content://com.android.externalstorage.documents/tree/1413-3A04%3Aorg/document/1413-3A04%3Aorg%2Fmanual.org"); + var encodedPath = "/document/1413-3A04%3Aorg%2Fmanual.org"; + + var uri2 = base.buildUpon().appendPath("path").build(); + var uri3 = uri2.buildUpon().appendPath("localPath").build(); + + var uri4 = OrganiceSync.pathToUri(base,"local3"); + + + DocumentFile.fromFile(new File("local")); + + System.out.println("" + base.getPath()); + + } + + +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 637f81f06..5c1acd510 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle:7.3.1' classpath 'com.google.gms:google-services:4.3.13' // NOTE: Do not place your application dependencies here; they belong diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 7030d8533..2085c8639 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -4,6 +4,3 @@ project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/ include ':capacitor-app' project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android') - -include ':send-intent' -project(':send-intent').projectDir = new File('../node_modules/send-intent/android') diff --git a/android/variables.gradle b/android/variables.gradle index 2dbd75af6..14edf659a 100644 --- a/android/variables.gradle +++ b/android/variables.gradle @@ -1,5 +1,5 @@ ext { - minSdkVersion = 22 + minSdkVersion = 26 compileSdkVersion = 32 targetSdkVersion = 32 androidxActivityVersion = '1.4.0' diff --git a/package.json b/package.json index 1b1201dc5..6451f2dd3 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "dependencies": { "@babel/helper-environment-visitor": "^7.18.2", "@bity/oauth2-auth-code-pkce": "^2.13.0", - "@capacitor/android": "^4.0.0", - "@capacitor/app": "^4.0.0", - "@capacitor/core": "^4.0.0", + "@capacitor/android": "^4.6.1", + "@capacitor/app": "^4.1.1", + "@capacitor/core": "^4.6.1", "aos": "^2.3.4", "bowser": "^2.11.0", "classnames": "^2.2.6", @@ -34,7 +34,6 @@ "redux": "^4.1.0", "redux-thunk": "^2.3.0", "redux-undo": "1.0.1", - "send-intent": "^3.0.11", "webdav": "^3.3.0" }, "scripts": { diff --git a/src/App.js b/src/App.js index 4bd5035bf..70516cc85 100644 --- a/src/App.js +++ b/src/App.js @@ -23,6 +23,7 @@ import { setDisappearingLoadingMessage, restoreStaticFile } from './actions/base import createDropboxSyncBackendClient from './sync_backend_clients/dropbox_sync_backend_client'; import createWebDAVSyncBackendClient from './sync_backend_clients/webdav_sync_backend_client'; +import createAndroidSyncBackendClient from './sync_backend_clients/android_sync_backend_client'; import createGitLabSyncBackendClient, { createGitlabOAuth, } from './sync_backend_clients/gitlab_sync_backend_client'; @@ -44,28 +45,28 @@ import AppUrlListener from './AppUrlListener'; import { configure } from 'react-hotkeys'; -import { SendIntent } from 'send-intent'; +// import { SendIntent } from 'send-intent'; // do handle hotkeys even if they come from within 'input', 'select' or 'textarea' configure({ ignoreTags: [] }); -SendIntent.checkSendIntentReceived() - .then((result) => { - if (result) { - console.log('SendIntent received'); - console.log(JSON.stringify(result)); - } - if (result.url) { - let resultUrl = decodeURIComponent(result.url); - console.log(resultUrl); - // Filesystem.readFile({path: resultUrl}) - // .then((content) => { - // console.log(content.data); - // }) - // .catch((err) => console.error(err)); - } - }) - .catch((err) => console.error(err)); +// SendIntent.checkSendIntentReceived() +// .then((result) => { +// if (result) { +// console.log('SendIntent received'); +// console.log(JSON.stringify(result)); +// } +// if (result.url) { +// let resultUrl = decodeURIComponent(result.url); +// console.log(resultUrl); +// // Filesystem.readFile({path: resultUrl}) +// // .then((content) => { +// // console.log(content.data); +// // }) +// // .catch((err) => console.error(err)); +// } +// }) +// .catch((err) => console.error(err)); const handleGitLabAuthResponse = async (oauthClient) => { let success = false; @@ -144,6 +145,16 @@ export function handleAuthenticatedSyncService(initialState) { client, }); break; + case 'AndroidStorage': + client = createAndroidSyncBackendClient( + getPersistedField('orgDirectory'), + getPersistedField('orgDirectoryPath') + ); + initialState.syncBackend = Map({ + isAuthenticated: true, + client, + }); + break; default: } } diff --git a/src/actions/sync_backend.js b/src/actions/sync_backend.js index aa69449fd..2ab6e0a4f 100644 --- a/src/actions/sync_backend.js +++ b/src/actions/sync_backend.js @@ -25,6 +25,9 @@ export const signOut = () => (dispatch, getState) => { persistField('gitLabProject', null); createGitlabOAuth().reset(); break; + case 'AndroidStorage': + persistField('orgDirectory', null); + break; default: } diff --git a/src/components/FileBrowser/index.js b/src/components/FileBrowser/index.js index a71635445..36dc2f8b8 100644 --- a/src/components/FileBrowser/index.js +++ b/src/components/FileBrowser/index.js @@ -31,6 +31,7 @@ const FileBrowser = ({ switch (syncBackendType) { case 'Dropbox': case 'GitLab': + case 'AndroidStorage': case 'WebDAV': const pathParts = path.split('/'); return pathParts.slice(0, pathParts.length - 1).join('/'); diff --git a/src/components/SyncServiceSignIn/index.js b/src/components/SyncServiceSignIn/index.js index fa32a6520..3e4e94bb2 100644 --- a/src/components/SyncServiceSignIn/index.js +++ b/src/components/SyncServiceSignIn/index.js @@ -1,3 +1,4 @@ +import { Capacitor } from '@capacitor/core'; import React, { PureComponent, useState } from 'react'; import './stylesheet.css'; @@ -5,12 +6,14 @@ import './stylesheet.css'; import DropboxLogo from './dropbox.svg'; import GitLabLogo from './gitlab.svg'; -import { persistField } from '../../util/settings_persister'; +import { getPersistedField, persistField } from '../../util/settings_persister'; import { createGitlabOAuth, gitLabProjectIdFromURL, } from '../../sync_backend_clients/gitlab_sync_backend_client'; +import { pickDirectory } from '../../sync_backend_clients/android_sync_backend_client'; + import { Dropbox } from 'dropbox'; import _ from 'lodash'; @@ -149,6 +152,70 @@ function GitLab() { ); } +const isNative = Capacitor.isNativePlatform(); + +function AndroidStorage() { + const [isVisible, setIsVisible] = useState(false); + const toggleVisible = () => setIsVisible(!isVisible); + + const defaultOrgDirectory = getPersistedField('orgDirectory'); + const defaultOrgDirectoryPath = getPersistedField('orgDirectoryPath'); + const [orgDirectory, setOrgDirectory] = useState(defaultOrgDirectory); + const [orgDirectoryPath, setOrgDirectoryPath] = useState(defaultOrgDirectoryPath); + + return ( +
+

+ + Local Storage + +

+ {isVisible && ( + <> +
+

+ + +

+ +
+
{ + event.preventDefault(); + persistField('authenticatedSyncService', 'AndroidStorage'); + persistField('orgDirectory', orgDirectory); + persistField('orgDirectoryPath', orgDirectoryPath); + window.location = window.location.origin + '/'; + }} + > + +
+ + )} +
+ ); +} + export default class SyncServiceSignIn extends PureComponent { constructor(props) { super(props); @@ -175,20 +242,28 @@ export default class SyncServiceSignIn extends PureComponent { organice syncs your files with Dropbox, GitLab, and WebDAV.

Click to sign in with:

+ {!isNative && ( + <> +
+ + Dropbox logo + +
-
- - Dropbox logo - -
- -
- -
+
+ +
-
- -
+
+ +
+ + )} + {isNative && ( +
+ +
+ )}