commit
95a100217c
24 changed files with 614 additions and 39 deletions
|
|
@ -36,6 +36,7 @@ android {
|
|||
buildTypes {
|
||||
debug {
|
||||
versionNameSuffix "-dev"
|
||||
applicationIdSuffix ".debug"
|
||||
debuggable true
|
||||
shrinkResources false
|
||||
minifyEnabled false
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ fun List<String>.joinNormalized() : String =
|
|||
fun String.separateIntoDenormalized() : List<String> =
|
||||
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<Long, String> {
|
||||
val time = takeWhile { it != TIME_SEPARATOR }.toLong()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <T, R> combineIntoPair(flow1: Flow<T>, flow2: Flow<R>): Flow<Pair<T, R>> =
|
||||
combine(flow1, flow2) { item1, item2 -> item1 to item2 }
|
||||
|
||||
private fun VideoInPendingLocalSource.observeLastPendingVideo(): Flow<VideoInPending?> =
|
||||
pendingVideos.map { it.lastOrNull() }.distinctUntilChanged()
|
||||
private fun VideoInPendingLocalSource.observeFirstPendingVideo(): Flow<VideoInPending?> =
|
||||
pendingVideos.map { it.firstOrNull() }.distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
|
|
@ -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() }
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ class ViewModelModule(private val useCaseModule: UseCaseModule) {
|
|||
get() = QueueViewModel(
|
||||
useCaseModule.stateOfVideosObservableUseCase,
|
||||
useCaseModule.addVideoToQueueUseCase,
|
||||
useCaseModule.videoDownloadingProcessorUseCase
|
||||
useCaseModule.removeVideoFromQueueUseCase,
|
||||
useCaseModule.videoDownloadingProcessorUseCase,
|
||||
useCaseModule.moveVideoInQueueUseCase
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package org.fnives.tiktokdownloader.ui.main.queue
|
||||
|
||||
interface MovingItemCallback {
|
||||
|
||||
fun onMovingStart()
|
||||
|
||||
fun onMovingEnd()
|
||||
}
|
||||
|
|
@ -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<RecyclerView>(R.id.recycler)
|
||||
val adapter = QueueItemAdapter(
|
||||
itemClicked = viewModel::onItemClicked,
|
||||
urlClicked = viewModel::onUrlClicked
|
||||
)
|
||||
recycler.adapter = adapter
|
||||
recyclerViewSetup(recycler)
|
||||
navigationSetup()
|
||||
|
||||
val saveUrlCta = view.findViewById<Button>(R.id.save_cta)
|
||||
val input = view.findViewById<EditText>(R.id.download_url_input)
|
||||
input.doAfterTextChanged {
|
||||
saveUrlCta.isEnabled = it?.isNotBlank() == true
|
||||
}
|
||||
saveUrlCta.setOnClickListener {
|
||||
recycler.smoothScrollToPosition(0)
|
||||
viewModel.onSaveClicked(input.text?.toString().orEmpty())
|
||||
input.setText("")
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.navigationEvent.observe(viewLifecycleOwner, Observer {
|
||||
private fun navigationSetup() {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun recyclerViewSetup(recycler: RecyclerView) {
|
||||
val adapter = QueueItemAdapter(
|
||||
itemClicked = viewModel::onItemClicked,
|
||||
urlClicked = viewModel::onUrlClicked
|
||||
)
|
||||
recycler.adapter = adapter
|
||||
|
||||
val callback = VideoStateItemTouchHelper(
|
||||
whichItem = { adapter.currentList.getOrNull(it.bindingAdapterPosition) },
|
||||
onDeleteElement = viewModel::onElementDeleted,
|
||||
onUIMoveElement = adapter::swap,
|
||||
onMoveElement = viewModel::onElementMoved
|
||||
)
|
||||
val touchHelper = ItemTouchHelper(callback)
|
||||
touchHelper.attachToRecyclerView(recycler)
|
||||
|
||||
viewModel.downloads.observe(viewLifecycleOwner) { videoStates ->
|
||||
callback.dragEnabled = videoStates.none { it is VideoState.InProcess }
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import android.view.ViewGroup
|
|||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
|
|
@ -53,12 +55,38 @@ class QueueItemAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
class DownloadActionsViewHolder(parent: ViewGroup) :
|
||||
fun swap(from: VideoState, to: VideoState) {
|
||||
val mutable = currentList.toMutableList()
|
||||
val fromIndex = currentList.indexOf(from)
|
||||
val toIndex = currentList.indexOf(to)
|
||||
mutable.removeAt(fromIndex)
|
||||
mutable.add(fromIndex, to)
|
||||
mutable.removeAt(toIndex)
|
||||
mutable.add(toIndex, from)
|
||||
|
||||
submitList(mutable)
|
||||
}
|
||||
|
||||
class DownloadActionsViewHolder(parent: ViewGroup) : MovingItemCallback,
|
||||
RecyclerView.ViewHolder(parent.inflate(R.layout.item_downloaded)) {
|
||||
val cardView: CardView = itemView as CardView
|
||||
val content: ConstraintLayout = itemView.findViewById(R.id.content)
|
||||
val urlView: TextView = itemView.findViewById(R.id.url)
|
||||
val statusView: TextView = itemView.findViewById(R.id.status)
|
||||
val thumbNailView: ImageView = itemView.findViewById(R.id.thumbnail)
|
||||
val progress: ProgressBar = itemView.findViewById(R.id.progress)
|
||||
|
||||
override fun onMovingEnd() {
|
||||
cardView.isEnabled = false
|
||||
cardView.isPressed = false
|
||||
content.isPressed = false
|
||||
}
|
||||
|
||||
override fun onMovingStart() {
|
||||
cardView.isEnabled = true
|
||||
cardView.isPressed = true
|
||||
content.isPressed = true
|
||||
}
|
||||
}
|
||||
|
||||
class DiffUtilItemCallback : DiffUtil.ItemCallback<VideoState>() {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ package org.fnives.tiktokdownloader.ui.main.queue
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||
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.VideoDownloadingProcessorUseCase
|
||||
import org.fnives.tiktokdownloader.ui.shared.Event
|
||||
|
|
@ -12,7 +15,9 @@ import org.fnives.tiktokdownloader.ui.shared.asLiveData
|
|||
class QueueViewModel(
|
||||
stateOfVideosObservableUseCase: StateOfVideosObservableUseCase,
|
||||
private val addVideoToQueueUseCase: AddVideoToQueueUseCase,
|
||||
private val videoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
||||
private val removeVideoFromQueueUseCase: RemoveVideoFromQueueUseCase,
|
||||
private val videoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase,
|
||||
private val moveVideoInQueueUseCase: MoveVideoInQueueUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
val downloads = asLiveData(stateOfVideosObservableUseCase())
|
||||
|
|
@ -32,6 +37,16 @@ class QueueViewModel(
|
|||
_navigationEvent.value = Event(NavigationEvent.OpenBrowser(url))
|
||||
}
|
||||
|
||||
fun onElementDeleted(videoState: VideoState) {
|
||||
removeVideoFromQueueUseCase(videoState)
|
||||
}
|
||||
|
||||
fun onElementMoved(moved: VideoState, positionDifference: Int): Boolean {
|
||||
if (moved !is VideoState.InPending) return false
|
||||
moveVideoInQueueUseCase(moved.videoInPending, positionDifference)
|
||||
return true
|
||||
}
|
||||
|
||||
sealed class NavigationEvent {
|
||||
data class OpenBrowser(val url: String) : NavigationEvent()
|
||||
data class OpenGallery(val uri: String) : NavigationEvent()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package org.fnives.tiktokdownloader.ui.main.queue
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||
|
||||
class VideoStateItemTouchHelper(
|
||||
private val whichItem: (RecyclerView.ViewHolder) -> VideoState?,
|
||||
private val onDeleteElement: (VideoState) -> Unit,
|
||||
private val onUIMoveElement: (VideoState, VideoState) -> Unit,
|
||||
private val onMoveElement: (VideoState, Int) -> Unit
|
||||
) : ItemTouchHelper.Callback() {
|
||||
|
||||
var dragEnabled: Boolean = true
|
||||
var swipeEnabled: Boolean = true
|
||||
private var index: Int? = null
|
||||
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
val item = whichItem(viewHolder) ?: return DISABLED_FLAG
|
||||
var dragEnabled = dragEnabled
|
||||
var swipeEnabled = swipeEnabled
|
||||
when (item) {
|
||||
is VideoState.InPending -> Unit
|
||||
is VideoState.Downloaded -> {
|
||||
dragEnabled = false
|
||||
}
|
||||
is VideoState.InProcess -> {
|
||||
dragEnabled = false
|
||||
swipeEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
val dragFlags = if (dragEnabled) ItemTouchHelper.UP or ItemTouchHelper.DOWN else DISABLED_FLAG
|
||||
val swipeFlags = if (swipeEnabled) ItemTouchHelper.START or ItemTouchHelper.END else DISABLED_FLAG
|
||||
return makeMovementFlags(dragFlags, swipeFlags)
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val dragged = whichItem(target)?.takeIf { it is VideoState.InPending } ?: return false
|
||||
val movedTo = whichItem(viewHolder)?.takeIf { it is VideoState.InPending } ?: return false
|
||||
onUIMoveElement(dragged, movedTo)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
whichItem(viewHolder)?.let { onDeleteElement(it) }
|
||||
}
|
||||
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
super.onSelectedChanged(viewHolder, actionState)
|
||||
(viewHolder as? MovingItemCallback)?.onMovingStart()
|
||||
index = when(actionState) {
|
||||
ItemTouchHelper.ACTION_STATE_DRAG -> viewHolder?.bindingAdapterPosition
|
||||
ItemTouchHelper.ACTION_STATE_IDLE -> index
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
super.clearView(recyclerView, viewHolder)
|
||||
(viewHolder as? MovingItemCallback)?.onMovingEnd()
|
||||
val videoState = whichItem(viewHolder) ?: return
|
||||
val startIndex = index ?: return
|
||||
|
||||
val endIndex = viewHolder.bindingAdapterPosition
|
||||
onMoveElement(videoState, endIndex - startIndex)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DISABLED_FLAG = 0
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <T> CoroutineScope.asLiveData(flow: Flow<T>): LiveData<T> {
|
||||
val liveData = MutableLiveData<T>()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
app:layoutDescription="@xml/queue_motion_description">
|
||||
|
||||
<View
|
||||
|
|
@ -91,6 +92,8 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_downloaded"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_background" />
|
||||
|
||||
</androidx.constraintlayout.motion.widget.MotionLayout>
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/medium_padding">
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@
|
|||
\n- Copy the link from TikTok into the input field.
|
||||
\n- Press start.
|
||||
\n- If error happens retry with the refresh button.
|
||||
\n\n<b>Note</b>: Clicking on video will open the Gallery app.</string>
|
||||
\n\n<b>Note</b>:
|
||||
\n- Clicking on video will open the Gallery app.
|
||||
\n- Clicking on a video URL will open in TikTok.
|
||||
\n- Swiping away a video will remove it from the list.
|
||||
\n- When not loading, Pending videos can be reordered by dragging.</string>
|
||||
<string name="how_to_use_from_tiktok">How To Use From TikTok</string>
|
||||
<string name="how_to_use_from_tiktok_description">- In TikTok press share.
|
||||
\n- Select Other.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.mockito.kotlin.whenever
|
|||
import java.io.InputStream
|
||||
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@Timeout(value = 2)
|
||||
class VideoDownloadedLocalSourceTest {
|
||||
|
||||
|
|
@ -195,6 +196,50 @@ class VideoDownloadedLocalSourceTest {
|
|||
Assertions.assertEquals(expected, actual.await())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_two_videos_WHEN_deleting_THEN_observer_is_updated() = runBlocking {
|
||||
val videoInSavingIntoFile1 = VideoInSavingIntoFile(
|
||||
id = "id1",
|
||||
url = "alma1",
|
||||
contentType = VideoInSavingIntoFile.ContentType("a1", "b1"),
|
||||
byteStream = FalseInputStream()
|
||||
)
|
||||
val videoInSavingIntoFile2 = VideoInSavingIntoFile(
|
||||
id = "id2",
|
||||
url = "alma2",
|
||||
contentType = VideoInSavingIntoFile.ContentType("a2", "b2"),
|
||||
byteStream = FalseInputStream()
|
||||
)
|
||||
whenever(mockVerifyFileForUriExists.invoke(anyOrNull())).doReturn(true)
|
||||
whenever(mockSaveVideoFile.invoke(anyOrNull(), anyOrNull(), anyOrNull())).then {
|
||||
"uri: " + (it.arguments[1] as String)
|
||||
}
|
||||
val expectedModel1 = VideoDownloaded(id = "id1", url = "alma1", uri = "uri: id1.b1")
|
||||
val expectedModel2 = VideoDownloaded(id = "id2", url = "alma2", uri = "uri: id2.b2")
|
||||
val expected = listOf(
|
||||
emptyList(),
|
||||
listOf(expectedModel1),
|
||||
listOf(expectedModel2, expectedModel1),
|
||||
listOf(expectedModel2),
|
||||
emptyList(),
|
||||
)
|
||||
val actual = async(coroutineContext) { sut.savedVideos.take(expected.size).toList() }
|
||||
|
||||
yield()
|
||||
sut.saveVideo(videoInSavingIntoFile1)
|
||||
delay(100)
|
||||
yield()
|
||||
sut.saveVideo(videoInSavingIntoFile2)
|
||||
yield()
|
||||
|
||||
sut.removeVideo(expectedModel1)
|
||||
yield()
|
||||
sut.removeVideo(expectedModel2)
|
||||
yield()
|
||||
|
||||
Assertions.assertIterableEquals(expected, actual.await())
|
||||
}
|
||||
|
||||
class FalseInputStream : InputStream() {
|
||||
override fun read(): Int = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import org.junit.jupiter.api.BeforeEach
|
|||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@Timeout(value = 2)
|
||||
class VideoInPendingLocalSourceTest {
|
||||
|
||||
|
|
@ -76,11 +76,10 @@ class VideoInPendingLocalSourceTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_observing_PendingVideos_WHEN_2_video_marked_as_pending_THEN_both_of_them_are_sent_out_in_correct_reverse_order() =
|
||||
runBlocking<Unit> {
|
||||
fun GIVEN_observing_PendingVideos_WHEN_2_video_marked_as_pending_THEN_both_of_them_are_sent_out_in_correct_order() = runBlocking<Unit> {
|
||||
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()
|
||||
|
|
@ -94,4 +93,92 @@ class VideoInPendingLocalSourceTest {
|
|||
Assertions.assertEquals(expected, actual.await())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_2_videos_WHEN_moving_first_one_down_by_one_THEN_it_is_moved_properly() = runBlocking<Unit> {
|
||||
val videoInPending1 = VideoInPending("id1", "alma1")
|
||||
val videoInPending2 = VideoInPending("id2", "alma2")
|
||||
|
||||
val expected = listOf(videoInPending2, videoInPending1)
|
||||
|
||||
yield()
|
||||
sut.saveUrlIntoQueue(videoInPending1)
|
||||
delay(10)
|
||||
sut.saveUrlIntoQueue(videoInPending2)
|
||||
delay(10)
|
||||
|
||||
sut.moveBy(videoInPending1, 1)
|
||||
|
||||
Assertions.assertEquals(expected, sut.pendingVideos.first())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_2_videos_WHEN_moving_second_one_up_by_one_THEN_it_is_moved_properly() = runBlocking<Unit> {
|
||||
val videoInPending1 = VideoInPending("id1", "alma1")
|
||||
val videoInPending2 = VideoInPending("id2", "alma2")
|
||||
|
||||
val expected = listOf(videoInPending2, videoInPending1)
|
||||
|
||||
yield()
|
||||
sut.saveUrlIntoQueue(videoInPending1)
|
||||
delay(10)
|
||||
sut.saveUrlIntoQueue(videoInPending2)
|
||||
delay(10)
|
||||
|
||||
sut.moveBy(videoInPending2, -1)
|
||||
|
||||
Assertions.assertEquals(expected, sut.pendingVideos.first())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_3_videos_WHEN_moving_first_moving_around_is_moved_properly() = runBlocking<Unit> {
|
||||
val videoInPending1 = VideoInPending("id1", "alma1")
|
||||
val videoInPending2 = VideoInPending("id2", "alma2")
|
||||
val videoInPending3 = VideoInPending("id3", "alma3")
|
||||
|
||||
val expected = listOf(
|
||||
listOf(videoInPending1, videoInPending2, videoInPending3), // start
|
||||
listOf(videoInPending2, videoInPending1, videoInPending3), // down 1
|
||||
listOf(videoInPending2, videoInPending3, videoInPending1), // down 1
|
||||
listOf(videoInPending2, videoInPending1, videoInPending3), // ++up 1
|
||||
listOf(videoInPending1, videoInPending2, videoInPending3), // ++up 1
|
||||
listOf(videoInPending2, videoInPending3, videoInPending1), // down 2
|
||||
listOf(videoInPending2, videoInPending1, videoInPending3), // ++up 1
|
||||
listOf(videoInPending1, videoInPending2, videoInPending3), // ++up 1
|
||||
listOf(videoInPending2, videoInPending3, videoInPending1), // down 2
|
||||
listOf(videoInPending1, videoInPending2, videoInPending3), // ++up 1
|
||||
)
|
||||
|
||||
yield()
|
||||
sut.saveUrlIntoQueue(videoInPending1)
|
||||
delay(10)
|
||||
sut.saveUrlIntoQueue(videoInPending2)
|
||||
delay(10)
|
||||
sut.saveUrlIntoQueue(videoInPending3)
|
||||
|
||||
val actual = async(coroutineContext) {
|
||||
sut.pendingVideos.take(expected.size).toList()
|
||||
}
|
||||
yield()
|
||||
|
||||
sut.moveBy(videoInPending1, 1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, 1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, -1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, -1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, 2)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, -1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, -1)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, 2)
|
||||
yield()
|
||||
sut.moveBy(videoInPending1, -2)
|
||||
yield()
|
||||
|
||||
Assertions.assertIterableEquals(expected, actual.await())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package org.fnives.tiktokdownloader.data.usecase
|
||||
|
||||
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoInteractions
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@Timeout(value = 2)
|
||||
class MoveVideoInQueueUseCaseTest {
|
||||
|
||||
private lateinit var sut: MoveVideoInQueueUseCase
|
||||
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockVideoInPendingLocalSource = mock()
|
||||
sut = MoveVideoInQueueUseCase(mockVideoInPendingLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun WHEN_no_action_THEN_no_delegation() {
|
||||
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_video_and_position_change_WHEN_invoked_THEN_delegated() {
|
||||
val video = VideoInPending("1", "url1")
|
||||
sut.invoke(video, 100)
|
||||
|
||||
verify(mockVideoInPendingLocalSource).moveBy(video, 100)
|
||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
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.VideoDownloaded
|
||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Timeout
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoInteractions
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@Timeout(value = 2)
|
||||
class RemoveVideoFromQueueUseCaseTest {
|
||||
|
||||
private lateinit var sut: RemoveVideoFromQueueUseCase
|
||||
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
||||
private lateinit var mockVideoDownloadedLocalSource: VideoDownloadedLocalSource
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockVideoInPendingLocalSource = mock()
|
||||
mockVideoDownloadedLocalSource = mock()
|
||||
sut = RemoveVideoFromQueueUseCase(mockVideoInPendingLocalSource, mockVideoDownloadedLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun WHEN_no_action_THEN_no_delegation() {
|
||||
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_pending_video_WHEN_invoked_THEN_delegated() {
|
||||
val video = VideoState.InPending(VideoInPending("1", "url1"))
|
||||
sut.invoke(video)
|
||||
|
||||
verify(mockVideoInPendingLocalSource).removeVideoFromQueue(video.videoInPending)
|
||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_downloaded_video_WHEN_invoked_THEN_delegated() {
|
||||
val video = VideoState.Downloaded(VideoDownloaded("1", "url1", "img1"))
|
||||
sut.invoke(video)
|
||||
|
||||
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||
verify(mockVideoDownloadedLocalSource).removeVideo(video.videoDownloaded)
|
||||
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_in_process_WHEN_invoked_THEN_no_delegation() {
|
||||
val video = VideoState.InProcess(VideoInProgress("1", "url1"))
|
||||
sut.invoke(video)
|
||||
|
||||
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,13 @@ package org.fnives.tiktokdownloader.ui.main.queue
|
|||
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||
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.VideoDownloadingProcessorUseCase
|
||||
import org.fnives.tiktokdownloader.helper.junit.rule.InstantExecutorExtension
|
||||
|
|
@ -28,7 +32,9 @@ class QueueViewModelTest {
|
|||
private lateinit var stateOfVideosFlow: MutableSharedFlow<List<VideoState>>
|
||||
private lateinit var mockStateOfVideosObservableUseCase: StateOfVideosObservableUseCase
|
||||
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
||||
private lateinit var mockRemoveVideoFromQueueUseCase: RemoveVideoFromQueueUseCase
|
||||
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
||||
private lateinit var mockMoveVideoInQueueUseCase: MoveVideoInQueueUseCase
|
||||
private lateinit var sut: QueueViewModel
|
||||
|
||||
@BeforeEach
|
||||
|
|
@ -37,8 +43,16 @@ class QueueViewModelTest {
|
|||
mockStateOfVideosObservableUseCase = mock()
|
||||
whenever(mockStateOfVideosObservableUseCase.invoke()).doReturn(stateOfVideosFlow)
|
||||
mockAddVideoToQueueUseCase = mock()
|
||||
mockRemoveVideoFromQueueUseCase = mock()
|
||||
mockVideoDownloadingProcessorUseCase = mock()
|
||||
sut = QueueViewModel(mockStateOfVideosObservableUseCase, mockAddVideoToQueueUseCase, mockVideoDownloadingProcessorUseCase)
|
||||
mockMoveVideoInQueueUseCase = mock()
|
||||
sut = QueueViewModel(
|
||||
mockStateOfVideosObservableUseCase,
|
||||
mockAddVideoToQueueUseCase,
|
||||
mockRemoveVideoFromQueueUseCase,
|
||||
mockVideoDownloadingProcessorUseCase,
|
||||
mockMoveVideoInQueueUseCase
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -48,6 +62,8 @@ class QueueViewModelTest {
|
|||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -60,6 +76,8 @@ class QueueViewModelTest {
|
|||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -77,6 +95,8 @@ class QueueViewModelTest {
|
|||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -89,6 +109,8 @@ class QueueViewModelTest {
|
|||
verifyNoMoreInteractions(mockAddVideoToQueueUseCase)
|
||||
verify(mockVideoDownloadingProcessorUseCase, times(1)).fetchVideoInState()
|
||||
verifyNoMoreInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -103,6 +125,8 @@ class QueueViewModelTest {
|
|||
verifyNoMoreInteractions(mockAddVideoToQueueUseCase)
|
||||
verify(mockVideoDownloadingProcessorUseCase, times(2)).fetchVideoInState()
|
||||
verifyNoMoreInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -125,4 +149,86 @@ class QueueViewModelTest {
|
|||
.assertHistorySize(1)
|
||||
.assertValue(Event(QueueViewModel.NavigationEvent.OpenGallery("alma.com")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_inProgress_onElementDeleted_THEN_remove_called() {
|
||||
val arg = VideoState.InProcess(VideoInProgress("1","2"))
|
||||
sut.onElementDeleted(arg)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verify(mockRemoveVideoFromQueueUseCase, times(1)).invoke(arg)
|
||||
verifyNoMoreInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_pending_onElementDeleted_THEN_remove_called() {
|
||||
val arg = VideoState.InPending(VideoInPending("1","2"))
|
||||
sut.onElementDeleted(arg)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verify(mockRemoveVideoFromQueueUseCase, times(1)).invoke(arg)
|
||||
verifyNoMoreInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_download_onElementDeleted_THEN_remove_called() {
|
||||
val arg = VideoState.Downloaded(VideoDownloaded("1","2","3"))
|
||||
sut.onElementDeleted(arg)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verify(mockRemoveVideoFromQueueUseCase, times(1)).invoke(arg)
|
||||
verifyNoMoreInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_in_progress_moved_THEN_nothing_changes() {
|
||||
val arg = VideoState.InProcess(VideoInProgress("1","2"))
|
||||
sut.onElementMoved(arg, 100)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_done_moved_THEN_nothing_changes() {
|
||||
val arg = VideoState.Downloaded(VideoDownloaded("1","2","3"))
|
||||
sut.onElementMoved(arg, 100)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verifyNoInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun GIVEN_initialized_WHEN_pending_moved_THEN_nothing_changes() {
|
||||
val arg = VideoState.InPending(VideoInPending("1","2"))
|
||||
sut.onElementMoved(arg, 100)
|
||||
|
||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||
verifyNoInteractions(mockRemoveVideoFromQueueUseCase)
|
||||
verify(mockMoveVideoInQueueUseCase, times(1)).invoke(arg.videoInPending, 100)
|
||||
verifyNoMoreInteractions(mockMoveVideoInQueueUseCase)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue