diff --git a/app/build.gradle b/app/build.gradle index f6c3faf..6ce5d91 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,6 +36,7 @@ android { buildTypes { debug { versionNameSuffix "-dev" + applicationIdSuffix ".debug" debuggable true shrinkResources false minifyEnabled false diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoDownloadedLocalSource.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoDownloadedLocalSource.kt index 0941333..40cb5a2 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoDownloadedLocalSource.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoDownloadedLocalSource.kt @@ -58,6 +58,12 @@ class VideoDownloadedLocalSource( .plus(videoDownloaded.asString().addTimeAtStart()) } + fun removeVideo(videoDownloaded: VideoDownloaded) { + sharedPreferencesManagerImpl.downloadedVideos = sharedPreferencesManagerImpl.downloadedVideos + .filterNot { it.getTimeAndOriginal().second.asVideoDownloaded() == videoDownloaded } + .toSet() + } + companion object { private fun VideoDownloaded.asString(): String = diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSource.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSource.kt index ed4d856..6161d2a 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSource.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSource.kt @@ -8,6 +8,7 @@ import org.fnives.tiktokdownloader.data.local.persistent.getTimeAndOriginal import org.fnives.tiktokdownloader.data.local.persistent.joinNormalized import org.fnives.tiktokdownloader.data.local.persistent.separateIntoDenormalized import org.fnives.tiktokdownloader.data.model.VideoInPending +import kotlin.math.abs class VideoInPendingLocalSource( private val sharedPreferencesManager: SharedPreferencesManager, @@ -16,7 +17,7 @@ class VideoInPendingLocalSource( get() = sharedPreferencesManager.pendingVideosFlow .map { stringSet -> stringSet.asSequence().map { timeThenUrl -> timeThenUrl.getTimeAndOriginal() } - .sortedByDescending { it.first } + .sortedBy { it.first } .map { it.second } .map { it.asVideoInPending() } .toList() @@ -33,6 +34,27 @@ class VideoInPendingLocalSource( .toSet() } + fun moveBy(videoInPending: VideoInPending, positionDifference: Int) { + if (positionDifference == 0) return + + val mutableOrdered = sharedPreferencesManager.pendingVideos + .map { it.getTimeAndOriginal() } + .sortedBy { it.first } + .toMutableList() + + val index = mutableOrdered.indexOfFirst { it.second.asVideoInPending() == videoInPending } + val endTime = mutableOrdered[index + positionDifference].first + + val direction = -positionDifference / abs(positionDifference) + val range = IntProgression.fromClosedRange(index + positionDifference, index - direction, direction) + range.forEach { + mutableOrdered[it] = mutableOrdered[it + direction].first to mutableOrdered[it].second + } + mutableOrdered[index] = endTime to mutableOrdered[index].second + + sharedPreferencesManager.pendingVideos = mutableOrdered.map { it.second.addTimeAtStart(it.first) }.toSet() + } + companion object { private fun VideoInPending.asString(): String = listOf(id, url).joinNormalized() diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/local/persistent/SerializationHelper.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/local/persistent/SerializationHelper.kt index d34d35b..6340bcb 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/data/local/persistent/SerializationHelper.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/local/persistent/SerializationHelper.kt @@ -12,8 +12,8 @@ fun List.joinNormalized() : String = fun String.separateIntoDenormalized() : List = split("$SEPARATOR$SEPARATOR").map { it.denormalize() } -fun String.addTimeAtStart(index: Int = 0) = - "${System.currentTimeMillis() + index}$TIME_SEPARATOR$this" +fun String.addTimeAtStart(time: Long = System.currentTimeMillis()) = + "${time}$TIME_SEPARATOR$this" fun String.getTimeAndOriginal(): Pair { val time = takeWhile { it != TIME_SEPARATOR }.toLong() diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueueUseCase.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueueUseCase.kt new file mode 100644 index 0000000..2b1e275 --- /dev/null +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueueUseCase.kt @@ -0,0 +1,13 @@ +package org.fnives.tiktokdownloader.data.usecase + +import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource +import org.fnives.tiktokdownloader.data.model.VideoInPending + +class MoveVideoInQueueUseCase( + private val videoInPendingLocalSource: VideoInPendingLocalSource +) { + + operator fun invoke(videoInPending: VideoInPending, positionDifference: Int) { + videoInPendingLocalSource.moveBy(videoInPending, positionDifference) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/RemoveVideoFromQueueUseCase.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/RemoveVideoFromQueueUseCase.kt new file mode 100644 index 0000000..e84fc4a --- /dev/null +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/RemoveVideoFromQueueUseCase.kt @@ -0,0 +1,20 @@ +package org.fnives.tiktokdownloader.data.usecase + +import org.fnives.tiktokdownloader.data.local.VideoDownloadedLocalSource +import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource +import org.fnives.tiktokdownloader.data.model.VideoInPending +import org.fnives.tiktokdownloader.data.model.VideoState + +class RemoveVideoFromQueueUseCase( + private val videoInPendingLocalSource: VideoInPendingLocalSource, + private val videoDownloadedLocalSource: VideoDownloadedLocalSource +) { + + operator fun invoke(videoState: VideoState) { + when(videoState) { + is VideoState.Downloaded -> videoDownloadedLocalSource.removeVideo(videoState.videoDownloaded) + is VideoState.InPending -> videoInPendingLocalSource.removeVideoFromQueue(videoState.videoInPending) + is VideoState.InProcess -> Unit + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/VideoDownloadingProcessorUseCase.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/VideoDownloadingProcessorUseCase.kt index b96bade..155df97 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/VideoDownloadingProcessorUseCase.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/VideoDownloadingProcessorUseCase.kt @@ -43,7 +43,7 @@ class VideoDownloadingProcessorUseCase( private val fetch = MutableStateFlow(ProcessingState.RUNNING) private val _processState by lazy { - combineIntoPair(fetch, videoInPendingLocalSource.observeLastPendingVideo()) + combineIntoPair(fetch, videoInPendingLocalSource.observeFirstPendingVideo()) .filter { it.first == ProcessingState.RUNNING } .map { it.second } .debounce(WORK_FLOW_DEBOUNCE) @@ -134,7 +134,7 @@ class VideoDownloadingProcessorUseCase( private fun combineIntoPair(flow1: Flow, flow2: Flow): Flow> = combine(flow1, flow2) { item1, item2 -> item1 to item2 } - private fun VideoInPendingLocalSource.observeLastPendingVideo(): Flow = - pendingVideos.map { it.lastOrNull() }.distinctUntilChanged() + private fun VideoInPendingLocalSource.observeFirstPendingVideo(): Flow = + pendingVideos.map { it.firstOrNull() }.distinctUntilChanged() } } \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/module/LocalSourceModule.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/module/LocalSourceModule.kt index 4c311b0..f0ce819 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/module/LocalSourceModule.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/module/LocalSourceModule.kt @@ -8,17 +8,17 @@ import java.util.concurrent.TimeUnit class LocalSourceModule(private val androidFileManagementModule: AndroidFileManagementModule) { - val videoDownloadedLocalSource: VideoDownloadedLocalSource - get() = VideoDownloadedLocalSource( + val videoDownloadedLocalSource: VideoDownloadedLocalSource by lazy { + VideoDownloadedLocalSource( saveVideoFile = androidFileManagementModule.saveVideoFile, sharedPreferencesManagerImpl = androidFileManagementModule.sharedPreferencesManager, verifyFileForUriExists = androidFileManagementModule.verifyFileForUriExists ) + } - val videoInPendingLocalSource: VideoInPendingLocalSource - get() = VideoInPendingLocalSource( - sharedPreferencesManager = androidFileManagementModule.sharedPreferencesManager - ) + val videoInPendingLocalSource: VideoInPendingLocalSource by lazy { + VideoInPendingLocalSource(sharedPreferencesManager = androidFileManagementModule.sharedPreferencesManager) + } val videoInProgressLocalSource: VideoInProgressLocalSource by lazy { VideoInProgressLocalSource() } diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/module/UseCaseModule.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/module/UseCaseModule.kt index c2a3977..95261f7 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/module/UseCaseModule.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/module/UseCaseModule.kt @@ -2,13 +2,16 @@ package org.fnives.tiktokdownloader.di.module import kotlinx.coroutines.Dispatchers import org.fnives.tiktokdownloader.data.usecase.AddVideoToQueueUseCase +import org.fnives.tiktokdownloader.data.usecase.MoveVideoInQueueUseCase +import org.fnives.tiktokdownloader.data.usecase.RemoveVideoFromQueueUseCase import org.fnives.tiktokdownloader.data.usecase.StateOfVideosObservableUseCase import org.fnives.tiktokdownloader.data.usecase.UrlVerificationUseCase import org.fnives.tiktokdownloader.data.usecase.VideoDownloadingProcessorUseCase class UseCaseModule( private val localSourceModule: LocalSourceModule, - private val networkModule: NetworkModule) { + private val networkModule: NetworkModule +) { val stateOfVideosObservableUseCase: StateOfVideosObservableUseCase get() = StateOfVideosObservableUseCase( @@ -24,7 +27,19 @@ class UseCaseModule( val addVideoToQueueUseCase: AddVideoToQueueUseCase get() = AddVideoToQueueUseCase( urlVerificationUseCase, - localSourceModule.videoInPendingLocalSource) + localSourceModule.videoInPendingLocalSource + ) + + val removeVideoFromQueueUseCase: RemoveVideoFromQueueUseCase + get() = RemoveVideoFromQueueUseCase( + localSourceModule.videoInPendingLocalSource, + localSourceModule.videoDownloadedLocalSource + ) + + val moveVideoInQueueUseCase: MoveVideoInQueueUseCase + get() = MoveVideoInQueueUseCase( + localSourceModule.videoInPendingLocalSource + ) val videoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase by lazy { VideoDownloadingProcessorUseCase( diff --git a/app/src/main/java/org/fnives/tiktokdownloader/di/module/ViewModelModule.kt b/app/src/main/java/org/fnives/tiktokdownloader/di/module/ViewModelModule.kt index e12b6bf..123b183 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/di/module/ViewModelModule.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/di/module/ViewModelModule.kt @@ -24,6 +24,8 @@ class ViewModelModule(private val useCaseModule: UseCaseModule) { get() = QueueViewModel( useCaseModule.stateOfVideosObservableUseCase, useCaseModule.addVideoToQueueUseCase, - useCaseModule.videoDownloadingProcessorUseCase + useCaseModule.removeVideoFromQueueUseCase, + useCaseModule.videoDownloadingProcessorUseCase, + useCaseModule.moveVideoInQueueUseCase ) } \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/MovingItemCallback.kt b/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/MovingItemCallback.kt new file mode 100644 index 0000000..b9f5b20 --- /dev/null +++ b/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/MovingItemCallback.kt @@ -0,0 +1,8 @@ +package org.fnives.tiktokdownloader.ui.main.queue + +interface MovingItemCallback { + + fun onMovingStart() + + fun onMovingEnd() +} \ No newline at end of file diff --git a/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/QueueFragment.kt b/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/QueueFragment.kt index 9baaf19..1502e10 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/QueueFragment.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/QueueFragment.kt @@ -9,7 +9,7 @@ import android.widget.EditText import androidx.core.net.toUri import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import org.fnives.tiktokdownloader.R import org.fnives.tiktokdownloader.data.model.VideoState @@ -22,41 +22,59 @@ class QueueFragment : Fragment(R.layout.fragment_queue) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val recycler = view.findViewById(R.id.recycler) - val adapter = QueueItemAdapter( - itemClicked = viewModel::onItemClicked, - urlClicked = viewModel::onUrlClicked - ) - recycler.adapter = adapter + recyclerViewSetup(recycler) + navigationSetup() + val saveUrlCta = view.findViewById