mirror of
https://github.com/nextcloud/android.git
synced 2024-11-27 09:39:25 +03:00
Merge pull request #10168 from pelzvieh/patch-1
Make SAF root IDs stable through server version updates
This commit is contained in:
commit
454f7c6b96
4 changed files with 112 additions and 17 deletions
|
@ -38,7 +38,7 @@ class DocumentsStorageProviderIT : AbstractOnServerIT() {
|
|||
private val authority = context.getString(R.string.document_provider_authority)
|
||||
|
||||
private val rootFileId = storageManager.getFileByEncryptedRemotePath(ROOT_PATH).fileId
|
||||
private val documentId = "${user.hashCode()}${DOCUMENTID_SEPARATOR}$rootFileId"
|
||||
private val documentId = "${DocumentsStorageProvider.rootIdForUser(user)}${DOCUMENTID_SEPARATOR}$rootFileId"
|
||||
private val uri = DocumentsContract.buildTreeDocumentUri(authority, documentId)
|
||||
private val rootDir get() = DocumentFile.fromTreeUri(context, uri)!!
|
||||
|
||||
|
|
42
app/src/main/java/com/nextcloud/client/utils/HashUtil.kt
Normal file
42
app/src/main/java/com/nextcloud/client/utils/HashUtil.kt
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Nextcloud Android Library is available under MIT license
|
||||
*
|
||||
* @author Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.utils
|
||||
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import java.security.MessageDigest
|
||||
|
||||
object HashUtil {
|
||||
private const val ALGORITHM_MD5 = "MD5"
|
||||
|
||||
@JvmStatic
|
||||
fun md5Hash(input: String): String {
|
||||
val digest = MessageDigest.getInstance(ALGORITHM_MD5)
|
||||
.digest(input.toByteArray())
|
||||
return String(Hex.encodeHex(digest))
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ import android.os.ParcelFileDescriptor;
|
|||
import android.provider.DocumentsContract;
|
||||
import android.provider.DocumentsProvider;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.nextcloud.client.account.User;
|
||||
|
@ -48,6 +47,7 @@ import com.nextcloud.client.account.UserAccountManager;
|
|||
import com.nextcloud.client.files.downloader.DownloadTask;
|
||||
import com.nextcloud.client.preferences.AppPreferences;
|
||||
import com.nextcloud.client.preferences.AppPreferencesImpl;
|
||||
import com.nextcloud.client.utils.HashUtil;
|
||||
import com.owncloud.android.MainApp;
|
||||
import com.owncloud.android.R;
|
||||
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||
|
@ -82,7 +82,9 @@ import java.io.File;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -113,7 +115,7 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
@VisibleForTesting
|
||||
static final String DOCUMENTID_SEPARATOR = "/";
|
||||
private static final int DOCUMENTID_PARTS = 2;
|
||||
private final SparseArray<FileDataStorageManager> rootIdToStorageManager = new SparseArray<>();
|
||||
private final Map<String, FileDataStorageManager> rootIdToStorageManager = new HashMap<>();
|
||||
|
||||
private final Executor executor = Executors.newCachedThreadPool();
|
||||
|
||||
|
@ -132,8 +134,8 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
}
|
||||
|
||||
final RootCursor result = new RootCursor(projection);
|
||||
for(int i = 0; i < rootIdToStorageManager.size(); i++) {
|
||||
result.addRoot(new Document(rootIdToStorageManager.valueAt(i), ROOT_PATH), getContext());
|
||||
for(FileDataStorageManager manager: rootIdToStorageManager.values()) {
|
||||
result.addRoot(new Document(manager, ROOT_PATH), getContext());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -678,14 +680,12 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
}
|
||||
|
||||
private FileDataStorageManager getStorageManager(String rootId) {
|
||||
for(int i = 0; i < rootIdToStorageManager.size(); i++) {
|
||||
FileDataStorageManager storageManager = rootIdToStorageManager.valueAt(i);
|
||||
if (storageManager.getUser().nameEquals(rootId)) {
|
||||
return storageManager;
|
||||
}
|
||||
}
|
||||
return rootIdToStorageManager.get(rootId);
|
||||
}
|
||||
|
||||
return null;
|
||||
@VisibleForTesting
|
||||
public static String rootIdForUser(User user) {
|
||||
return HashUtil.md5Hash(user.getAccountName());
|
||||
}
|
||||
|
||||
private void initiateStorageMap() {
|
||||
|
@ -696,7 +696,7 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
|
||||
for (User user : accountManager.getAllUsers()) {
|
||||
final FileDataStorageManager storageManager = new FileDataStorageManager(user, contentResolver);
|
||||
rootIdToStorageManager.put(user.hashCode(), storageManager);
|
||||
rootIdToStorageManager.put(rootIdForUser(user), storageManager);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,7 +725,7 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
throw new FileNotFoundException("Invalid documentID " + documentId + "!");
|
||||
}
|
||||
|
||||
FileDataStorageManager storageManager = rootIdToStorageManager.get(Integer.parseInt(separated[0]));
|
||||
FileDataStorageManager storageManager = rootIdToStorageManager.get(separated[0]);
|
||||
if (storageManager == null) {
|
||||
throw new FileNotFoundException("No storage manager associated for " + documentId + "!");
|
||||
}
|
||||
|
@ -803,9 +803,9 @@ public class DocumentsStorageProvider extends DocumentsProvider {
|
|||
}
|
||||
|
||||
public String getDocumentId() {
|
||||
for(int i = 0; i < rootIdToStorageManager.size(); i++) {
|
||||
if (Objects.equals(storageManager, rootIdToStorageManager.valueAt(i))) {
|
||||
return rootIdToStorageManager.keyAt(i) + DOCUMENTID_SEPARATOR + fileId;
|
||||
for(String key: rootIdToStorageManager.keySet()) {
|
||||
if (Objects.equals(storageManager, rootIdToStorageManager.get(key))) {
|
||||
return key + DOCUMENTID_SEPARATOR + fileId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
53
app/src/test/java/com/nextcloud/client/utils/HashUtilTest.kt
Normal file
53
app/src/test/java/com/nextcloud/client/utils/HashUtilTest.kt
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Nextcloud Android Library is available under MIT license
|
||||
*
|
||||
* @author Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Álvaro Brey Vilas
|
||||
* Copyright (C) 2022 Nextcloud GmbH
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.nextcloud.client.utils
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class HashUtilTest(private val input: String, private val expected: String) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun params(): List<Array<Any>> = listOf(
|
||||
arrayOf("", "d41d8cd98f00b204e9800998ecf8427e"),
|
||||
arrayOf("test", "098f6bcd4621d373cade4e832627b4f6"),
|
||||
arrayOf("test@nextcloud.localhost", "12aa338095d171f307c3e3f724702ab1"),
|
||||
arrayOf("tost@nextcloud.localhost", "e01e5301f90c123a65e872d68e84c4b2")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMd5Hash() {
|
||||
val hash = HashUtil.md5Hash(input)
|
||||
Assert.assertEquals("Wrong hash for input", expected, hash)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue