From 3606d1ec054159c299dfd8ff9d4196ac9d3927b8 Mon Sep 17 00:00:00 2001 From: LagradOst Date: Sat, 24 Jul 2021 22:50:57 +0200 Subject: [PATCH] download stuff --- .../ui/download/DownloadButtonSetup.kt | 195 ++++++++++++++++++ .../ui/download/DownloadChildAdapter.kt | 13 +- .../ui/download/DownloadChildFragment.kt | 58 +----- .../cloudstream3/ui/result/EpisodeAdapter.kt | 38 +++- .../cloudstream3/ui/result/ResultFragment.kt | 12 +- .../utils/VideoDownloadManager.kt | 10 +- app/src/main/res/layout/result_episode.xml | 44 +++- .../main/res/layout/result_episode_large.xml | 44 +++- 8 files changed, 321 insertions(+), 93 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt new file mode 100644 index 00000000..25d3da28 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt @@ -0,0 +1,195 @@ +package com.lagradost.cloudstream3.ui.download + +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.app.Activity +import android.view.View +import android.view.animation.DecelerateInterpolator +import android.widget.ImageView +import android.widget.TextView +import androidx.core.widget.ContentLoadingProgressBar +import androidx.fragment.app.FragmentActivity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.UIHelper.popupMenuNoIcons +import com.lagradost.cloudstream3.ui.player.PlayerFragment +import com.lagradost.cloudstream3.ui.player.UriData +import com.lagradost.cloudstream3.utils.Coroutines +import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos +import com.lagradost.cloudstream3.utils.VideoDownloadHelper +import com.lagradost.cloudstream3.utils.VideoDownloadManager + +object DownloadButtonSetup { + fun handleDownloadClick(activity: Activity?, headerName: String?, click: DownloadClickEvent) { + val id = click.data.id + when (click.action) { + DOWNLOAD_ACTION_DELETE_FILE -> { + activity?.let { ctx -> + VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id) + } + } + DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> { + VideoDownloadManager.downloadEvent.invoke( + Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause) + ) + } + DOWNLOAD_ACTION_RESUME_DOWNLOAD -> { + activity?.let { ctx -> + val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id) + if (pkg != null) { + VideoDownloadManager.downloadFromResume(ctx, pkg) + } else { + VideoDownloadManager.downloadEvent.invoke( + Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume) + ) + } + } + } + DOWNLOAD_ACTION_PLAY_FILE -> { + activity?.let { act -> + val info = + VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(act, click.data.id) + ?: return + + (act as FragmentActivity).supportFragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.enter_anim, + R.anim.exit_anim, + R.anim.pop_enter, + R.anim.pop_exit + ) + .add( + R.id.homeRoot, + PlayerFragment.newInstance( + UriData( + info.path.toString(), + click.data.id, + headerName ?: "null", + click.data.episode, + click.data.season + ), + act.getViewPos(click.data.id)?.position ?: 0 + ) + ) + .commit() + } + } + } + } + + fun setUpButton( + setupCurrentBytes: Long?, + setupTotalBytes: Long?, + progressBar: ContentLoadingProgressBar, + downloadImage: ImageView, + textView: TextView?, + data: VideoDownloadHelper.DownloadEpisodeCached, + clickCallback: (DownloadClickEvent) -> Unit, + ) { + var lastState: VideoDownloadManager.DownloadType? = null + var currentBytes = setupCurrentBytes ?: 0 + var totalBytes = setupTotalBytes ?: 0 + var needImageUpdate = false + + fun changeDownloadImage(state: VideoDownloadManager.DownloadType) { + Coroutines.runOnMainThread { + lastState = state + if (currentBytes <= 0) needImageUpdate = true + val img = if (currentBytes > 0) when (state) { + VideoDownloadManager.DownloadType.IsPaused -> R.drawable.ic_baseline_play_arrow_24 + VideoDownloadManager.DownloadType.IsDownloading -> R.drawable.netflix_pause + else -> R.drawable.ic_baseline_delete_outline_24 + } else R.drawable.netflix_download + downloadImage?.setImageResource(img) + } + } + + @SuppressLint("SetTextI18n") + fun fixDownloadedBytes(setCurrentBytes: Long, setTotalBytes: Long, animate: Boolean) { + Coroutines.runOnMainThread { + currentBytes = setCurrentBytes + totalBytes = setTotalBytes + + if (currentBytes == 0L) { + changeDownloadImage(VideoDownloadManager.DownloadType.IsStopped) + textView?.visibility = View.GONE + progressBar?.visibility = View.GONE + } else { + if (lastState == VideoDownloadManager.DownloadType.IsStopped) { + changeDownloadImage(VideoDownloadManager.getDownloadState(data.id)) + } + textView?.visibility = View.VISIBLE + progressBar?.visibility = View.VISIBLE + val currentMbString = "%.1f".format(setCurrentBytes / 1000000f) + val totalMbString = "%.1f".format(setTotalBytes / 1000000f) + + textView?.text = + "${currentMbString}MB / ${totalMbString}MB" + + progressBar?.let { bar -> + bar.max = (setTotalBytes / 1000).toInt() + + if (animate) { + val animation: ObjectAnimator = ObjectAnimator.ofInt( + bar, + "progress", + bar.progress, + (setCurrentBytes / 1000).toInt() + ) + animation.duration = 500 + animation.setAutoCancel(true) + animation.interpolator = DecelerateInterpolator() + animation.start() + } else { + bar.progress = (setCurrentBytes / 1000).toInt() + } + } + } + } + } + + fixDownloadedBytes(currentBytes, totalBytes, false) + changeDownloadImage(VideoDownloadManager.getDownloadState(data.id)) + + VideoDownloadManager.downloadProgressEvent += { downloadData -> + if (data.id == downloadData.first) { + if (downloadData.second != currentBytes || downloadData.third != totalBytes) { // TO PREVENT WASTING UI TIME + fixDownloadedBytes(downloadData.second, downloadData.third, true) + } + } + } + + VideoDownloadManager.downloadStatusEvent += { downloadData -> + if (data.id == downloadData.first) { + if (lastState != downloadData.second || needImageUpdate) { // TO PREVENT WASTING UI TIME + changeDownloadImage(downloadData.second) + } + } + } + + downloadImage.setOnClickListener { + if (currentBytes <= 0) { + clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data)) + } else { + val list = arrayListOf( + Pair(DOWNLOAD_ACTION_DELETE_FILE, R.string.popup_delete_file), + ) + + // DON'T RESUME A DOWNLOADED FILE + if (lastState != VideoDownloadManager.DownloadType.IsDone && ((currentBytes * 100 / totalBytes) < 98)) { + list.add( + if (lastState == VideoDownloadManager.DownloadType.IsDownloading) + Pair(DOWNLOAD_ACTION_PAUSE_DOWNLOAD, R.string.popup_pause_download) + else + Pair(DOWNLOAD_ACTION_RESUME_DOWNLOAD, R.string.popup_resume_download) + ) + } + + it.popupMenuNoIcons( + list + ) { + clickCallback.invoke(DownloadClickEvent(itemId, data)) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt index ea30530a..73061721 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt @@ -28,6 +28,7 @@ const val DOWNLOAD_ACTION_PLAY_FILE = 0 const val DOWNLOAD_ACTION_DELETE_FILE = 1 const val DOWNLOAD_ACTION_RESUME_DOWNLOAD = 2 const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3 +const val DOWNLOAD_ACTION_DOWNLOAD = 4 data class VisualDownloadChildCached( val currentBytes: Long, @@ -89,6 +90,16 @@ class DownloadChildAdapter( } title.text = d.name ?: "Episode ${d.episode}" //TODO FIX + DownloadButtonSetup.setUpButton( + card.currentBytes, + card.totalBytes, + progressBarDownload, + downloadImage, + extraInfo, + card.data, + clickCallback + ) + /* val totalMbString = "%.1f".format(card.totalBytes / 1000000f) var lastState: VideoDownloadManager.DownloadType? = null @@ -175,7 +186,7 @@ class DownloadChildAdapter( ) { clickCallback.invoke(DownloadClickEvent(itemId, d)) } - } + }*/ } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt index e37d6c76..4cfb814d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.player.PlayerFragment import com.lagradost.cloudstream3.ui.player.UriData import com.lagradost.cloudstream3.utils.Coroutines.main @@ -35,63 +36,6 @@ class DownloadChildFragment : Fragment() { putString("name", headerName) } } - - fun handleDownloadClick(activity: Activity?, headerName: String?, click: DownloadClickEvent) { - val id = click.data.id - when (click.action) { - DOWNLOAD_ACTION_DELETE_FILE -> { - activity?.let { ctx -> - VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id) - } - } - DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> { - VideoDownloadManager.downloadEvent.invoke( - Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause) - ) - } - DOWNLOAD_ACTION_RESUME_DOWNLOAD -> { - activity?.let { ctx -> - val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id) - if (pkg != null) { - VideoDownloadManager.downloadFromResume(ctx, pkg) - } else { - VideoDownloadManager.downloadEvent.invoke( - Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume) - ) - } - } - } - DOWNLOAD_ACTION_PLAY_FILE -> { - activity?.let { act -> - val info = - VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(act, click.data.id) - ?: return - - (act as FragmentActivity).supportFragmentManager.beginTransaction() - .setCustomAnimations( - R.anim.enter_anim, - R.anim.exit_anim, - R.anim.pop_enter, - R.anim.pop_exit - ) - .add( - R.id.homeRoot, - PlayerFragment.newInstance( - UriData( - info.path.toString(), - click.data.id, - headerName ?: "null", - click.data.episode, - click.data.season - ), - act.getViewPos(click.data.id)?.position ?: 0 - ) - ) - .commit() - } - } - } - } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 566e0a3c..43843e5c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -12,6 +12,11 @@ import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.load.model.GlideUrl import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD +import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup +import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick +import com.lagradost.cloudstream3.ui.download.DownloadClickEvent +import com.lagradost.cloudstream3.utils.VideoDownloadHelper import kotlinx.android.synthetic.main.result_episode.view.episode_holder import kotlinx.android.synthetic.main.result_episode.view.episode_text import kotlinx.android.synthetic.main.result_episode_large.view.* @@ -37,8 +42,9 @@ data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) class EpisodeAdapter( var cardList: List, - val hasDownloadSupport : Boolean, + private val hasDownloadSupport: Boolean, private val clickCallback: (EpisodeClickEvent) -> Unit, + private val downloadClickCallback: (DownloadClickEvent) -> Unit, ) : RecyclerView.Adapter() { @LayoutRes @@ -57,7 +63,8 @@ class EpisodeAdapter( return CardViewHolder( LayoutInflater.from(parent.context).inflate(layout, parent, false), hasDownloadSupport, - clickCallback + clickCallback, + downloadClickCallback ) } @@ -76,15 +83,18 @@ class EpisodeAdapter( class CardViewHolder constructor( itemView: View, - private val hasDownloadSupport : Boolean, + private val hasDownloadSupport: Boolean, private val clickCallback: (EpisodeClickEvent) -> Unit, + private val downloadClickCallback: (DownloadClickEvent) -> Unit, ) : RecyclerView.ViewHolder(itemView) { private val episodeText: TextView = itemView.episode_text private val episodeRating: TextView? = itemView.episode_rating private val episodeDescript: TextView? = itemView.episode_descript private val episodeProgress: ContentLoadingProgressBar? = itemView.episode_progress private val episodePoster: ImageView? = itemView.episode_poster - private val episodeDownload: ImageView? = itemView.episode_download + + private val episodeDownloadBar: ContentLoadingProgressBar = itemView.result_episode_progress_downloaded + private val episodeDownloadImage: ImageView = itemView.result_episode_download private val episodeHolder = itemView.episode_holder @@ -134,11 +144,23 @@ class EpisodeAdapter( return@setOnLongClickListener true } - episodeDownload?.visibility = if(hasDownloadSupport) View.VISIBLE else View.GONE - - episodeDownload?.setOnClickListener { - clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) + if(hasDownloadSupport) { + DownloadButtonSetup.setUpButton( + null, null, episodeDownloadBar, episodeDownloadImage, null, + VideoDownloadHelper.DownloadEpisodeCached( + card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript + ) + ) { + if(it.action == DOWNLOAD_ACTION_DOWNLOAD) { + clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card)) + } else { + downloadClickCallback.invoke(it) + } + } } + + episodeDownloadImage.visibility = if (hasDownloadSupport) View.VISIBLE else View.GONE + episodeDownloadBar.visibility = if (hasDownloadSupport) View.VISIBLE else View.GONE } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 91c3c8da..6b50cbde 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -47,6 +47,8 @@ import com.lagradost.cloudstream3.UIHelper.requestRW import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD +import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.player.PlayerData import com.lagradost.cloudstream3.ui.player.PlayerFragment import com.lagradost.cloudstream3.utils.* @@ -625,9 +627,13 @@ class ResultFragment : Fragment() { EpisodeAdapter( ArrayList(), api.hasDownloadSupport, - ) { episodeClick -> - handleAction(episodeClick) - } + { episodeClick -> + handleAction(episodeClick) + }, + { downloadClickEvent -> + handleDownloadClick(activity, currentHeaderName, downloadClickEvent) + } + ) result_episodes.adapter = adapter result_episodes.layoutManager = GridLayoutManager(context, 1) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt index 25d140af..f42dde77 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -144,7 +144,7 @@ object VideoDownloadManager { val downloadStatus = HashMap() val downloadStatusEvent = Event>() val downloadEvent = Event>() - val downloadProgressEvent = Event>() + val downloadProgressEvent = Event>() val downloadQueue = LinkedList() private var hasCreatedNotChanel = false @@ -542,7 +542,7 @@ object VideoDownloadManager { try { downloadStatus[ep.id] = type downloadStatusEvent.invoke(Pair(ep.id, type)) - downloadProgressEvent.invoke(Pair(ep.id, bytesDownloaded)) + downloadProgressEvent.invoke(Triple(ep.id, bytesDownloaded, bytesTotal)) } catch (e: Exception) { // IDK MIGHT ERROR } @@ -626,13 +626,15 @@ object VideoDownloadManager { // RETURN MESSAGE return when { isFailed -> { + downloadProgressEvent.invoke(Triple(id, 0, 0)) ERROR_CONNECTION_ERROR } isStopped -> { + downloadProgressEvent.invoke(Triple(id, 0, 0)) deleteFile() } else -> { - downloadProgressEvent.invoke(Pair(id, bytesDownloaded)) + downloadProgressEvent.invoke(Triple(id, bytesDownloaded, bytesTotal)) isDone = true updateNotification() SUCCESS_DOWNLOAD_DONE @@ -715,6 +717,8 @@ object VideoDownloadManager { private fun deleteFile(context: Context, id: Int): Boolean { downloadEvent.invoke(Pair(id, DownloadActionType.Stop)) + downloadProgressEvent.invoke(Triple(id, 0, 0)) + downloadStatusEvent.invoke(Pair(id, DownloadType.IsStopped)) val info = context.getKey(KEY_DOWNLOAD_INFO, id.toString()) ?: return false if (isScopedStorage()) { diff --git a/app/src/main/res/layout/result_episode.xml b/app/src/main/res/layout/result_episode.xml index bb116643..e281003b 100644 --- a/app/src/main/res/layout/result_episode.xml +++ b/app/src/main/res/layout/result_episode.xml @@ -61,16 +61,38 @@ android:textColor="@color/textColor" android:layout_width="wrap_content" android:layout_height="match_parent"> - + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/result_episode_large.xml b/app/src/main/res/layout/result_episode_large.xml index 56434537..8f00b63a 100644 --- a/app/src/main/res/layout/result_episode_large.xml +++ b/app/src/main/res/layout/result_episode_large.xml @@ -76,16 +76,40 @@ android:layout_height="wrap_content"> - + + + + +