diff --git a/app/src/androidTest/java/com/orgzly/android/repos/DocumentRepoTest.kt b/app/src/androidTest/java/com/orgzly/android/repos/DocumentRepoTest.kt index 6ee38a981..c451503fb 100644 --- a/app/src/androidTest/java/com/orgzly/android/repos/DocumentRepoTest.kt +++ b/app/src/androidTest/java/com/orgzly/android/repos/DocumentRepoTest.kt @@ -51,8 +51,13 @@ class DocumentRepoTest : SyncRepoTest, OrgzlyTest() { } @Test - override fun testGetBooks_singleFileInSubfolder() { - SyncRepoTest.testGetBooks_singleFileInSubfolder(repoDirectory, syncRepo) + override fun testGetBooks_singleFileInSubfolderWhenEnabled() { + SyncRepoTest.testGetBooks_singleFileInSubfolderWhenEnabled(repoDirectory, syncRepo) + } + + @Test + override fun testGetBooks_singleFileInSubfolderWhenDisabled() { + SyncRepoTest.testGetBooks_singleFileInSubfolderWhenDisabled(repoDirectory, syncRepo) } @Test @@ -81,8 +86,13 @@ class DocumentRepoTest : SyncRepoTest, OrgzlyTest() { } @Test - override fun testStoreBook_producesSameUriAsRetrieveBook() { - SyncRepoTest.testStoreBook_producesSameUriAsRetrieveBook(syncRepo) + override fun testStoreBook_producesSameUriAsRetrieveBookWithSubfolder() { + SyncRepoTest.testStoreBook_producesSameUriAsRetrieveBookWithSubfolder(syncRepo) + } + + @Test + override fun testStoreBook_producesSameUriAsRetrieveBookWithoutSubfolder() { + SyncRepoTest.testStoreBook_producesSameUriAsRetrieveBookWithoutSubfolder(syncRepo) } @Test @@ -95,6 +105,11 @@ class DocumentRepoTest : SyncRepoTest, OrgzlyTest() { SyncRepoTest.testStoreBook_inSubfolder(repoDirectory, syncRepo) } + @Test(expected = IOException::class) + override fun testStoreBook_inSubfolderWhenDisabled() { + SyncRepoTest.testStoreBook_inSubfolderWhenDisabled(syncRepo) + } + @Test override fun testRenameBook_expectedUri() { SyncRepoTest.testRenameBook_expectedUri(syncRepo) @@ -106,8 +121,13 @@ class DocumentRepoTest : SyncRepoTest, OrgzlyTest() { } @Test - override fun testRenameBook_fromRootToSubfolder() { - SyncRepoTest.testRenameBook_fromRootToSubfolder(syncRepo) + override fun testRenameBook_fromRootToSubfolderWhenEnabled() { + SyncRepoTest.testRenameBook_fromRootToSubfolderWhenEnabled(syncRepo) + } + + @Test(expected = IOException::class) + override fun testRenameBook_fromRootToSubfolderWhenDisabled() { + SyncRepoTest.testRenameBook_fromRootToSubfolderWhenDisabled(syncRepo) } @Test diff --git a/app/src/main/java/com/orgzly/android/prefs/AppPreferences.java b/app/src/main/java/com/orgzly/android/prefs/AppPreferences.java index 99015bc6b..4ffcb792c 100644 --- a/app/src/main/java/com/orgzly/android/prefs/AppPreferences.java +++ b/app/src/main/java/com/orgzly/android/prefs/AppPreferences.java @@ -1106,6 +1106,20 @@ public static void refileLastLocation(Context context, String value) { getStateSharedPreferences(context).edit().putString(key, value).apply(); } + /* + * Subfolder support + */ + public static boolean subfolderSupport(Context context) { + return getDefaultSharedPreferences(context).getBoolean( + context.getResources().getString(R.string.pref_key_enable_repo_subfolders), + context.getResources().getBoolean(R.bool.pref_default_enable_repo_subfolders)); + } + + public static void subfolderSupport(Context context, boolean value) { + String key = context.getResources().getString(R.string.pref_key_enable_repo_subfolders); + getDefaultSharedPreferences(context).edit().putBoolean(key, value).apply(); + } + /* * Repository properties map */ diff --git a/app/src/main/java/com/orgzly/android/repos/DocumentRepo.java b/app/src/main/java/com/orgzly/android/repos/DocumentRepo.java index 03dc61eca..d2b0068bc 100644 --- a/app/src/main/java/com/orgzly/android/repos/DocumentRepo.java +++ b/app/src/main/java/com/orgzly/android/repos/DocumentRepo.java @@ -12,6 +12,7 @@ import com.orgzly.R; import com.orgzly.android.BookName; import com.orgzly.android.db.entity.Repo; +import com.orgzly.android.prefs.AppPreferences; import com.orgzly.android.util.LogUtils; import com.orgzly.android.util.MiscUtils; @@ -117,6 +118,8 @@ private List walkFileTree() { for (DocumentFile node : currentDir.listFiles()) { String repoRelativePath = BookName.getRepoRelativePath(repoUri, node.getUri()); if (node.isDirectory()) { + if (!AppPreferences.subfolderSupport(context)) + continue; if (Build.VERSION.SDK_INT >= 26) { if (ignores.isPathIgnored(repoRelativePath, true)) { continue; @@ -176,10 +179,14 @@ public VersionedRook storeBook(File file, String repoRelativePath) throws IOExce } DocumentFile destinationFile = getDocumentFileFromPath(repoRelativePath); if (repoRelativePath.contains("/")) { - DocumentFile destinationDir = ensureDirectoryHierarchy(repoRelativePath); - String fileName = Uri.parse(repoRelativePath).getLastPathSegment(); - if (destinationDir.findFile(fileName) == null) { - destinationFile = destinationDir.createFile("text/*", fileName); + if (AppPreferences.subfolderSupport(context)) { + DocumentFile destinationDir = ensureDirectoryHierarchy(repoRelativePath); + String fileName = Uri.parse(repoRelativePath).getLastPathSegment(); + if (destinationDir.findFile(fileName) == null) { + destinationFile = destinationDir.createFile("text/*", fileName); + } + } else { + throw new IOException(context.getString(R.string.subfolder_support_disabled)); } } else { if (!destinationFile.exists()) { @@ -246,7 +253,11 @@ public VersionedRook renameBook(Uri oldFullUri, String newName) throws IOExcepti Uri newUri = oldFullUri; if (newName.contains("/")) { - newDir = ensureDirectoryHierarchy(newName); + if (AppPreferences.subfolderSupport(context)) { + newDir = ensureDirectoryHierarchy(newName); + } else { + throw new IOException(context.getString(R.string.subfolder_support_disabled)); + } } else { newDir = repoDocumentFile; } diff --git a/app/src/main/java/com/orgzly/android/repos/DropboxClient.java b/app/src/main/java/com/orgzly/android/repos/DropboxClient.java index fceec62ee..5d76ba55f 100644 --- a/app/src/main/java/com/orgzly/android/repos/DropboxClient.java +++ b/app/src/main/java/com/orgzly/android/repos/DropboxClient.java @@ -180,7 +180,7 @@ public List getBooks(Uri repoUri, RepoIgnoreNode ignores) throws list.add(book); } } - if (metadata instanceof FolderMetadata) { + if (metadata instanceof FolderMetadata && AppPreferences.subfolderSupport(mContext)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (ignores.isPathIgnored(pathRelativeToRepoRoot, true)) { continue; @@ -385,6 +385,8 @@ public void deleteFolder(String path) throws IOException { } else { throw new IOException("Not a directory: " + path); } + } catch (GetMetadataErrorException e) { + // Do nothing; the folder does not exist. } catch (DbxException e) { e.printStackTrace(); if (e.getMessage() != null) { diff --git a/app/src/main/java/com/orgzly/android/repos/DropboxRepo.java b/app/src/main/java/com/orgzly/android/repos/DropboxRepo.java index 21cfdf65c..85b26acc4 100644 --- a/app/src/main/java/com/orgzly/android/repos/DropboxRepo.java +++ b/app/src/main/java/com/orgzly/android/repos/DropboxRepo.java @@ -5,7 +5,10 @@ import androidx.annotation.NonNull; +import com.orgzly.R; +import com.orgzly.android.App; import com.orgzly.android.BookName; +import com.orgzly.android.prefs.AppPreferences; import java.io.File; import java.io.IOException; @@ -56,11 +59,17 @@ public InputStream openRepoFileInputStream(String repoRelativePath) throws IOExc @Override public VersionedRook storeBook(File file, String repoRelativePath) throws IOException { + Context context = App.getAppContext(); + if (repoRelativePath.contains("/") && !AppPreferences.subfolderSupport(context)) + throw new IOException(context.getString(R.string.subfolder_support_disabled)); return client.upload(file, repoUri, repoRelativePath); } @Override public VersionedRook renameBook(Uri oldFullUri, String newName) throws IOException { + Context context = App.getAppContext(); + if (newName.contains("/") && !AppPreferences.subfolderSupport(context)) + throw new IOException(context.getString(R.string.subfolder_support_disabled)); BookName oldBookName = BookName.fromRepoRelativePath(BookName.getRepoRelativePath(repoUri, oldFullUri)); String newRelativePath = BookName.repoRelativePath(newName, oldBookName.getFormat()); String newEncodedRelativePath = Uri.encode(newRelativePath, "/"); diff --git a/app/src/main/java/com/orgzly/android/repos/GitRepo.java b/app/src/main/java/com/orgzly/android/repos/GitRepo.java index 2cf1941e1..056633a12 100644 --- a/app/src/main/java/com/orgzly/android/repos/GitRepo.java +++ b/app/src/main/java/com/orgzly/android/repos/GitRepo.java @@ -7,6 +7,8 @@ import android.util.Log; import com.orgzly.BuildConfig; +import com.orgzly.R; +import com.orgzly.android.App; import com.orgzly.android.BookFormat; import com.orgzly.android.BookName; import com.orgzly.android.db.entity.Repo; @@ -14,6 +16,7 @@ import com.orgzly.android.git.GitPreferences; import com.orgzly.android.git.GitPreferencesFromRepoPrefs; import com.orgzly.android.git.GitTransportSetter; +import com.orgzly.android.prefs.AppPreferences; import com.orgzly.android.prefs.RepoPreferences; import com.orgzly.android.util.LogUtils; @@ -260,6 +263,7 @@ public List getBooks() throws IOException { walk.setRecursive(true); walk.addTree(synchronizer.currentHead().getTree()); final RepoIgnoreNode ignores = new RepoIgnoreNode(this); + boolean supportsSubFolders = AppPreferences.subfolderSupport(App.getAppContext()); walk.setFilter(new TreeFilter() { @Override public boolean include(TreeWalk walker) { @@ -269,7 +273,7 @@ public boolean include(TreeWalk walker) { if (ignores.isIgnored(repoRelativePath, isDirectory) == IgnoreNode.MatchResult.IGNORED) return false; if (isDirectory) - return true; + return supportsSubFolders; return BookName.isSupportedFormatFileName(repoRelativePath); } @@ -298,6 +302,10 @@ public void delete(Uri uri) throws IOException { } public VersionedRook renameBook(Uri oldFullUri, String newName) throws IOException { + Context context = App.getAppContext(); + if (newName.contains("/") && !AppPreferences.subfolderSupport(context)) { + throw new IOException(context.getString(R.string.subfolder_support_disabled)); + } String oldPath = oldFullUri.toString().replaceFirst("^/", ""); String newPath = BookName.repoRelativePath(newName, BookFormat.ORG); if (synchronizer.renameFileInRepo(oldPath, newPath)) { diff --git a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt index 81599b893..034ebfb93 100644 --- a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt +++ b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt @@ -9,7 +9,10 @@ import com.burgstaller.okhttp.basic.BasicAuthenticator import com.burgstaller.okhttp.digest.CachingAuthenticator import com.burgstaller.okhttp.digest.Credentials import com.burgstaller.okhttp.digest.DigestAuthenticator +import com.orgzly.R +import com.orgzly.android.App import com.orgzly.android.BookName +import com.orgzly.android.prefs.AppPreferences import com.thegrizzlylabs.sardineandroid.DavResource import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine import okhttp3.OkHttpClient @@ -166,8 +169,14 @@ class WebdavRepo( val ignores = RepoIgnoreNode(this) + val listDepth = if (AppPreferences.subfolderSupport(App.getAppContext())) { + -1 + } else { + 1 + } + return sardine - .list(url, -1) + .list(url, listDepth) .mapNotNull { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (!BookName.isSupportedFormatFileName(it.name) || ignores.isPathIgnored(it.getRelativePath(), it.isDirectory)) { @@ -222,6 +231,9 @@ class WebdavRepo( val encodedRepoPath = Uri.encode(repoRelativePath, "/") if (encodedRepoPath != null) { if (encodedRepoPath.contains("/")) { + val context = App.getAppContext() + if (!AppPreferences.subfolderSupport(context)) + throw IOException(context.getString(R.string.subfolder_support_disabled)) ensureDirectoryHierarchy(encodedRepoPath) } } @@ -244,6 +256,9 @@ class WebdavRepo( } if (newName.contains("/")) { + val context = App.getAppContext() + if (!AppPreferences.subfolderSupport(context)) + throw IOException(context.getString(R.string.subfolder_support_disabled)) ensureDirectoryHierarchy(newEncodedRelativePath) } diff --git a/app/src/main/res/values/prefs_keys.xml b/app/src/main/res/values/prefs_keys.xml index 4d2c14855..686ef9ad0 100644 --- a/app/src/main/res/values/prefs_keys.xml +++ b/app/src/main/res/values/prefs_keys.xml @@ -561,6 +561,10 @@ pref_key_highlight_edited_rich_text false + + pref_key_enable_repo_subfolders + false + pref_key_git_ssh_key_type pref_key_git_author diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c26eeac1d..32016cc3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -87,6 +87,8 @@ Repositories Location to synchronize your notebooks with + Support repository subfolders + Load from and write to subfolders SSH key generation Generate key pair for Git repo sync View generated SSH public key @@ -784,4 +786,6 @@ Saved to %s Renaming notebook to a different subdirectory requires Android 7 or higher + + Support for subfolders is disabled diff --git a/app/src/main/res/xml/prefs_screen_sync.xml b/app/src/main/res/xml/prefs_screen_sync.xml index 78c2d01cf..b5b8ca710 100644 --- a/app/src/main/res/xml/prefs_screen_sync.xml +++ b/app/src/main/res/xml/prefs_screen_sync.xml @@ -15,6 +15,12 @@ android:targetClass="com.orgzly.android.ui.repos.ReposActivity"/> + +