diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index fbc4438d..e58eef1c 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,7 +4,7 @@ - - \ No newline at end of file diff --git a/app/src/main/kotlin/com/w2sv/filenavigator/MainActivity.kt b/app/src/main/kotlin/com/w2sv/filenavigator/MainActivity.kt index f3c126f6..3ff694fe 100644 --- a/app/src/main/kotlin/com/w2sv/filenavigator/MainActivity.kt +++ b/app/src/main/kotlin/com/w2sv/filenavigator/MainActivity.kt @@ -148,7 +148,7 @@ private fun useDarkTheme(theme: Theme): Boolean { } } -private class SwipeRightSplashScreenExitAnimation(private val onAnimationEnd: () -> Unit) : +private class SwipeRightSplashScreenExitAnimation(private val onAnimationEnd: () -> Unit = {}) : SplashScreen.OnExitAnimationListener { override fun onSplashScreenExit(splashScreenViewProvider: SplashScreenViewProvider) { ObjectAnimator.ofFloat( diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 5773029b..ed353932 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -4,6 +4,6 @@ \ No newline at end of file diff --git a/core/database/schemas/com.w2sv.database.AppDatabase/5.json b/core/database/schemas/com.w2sv.database.AppDatabase/5.json index 084d54bd..d9437480 100644 --- a/core/database/schemas/com.w2sv.database.AppDatabase/5.json +++ b/core/database/schemas/com.w2sv.database.AppDatabase/5.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 5, - "identityHash": "beddb7fbaf7eb898c488863ea61100c3", + "identityHash": "baf06b9c9598808e35a51d2ea98f893f", "entities": [ { "tableName": "MovedFileEntity", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`documentUri` TEXT NOT NULL, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `sourceType` TEXT NOT NULL, `moveDateTime` TEXT NOT NULL, `autoMoved` INTEGER NOT NULL, `local_mediaUri` TEXT, `local_moveDestination` TEXT, `external_providerPackageName` TEXT, `external_providerAppLabel` TEXT, PRIMARY KEY(`moveDateTime`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`documentUri` TEXT NOT NULL, `name` TEXT NOT NULL, `originalName` TEXT, `type` TEXT NOT NULL, `sourceType` TEXT NOT NULL, `moveDateTime` TEXT NOT NULL, `autoMoved` INTEGER NOT NULL, `local_mediaUri` TEXT, `local_moveDestination` TEXT, `external_providerPackageName` TEXT, `external_providerAppLabel` TEXT, PRIMARY KEY(`moveDateTime`))", "fields": [ { "fieldPath": "documentUri", @@ -20,6 +20,12 @@ "affinity": "TEXT", "notNull": true }, + { + "fieldPath": "originalName", + "columnName": "originalName", + "affinity": "TEXT", + "notNull": false + }, { "fieldPath": "type", "columnName": "type", @@ -82,7 +88,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'beddb7fbaf7eb898c488863ea61100c3')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'baf06b9c9598808e35a51d2ea98f893f')" ] } } \ No newline at end of file diff --git a/core/database/src/androidTest/kotlin/com/w2sv/database/migration/MigrationTest.kt b/core/database/src/androidTest/kotlin/com/w2sv/database/migration/MigrationTest.kt index 6feed164..aa369f66 100644 --- a/core/database/src/androidTest/kotlin/com/w2sv/database/migration/MigrationTest.kt +++ b/core/database/src/androidTest/kotlin/com/w2sv/database/migration/MigrationTest.kt @@ -72,6 +72,7 @@ internal class MigrationTest { // Validate that the data exists and matches the original assertTrue(migratedData.moveToFirst()) assertEquals(fileName, migratedData.getString("name")) + assertEquals("null", migratedData.getString("originalName")) assertEquals(fileType, migratedData.getString("type")) assertEquals(sourceType, migratedData.getString("sourceType")) assertEquals(destinationDocumentUri, migratedData.getString("local_moveDestination")) diff --git a/core/database/src/main/kotlin/com/w2sv/database/entity/MovedFileEntity.kt b/core/database/src/main/kotlin/com/w2sv/database/entity/MovedFileEntity.kt index daa10da3..981badb4 100644 --- a/core/database/src/main/kotlin/com/w2sv/database/entity/MovedFileEntity.kt +++ b/core/database/src/main/kotlin/com/w2sv/database/entity/MovedFileEntity.kt @@ -17,6 +17,7 @@ import java.time.LocalDateTime internal data class MovedFileEntity( val documentUri: Uri, val name: String, + val originalName: String?, val type: FileType, val sourceType: SourceType, @PrimaryKey val moveDateTime: LocalDateTime, @@ -34,6 +35,7 @@ internal data class MovedFileEntity( constructor(movedFile: MovedFile) : this( documentUri = movedFile.documentUri.uri, name = movedFile.name, + originalName = movedFile.originalName, type = movedFile.type, sourceType = movedFile.sourceType, moveDateTime = movedFile.moveDateTime, @@ -56,6 +58,7 @@ internal data class MovedFileEntity( documentUri = documentUri.documentUri, mediaUri = local.mediaUri.mediaUri, name = name, + originalName = originalName, type = type, sourceType = sourceType, moveDestination = LocalDestination(local.moveDestination.documentUri), @@ -72,6 +75,7 @@ internal data class MovedFileEntity( providerAppLabel = external?.providerAppLabel ), name = name, + originalName = originalName, type = type, sourceType = sourceType, moveDateTime = moveDateTime, diff --git a/core/database/src/main/kotlin/com/w2sv/database/migration/Migrations.kt b/core/database/src/main/kotlin/com/w2sv/database/migration/Migrations.kt index 4d3d0451..c09496c2 100644 --- a/core/database/src/main/kotlin/com/w2sv/database/migration/Migrations.kt +++ b/core/database/src/main/kotlin/com/w2sv/database/migration/Migrations.kt @@ -108,6 +108,7 @@ internal object Migrations { """CREATE TABLE IF NOT EXISTS $VERSION_5_TABLE_NAME ( documentUri TEXT NOT NULL, name TEXT NOT NULL, + originalName TEXT DEFAULT NULL, type TEXT NOT NULL, sourceType TEXT NOT NULL, moveDateTime TEXT NOT NULL, diff --git a/core/database/src/test/kotlin/com/w2sv/database/repository/RoomMovedFileRepositoryTest.kt b/core/database/src/test/kotlin/com/w2sv/database/repository/RoomMovedFileRepositoryTest.kt index 9aed1999..ca5f8918 100644 --- a/core/database/src/test/kotlin/com/w2sv/database/repository/RoomMovedFileRepositoryTest.kt +++ b/core/database/src/test/kotlin/com/w2sv/database/repository/RoomMovedFileRepositoryTest.kt @@ -59,6 +59,7 @@ internal class RoomMovedFileRepositoryTest { documentUri = DocumentUri.parse("kjhasdfkjh"), mediaUri = MediaUri.parse("kjasdf"), name = "someFile.jpg", + originalName = null, type = FileType.Image, sourceType = SourceType.Screenshot, moveDestination = LocalDestination.parse("kjhasdfkjh"), @@ -72,6 +73,7 @@ internal class RoomMovedFileRepositoryTest { providerAppLabel = "Drive" ), name = "someFile.jpg", + originalName = "previousName.jpg", type = FileType.Image, sourceType = SourceType.Screenshot, moveDateTime = LocalDateTime.now() @@ -83,6 +85,7 @@ internal class RoomMovedFileRepositoryTest { providerAppLabel = null ), name = "someFile.jpg", + originalName = null, type = FileType.Image, sourceType = SourceType.Screenshot, moveDateTime = LocalDateTime.now() diff --git a/core/datastore/src/main/kotlin/com/w2sv/datastore/migration/NavigatorPreferencesToProtoMigration.kt b/core/datastore/src/main/kotlin/com/w2sv/datastore/migration/NavigatorPreferencesToProtoMigration.kt index 18931657..7b9eb5fe 100644 --- a/core/datastore/src/main/kotlin/com/w2sv/datastore/migration/NavigatorPreferencesToProtoMigration.kt +++ b/core/datastore/src/main/kotlin/com/w2sv/datastore/migration/NavigatorPreferencesToProtoMigration.kt @@ -62,7 +62,7 @@ internal class NavigatorPreferencesToProtoMigration( ), sourceConfig.enabled ), - lastMoveDestinations = preferences[PreMigrationNavigatorPreferencesKey.lastMoveDestination( + quickMoveDestinations = preferences[PreMigrationNavigatorPreferencesKey.lastMoveDestination( fileType = fileType, sourceType = sourceType )]?.let { lastMoveDestination -> diff --git a/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImpl.kt b/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImpl.kt index a02e544d..a4fe4542 100644 --- a/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImpl.kt +++ b/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImpl.kt @@ -1,10 +1,12 @@ package com.w2sv.datastore.proto.navigatorconfig +import androidx.annotation.VisibleForTesting import androidx.datastore.core.DataStore +import com.w2sv.common.util.log import com.w2sv.datastore.NavigatorConfigProto import com.w2sv.domain.model.FileType -import com.w2sv.domain.model.movedestination.LocalDestinationApi import com.w2sv.domain.model.SourceType +import com.w2sv.domain.model.movedestination.LocalDestinationApi import com.w2sv.domain.model.navigatorconfig.AutoMoveConfig import com.w2sv.domain.model.navigatorconfig.NavigatorConfig import com.w2sv.domain.repository.NavigatorConfigDataSource @@ -54,19 +56,10 @@ internal class NavigatorConfigDataSourceImpl @Inject constructor(private val nav sourceType ) { sourceConfig -> sourceConfig.copy( - lastMoveDestinations = sourceConfig.lastMoveDestinations.let { currentDestinations -> - when (destination) { - currentDestinations.firstOrNull() -> currentDestinations - currentDestinations.getOrNull(1) -> currentDestinations.reversed() - else -> buildList { - add(destination) - currentDestinations.firstOrNull() - ?.let { firstCurrentElement -> - add(firstCurrentElement) - } - } - } - } + quickMoveDestinations = updatedQuickMoveDestinations( + currentDestinations = sourceConfig.quickMoveDestinations, + destination = destination + ) ) } } @@ -81,7 +74,7 @@ internal class NavigatorConfigDataSourceImpl @Inject constructor(private val nav fileType, sourceType ) { - it.copy(lastMoveDestinations = listOf()) + it.copy(quickMoveDestinations = listOf()) } } } @@ -90,8 +83,22 @@ internal class NavigatorConfigDataSourceImpl @Inject constructor(private val nav fileType: FileType, sourceType: SourceType ): Flow> = - navigatorConfig.map { it.sourceConfig(fileType, sourceType).lastMoveDestinations } + navigatorConfig.map { it.sourceConfig(fileType, sourceType).quickMoveDestinations } } - - +@VisibleForTesting +internal fun updatedQuickMoveDestinations( + currentDestinations: List, + destination: LocalDestinationApi +): List = + when (destination.documentUri) { + currentDestinations.firstOrNull()?.documentUri -> currentDestinations + currentDestinations.getOrNull(1)?.documentUri -> currentDestinations.reversed() + else -> buildList { + add(destination) + currentDestinations.firstOrNull() + ?.let { firstCurrentElement -> + add(firstCurrentElement) + } + } + } \ No newline at end of file diff --git a/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapper.kt b/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapper.kt index d49d708d..d38d21fc 100644 --- a/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapper.kt +++ b/core/datastore/src/main/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapper.kt @@ -78,7 +78,7 @@ private object SourceConfigMapper : override fun toExternal(proto: SourceConfigProto): SourceConfig = SourceConfig( enabled = proto.enabled, - lastMoveDestinations = proto.lastMoveDestinationsList.map { LocalDestination.parse(it) }, + quickMoveDestinations = proto.lastMoveDestinationsList.map { LocalDestination.parse(it) }, autoMoveConfig = AutoMoveConfigMapper.toExternal(proto.autoMoveConfig) ) @@ -87,7 +87,7 @@ private object SourceConfigMapper : this.enabled = external.enabled this.lastMoveDestinations.apply { clear() - addAll(external.lastMoveDestinations.map { it.uriString }) + addAll(external.quickMoveDestinations.map { it.uriString }) } this.autoMoveConfig = AutoMoveConfigMapper.toProto(external.autoMoveConfig) } diff --git a/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImplTest.kt b/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImplTest.kt new file mode 100644 index 00000000..b4d50817 --- /dev/null +++ b/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigDataSourceImplTest.kt @@ -0,0 +1,75 @@ +package com.w2sv.datastore.proto.navigatorconfig + +import com.w2sv.domain.model.movedestination.LocalDestination +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class NavigatorConfigDataSourceImplTest { + + private val localDestinationA = + LocalDestination.parse("content://com.android.externalstorage.documents/document/primary%3AMoved%2FGIFs") + private val localDestinationB = + LocalDestination.parse("content://com.android.externalstorage.documents/document/primary%3AMoved%2FScreenshots") + private val localDestinationC = + LocalDestination.parse("content://com.android.externalstorage.documents/document/primary%3AMoved") + + @Test + fun testUpdatedQuickMoveDestinations() { + // When current destinations empty -> destination is added + assertEquals( + listOf(localDestinationA), + updatedQuickMoveDestinations( + currentDestinations = emptyList(), + destination = localDestinationA + ) + ) + + // When new destination equals first destination -> list stays as is + assertEquals( + listOf(localDestinationA), + updatedQuickMoveDestinations( + currentDestinations = listOf(localDestinationA), + destination = localDestinationA + ) + ) + + // When new destination equals first destination -> list stays as is + assertEquals( + listOf(localDestinationA, localDestinationB), + updatedQuickMoveDestinations( + currentDestinations = listOf(localDestinationA, localDestinationB), + destination = localDestinationA + ) + ) + + // When new destination equals second destination -> list gets reversed + assertEquals( + listOf(localDestinationB, localDestinationA), + updatedQuickMoveDestinations( + currentDestinations = listOf(localDestinationA, localDestinationB), + destination = localDestinationB + ) + ) + + // When new destination not in list -> second is removed, first becomes second, new becomes first + assertEquals( + listOf(localDestinationC, localDestinationA), + updatedQuickMoveDestinations( + currentDestinations = listOf(localDestinationA, localDestinationB), + destination = localDestinationC + ) + ) + + // When new destination not in list -> first becomes second, new becomes first + assertEquals( + listOf(localDestinationB, localDestinationA), + updatedQuickMoveDestinations( + currentDestinations = listOf(localDestinationA), + destination = localDestinationB + ) + ) + } +} \ No newline at end of file diff --git a/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapperTest.kt b/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapperTest.kt index 589d2bee..8c37df1f 100644 --- a/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapperTest.kt +++ b/core/datastore/src/test/kotlin/com/w2sv/datastore/proto/navigatorconfig/NavigatorConfigMapperTest.kt @@ -40,7 +40,7 @@ internal class NavigatorConfigMapperTest { .copyWithAlteredSourceConfig(FileType.Audio, SourceType.Recording) { it.copy( enabled = false, - lastMoveDestinations = listOf( + quickMoveDestinations = listOf( LocalDestination.parse("last/move/destination"), LocalDestination.parse("before/last/move/destination") ) diff --git a/core/domain/src/main/kotlin/com/w2sv/domain/model/MovedFile.kt b/core/domain/src/main/kotlin/com/w2sv/domain/model/MovedFile.kt index 24b5485e..d2b1b505 100644 --- a/core/domain/src/main/kotlin/com/w2sv/domain/model/MovedFile.kt +++ b/core/domain/src/main/kotlin/com/w2sv/domain/model/MovedFile.kt @@ -10,6 +10,7 @@ import java.time.LocalDateTime sealed interface MovedFile { val documentUri: DocumentUri val name: String + val originalName: String? val type: FileType val sourceType: SourceType val moveDestination: MoveDestinationApi @@ -33,6 +34,7 @@ sealed interface MovedFile { override val documentUri: DocumentUri, val mediaUri: MediaUri, override val name: String, + override val originalName: String?, override val type: FileType, override val sourceType: SourceType, override val moveDestination: LocalDestinationApi, @@ -46,6 +48,7 @@ sealed interface MovedFile { */ data class External( override val name: String, + override val originalName: String?, override val type: FileType, override val sourceType: SourceType, override val moveDestination: ExternalDestinationApi, diff --git a/core/domain/src/main/kotlin/com/w2sv/domain/model/navigatorconfig/SourceConfig.kt b/core/domain/src/main/kotlin/com/w2sv/domain/model/navigatorconfig/SourceConfig.kt index 0d52da2f..f793bdb3 100644 --- a/core/domain/src/main/kotlin/com/w2sv/domain/model/navigatorconfig/SourceConfig.kt +++ b/core/domain/src/main/kotlin/com/w2sv/domain/model/navigatorconfig/SourceConfig.kt @@ -4,6 +4,6 @@ import com.w2sv.domain.model.movedestination.LocalDestinationApi data class SourceConfig( val enabled: Boolean = true, - val lastMoveDestinations: List = emptyList(), + val quickMoveDestinations: List = emptyList(), val autoMoveConfig: AutoMoveConfig = AutoMoveConfig.Empty ) \ No newline at end of file diff --git a/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/activity/destination_picking/FileDestinationPickerActivity.kt b/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/activity/destination_picking/FileDestinationPickerActivity.kt index 99583282..f43f6cf0 100644 --- a/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/activity/destination_picking/FileDestinationPickerActivity.kt +++ b/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/activity/destination_picking/FileDestinationPickerActivity.kt @@ -12,13 +12,13 @@ import com.w2sv.common.util.documentUri import com.w2sv.common.util.emit import com.w2sv.common.util.log import com.w2sv.common.util.takePersistableReadAndWriteUriPermission -import com.w2sv.navigator.moving.model.NavigatorMoveDestination import com.w2sv.navigator.MoveResultChannel import com.w2sv.navigator.moving.model.DestinationSelectionManner import com.w2sv.navigator.moving.model.MediaIdWithMediaType import com.w2sv.navigator.moving.model.MoveBundle import com.w2sv.navigator.moving.model.MoveFile import com.w2sv.navigator.moving.model.MoveResult +import com.w2sv.navigator.moving.model.NavigatorMoveDestination import com.w2sv.navigator.moving.receiver.MoveBroadcastReceiver import com.w2sv.navigator.notifications.NotificationResources import dagger.hilt.android.AndroidEntryPoint diff --git a/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/model/MoveBundle.kt b/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/model/MoveBundle.kt index 41c94fd6..f22f3a2f 100644 --- a/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/model/MoveBundle.kt +++ b/core/navigator/src/main/kotlin/com/w2sv/navigator/moving/model/MoveBundle.kt @@ -62,13 +62,16 @@ internal sealed interface MoveBundle { MovedFile.Local( documentUri = capturedDestination.documentUri, mediaUri = capturedDestination.mediaUri, - name = capturedDestination.fileName(context), + name = name, + originalName = originalName, type = file.fileType, sourceType = file.sourceType, moveDestination = capturedDestination.parent, @@ -80,7 +83,8 @@ internal sealed interface MoveBundle { MovedFile.External( moveDestination = capturedDestination, - name = capturedDestination.fileName(context), + name = name, + originalName = originalName, type = file.fileType, sourceType = file.sourceType, moveDateTime = dateTime @@ -94,6 +98,7 @@ internal sealed interface MoveBundle throw IllegalArgumentException() // I have no clue why this is necessary here } - + } companion object { const val EXTRA = "com.w2sv.navigator.extra.MoveBundle" diff --git a/core/navigator/src/test/kotlin/com/w2sv/navigator/moving/model/NavigatorMoveDestinationTest.kt b/core/navigator/src/test/kotlin/com/w2sv/navigator/moving/model/NavigatorMoveDestinationTest.kt index 3424c9f4..52de57b4 100644 --- a/core/navigator/src/test/kotlin/com/w2sv/navigator/moving/model/NavigatorMoveDestinationTest.kt +++ b/core/navigator/src/test/kotlin/com/w2sv/navigator/moving/model/NavigatorMoveDestinationTest.kt @@ -2,7 +2,10 @@ package com.w2sv.navigator.moving.model import com.w2sv.common.util.DocumentUri import com.w2sv.common.util.MediaUri +import com.w2sv.domain.model.movedestination.LocalDestinationApi import com.w2sv.test.testParceling +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @@ -35,4 +38,18 @@ internal class NavigatorMoveDestinationTest { ) .testParceling() } + + @Test + fun `Directory equals working correctly`() { + val directory = NavigatorMoveDestination.Directory + .parse("content://com.android.externalstorage.documents/document/primary%3AMoved%2FGIFs") + val equalDirectory = NavigatorMoveDestination.Directory + .parse("content://com.android.externalstorage.documents/document/primary%3AMoved%2FGIFs") + val differentDirectory = NavigatorMoveDestination.Directory + .parse("content://com.android.externalstorage.documents/document/primary%3AMoved%2FScreenshots") + + assertTrue(directory == equalDirectory) + assertTrue(directory != differentDirectory) + assertFalse(directory == differentDirectory) + } } \ No newline at end of file