download stuff

This commit is contained in:
LagradOst 2021-07-24 22:50:57 +02:00
parent 472a9d678a
commit 3606d1ec05
8 changed files with 321 additions and 93 deletions

View file

@ -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))
}
}
}
}
}

View file

@ -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))
}
}
}*/
}
}
}

View file

@ -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? {

View file

@ -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<ResultEpisode>,
val hasDownloadSupport : Boolean,
private val hasDownloadSupport: Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@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
}
}
}

View file

@ -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)

View file

@ -144,7 +144,7 @@ object VideoDownloadManager {
val downloadStatus = HashMap<Int, DownloadType>()
val downloadStatusEvent = Event<Pair<Int, DownloadType>>()
val downloadEvent = Event<Pair<Int, DownloadActionType>>()
val downloadProgressEvent = Event<Pair<Int, Long>>()
val downloadProgressEvent = Event<Triple<Int, Long, Long>>()
val downloadQueue = LinkedList<DownloadResumePackage>()
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<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return false
if (isScopedStorage()) {

View file

@ -61,16 +61,38 @@
android:textColor="@color/textColor" android:layout_width="wrap_content"
android:layout_height="match_parent">
</TextView>
<ImageView
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:layout_width="30dp"
android:id="@+id/episode_download"
android:background="?selectableItemBackgroundBorderless"
android:layout_gravity="center_vertical|end"
android:src="@drawable/netflix_download"
android:contentDescription="@string/download_descript"/>
<FrameLayout
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:id="@+id/result_episode_progress_downloaded"
android:indeterminate="false"
android:progressDrawable="@drawable/circular_progress_bar"
android:background="@drawable/circle_shape"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:layout_margin="5dp"
android:layout_gravity="end|center_vertical"
android:progress="0"
android:visibility="visible"
/>
<ImageView
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/result_episode_download"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download_descript"/>
</FrameLayout>
</GridLayout>
</androidx.cardview.widget.CardView>

View file

@ -76,16 +76,40 @@
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
<ImageView
android:visibility="visible"
android:layout_marginStart="-40dp"
android:layout_height="match_parent"
android:layout_width="30dp"
android:id="@+id/episode_download"
android:background="?selectableItemBackgroundBorderless"
android:layout_gravity="center_vertical|end"
android:src="@drawable/netflix_download"
android:contentDescription="@string/download_descript"/>
<FrameLayout
android:layout_marginStart="-50dp"
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:id="@+id/result_episode_progress_downloaded"
android:indeterminate="false"
android:progressDrawable="@drawable/circular_progress_bar"
android:background="@drawable/circle_shape"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:layout_margin="5dp"
android:layout_gravity="end|center_vertical"
android:progress="0"
android:visibility="visible"
/>
<ImageView
android:visibility="visible"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_height="match_parent"
android:padding="2dp"
android:layout_width="30dp"
android:id="@+id/result_episode_download"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download_descript"/>
</FrameLayout>
</LinearLayout>
<TextView
android:paddingTop="10dp"