forked from recloudstream/cloudstream
download child
This commit is contained in:
parent
ddd722f19e
commit
3bf76a9563
11 changed files with 292 additions and 9 deletions
|
@ -0,0 +1,86 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.download
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
|
import androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
import kotlinx.android.synthetic.main.download_child_episode.view.*
|
||||||
|
|
||||||
|
data class VisualDownloadChildCached(
|
||||||
|
val currentBytes: Long,
|
||||||
|
val totalBytes: Long,
|
||||||
|
val data: VideoDownloadHelper.DownloadEpisodeCached,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DownloadClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadEpisodeCached)
|
||||||
|
|
||||||
|
class DownloadChildAdapter(
|
||||||
|
var cardList: List<VisualDownloadChildCached>,
|
||||||
|
private val clickCallback: (DownloadClickEvent) -> Unit,
|
||||||
|
) :
|
||||||
|
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
return DownloadChildViewHolder(
|
||||||
|
LayoutInflater.from(parent.context).inflate(R.layout.download_child_episode, parent, false),
|
||||||
|
clickCallback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
when (holder) {
|
||||||
|
is DownloadChildViewHolder -> {
|
||||||
|
holder.bind(cardList[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return cardList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadChildViewHolder
|
||||||
|
constructor(
|
||||||
|
itemView: View,
|
||||||
|
private val clickCallback: (DownloadClickEvent) -> Unit,
|
||||||
|
) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
private val title: TextView = itemView.download_child_episode_text
|
||||||
|
private val extraInfo: TextView = itemView.download_child_episode_text_extra
|
||||||
|
private val holder: CardView = itemView.download_child_episode_holder
|
||||||
|
private val progressBar: ContentLoadingProgressBar = itemView.download_child_episode_progress
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
fun bind(card: VisualDownloadChildCached) {
|
||||||
|
val d = card.data
|
||||||
|
|
||||||
|
val posDur = itemView.context.getViewPos(d.id)
|
||||||
|
if (posDur != null) {
|
||||||
|
val visualPos = posDur.fixVisual()
|
||||||
|
progressBar.max = (visualPos.duration / 1000).toInt()
|
||||||
|
progressBar.progress = (visualPos.position / 1000).toInt()
|
||||||
|
progressBar.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
progressBar.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
title.text = d.name ?: "Episode ${d.episode}" //TODO FIX
|
||||||
|
val totalMbString = "%.1f".format(card.totalBytes / 1000000f)
|
||||||
|
val currentMbString = "%.1f".format(card.currentBytes / 1000000f)
|
||||||
|
|
||||||
|
extraInfo.text =
|
||||||
|
"${currentMbString}MB / ${totalMbString}MB"
|
||||||
|
|
||||||
|
holder.setOnClickListener {
|
||||||
|
clickCallback.invoke(DownloadClickEvent(0, d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.download
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
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.utils.Coroutines.main
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getKeys
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||||
|
import kotlinx.android.synthetic.main.fragment_child_downloads.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class DownloadChildFragment : Fragment() {
|
||||||
|
companion object {
|
||||||
|
fun newInstance(headerName: String, folder: String) =
|
||||||
|
DownloadChildFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putString("folder", folder)
|
||||||
|
putString("name", headerName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_child_downloads, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateList(folder: String) = main {
|
||||||
|
val data = withContext(Dispatchers.IO) { context?.getKeys(folder) }
|
||||||
|
if (data == null) {
|
||||||
|
activity?.onBackPressed() // TODO FIX
|
||||||
|
return@main
|
||||||
|
}
|
||||||
|
val eps = withContext(Dispatchers.IO) {
|
||||||
|
data.mapNotNull { key ->
|
||||||
|
context?.getKey<VideoDownloadHelper.DownloadEpisodeCached>(key)
|
||||||
|
}.mapNotNull {
|
||||||
|
val info = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), it.id)
|
||||||
|
?: return@mapNotNull null
|
||||||
|
VisualDownloadChildCached(info.fileLength, info.totalBytes, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (eps.isEmpty()) {
|
||||||
|
activity?.onBackPressed()
|
||||||
|
return@main
|
||||||
|
}
|
||||||
|
|
||||||
|
(download_child_list?.adapter as DownloadChildAdapter? ?: return@main).cardList = eps
|
||||||
|
download_child_list?.adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val folder = arguments?.getString("folder")
|
||||||
|
val name = arguments?.getString("name")
|
||||||
|
if (folder == null) {
|
||||||
|
activity?.onBackPressed() // TODO FIX
|
||||||
|
return
|
||||||
|
}
|
||||||
|
context?.fixPaddingStatusbar(download_child_root)
|
||||||
|
|
||||||
|
|
||||||
|
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
||||||
|
DownloadChildAdapter(
|
||||||
|
ArrayList(),
|
||||||
|
) { click ->
|
||||||
|
if (click.action == 0) { // TODO PLAY
|
||||||
|
val info =
|
||||||
|
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(requireContext(), click.data.id)
|
||||||
|
?: return@DownloadChildAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
download_child_list.adapter = adapter
|
||||||
|
download_child_list.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
||||||
|
updateList(folder)
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,11 @@ import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.UIHelper.fixPaddingStatusbar
|
||||||
|
import com.lagradost.cloudstream3.isMovieType
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.result.ResultFragment
|
||||||
|
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
|
||||||
import kotlinx.android.synthetic.main.fragment_downloads.*
|
import kotlinx.android.synthetic.main.fragment_downloads.*
|
||||||
import kotlinx.android.synthetic.main.fragment_result.*
|
import kotlinx.android.synthetic.main.fragment_result.*
|
||||||
|
|
||||||
|
@ -70,7 +74,16 @@ class DownloadFragment : Fragment() {
|
||||||
DownloadHeaderAdapter(
|
DownloadHeaderAdapter(
|
||||||
ArrayList(),
|
ArrayList(),
|
||||||
) { click ->
|
) { click ->
|
||||||
|
if(click.data.type.isMovieType()) {
|
||||||
|
//TODO MOVIE
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
|
||||||
|
activity?.supportFragmentManager?.beginTransaction()
|
||||||
|
?.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim, R.anim.pop_enter, R.anim.pop_exit)
|
||||||
|
?.add(R.id.homeRoot, DownloadChildFragment.newInstance(click.data.name, folder))
|
||||||
|
?.commit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
download_list.adapter = adapter
|
download_list.adapter = adapter
|
||||||
download_list.layoutManager = GridLayoutManager(context, 1)
|
download_list.layoutManager = GridLayoutManager(context, 1)
|
||||||
|
|
|
@ -23,7 +23,6 @@ data class VisualDownloadHeaderCached(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadHeaderCached)
|
data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadHeaderCached)
|
||||||
data class DownloadClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadEpisodeCached)
|
|
||||||
|
|
||||||
class DownloadHeaderAdapter(
|
class DownloadHeaderAdapter(
|
||||||
var cardList: List<VisualDownloadHeaderCached>,
|
var cardList: List<VisualDownloadHeaderCached>,
|
||||||
|
|
|
@ -52,6 +52,7 @@ import com.lagradost.cloudstream3.ui.player.PlayerFragment
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
import com.lagradost.cloudstream3.utils.CastHelper.startCast
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||||
|
|
||||||
|
@ -396,7 +397,7 @@ class ResultFragment : Fragment() {
|
||||||
|
|
||||||
val epData = episodeClick.data
|
val epData = episodeClick.data
|
||||||
ctx.setKey(
|
ctx.setKey(
|
||||||
DOWNLOAD_EPISODE_CACHE,
|
getFolderName(DOWNLOAD_EPISODE_CACHE, (currentId ?: return@let).toString()), // 3 deep folder for faster acess
|
||||||
epData.id.toString(),
|
epData.id.toString(),
|
||||||
VideoDownloadHelper.DownloadEpisodeCached(
|
VideoDownloadHelper.DownloadEpisodeCached(
|
||||||
epData.name,
|
epData.name,
|
||||||
|
|
|
@ -9,9 +9,18 @@ const val VIDEO_POS_DUR = "video_pos_dur"
|
||||||
const val RESULT_WATCH_STATE = "result_watch_state"
|
const val RESULT_WATCH_STATE = "result_watch_state"
|
||||||
const val RESULT_SEASON = "result_season"
|
const val RESULT_SEASON = "result_season"
|
||||||
|
|
||||||
data class PosDur(val position: Long, val duration: Long)
|
|
||||||
|
|
||||||
object DataStoreHelper {
|
object DataStoreHelper {
|
||||||
|
data class PosDur(val position: Long, val duration: Long)
|
||||||
|
fun PosDur.fixVisual(): PosDur {
|
||||||
|
if (duration <= 0) return PosDur(0, duration)
|
||||||
|
val percentage = position * 100 / duration
|
||||||
|
if (percentage <= 1) return PosDur(0, duration)
|
||||||
|
if (percentage <= 5) return PosDur(5 * duration / 100, duration)
|
||||||
|
if (percentage >= 95) return PosDur(duration, duration)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
|
||||||
|
|
||||||
fun Context.setViewPos(id: Int?, pos: Long, dur: Long) {
|
fun Context.setViewPos(id: Int?, pos: Long, dur: Long) {
|
||||||
|
|
|
@ -22,9 +22,6 @@ import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.UIHelper.colorFromAttribute
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.services.RESTART_NONE
|
|
||||||
import com.lagradost.cloudstream3.services.START_VALUE_KEY
|
|
||||||
import com.lagradost.cloudstream3.services.VideoDownloadKeepAliveService
|
|
||||||
import com.lagradost.cloudstream3.services.VideoDownloadService
|
import com.lagradost.cloudstream3.services.VideoDownloadService
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||||
|
|
73
app/src/main/res/layout/download_child_episode.xml
Normal file
73
app/src/main/res/layout/download_child_episode.xml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
app:cardCornerRadius="@dimen/roundedImageRadius"
|
||||||
|
app:cardBackgroundColor="@color/transparent"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
android:id="@+id/download_child_episode_holder"
|
||||||
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:layout_marginBottom="5dp"
|
||||||
|
>
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
android:layout_marginBottom="-1.5dp"
|
||||||
|
android:id="@+id/download_child_episode_progress"
|
||||||
|
android:progressTint="@color/colorPrimary"
|
||||||
|
android:progressBackgroundTint="@color/colorPrimary"
|
||||||
|
style="@android:style/Widget.Material.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
tools:progress="50"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:layout_height="5dp">
|
||||||
|
</androidx.core.widget.ContentLoadingProgressBar>
|
||||||
|
<GridLayout android:layout_width="match_parent" android:layout_height="match_parent">
|
||||||
|
<ImageView
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:id="@+id/download_child_episode_play"
|
||||||
|
android:src="@drawable/ic_baseline_play_arrow_24"
|
||||||
|
android:contentDescription="@string/episode_play_descript"/>
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/download_child_episode_text"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_gravity="center_vertical" android:gravity="center_vertical" tools:text="Episode 1"
|
||||||
|
android:textColor="@color/textColor" android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
</TextView>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/download_child_episode_text_extra"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
tools:text="128MB / 237MB"
|
||||||
|
android:textColor="@color/grayTextColor"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
</TextView>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:id="@+id/download_child_episode_download"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:src="@drawable/netflix_download"
|
||||||
|
android:contentDescription="@string/download_descript"/>
|
||||||
|
</GridLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
|
@ -17,7 +17,6 @@
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
|
<!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
|
|
||||||
android:layout_width="70dp"
|
android:layout_width="70dp"
|
||||||
android:layout_height="104dp"
|
android:layout_height="104dp"
|
||||||
>
|
>
|
||||||
|
|
19
app/src/main/res/layout/fragment_child_downloads.xml
Normal file
19
app/src/main/res/layout/fragment_child_downloads.xml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
android:background="@color/bitDarkerGrayBackground"
|
||||||
|
android:id="@+id/download_child_root"
|
||||||
|
android:orientation="vertical"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.download.DownloadFragment">
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
tools:listitem="@layout/download_child_episode"
|
||||||
|
android:id="@+id/download_child_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
</LinearLayout>
|
|
@ -99,8 +99,8 @@
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:id="@+id/download_list"
|
android:id="@+id/download_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
tools:listitem="@layout/download_header_episode"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_no_downloads"
|
android:id="@+id/text_no_downloads"
|
||||||
|
|
Loading…
Reference in a new issue