From d1029029bc426af490f355b77a75aa7f302eb5a5 Mon Sep 17 00:00:00 2001 From: Falk Loewert Date: Sun, 18 Oct 2026 21:53:53 +0200 Subject: [PATCH] Show unlock action for the file owner The server allows the owner of a file to unlock TYPE_USER locks (LockService::canUnlock), so a stale lock created by another client - e.g. the Text app, whose locks have the owner "text" - can be released by the user. The client only offered the action when the lock owner matched the current user, leaving users without a way to clear such locks until the lock timeout expires (which defaults to unlimited). Signed-off-by: Falk Loewert --- .../com/nextcloud/android/files/FileLockingHelper.kt | 4 +++- .../nextcloud/android/files/FileLockingHelperTest.kt | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt b/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt index 5e3c7a66a964..c864147c8129 100644 --- a/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt +++ b/app/src/main/java/com/nextcloud/android/files/FileLockingHelper.kt @@ -19,6 +19,8 @@ object FileLockingHelper { if (!file.isLocked || file.lockOwnerId == null || file.lockType != FileLockType.MANUAL) { return false } - return file.lockOwnerId == userId + // Lock owner can always unlock. The file owner can as well: server-side + // LockService::canUnlock allows TYPE_USER locks for the file owner. + return file.lockOwnerId == userId || file.ownerId == userId } } diff --git a/app/src/test/java/com/nextcloud/android/files/FileLockingHelperTest.kt b/app/src/test/java/com/nextcloud/android/files/FileLockingHelperTest.kt index d105e99cfc8c..ec603758d81b 100644 --- a/app/src/test/java/com/nextcloud/android/files/FileLockingHelperTest.kt +++ b/app/src/test/java/com/nextcloud/android/files/FileLockingHelperTest.kt @@ -34,6 +34,17 @@ class FileLockingHelperTest { Assert.assertFalse(FileLockingHelper.canUserUnlockFile(USER_NAME, file)) } + @Test + fun fileOwnerCanUnlockLockOfAnotherClient() { + val file = OCFile("/foo.md").apply { + isLocked = true + lockOwnerId = "text" + lockType = FileLockType.MANUAL + ownerId = USER_NAME + } + Assert.assertTrue(FileLockingHelper.canUserUnlockFile(USER_NAME, file)) + } + @Test fun typeNotManual_cannotUnlock() { val file = OCFile("/foo.md").apply {