From 8d709206fd5f6b61464e9c538b8d1b25cc1f438c Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 21 Apr 2022 20:37:32 +0300 Subject: [PATCH 1/4] Issue#7 Fix order of videos Show the Pending Videos in the order they will be downloaded in --- app/build.gradle | 1 + .../data/local/VideoInPendingLocalSource.kt | 2 +- .../data/usecase/VideoDownloadingProcessorUseCase.kt | 6 +++--- .../tiktokdownloader/ui/main/queue/QueueFragment.kt | 10 +++++----- .../tiktokdownloader/ui/shared/ViewModelExtensions.kt | 8 ++++---- .../data/local/VideoInPendingLocalSourceTest.kt | 4 ++-- 6 files changed, 16 insertions(+), 15 deletions(-) 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/VideoInPendingLocalSource.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSource.kt index ed4d856..1d23967 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 @@ -16,7 +16,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() 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/ui/main/queue/QueueFragment.kt b/app/src/main/java/org/fnives/tiktokdownloader/ui/main/queue/QueueFragment.kt index 9baaf19..9943908 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 @@ -38,25 +38,25 @@ class QueueFragment : Fragment(R.layout.fragment_queue) { input.setText("") } - viewModel.navigationEvent.observe(viewLifecycleOwner, Observer { + viewModel.navigationEvent.observe(viewLifecycleOwner) { val intent = when (val data = it.item) { is QueueViewModel.NavigationEvent.OpenBrowser -> { createBrowserIntent(data.url) } is QueueViewModel.NavigationEvent.OpenGallery -> createGalleryIntent(data.uri) - null -> return@Observer + null -> return@observe } startActivity(intent) - }) + } - viewModel.downloads.observe(viewLifecycleOwner, { videoStates -> + viewModel.downloads.observe(viewLifecycleOwner) { videoStates -> adapter.submitList(videoStates, Runnable { val indexToScrollTo = videoStates.indexOfFirst { it is VideoState.InProcess } .takeIf { it != -1 } ?: return@Runnable recycler.smoothScrollToPosition(indexToScrollTo) }) - }) + } } companion object { diff --git a/app/src/main/java/org/fnives/tiktokdownloader/ui/shared/ViewModelExtensions.kt b/app/src/main/java/org/fnives/tiktokdownloader/ui/shared/ViewModelExtensions.kt index a93ef45..6ea3eea 100644 --- a/app/src/main/java/org/fnives/tiktokdownloader/ui/shared/ViewModelExtensions.kt +++ b/app/src/main/java/org/fnives/tiktokdownloader/ui/shared/ViewModelExtensions.kt @@ -1,12 +1,12 @@ package org.fnives.tiktokdownloader.ui.shared -import androidx.fragment.app.Fragment -import androidx.lifecycle.* +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import kotlin.properties.ReadOnlyProperty fun CoroutineScope.asLiveData(flow: Flow): LiveData { val liveData = MutableLiveData() diff --git a/app/src/test/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSourceTest.kt b/app/src/test/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSourceTest.kt index 03a10b2..b65bf87 100644 --- a/app/src/test/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSourceTest.kt +++ b/app/src/test/java/org/fnives/tiktokdownloader/data/local/VideoInPendingLocalSourceTest.kt @@ -76,11 +76,11 @@ class VideoInPendingLocalSourceTest { } @Test - fun GIVEN_observing_PendingVideos_WHEN_2_video_marked_as_pending_THEN_both_of_them_are_sent_out_in_correct_reverse_order() = + fun GIVEN_observing_PendingVideos_WHEN_2_video_marked_as_pending_THEN_both_of_them_are_sent_out_in_correct_order() = runBlocking { val videoInPending1 = VideoInPending("id1", "alma1") val videoInPending2 = VideoInPending("id2", "alma2") - val expected = listOf(emptyList(), listOf(videoInPending1), listOf(videoInPending2, videoInPending1)) + val expected = listOf(emptyList(), listOf(videoInPending1), listOf(videoInPending1, videoInPending2)) val actual = async(coroutineContext) { sut.pendingVideos.take(3).toList() From 136530b927fba0da597603816eaf0bed41bcb838 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Thu, 21 Apr 2022 21:17:21 +0300 Subject: [PATCH 2/4] Issue#7 Enable Deleting Pending Video by swiping --- .../data/usecase/MoveVideoInQueue.kt | 13 +++++ .../usecase/RemoveVideoFromQueueUseCase.kt | 13 +++++ .../di/module/UseCaseModule.kt | 18 +++++- .../di/module/ViewModelModule.kt | 1 + .../ui/main/queue/QueueFragment.kt | 58 ++++++++++++++++--- .../ui/main/queue/QueueViewModel.kt | 22 ++++++- 6 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueue.kt create mode 100644 app/src/main/java/org/fnives/tiktokdownloader/data/usecase/RemoveVideoFromQueueUseCase.kt diff --git a/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueue.kt b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueue.kt new file mode 100644 index 0000000..36d0863 --- /dev/null +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/MoveVideoInQueue.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 MoveVideoInQueue( + private val videoInPendingLocalSource: VideoInPendingLocalSource +) { + + operator fun invoke(videoInPending: VideoInPending, to: VideoInPending) { + + } +} \ 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..442b4e5 --- /dev/null +++ b/app/src/main/java/org/fnives/tiktokdownloader/data/usecase/RemoveVideoFromQueueUseCase.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 RemoveVideoFromQueueUseCase( + private val videoInPendingLocalSource: VideoInPendingLocalSource +) { + + operator fun invoke(videoInPending: VideoInPending) { + videoInPendingLocalSource.removeVideoFromQueue(videoInPending) + } +} \ No newline at end of file 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..4602dd7 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.MoveVideoInQueue +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,18 @@ class UseCaseModule( val addVideoToQueueUseCase: AddVideoToQueueUseCase get() = AddVideoToQueueUseCase( urlVerificationUseCase, - localSourceModule.videoInPendingLocalSource) + localSourceModule.videoInPendingLocalSource + ) + + val removeVideoFromQueueUseCase: RemoveVideoFromQueueUseCase + get() = RemoveVideoFromQueueUseCase( + localSourceModule.videoInPendingLocalSource + ) + + val moveVideoInQueue: MoveVideoInQueue + get() = MoveVideoInQueue( + 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..db931be 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,7 @@ class ViewModelModule(private val useCaseModule: UseCaseModule) { get() = QueueViewModel( useCaseModule.stateOfVideosObservableUseCase, useCaseModule.addVideoToQueueUseCase, + useCaseModule.removeVideoFromQueueUseCase, useCaseModule.videoDownloadingProcessorUseCase ) } \ 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 9943908..6b93229 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,22 +22,21 @@ 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