Improve local archive folder structure recognition.

This commit is contained in:
Greyson Parrelli
2026-07-13 21:39:20 +00:00
committed by Michelle Tang
parent 7a2c084b49
commit 687818fbb5
2 changed files with 53 additions and 3 deletions
@@ -104,11 +104,23 @@ class ArchiveFileSystem private constructor(private val context: Context, root:
}
/**
* Returns true if [dir] appears to be a SignalBackups directory based on its name and
* expected internal structure (presence of the "files" subdirectory).
* Returns true if [dir] appears to be a SignalBackups directory based on its expected internal
* structure. We can't rely on the directory being named [MAIN_DIRECTORY_NAME], since users may
* rename it or restore it into a differently-named folder (e.g. when transferring a backup
* between devices). A directory is therefore considered a backups directory if it either matches
* the expected name or directly contains the archive contents (a "files" subdirectory alongside
* at least one snapshot directory).
*/
private fun looksLikeSignalBackupsDirectory(dir: DocumentFile): Boolean {
return dir.name == MAIN_DIRECTORY_NAME && dir.findFile("files") != null
if (dir.findFile("files") == null) {
return false
}
if (dir.name == MAIN_DIRECTORY_NAME) {
return true
}
return dir.listFiles().any { it.isDirectory && it.name?.startsWith(BACKUP_DIRECTORY_PREFIX) == true }
}
/**
@@ -98,6 +98,36 @@ class ArchiveFileSystemTest {
assertThat(result).isNull()
}
@Test
fun `openForRestore succeeds when the archive lives in a differently-named directory`() {
val renamed = temporaryFolder.newFolder("Signal")
buildArchiveContents(renamed)
val result = ArchiveFileSystem.openForRestore(context, DocumentFile.fromFile(renamed))
assertThat(result).isNotNull()
}
@Test
fun `openForRestore isRootedAtSignalBackups is true for a differently-named archive directory`() {
val renamed = temporaryFolder.newFolder("Signal")
buildArchiveContents(renamed)
val result = ArchiveFileSystem.openForRestore(context, DocumentFile.fromFile(renamed))!!
assertThat(result.isRootedAtSignalBackups).isTrue()
}
@Test
fun `openForRestore returns null for a differently-named directory that only contains a files folder`() {
val renamed = temporaryFolder.newFolder("Signal")
renamed.resolve("files").mkdir()
val result = ArchiveFileSystem.openForRestore(context, DocumentFile.fromFile(renamed))
assertThat(result).isNull()
}
/**
* Creates the SignalBackups directory structure inside [parent] and returns the SignalBackups directory.
*/
@@ -106,4 +136,12 @@ class ArchiveFileSystemTest {
signalBackups.resolve("files").mkdir()
return signalBackups
}
/**
* Creates the raw archive contents (a "files" directory and a snapshot directory) directly inside [dir].
*/
private fun buildArchiveContents(dir: java.io.File) {
dir.resolve("files").mkdir()
dir.resolve("${ArchiveFileSystem.BACKUP_DIRECTORY_PREFIX}-2026-01-01-00-00-00").mkdir()
}
}