Compare commits

...

7 Commits

Author SHA1 Message Date
reduplicated a1165344d3 download UI fixes 2022-10-30 22:41:26 +01:00
reduplicated fa399cd350 play video and delete stuff 2022-10-30 02:35:37 +01:00
reduplicated 6a721941ac set keys things 2022-10-29 03:50:59 +02:00
reduplicated 572aa6de3e shit not working due to android storage fuckery 2022-09-23 22:05:06 +02:00
reduplicated 45ea1a8d8e testing movies 2022-09-22 00:23:26 +02:00
reduplicated 63e4e670c0 episode test 2022-09-18 13:25:17 +02:00
reduplicated 266a511cd7 testing aria2c UI 2022-09-18 00:05:28 +02:00
28 changed files with 1278 additions and 954 deletions

View File

@ -199,6 +199,9 @@ dependencies {
// Library/extensions searching with Levenshtein distance // Library/extensions searching with Levenshtein distance
implementation 'me.xdrop:fuzzywuzzy:1.4.0' implementation 'me.xdrop:fuzzywuzzy:1.4.0'
// aria2c downloader
implementation 'com.github.LagradOst:Aria2cButton:v0.0.6'
} }
task androidSourcesJar(type: Jar) { task androidSourcesJar(type: Jar) {

View File

@ -17,6 +17,8 @@ import androidx.appcompat.widget.SearchView
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastSession import com.google.android.gms.cast.framework.CastSession
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.download.Aria2cHelper.removeMetadata
import com.lagradost.cloudstream3.ui.download.Aria2cHelper.saveMetadata
import com.lagradost.cloudstream3.ui.player.PlayerEventType import com.lagradost.cloudstream3.ui.player.PlayerEventType
import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateTv
@ -25,8 +27,13 @@ import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.fetchbutton.aria2c.Aria2Settings
import com.lagradost.fetchbutton.aria2c.Aria2Starter
import com.lagradost.fetchbutton.aria2c.DownloadListener
import com.lagradost.fetchbutton.aria2c.DownloadStatusTell
import org.schabi.newpipe.extractor.NewPipe import org.schabi.newpipe.extractor.NewPipe
import java.util.* import java.util.*
import kotlin.concurrent.thread
object CommonActivity { object CommonActivity {
@MainThread @MainThread
@ -129,6 +136,43 @@ object CommonActivity {
act.updateLocale() act.updateLocale()
act.updateTv() act.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance()) NewPipe.init(DownloaderTestImpl.getInstance())
DownloadListener.mainListener = { (data, metadata) ->
//TODO FIX
DownloadListener.sessionGidToId[data.gid]?.let { id ->
if (metadata.status == DownloadStatusTell.Removed
|| metadata.status == DownloadStatusTell.Error
|| metadata.status == DownloadStatusTell.Waiting
|| metadata.status == null) {
removeMetadata(id)
} else {
saveMetadata(id, metadata)
}
/*val mainpath = metadata.items[0].files[0].path
AcraApplication.setKey(
VideoDownloadManager.KEY_DOWNLOAD_INFO,
id.toString(),
VideoDownloadManager.DownloadedFileInfo(
metadata.totalLength,
relativePath ?: "",
,
basePath = basePath.second
)
)*/
}
}
thread {
Aria2Starter.start(
act,
Aria2Settings(
"1337", //UUID.randomUUID().toString()
4337,
act.filesDir.path + "/download", //"/storage/emulated/0/Download",//
null//"${act.filesDir.path}/session"
)
)
}
} }
private fun Activity.enterPIPMode() { private fun Activity.enterPIPMode() {

View File

@ -10,10 +10,10 @@ import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.NavController import androidx.navigation.NavController
@ -107,6 +107,8 @@ const val VLC_EXTRA_POSITION_OUT = "extra_position"
const val VLC_EXTRA_DURATION_OUT = "extra_duration" const val VLC_EXTRA_DURATION_OUT = "extra_duration"
const val VLC_LAST_ID_KEY = "vlc_last_open_id" const val VLC_LAST_ID_KEY = "vlc_last_open_id"
const val DOWNLOAD_COUNT_KEY = "download_badge_count"
// Short name for requests client to make it nicer to use // Short name for requests client to make it nicer to use
var app = Requests(responseParser = object : ResponseParser { var app = Requests(responseParser = object : ResponseParser {
@ -150,7 +152,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
* @return true if the str has launched an app task (be it successful or not) * @return true if the str has launched an app task (be it successful or not)
* @param isWebview does not handle providers and opening download page if true. Can still add repos and login. * @param isWebview does not handle providers and opening download page if true. Can still add repos and login.
* */ * */
fun handleAppIntentUrl(activity: FragmentActivity?, str: String?, isWebview: Boolean): Boolean = fun handleAppIntentUrl(
activity: FragmentActivity?,
str: String?,
isWebview: Boolean
): Boolean =
with(activity) { with(activity) {
if (str != null && this != null) { if (str != null && this != null) {
if (str.startsWith("https://cs.repo")) { if (str.startsWith("https://cs.repo")) {
@ -191,7 +197,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
val url = str.replaceFirst(appStringRepo, "https") val url = str.replaceFirst(appStringRepo, "https")
loadRepository(url) loadRepository(url)
return true return true
} else if (!isWebview){ } else if (!isWebview) {
if (str.startsWith(DOWNLOAD_NAVIGATE_TO)) { if (str.startsWith(DOWNLOAD_NAVIGATE_TO)) {
this.navigate(R.id.navigation_downloads) this.navigate(R.id.navigation_downloads)
return true return true
@ -267,6 +273,25 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
} }
if (destination.id == R.id.navigation_download_child || destination.id == R.id.navigation_downloads) {
setKey(DOWNLOAD_COUNT_KEY, 0)
}
nav_view?.getOrCreateBadge(R.id.navigation_downloads)?.apply {
val count = getKey(DOWNLOAD_COUNT_KEY) ?: 0
if (count <= 0) {
clearNumber()
isVisible = false
} else {
this.backgroundColor =
getResourceColor(R.attr.colorPrimary)
this.badgeTextColor =
getResourceColor(R.attr.colorOnPrimary)
isVisible = true
number = count
}
}
nav_view?.isVisible = isNavVisible && !landscape nav_view?.isVisible = isNavVisible && !landscape
nav_rail_view?.isVisible = isNavVisible && landscape nav_rail_view?.isVisible = isNavVisible && landscape
} }

View File

@ -0,0 +1,55 @@
package com.lagradost.cloudstream3.ui
import android.content.Context
import android.util.AttributeSet
import android.widget.TextView
import androidx.core.view.isVisible
import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.R
import com.lagradost.fetchbutton.aria2c.DownloadStatusTell
import com.lagradost.fetchbutton.aria2c.Metadata
import com.lagradost.fetchbutton.ui.PieFetchButton
class DownloadButton(context: Context, attributeSet: AttributeSet) :
PieFetchButton(context, attributeSet) {
var progressText: TextView? = null
var mainText: TextView? = null
var bigButton: MaterialButton? = null
override fun onInflate() {
overrideLayout = R.layout.download_button_layout
super.onInflate()
progressText = findViewById(R.id.result_movie_download_text_precentage)
mainText = findViewById(R.id.result_movie_download_text)
bigButton = findViewById(R.id.download_big_button)
}
override fun setOnClickListener(l: OnClickListener?) {
bigButton?.setOnClickListener(l)
}
override fun setOnLongClickListener(l: OnLongClickListener?) {
bigButton?.setOnLongClickListener(l)
}
override fun setStatus(status: DownloadStatusTell?) {
super.setStatus(status)
val txt = when (status) {
DownloadStatusTell.Paused -> R.string.download_paused
DownloadStatusTell.Active -> R.string.downloading
DownloadStatusTell.Complete -> R.string.downloaded
else -> R.string.download
}
mainText?.setText(txt)
}
override fun updateViewOnDownload(metadata: Metadata) {
super.updateViewOnDownload(metadata)
val isVis = metadata.progressPercentage > 0
progressText?.isVisible = isVis
if (isVis)
progressText?.text = "${metadata.progressPercentage}%"
}
}

View File

@ -0,0 +1,68 @@
package com.lagradost.cloudstream3.ui.download
import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import com.lagradost.cloudstream3.utils.VideoDownloadManager.KEY_DOWNLOAD_INFO
import com.lagradost.fetchbutton.aria2c.Aria2Starter
import com.lagradost.fetchbutton.aria2c.DownloadListener
import com.lagradost.fetchbutton.aria2c.Metadata
import java.io.File
const val KEY_DOWNLOAD_INFO_METADATA = "download_info_metadata"
object Aria2cHelper {
fun deleteId(id: Long) {
// backward compatibility
VideoDownloadManager.downloadDeleteEvent.invoke(id.toInt())
getMetadata(id)?.let { data ->
Aria2Starter.delete(
DownloadListener.sessionIdToGid[id],
id,
data.items.flatMap { it.files })
}
removeMetadata(id)
AcraApplication.removeKey(KEY_DOWNLOAD_INFO, id.toString())
}
fun saveMetadata(id: Long, meta: Metadata) {
AcraApplication.setKey(KEY_DOWNLOAD_INFO_METADATA, id.toString(), meta)
}
fun removeMetadata(id: Long) {
AcraApplication.removeKey(KEY_DOWNLOAD_INFO_METADATA, id.toString())
}
fun downloadExist(data: Metadata): Boolean {
return data.items.any {
it.files.any { file ->
try {
//println("TESTING PATH: ${file.path}")
File(file.path).exists()
} catch (e: Exception) {
false
}
}
}
}
fun downloadExist(id: Long): Boolean {
return downloadExist(getMetadata(id) ?: return false)
}
fun getMetadata(id: Long): Metadata? {
return AcraApplication.getKey(KEY_DOWNLOAD_INFO_METADATA, id.toString())
}
fun pause(id: Long) {
DownloadListener.sessionIdToGid[id]?.let { gid ->
Aria2Starter.pause(gid, all = true)
}
}
fun unpause(id: Long) {
DownloadListener.sessionIdToGid[id]?.let { gid ->
Aria2Starter.unpause(gid, all = true)
}
}
}

View File

@ -2,22 +2,160 @@ package com.lagradost.cloudstream3.ui.download
import android.app.Activity import android.app.Activity
import android.content.DialogInterface import android.content.DialogInterface
import android.text.format.Formatter
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.doOnAttach
import androidx.lifecycle.findViewTreeLifecycleOwner
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.DownloadFileGenerator import com.lagradost.cloudstream3.ui.player.DownloadFileGenerator
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.result.DownloadHelper.play
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.ExtractorUri
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import com.lagradost.fetchbutton.aria2c.DownloadListener
import com.lagradost.fetchbutton.aria2c.DownloadStatusTell
import com.lagradost.fetchbutton.ui.PieFetchButton
object DownloadButtonSetup { object DownloadButtonSetup {
fun bind(
card: IVisualDownloadChildCached,
downloadButton: PieFetchButton,
extraInfo: TextView,
clickCallback: (DownloadClickEvent) -> Unit
) {
val d = card.data ?: return
fun updateText(downloadBytes: Long, totalBytes: Long) {
extraInfo?.apply {
text =
context.getString(R.string.download_size_format).format(
Formatter.formatShortFileSize(context, downloadBytes),
Formatter.formatShortFileSize(context, totalBytes)
)
}
}
updateText(card.currentBytes, card.totalBytes)
downloadButton.apply {
val play =
if (card is VisualDownloadChildCached) R.string.play_episode else R.string.play_movie_button//if (card.episode <= 0) R.string.play_movie_button else R.string.play_episode
setPersistentId(d.id.toLong())
doOnAttach { view ->
view.findViewTreeLifecycleOwner()?.let { life ->
DownloadListener.observe(life) {
gid?.let { realGId ->
val meta = DownloadListener.getInfo(realGId)
updateText(meta.downloadedLength, meta.totalLength)
}
}
}
}
val isValid = when (downloadButton.currentStatus) {
null, DownloadStatusTell.Removed -> false
else -> true
}
downloadButton.setOnClickListener {
val view = downloadButton
fun delete() {
// view.deleteAllFiles()
clickCallback.invoke(
DownloadClickEvent(
DOWNLOAD_ACTION_DELETE_FILE,
d
)
)
}
//if (view !is PieFetchButton) return@setOnClickListener
when (view.currentStatus) {
/*null, DownloadStatusTell.Removed -> {
view.setStatus(DownloadStatusTell.Waiting)
downloadClickCallback.invoke(
DownloadEpisodeClickEvent(
DOWNLOAD_ACTION_DOWNLOAD,
card
)
)
}*/
DownloadStatusTell.Paused -> {
view.popupMenuNoIcons(
if (isValid) listOf(
1 to R.string.resume,
2 to play,
3 to R.string.delete
) else listOf(2 to play, 3 to R.string.delete)
) {
when (itemId) {
1 -> if (!view.resumeDownload()) {
/*downloadClickCallback.invoke(
DownloadEpisodeClickEvent(
DOWNLOAD_ACTION_DOWNLOAD,
card
)
)*/
}
2 -> play(d)
3 -> delete()
}
}
}
DownloadStatusTell.Complete -> {
view.popupMenuNoIcons(
listOf(
2 to play,
3 to R.string.delete
)
) {
when (itemId) {
2 -> play(d)
3 -> delete()
}
}
}
DownloadStatusTell.Active -> {
view.popupMenuNoIcons(
if (isValid) listOf(
4 to R.string.pause,
2 to play,
3 to R.string.delete
) else listOf(
2 to play,
3 to R.string.delete
)
) {
when (itemId) {
4 -> view.pauseDownload()
2 -> play(d)
3 -> delete()
}
}
}
DownloadStatusTell.Error -> {
view.redownload()
}
DownloadStatusTell.Waiting -> {
}
else -> {}
}
}
}
}
fun handleDownloadClick(activity: Activity?, click: DownloadClickEvent) { fun handleDownloadClick(activity: Activity?, click: DownloadClickEvent) {
val id = click.data.id val id = click.data.id
if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return if (click.data !is VideoDownloadHelper.DownloadEpisodeCached) return
@ -29,6 +167,7 @@ object DownloadButtonSetup {
DialogInterface.OnClickListener { _, which -> DialogInterface.OnClickListener { _, which ->
when (which) { when (which) {
DialogInterface.BUTTON_POSITIVE -> { DialogInterface.BUTTON_POSITIVE -> {
Aria2cHelper.deleteId(id.toLong())
VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id) VideoDownloadManager.deleteFileAndUpdateSettings(ctx, id)
} }
DialogInterface.BUTTON_NEGATIVE -> { DialogInterface.BUTTON_NEGATIVE -> {
@ -57,11 +196,14 @@ object DownloadButtonSetup {
} }
} }
DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> { DOWNLOAD_ACTION_PAUSE_DOWNLOAD -> {
Aria2cHelper.pause(click.data.id.toLong())
VideoDownloadManager.downloadEvent.invoke( VideoDownloadManager.downloadEvent.invoke(
Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause) Pair(click.data.id, VideoDownloadManager.DownloadActionType.Pause)
) )
} }
DOWNLOAD_ACTION_RESUME_DOWNLOAD -> { DOWNLOAD_ACTION_RESUME_DOWNLOAD -> {
Aria2cHelper.unpause(click.data.id.toLong())
activity?.let { ctx -> activity?.let { ctx ->
if (VideoDownloadManager.downloadStatus.containsKey(id) && VideoDownloadManager.downloadStatus[id] == VideoDownloadManager.DownloadType.IsPaused) { if (VideoDownloadManager.downloadStatus.containsKey(id) && VideoDownloadManager.downloadStatus[id] == VideoDownloadManager.DownloadType.IsPaused) {
VideoDownloadManager.downloadEvent.invoke( VideoDownloadManager.downloadEvent.invoke(
@ -86,6 +228,7 @@ object DownloadButtonSetup {
act, act,
click.data.id click.data.id
)?.fileLength )?.fileLength
?: Aria2cHelper.getMetadata(click.data.id.toLong())?.downloadedLength
?: 0 ?: 0
if (length > 0) { if (length > 0) {
showToast(act, R.string.delete, Toast.LENGTH_LONG) showToast(act, R.string.delete, Toast.LENGTH_LONG)

View File

@ -3,18 +3,20 @@ package com.lagradost.cloudstream3.ui.download
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.result.DownloadHelper.play
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.fetchbutton.ui.PieFetchButton
import kotlinx.android.synthetic.main.download_child_episode.view.* import kotlinx.android.synthetic.main.download_child_episode.view.*
import java.util.*
const val DOWNLOAD_ACTION_PLAY_FILE = 0 const val DOWNLOAD_ACTION_PLAY_FILE = 0
const val DOWNLOAD_ACTION_DELETE_FILE = 1 const val DOWNLOAD_ACTION_DELETE_FILE = 1
@ -23,52 +25,31 @@ const val DOWNLOAD_ACTION_PAUSE_DOWNLOAD = 3
const val DOWNLOAD_ACTION_DOWNLOAD = 4 const val DOWNLOAD_ACTION_DOWNLOAD = 4
const val DOWNLOAD_ACTION_LONG_CLICK = 5 const val DOWNLOAD_ACTION_LONG_CLICK = 5
interface IVisualDownloadChildCached{
val currentBytes: Long
val totalBytes: Long
val data: VideoDownloadHelper.DownloadEpisodeCached?
}
data class VisualDownloadChildCached( data class VisualDownloadChildCached(
val currentBytes: Long, override val currentBytes: Long,
val totalBytes: Long, override val totalBytes: Long,
val data: VideoDownloadHelper.DownloadEpisodeCached, override val data: VideoDownloadHelper.DownloadEpisodeCached,
) ) : IVisualDownloadChildCached
data class DownloadClickEvent(val action: Int, val data: EasyDownloadButton.IMinimumData) data class DownloadClickEvent(val action: Int, val data: EasyDownloadButton.IMinimumData)
data class DownloadEpisodeClickEvent(val action: Int, val data: ResultEpisode)
class DownloadChildAdapter( class DownloadChildAdapter(
var cardList: List<VisualDownloadChildCached>, var cardList: List<VisualDownloadChildCached>,
private val clickCallback: (DownloadClickEvent) -> Unit, private val clickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val mBoundViewHolders: HashSet<DownloadButtonViewHolder> = HashSet()
private fun getAllBoundViewHolders(): Set<DownloadButtonViewHolder?>? {
return Collections.unmodifiableSet(mBoundViewHolders)
}
fun killAdapter() {
getAllBoundViewHolders()?.forEach { view ->
view?.downloadButton?.dispose()
}
}
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.downloadButton.dispose()
}
}
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.downloadButton.dispose()
mBoundViewHolders.remove(holder)
}
}
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.reattachDownloadButton()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DownloadChildViewHolder( return DownloadChildViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.download_child_episode, parent, false), LayoutInflater.from(parent.context)
.inflate(R.layout.download_child_episode, parent, false),
clickCallback clickCallback
) )
} }
@ -77,7 +58,6 @@ class DownloadChildAdapter(
when (holder) { when (holder) {
is DownloadChildViewHolder -> { is DownloadChildViewHolder -> {
holder.bind(cardList[position]) holder.bind(cardList[position])
mBoundViewHolders.add(holder)
} }
} }
} }
@ -90,15 +70,13 @@ class DownloadChildAdapter(
constructor( constructor(
itemView: View, itemView: View,
private val clickCallback: (DownloadClickEvent) -> Unit, private val clickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { ) : RecyclerView.ViewHolder(itemView) {
override var downloadButton = EasyDownloadButton()
private val title: TextView = itemView.download_child_episode_text private val title: TextView = itemView.download_child_episode_text
private val extraInfo: TextView = itemView.download_child_episode_text_extra private val extraInfo: TextView = itemView.download_child_episode_text_extra
private val holder: CardView = itemView.download_child_episode_holder private val holder: CardView = itemView.download_child_episode_holder
private val progressBar: ContentLoadingProgressBar = itemView.download_child_episode_progress private val progressBar: ContentLoadingProgressBar =
private val progressBarDownload: ContentLoadingProgressBar = itemView.download_child_episode_progress_downloaded itemView.download_child_episode_progress
private val downloadImage: ImageView = itemView.download_child_episode_download private val downloadButton: PieFetchButton = itemView.download_child_episode_download
private var localCard: VisualDownloadChildCached? = null private var localCard: VisualDownloadChildCached? = null
@ -118,35 +96,17 @@ class DownloadChildAdapter(
title.text = title.context.getNameFull(d.name, d.episode, d.season) title.text = title.context.getNameFull(d.name, d.episode, d.season)
title.isSelected = true // is needed for text repeating title.isSelected = true // is needed for text repeating
//extraInfo.text = card.currentBytes
downloadButton.setUpButton( DownloadButtonSetup.bind(card, downloadButton, extraInfo, clickCallback)
card.currentBytes,
card.totalBytes,
progressBarDownload,
downloadImage,
extraInfo,
card.data,
clickCallback
)
holder.setOnClickListener { holder.setOnClickListener {
clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d)) if (downloadButton.isVisible) {
} downloadButton.play(d)
} } else {
holder.setOnClickListener {
override fun reattachDownloadButton() { clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, d))
downloadButton.dispose() }
val card = localCard }
if (card != null) {
downloadButton.setUpButton(
card.currentBytes,
card.totalBytes,
progressBarDownload,
downloadImage,
extraInfo,
card.data,
clickCallback
)
} }
} }
} }

View File

@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_child_downloads.* import kotlinx.android.synthetic.main.fragment_child_downloads.*
@ -21,7 +22,7 @@ import kotlinx.coroutines.withContext
class DownloadChildFragment : Fragment() { class DownloadChildFragment : Fragment() {
companion object { companion object {
fun newInstance(headerName: String, folder: String) : Bundle { fun newInstance(headerName: String, folder: String): Bundle {
return Bundle().apply { return Bundle().apply {
putString("folder", folder) putString("folder", folder)
putString("name", headerName) putString("name", headerName)
@ -30,15 +31,18 @@ class DownloadChildFragment : Fragment() {
} }
override fun onDestroyView() { override fun onDestroyView() {
(download_child_list?.adapter as DownloadChildAdapter?)?.killAdapter()
downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it } downloadDeleteEventListener?.let { VideoDownloadManager.downloadDeleteEvent -= it }
super.onDestroyView() super.onDestroyView()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_child_downloads, container, false) return inflater.inflate(R.layout.fragment_child_downloads, container, false)
} }
private var hasPopped = false // fix stupid bug
private fun updateList(folder: String) = main { private fun updateList(folder: String) = main {
context?.let { ctx -> context?.let { ctx ->
val data = withContext(Dispatchers.IO) { ctx.getKeys(folder) } val data = withContext(Dispatchers.IO) { ctx.getKeys(folder) }
@ -50,9 +54,11 @@ class DownloadChildFragment : Fragment() {
?: return@mapNotNull null ?: return@mapNotNull null
VisualDownloadChildCached(info.fileLength, info.totalBytes, it) VisualDownloadChildCached(info.fileLength, info.totalBytes, it)
} }
}.sortedBy { it.data.episode + (it.data.season?: 0)*100000 } }.sortedBy { it.data.episode + (it.data.season ?: 0) * 100000 }
if (eps.isEmpty()) {
activity?.onBackPressed() if (eps.isEmpty() && !hasPopped) {
hasPopped = true
activity?.popCurrentPage()
return@main return@main
} }
@ -85,6 +91,13 @@ class DownloadChildFragment : Fragment() {
ArrayList(), ArrayList(),
) { click -> ) { click ->
handleDownloadClick(activity, click) handleDownloadClick(activity, click)
/* println("HANDLE ACTION :${click.action}")
if (click.action == DOWNLOAD_ACTION_DELETE_FILE) {
val list = (download_child_list?.adapter as DownloadChildAdapter?)?.cardList
if (list != null) {
updateList(folder)
}
}*/
} }
downloadDeleteEventListener = { id: Int -> downloadDeleteEventListener = { id: Int ->

View File

@ -69,7 +69,6 @@ class DownloadFragment : Fragment() {
VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!! VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!!
downloadDeleteEventListener = null downloadDeleteEventListener = null
} }
(download_list?.adapter as DownloadHeaderAdapter?)?.killAdapter()
super.onDestroyView() super.onDestroyView()
} }
@ -165,7 +164,7 @@ class DownloadFragment : Fragment() {
downloadDeleteEventListener = { id -> downloadDeleteEventListener = { id ->
val list = (download_list?.adapter as DownloadHeaderAdapter?)?.cardList val list = (download_list?.adapter as DownloadHeaderAdapter?)?.cardList
if (list != null) { if (list != null) {
if (list.any { it.data.id == id }) { if (list.any { it.data?.id == id }) {
context?.let { ctx -> context?.let { ctx ->
setList(ArrayList()) setList(ArrayList())
downloadsViewModel.updateList(ctx) downloadsViewModel.updateList(ctx)

View File

@ -8,23 +8,25 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar import androidx.core.widget.ContentLoadingProgressBar
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.fetchbutton.ui.PieFetchButton
import kotlinx.android.synthetic.main.download_header_episode.view.* import kotlinx.android.synthetic.main.download_header_episode.view.*
import java.util.* import java.util.*
data class VisualDownloadHeaderCached( data class VisualDownloadHeaderCached(
val currentOngoingDownloads: Int, val currentOngoingDownloads: Int,
val totalDownloads: Int, val totalDownloads: Int,
val totalBytes: Long, override val totalBytes: Long,
val currentBytes: Long, override val currentBytes: Long,
val data: VideoDownloadHelper.DownloadHeaderCached, val header: VideoDownloadHelper.DownloadHeaderCached,
val child: VideoDownloadHelper.DownloadEpisodeCached?, override val data: VideoDownloadHelper.DownloadEpisodeCached?,
) ) : IVisualDownloadChildCached
data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadHeaderCached) data class DownloadHeaderClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadHeaderCached)
@ -34,35 +36,6 @@ class DownloadHeaderAdapter(
private val movieClickCallback: (DownloadClickEvent) -> Unit, private val movieClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val mBoundViewHolders: HashSet<DownloadButtonViewHolder> = HashSet()
private fun getAllBoundViewHolders(): Set<DownloadButtonViewHolder?>? {
return Collections.unmodifiableSet(mBoundViewHolders)
}
fun killAdapter() {
getAllBoundViewHolders()?.forEach { view ->
view?.downloadButton?.dispose()
}
}
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.downloadButton.dispose()
}
}
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.downloadButton.dispose()
mBoundViewHolders.remove(holder)
}
}
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.reattachDownloadButton()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return DownloadHeaderViewHolder( return DownloadHeaderViewHolder(
@ -76,7 +49,6 @@ class DownloadHeaderAdapter(
when (holder) { when (holder) {
is DownloadHeaderViewHolder -> { is DownloadHeaderViewHolder -> {
holder.bind(cardList[position]) holder.bind(cardList[position])
mBoundViewHolders.add(holder)
} }
} }
} }
@ -90,23 +62,17 @@ class DownloadHeaderAdapter(
itemView: View, itemView: View,
private val clickCallback: (DownloadHeaderClickEvent) -> Unit, private val clickCallback: (DownloadHeaderClickEvent) -> Unit,
private val movieClickCallback: (DownloadClickEvent) -> Unit, private val movieClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { ) : RecyclerView.ViewHolder(itemView) {
override var downloadButton = EasyDownloadButton()
private val poster: ImageView? = itemView.download_header_poster private val poster: ImageView? = itemView.download_header_poster
private val title: TextView = itemView.download_header_title private val title: TextView = itemView.download_header_title
private val extraInfo: TextView = itemView.download_header_info private val extraInfo: TextView = itemView.download_header_info
private val holder: CardView = itemView.episode_holder private val holder: CardView = itemView.episode_holder
private val downloadBar: ContentLoadingProgressBar = itemView.download_header_progress_downloaded private val downloadButton: PieFetchButton = itemView.download_header_download
private val downloadImage: ImageView = itemView.download_header_episode_download
private val normalImage: ImageView = itemView.download_header_goto_child
private var localCard: VisualDownloadHeaderCached? = null
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(card: VisualDownloadHeaderCached) { fun bind(card: VisualDownloadHeaderCached) {
localCard = card val d = card.header
val d = card.data
poster?.setImage(d.poster) poster?.setImage(d.poster)
poster?.setOnClickListener { poster?.setOnClickListener {
@ -117,10 +83,8 @@ class DownloadHeaderAdapter(
val mbString = formatShortFileSize(itemView.context, card.totalBytes) val mbString = formatShortFileSize(itemView.context, card.totalBytes)
//val isMovie = d.type.isMovieType() //val isMovie = d.type.isMovieType()
if (card.child != null) { if (card.data != null) {
downloadBar.visibility = View.VISIBLE downloadButton.isVisible = true
downloadImage.visibility = View.VISIBLE
normalImage.visibility = View.GONE
/*setUpButton( /*setUpButton(
card.currentBytes, card.currentBytes,
card.totalBytes, card.totalBytes,
@ -130,14 +94,15 @@ class DownloadHeaderAdapter(
card.child, card.child,
movieClickCallback movieClickCallback
)*/ )*/
DownloadButtonSetup.bind(card, downloadButton, extraInfo) { click ->
movieClickCallback.invoke(DownloadClickEvent(click.action, card.data))
}
holder.setOnClickListener { holder.setOnClickListener {
movieClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.child)) movieClickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_PLAY_FILE, card.data))
} }
} else { } else {
downloadBar.visibility = View.GONE downloadButton.isVisible = false
downloadImage.visibility = View.GONE
normalImage.visibility = View.VISIBLE
try { try {
extraInfo.text = extraInfo.text =
@ -160,21 +125,5 @@ class DownloadHeaderAdapter(
} }
} }
} }
override fun reattachDownloadButton() {
downloadButton.dispose()
val card = localCard
if (card?.child != null) {
downloadButton.setUpButton(
card.currentBytes,
card.totalBytes,
downloadBar,
downloadImage,
extraInfo,
card.child,
movieClickCallback
)
}
}
} }
} }

View File

@ -1,12 +1,15 @@
package com.lagradost.cloudstream3.ui.download package com.lagradost.cloudstream3.ui.download
import android.content.Context import android.content.Context
import android.net.Uri
import android.os.Environment import android.os.Environment
import android.os.StatFs import android.os.StatFs
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.isMovieType import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -46,6 +49,8 @@ class DownloadViewModel : ViewModel() {
.distinctBy { it.id } // Remove duplicates .distinctBy { it.id } // Remove duplicates
} }
println("CHILDRE:$children")
// parentId : bytes // parentId : bytes
val totalBytesUsedByChild = HashMap<Int, Long>() val totalBytesUsedByChild = HashMap<Int, Long>()
// parentId : bytes // parentId : bytes
@ -53,12 +58,10 @@ class DownloadViewModel : ViewModel() {
// parentId : downloadsCount // parentId : downloadsCount
val totalDownloads = HashMap<Int, Int>() val totalDownloads = HashMap<Int, Int>()
// Gets all children downloads // Gets all children downloads
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
for (c in children) { for (c in children) {
val childFile = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, c.id) ?: continue val childFile = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(context, c.id) ?: continue
if (childFile.fileLength <= 1) continue if (childFile.fileLength <= 1) continue
val len = childFile.totalBytes val len = childFile.totalBytes
val flen = childFile.fileLength val flen = childFile.fileLength
@ -68,7 +71,8 @@ class DownloadViewModel : ViewModel() {
totalDownloads[c.parentId] = totalDownloads[c.parentId]?.plus(1) ?: 1 totalDownloads[c.parentId] = totalDownloads[c.parentId]?.plus(1) ?: 1
} }
} }
println("TotalDownloads:$totalDownloads")
//println("FIXED: $totalDownloads")
val cached = withContext(Dispatchers.IO) { // wont fetch useless keys val cached = withContext(Dispatchers.IO) { // wont fetch useless keys
totalDownloads.entries.filter { it.value > 0 }.mapNotNull { totalDownloads.entries.filter { it.value > 0 }.mapNotNull {
context.getKey<VideoDownloadHelper.DownloadHeaderCached>( context.getKey<VideoDownloadHelper.DownloadHeaderCached>(
@ -77,6 +81,7 @@ class DownloadViewModel : ViewModel() {
) )
} }
} }
println("Cached:$cached")
val visual = withContext(Dispatchers.IO) { val visual = withContext(Dispatchers.IO) {
cached.mapNotNull { // TODO FIX cached.mapNotNull { // TODO FIX
@ -99,7 +104,7 @@ class DownloadViewModel : ViewModel() {
movieEpisode movieEpisode
) )
}.sortedBy { }.sortedBy {
(it.child?.episode ?: 0) + (it.child?.season?.times(10000) ?: 0) (it.data?.episode ?: 0) + (it.data?.season?.times(10000) ?: 0)
} // episode sorting by episode, lowest to highest } // episode sorting by episode, lowest to highest
} }
try { try {

View File

@ -0,0 +1,166 @@
package com.lagradost.cloudstream3.ui.result
import androidx.core.net.toUri
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
import com.lagradost.cloudstream3.ui.download.DownloadEpisodeClickEvent
import com.lagradost.cloudstream3.ui.player.DownloadFileGenerator
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.utils.ExtractorUri
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.fetchbutton.aria2c.Aria2Starter
import com.lagradost.fetchbutton.aria2c.DownloadStatusTell
import com.lagradost.fetchbutton.ui.PieFetchButton
import java.io.File
object DownloadHelper {
fun PieFetchButton.play(card: VideoDownloadHelper.DownloadEpisodeCached) {
val files = this.getVideos()
// fucked af, but who cares
Aria2Starter.saveActivity.get()?.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(DownloadFileGenerator(
files.map { path ->
val file = File(path)
ExtractorUri(
uri = file.toUri(),
id = card.id,
parentId = card.parentId,
name = context.getString(R.string.downloaded_file), //click.data.name ?: keyInfo.displayName
season = card.season,
episode = card.episode,
basePath = file.path,
displayName = file.name
//displayName = keyInfo.displayName,
//relativePath = keyInfo.relativePath,
)
}
)))
}
fun PieFetchButton.play(card: ResultEpisode) {
val files = this.getVideos()
// fucked af, but who cares
Aria2Starter.saveActivity.get()?.navigate(
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(DownloadFileGenerator(
files.map { path ->
val file = File(path)
ExtractorUri(
uri = file.toUri(),
id = card.id,
parentId = card.parentId,
name = context.getString(R.string.downloaded_file), //click.data.name ?: keyInfo.displayName
season = card.season,
episode = card.episode,
headerName = card.headerName,
tvType = card.tvType,
basePath = file.path,
displayName = file.name,
//relativePath = keyInfo.relativePath,
)
}
)))
}
fun PieFetchButton.setUp(
card: ResultEpisode,
downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit
) {
setPersistentId(card.id.toLong())
val play = if (card.episode <= 0) R.string.play_movie_button else R.string.play_episode
setOnLongClickListener { //Aria2Starter.saveActivity.get()
downloadClickCallback.invoke(
DownloadEpisodeClickEvent(
DOWNLOAD_ACTION_LONG_CLICK,
card
)
)
//showToast(it.context as? Activity, R.string.download, Toast.LENGTH_SHORT)
return@setOnLongClickListener true
}
setOnClickListener {
val view = this
//if (view !is PieFetchButton) return@setOnClickListener
when (view.currentStatus) {
null, DownloadStatusTell.Removed -> {
view.setStatus(DownloadStatusTell.Waiting)
downloadClickCallback.invoke(
DownloadEpisodeClickEvent(
DOWNLOAD_ACTION_DOWNLOAD,
card
)
)
}
DownloadStatusTell.Paused -> {
view.popupMenuNoIcons(
listOf(
1 to R.string.resume,
2 to play,
3 to R.string.delete
)
) {
when (itemId) {
1 -> if (!view.resumeDownload()) {
downloadClickCallback.invoke(
DownloadEpisodeClickEvent(
DOWNLOAD_ACTION_DOWNLOAD,
card
)
)
}
2 -> play(card)
3 -> view.deleteAllFiles()
}
}
}
DownloadStatusTell.Complete -> {
view.popupMenuNoIcons(
listOf(
2 to play,
3 to R.string.delete
)
) {
when (itemId) {
2 -> play(card)
3 -> view.deleteAllFiles()
}
}
}
DownloadStatusTell.Active -> {
view.popupMenuNoIcons(
listOf(
4 to R.string.pause,
2 to play,
3 to R.string.delete
)
) {
when (itemId) {
4 -> view.pauseDownload()
2 -> play(card)
3 -> view.deleteAllFiles()
}
}
}
DownloadStatusTell.Error -> {
view.redownload()
}
DownloadStatusTell.Waiting -> {
}
else -> {}
}
}
}
}

View File

@ -13,23 +13,17 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.ui.download.DownloadEpisodeClickEvent
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton import com.lagradost.cloudstream3.ui.result.DownloadHelper.setUp
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.result_episode.view.* import kotlinx.android.synthetic.main.result_episode.view.*
import kotlinx.android.synthetic.main.result_episode.view.episode_text import kotlinx.android.synthetic.main.result_episode.view.episode_text
import kotlinx.android.synthetic.main.result_episode_large.view.*
import kotlinx.android.synthetic.main.result_episode_large.view.episode_filler import kotlinx.android.synthetic.main.result_episode_large.view.episode_filler
import kotlinx.android.synthetic.main.result_episode_large.view.episode_progress import kotlinx.android.synthetic.main.result_episode_large.view.episode_progress
import kotlinx.android.synthetic.main.result_episode_large.view.result_episode_download import kotlinx.android.synthetic.main.result_episode_large_tv.view.*
import kotlinx.android.synthetic.main.result_episode_large.view.result_episode_progress_downloaded
import java.util.*
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1 const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2 const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2
@ -58,21 +52,10 @@ data class EpisodeClickEvent(val action: Int, val data: ResultEpisode)
class EpisodeAdapter( class EpisodeAdapter(
private val hasDownloadSupport: Boolean, private val hasDownloadSupport: Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit, private val clickCallback: (EpisodeClickEvent) -> Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit, private val downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var cardList: MutableList<ResultEpisode> = mutableListOf() private var cardList: MutableList<ResultEpisode> = mutableListOf()
private val mBoundViewHolders: HashSet<DownloadButtonViewHolder> = HashSet()
private fun getAllBoundViewHolders(): Set<DownloadButtonViewHolder?>? {
return Collections.unmodifiableSet(mBoundViewHolders)
}
fun killAdapter() {
getAllBoundViewHolders()?.forEach { view ->
view?.downloadButton?.dispose()
}
}
override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) { override fun onViewDetachedFromWindow(holder: RecyclerView.ViewHolder) {
if (holder.itemView.hasFocus()) { if (holder.itemView.hasFocus()) {
holder.itemView.clearFocus() holder.itemView.clearFocus()
@ -85,15 +68,6 @@ class EpisodeAdapter(
} }
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) {
holder.downloadButton.dispose()
mBoundViewHolders.remove(holder)
//(holder.itemView as? FrameLayout?)?.descendantFocusability =
// ViewGroup.FOCUS_BLOCK_DESCENDANTS
}
}
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) { override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
if (holder is DownloadButtonViewHolder) { if (holder is DownloadButtonViewHolder) {
//println("onViewAttachedToWindow = ${holder.absoluteAdapterPosition}") //println("onViewAttachedToWindow = ${holder.absoluteAdapterPosition}")
@ -138,7 +112,6 @@ class EpisodeAdapter(
when (holder) { when (holder) {
is EpisodeCardViewHolder -> { is EpisodeCardViewHolder -> {
holder.bind(cardList[position]) holder.bind(cardList[position])
mBoundViewHolders.add(holder)
} }
} }
} }
@ -152,18 +125,12 @@ class EpisodeAdapter(
itemView: View, itemView: View,
private val hasDownloadSupport: Boolean, private val hasDownloadSupport: Boolean,
private val clickCallback: (EpisodeClickEvent) -> Unit, private val clickCallback: (EpisodeClickEvent) -> Unit,
private val downloadClickCallback: (DownloadClickEvent) -> Unit, private val downloadClickCallback: (DownloadEpisodeClickEvent) -> Unit,
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder { ) : RecyclerView.ViewHolder(itemView) {
override var downloadButton = EasyDownloadButton() //override var downloadButton = EasyDownloadButton()
var episodeDownloadBar: ContentLoadingProgressBar? = null
var episodeDownloadImage: ImageView? = null
var localCard: ResultEpisode? = null
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun bind(card: ResultEpisode) { fun bind(card: ResultEpisode) {
localCard = card
val isTrueTv = isTrueTvSettings() val isTrueTv = isTrueTvSettings()
val (parentView, otherView) = if (card.poster == null) { val (parentView, otherView) = if (card.poster == null) {
@ -181,9 +148,10 @@ class EpisodeAdapter(
val episodeProgress: ContentLoadingProgressBar? = parentView.episode_progress val episodeProgress: ContentLoadingProgressBar? = parentView.episode_progress
val episodePoster: ImageView? = parentView.episode_poster val episodePoster: ImageView? = parentView.episode_poster
episodeDownloadBar = val downloadButton = parentView.result_episode_download
parentView.result_episode_progress_downloaded
episodeDownloadImage = parentView.result_episode_download downloadButton.isVisible = hasDownloadSupport
downloadButton.setUp(card, downloadClickCallback)
val name = val name =
if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}" if (card.name == null) "${episodeText.context.getString(R.string.episode)} ${card.episode}" else "${card.episode}. ${card.name}"
@ -242,49 +210,6 @@ class EpisodeAdapter(
return@setOnLongClickListener true return@setOnLongClickListener true
} }
episodeDownloadImage?.isVisible = hasDownloadSupport
episodeDownloadBar?.isVisible = hasDownloadSupport
reattachDownloadButton()
}
override fun reattachDownloadButton() {
downloadButton.dispose()
val card = localCard
if (hasDownloadSupport && card != null) {
if (episodeDownloadBar == null ||
episodeDownloadImage == null
) return
val downloadInfo = VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(
itemView.context,
card.id
)
downloadButton.setUpButton(
downloadInfo?.fileLength,
downloadInfo?.totalBytes,
episodeDownloadBar ?: return,
episodeDownloadImage ?: return,
null,
VideoDownloadHelper.DownloadEpisodeCached(
card.name,
card.poster,
card.episode,
card.season,
card.id,
card.parentId,
card.rating,
card.description,
System.currentTimeMillis(),
)
) {
if (it.action == DOWNLOAD_ACTION_DOWNLOAD) {
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
} else {
downloadClickCallback.invoke(it)
}
}
}
} }
} }
} }

View File

@ -35,20 +35,20 @@ import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_LONG_CLICK
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.DownloadHelper.setUp
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
@ -72,11 +72,6 @@ import kotlinx.android.synthetic.main.fragment_result.result_meta_rating
import kotlinx.android.synthetic.main.fragment_result.result_meta_site import kotlinx.android.synthetic.main.fragment_result.result_meta_site
import kotlinx.android.synthetic.main.fragment_result.result_meta_type import kotlinx.android.synthetic.main.fragment_result.result_meta_type
import kotlinx.android.synthetic.main.fragment_result.result_meta_year import kotlinx.android.synthetic.main.fragment_result.result_meta_year
import kotlinx.android.synthetic.main.fragment_result.result_movie_download_icon
import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text
import kotlinx.android.synthetic.main.fragment_result.result_movie_download_text_precentage
import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded
import kotlinx.android.synthetic.main.fragment_result.result_movie_progress_downloaded_holder
import kotlinx.android.synthetic.main.fragment_result.result_next_airing import kotlinx.android.synthetic.main.fragment_result.result_next_airing
import kotlinx.android.synthetic.main.fragment_result.result_next_airing_time import kotlinx.android.synthetic.main.fragment_result.result_next_airing_time
import kotlinx.android.synthetic.main.fragment_result.result_no_episodes import kotlinx.android.synthetic.main.fragment_result.result_no_episodes
@ -253,11 +248,8 @@ open class ResultFragment : ResultTrailerPlayer() {
return inflater.inflate(resultLayout, container, false) return inflater.inflate(resultLayout, container, false)
} }
private var downloadButton: EasyDownloadButton? = null
override fun onDestroyView() { override fun onDestroyView() {
updateUIListener = null updateUIListener = null
(result_episodes?.adapter as EpisodeAdapter?)?.killAdapter()
downloadButton?.dispose()
super.onDestroyView() super.onDestroyView()
} }
@ -344,7 +336,18 @@ open class ResultFragment : ResultTrailerPlayer() {
return@setOnLongClickListener true return@setOnLongClickListener true
} }
main {
result_download_movie?.setUp(ep) {
when (it.action) {
DOWNLOAD_ACTION_DOWNLOAD -> viewModel.download(activity, it.data)
DOWNLOAD_ACTION_LONG_CLICK -> viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, ep)
)
}
}
result_download_movie?.isVisible = true
/*main {
val file = val file =
ioWorkSafe { ioWorkSafe {
context?.let { context?.let {
@ -389,11 +392,12 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
} }
result_movie_progress_downloaded_holder?.isVisible = true result_movie_progress_downloaded_holder?.isVisible = true
} }*/
} }
} }
else -> { else -> {
result_movie_progress_downloaded_holder?.isVisible = false //result_movie_progress_downloaded_holder?.isVisible = false
result_download_movie?.isVisible = false
result_play_movie?.isVisible = false result_play_movie?.isVisible = false
} }
} }
@ -458,7 +462,14 @@ open class ResultFragment : ResultTrailerPlayer() {
val storedData = getStoredData(activity ?: context ?: return) ?: return val storedData = getStoredData(activity ?: context ?: return) ?: return
//viewModel.clear() //viewModel.clear()
viewModel.load(activity, storedData.url ?: return, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) viewModel.load(
activity,
storedData.url ?: return,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
} }
} }
@ -507,8 +518,15 @@ open class ResultFragment : ResultTrailerPlayer() {
{ episodeClick -> { episodeClick ->
viewModel.handleAction(activity, episodeClick) viewModel.handleAction(activity, episodeClick)
}, },
{ downloadClickEvent -> { clickEvent ->
handleDownloadClick(activity, downloadClickEvent) when (clickEvent.action) {
DOWNLOAD_ACTION_DOWNLOAD -> viewModel.download(activity, clickEvent.data)
DOWNLOAD_ACTION_LONG_CLICK -> viewModel.handleAction(
activity,
EpisodeClickEvent(ACTION_DOWNLOAD_MIRROR, clickEvent.data)
)
}
//handleDownloadClick(activity, downloadClickEvent)
} }
) )
@ -916,7 +934,14 @@ open class ResultFragment : ResultTrailerPlayer() {
if (storedData?.url != null) { if (storedData?.url != null) {
result_reload_connectionerror.setOnClickListener { result_reload_connectionerror.setOnClickListener {
viewModel.load(activity, storedData.url, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
} }
result_reload_connection_open_in_browser?.setOnClickListener { result_reload_connection_open_in_browser?.setOnClickListener {
@ -952,7 +977,14 @@ open class ResultFragment : ResultTrailerPlayer() {
if (restart || !viewModel.hasLoaded()) { if (restart || !viewModel.hasLoaded()) {
//viewModel.clear() //viewModel.clear()
viewModel.load(activity, storedData.url, storedData.apiName, storedData.showFillers, storedData.dubStatus, storedData.start) viewModel.load(
activity,
storedData.url,
storedData.apiName,
storedData.showFillers,
storedData.dubStatus,
storedData.start
)
} }
} }
} }

View File

@ -16,6 +16,8 @@ import androidx.lifecycle.viewModelScope
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
@ -28,7 +30,6 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.IGenerator import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
@ -53,8 +54,14 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultEpisode
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.fetchbutton.NotificationMetaData
import com.lagradost.fetchbutton.aria2c.Aria2Starter
import com.lagradost.fetchbutton.aria2c.UriRequest
import com.lagradost.fetchbutton.aria2c.newUriRequest
import com.lagradost.fetchbutton.utils.Coroutines.mainThread
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.lang.Math.abs import java.lang.Math.abs
@ -529,24 +536,16 @@ class ResultViewModel2 : ViewModel() {
} }
private fun downloadSubtitle( private fun downloadSubtitle(
context: Context?,
link: ExtractorSubtitleLink, link: ExtractorSubtitleLink,
fileName: String, fileName: String,
folder: String folder: String
) { ) {
ioSafe { Aria2Starter.download(
VideoDownloadManager.downloadThing( newUriRequest(
context ?: return@ioSafe, null, link.url, fileName, folder, link.headers,
link, USER_AGENT
"$fileName ${link.name}", )
folder, )
if (link.url.contains(".srt")) ".srt" else "vtt",
false,
null
) {
// no notification
}
}
} }
private fun getFolder(currentType: TvType, titleName: String): String { private fun getFolder(currentType: TvType, titleName: String): String {
@ -576,7 +575,6 @@ class ResultViewModel2 : ViewModel() {
val fileName = VideoDownloadManager.getFileName(ctx, meta) val fileName = VideoDownloadManager.getFileName(ctx, meta)
val folder = getFolder(meta.type ?: return, meta.mainName) val folder = getFolder(meta.type ?: return, meta.mainName)
downloadSubtitle( downloadSubtitle(
ctx,
ExtractorSubtitleLink(link.name, link.url, ""), ExtractorSubtitleLink(link.name, link.url, ""),
fileName, fileName,
folder folder
@ -584,8 +582,7 @@ class ResultViewModel2 : ViewModel() {
} }
} }
fun startDownload( fun getDownloadRequest(
context: Context?,
episode: ResultEpisode, episode: ResultEpisode,
currentIsMovie: Boolean, currentIsMovie: Boolean,
currentHeaderName: String, currentHeaderName: String,
@ -596,93 +593,182 @@ class ResultViewModel2 : ViewModel() {
url: String, url: String,
links: List<ExtractorLink>, links: List<ExtractorLink>,
subs: List<SubtitleData>? subs: List<SubtitleData>?
) { ): DownloadRequest? {
try { val meta =
if (context == null) return getMeta(
episode,
val meta = currentHeaderName,
getMeta( apiName,
episode, currentPoster,
currentHeaderName, currentIsMovie,
apiName, currentType
currentPoster,
currentIsMovie,
currentType
)
val folder = getFolder(currentType, currentHeaderName)
val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let
// SET VISUAL KEYS
AcraApplication.setKey(
DOWNLOAD_HEADER_CACHE,
parentId.toString(),
VideoDownloadHelper.DownloadHeaderCached(
apiName,
url,
currentType,
currentHeaderName,
currentPoster,
parentId,
System.currentTimeMillis(),
)
) )
AcraApplication.setKey( val topFolder = VideoDownloadManager.getDownloadDir()?.filePath
DataStore.getFolderName( ?: throw RuntimeException("FUCK YOU")//AcraApplication.context?.getBasePath()?.first //?.second?.also { println("URIIIIII: $it") } ?: throw RuntimeException("FUCK YOU")
DOWNLOAD_EPISODE_CACHE, //?: VideoDownloadManager.getDownloadDir()?.filePath ?: return null
parentId.toString()
), // 3 deep folder for faster acess
episode.id.toString(),
VideoDownloadHelper.DownloadEpisodeCached(
episode.name,
episode.poster,
episode.episode,
episode.season,
episode.id,
parentId,
episode.rating,
episode.description,
System.currentTimeMillis(),
)
)
// DOWNLOAD VIDEO val folder =
VideoDownloadManager.downloadEpisodeUsingWorker( topFolder//topFolder?.gotoDir(getFolder(currentType, currentHeaderName).replace(".", ""), true)?.uri?.toString() ?: throw RuntimeException("FUCK YOU")
context, //val folder =
src,//url ?: return, // topFolder + "/" + getFolder(currentType, currentHeaderName).replace(".", "")
folder, //val src = "$DOWNLOAD_NAVIGATE_TO/$parentId" // url ?: return@let
meta,
links
)
// 1. Checks if the lang should be downloaded // SET VISUAL KEYS
// 2. Makes it into the download format AcraApplication.setKey(
// 3. Downloads it as a .vtt file DOWNLOAD_HEADER_CACHE,
val downloadList = SubtitlesFragment.getDownloadSubsLanguageISO639_1() parentId.toString(),
subs?.let { subsList -> VideoDownloadHelper.DownloadHeaderCached(
subsList.filter { apiName,
downloadList.contains( url,
SubtitleHelper.fromLanguageToTwoLetters( currentType,
it.name, currentHeaderName,
true currentPoster,
) parentId,
) System.currentTimeMillis(),
} )
.map { ExtractorSubtitleLink(it.name, it.url, "") } )
.forEach { link ->
val fileName = VideoDownloadManager.getFileName(context, meta) AcraApplication.setKey(
downloadSubtitle(context, link, fileName, folder) DataStore.getFolderName(
} DOWNLOAD_EPISODE_CACHE,
parentId.toString()
), // 3 deep folder for faster acess
episode.id.toString(),
VideoDownloadHelper.DownloadEpisodeCached(
episode.name,
episode.poster,
episode.episode,
episode.season,
episode.id,
parentId,
episode.rating,
episode.description,
System.currentTimeMillis(),
)
)
val notification = AcraApplication.context?.let { ctx ->
val rowTwoExtra = if (episode.name != null) " - ${episode.name}\n" else ""
val rowTwo = if (episode.season != null && episode.episode > 0) {
"${ctx.getString(R.string.season_short)}${episode.season}:${ctx.getString(R.string.episode_short)}${episode.episode}" + rowTwoExtra
} else if (episode.episode > 0) {
"${ctx.getString(R.string.episode)} ${episode.episode}" + rowTwoExtra
} else {
(episode.name ?: "") + ""
} }
} catch (e: Exception) { NotificationMetaData(
logError(e) episode.id,
iconColor = ctx.colorFromAttribute(R.attr.colorPrimary),
posterUrl = currentPoster,
contentTitle = currentHeaderName,
secondRow = rowTwo,
subText = null,
linkName = null,
)
} }
val linkRequests = links.filter { link -> !link.isM3u8 }.map { link ->
newUriRequest(
episode.id.toLong(), link.url,
getFolder(currentType, currentHeaderName) + File.pathSeparator +
VideoDownloadManager.getDisplayName(
VideoDownloadManager.getFileName(
AcraApplication.context ?: return null,
meta
), "mp4"
), null, // we use the dir set at start
link.headers, USER_AGENT,
notificationMetaData = notification?.copy(
linkName = "${link.name} ${
Qualities.getStringByInt(
link.quality
)
}"
)
)
}
val downloadList = SubtitlesFragment.getDownloadSubsLanguageISO639_1()
val downloadSubsList = (subs ?: emptyList()).filter {
downloadList.contains(
SubtitleHelper.fromLanguageToTwoLetters(
it.name,
true
)
)
}.distinctBy { it.url }.groupBy { link ->
SubtitleHelper.fromLanguageToTwoLetters(
link.name,
true
)
}.map {
it.value.map { link ->
val fileName = VideoDownloadManager.getDisplayName(
VideoDownloadManager.getFileName(
AcraApplication.context ?: return null,
meta
), "vtt"
)
newUriRequest(null, link.url, fileName, folder, link.headers, USER_AGENT)
//downloadSubtitle(context, link, fileName, folder)
}
}
return DownloadRequest(linkRequests, downloadSubsList)
// DOWNLOAD VIDEO
//VideoDownloadManager.downloadEpisodeUsingWorker(
// context,
// src,//url ?: return,
// folder,
// meta,
// links
//)
// 1. Checks if the lang should be downloaded
// 2. Makes it into the download format
// 3. Downloads it as a .vtt file
//val downloadList = SubtitlesFragment.getDownloadSubsLanguageISO639_1()
//subs?.let { subsList ->
// subsList.filter {
// downloadList.contains(
// SubtitleHelper.fromLanguageToTwoLetters(
// it.name,
// true
// )
// )
// }
// .map { ExtractorSubtitleLink(it.name, it.url, "") }
// .forEach { link ->
// val fileName = VideoDownloadManager.getFileName(context, meta)
// downloadSubtitle(context, link, fileName, folder)
// }
//}
} }
data class DownloadRequest(
val links: List<UriRequest>,
val subs: List<List<UriRequest>>,
)
/*suspend fun download(episode: ResultEpisode): DownloadRequest {
val generator = RepoLinkGenerator(listOf(episode))
val currentLinks = mutableSetOf<ExtractorLink>()
val currentSubs = mutableSetOf<SubtitleData>()
generator.generateLinks(clearCache = false, isCasting = false, callback = {
it.first?.let { link ->
currentLinks.add(link)
}
}, subtitleCallback = { sub ->
currentSubs.add(sub)
})
}*/
suspend fun downloadEpisode( suspend fun downloadEpisode(
activity: Activity?,
episode: ResultEpisode, episode: ResultEpisode,
currentIsMovie: Boolean, currentIsMovie: Boolean,
currentHeaderName: String, currentHeaderName: String,
@ -691,52 +777,49 @@ class ResultViewModel2 : ViewModel() {
apiName: String, apiName: String,
parentId: Int, parentId: Int,
url: String, url: String,
) { ): DownloadRequest? {
ioSafe { val generator = RepoLinkGenerator(listOf(episode))
val generator = RepoLinkGenerator(listOf(episode)) val currentLinks = mutableSetOf<ExtractorLink>()
val currentLinks = mutableSetOf<ExtractorLink>() val currentSubs = mutableSetOf<SubtitleData>()
val currentSubs = mutableSetOf<SubtitleData>() generator.generateLinks(clearCache = false, isCasting = false, callback = {
generator.generateLinks(clearCache = false, isCasting = false, callback = { it.first?.let { link ->
it.first?.let { link -> currentLinks.add(link)
currentLinks.add(link)
}
}, subtitleCallback = { sub ->
currentSubs.add(sub)
})
if (currentLinks.isEmpty()) {
main {
showToast(
activity,
R.string.no_links_found_toast,
Toast.LENGTH_SHORT
)
}
return@ioSafe
} else {
main {
showToast(
activity,
R.string.download_started,
Toast.LENGTH_SHORT
)
}
} }
}, subtitleCallback = { sub ->
currentSubs.add(sub)
})
startDownload( //if (currentLinks.isEmpty()) {
activity, // main {
episode, // showToast(
currentIsMovie, // activity,
currentHeaderName, // R.string.no_links_found_toast,
currentType, // Toast.LENGTH_SHORT
currentPoster, // )
apiName, // }
parentId, // return@ioSafe
url, //} else {
sortUrls(currentLinks), // main {
sortSubs(currentSubs), // showToast(
) // activity,
} // R.string.download_started,
// Toast.LENGTH_SHORT
// )
// }
//}
return getDownloadRequest(
episode,
currentIsMovie,
currentHeaderName,
currentType,
currentPoster,
apiName,
parentId,
url,
sortUrls(currentLinks),
sortSubs(currentSubs),
)
} }
private fun getMeta( private fun getMeta(
@ -1021,6 +1104,42 @@ class ResultViewModel2 : ViewModel() {
handleEpisodeClickEvent(activity, click) handleEpisodeClickEvent(activity, click)
} }
private fun downloadFromRequest(activity: Activity?, req: DownloadRequest) {
Aria2Starter.download(req.links)
for (sub in req.subs.take(4)) { // download max 4 langs to not block real download
Aria2Starter.download(sub)
}
val linksFound = req.links.isNotEmpty()
setKey(DOWNLOAD_COUNT_KEY, (getKey(DOWNLOAD_COUNT_KEY) ?: 0) + 1)
mainThread {
showToast(
activity,
if (linksFound) R.string.download_started else R.string.no_links_found_toast,
Toast.LENGTH_SHORT
)
}
}
fun download(activity: Activity?, card: ResultEpisode) = ioSafe {
getRequest(card)?.let { req ->
downloadFromRequest(activity, req)
}
}
suspend fun getRequest(card: ResultEpisode): DownloadRequest? {
val response = currentResponse ?: return null
return downloadEpisode(
card,
response.isMovie(),
response.name,
response.type,
response.posterUrl,
response.apiName,
response.getId(),
response.url
)
}
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) { private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
when (click.action) { when (click.action) {
ACTION_SHOW_OPTIONS -> { ACTION_SHOW_OPTIONS -> {
@ -1117,18 +1236,11 @@ class ResultViewModel2 : ViewModel() {
showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT) showToast(activity, R.string.play_episode_toast, Toast.LENGTH_SHORT)
} }
ACTION_DOWNLOAD_EPISODE -> { ACTION_DOWNLOAD_EPISODE -> {
val response = currentResponse ?: return ioSafe {
downloadEpisode( val response = currentResponse ?: return@ioSafe
activity, val req = getRequest(click.data) ?: return@ioSafe
click.data, Aria2Starter.client?.downloadFailQueue(req.links) { _, _ -> }
response.isMovie(), }
response.name,
response.type,
response.posterUrl,
response.apiName,
response.getId(),
response.url
)
} }
ACTION_DOWNLOAD_MIRROR -> { ACTION_DOWNLOAD_MIRROR -> {
val response = currentResponse ?: return val response = currentResponse ?: return
@ -1138,8 +1250,7 @@ class ResultViewModel2 : ViewModel() {
txt(R.string.episode_action_download_mirror) txt(R.string.episode_action_download_mirror)
) { (result, index) -> ) { (result, index) ->
ioSafe { ioSafe {
startDownload( val req = getDownloadRequest(
activity,
click.data, click.data,
response.isMovie(), response.isMovie(),
response.name, response.name,
@ -1150,13 +1261,9 @@ class ResultViewModel2 : ViewModel() {
response.url, response.url,
listOf(result.links[index]), listOf(result.links[index]),
result.subs, result.subs,
) ) ?: return@ioSafe
downloadFromRequest(activity, req)
} }
showToast(
activity,
R.string.download_started,
Toast.LENGTH_SHORT
)
} }
} }
ACTION_RELOAD_EPISODE -> { ACTION_RELOAD_EPISODE -> {
@ -1592,7 +1699,8 @@ class ResultViewModel2 : ViewModel() {
val idIndex = ep.key.id val idIndex = ep.key.id
for ((index, i) in ep.value.withIndex()) { for ((index, i) in ep.value.withIndex()) {
val episode = i.episode ?: (index + 1) val episode = i.episode ?: (index + 1)
val id = mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000) ?: 0) val id =
mainId + episode + idIndex * 1_000_000 + (i.season?.times(10_000) ?: 0)
if (!existingEpisodes.contains(id)) { if (!existingEpisodes.contains(id)) {
existingEpisodes.add(id) existingEpisodes.add(id)
val seasonData = loadResponse.seasonNames.getSeason(i.season) val seasonData = loadResponse.seasonNames.getSeason(i.season)

View File

@ -22,15 +22,14 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
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.VideoDownloadService import com.lagradost.cloudstream3.services.VideoDownloadService
import com.lagradost.cloudstream3.ui.download.Aria2cHelper
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
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
@ -426,7 +425,7 @@ object VideoDownloadManager {
} }
private const val reservedChars = "|\\?*<\":>+[]/\'" private const val reservedChars = "|\\?*<\":>+[]/\'"
fun sanitizeFilename(name: String, removeSpaces: Boolean= false): String { fun sanitizeFilename(name: String, removeSpaces: Boolean = false): String {
var tempName = name var tempName = name
for (c in reservedChars) { for (c in reservedChars) {
tempName = tempName.replace(c, ' ') tempName = tempName.replace(c, ' ')
@ -531,6 +530,7 @@ object VideoDownloadManager {
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY),
projection, selection, null, null projection, selection, null, null
) )
println("result:$result ${result?.count}")
result.use { c -> result.use { c ->
if (c != null && c.count >= 1) { if (c != null && c.count >= 1) {
@ -941,7 +941,7 @@ object VideoDownloadManager {
* @param directoryName if null will use the current path. * @param directoryName if null will use the current path.
* @return UniFile / null if createMissingDirectories = false and folder is not found. * @return UniFile / null if createMissingDirectories = false and folder is not found.
* */ * */
private fun UniFile.gotoDir( fun UniFile.gotoDir(
directoryName: String?, directoryName: String?,
createMissingDirectories: Boolean = true createMissingDirectories: Boolean = true
): UniFile? { ): UniFile? {
@ -995,7 +995,7 @@ object VideoDownloadManager {
} }
} }
private fun getDisplayName(name: String, extension: String): String { fun getDisplayName(name: String, extension: String): String {
return "$name.$extension" return "$name.$extension"
} }
@ -1487,27 +1487,50 @@ object VideoDownloadManager {
fun getDownloadFileInfoAndUpdateSettings(context: Context, id: Int): DownloadedFileInfoResult? { fun getDownloadFileInfoAndUpdateSettings(context: Context, id: Int): DownloadedFileInfoResult? {
val res = getDownloadFileInfo(context, id) val res = getDownloadFileInfo(context, id)
if (res == null) context.removeKey(KEY_DOWNLOAD_INFO, id.toString()) if (res == null) {
Aria2cHelper.getMetadata(id.toLong())?.let { data ->
if (Aria2cHelper.downloadExist(data)) {
return DownloadedFileInfoResult(
data.downloadedLength,
data.totalLength,
Uri.EMPTY
)
}
}
Aria2cHelper.deleteId(id.toLong())
context.removeKey(KEY_DOWNLOAD_INFO, id.toString())
}
return res return res
} }
private fun getDownloadFileInfo(context: Context, id: Int): DownloadedFileInfoResult? { private fun getDownloadFileInfo(context: Context, id: Int): DownloadedFileInfoResult? {
try { try {
println("getDownloadFileInfo:$id")
val info = val info =
context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return null context.getKey<DownloadedFileInfo>(KEY_DOWNLOAD_INFO, id.toString()) ?: return null
val base = basePathToFile(context, info.basePath) val base = basePathToFile(context, info.basePath)
println("BASE:$info")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && base.isDownloadDir()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && base.isDownloadDir()) {
val cr = context.contentResolver ?: return null val cr = context.contentResolver ?: return null
println("CR:$cr")
val fileUri = val fileUri =
cr.getExistingDownloadUriOrNullQ(info.relativePath, info.displayName) cr.getExistingDownloadUriOrNullQ(info.relativePath, info.displayName)
?: return null ?: return null
println("FILEURI:$fileUri")
val fileLength = cr.getFileLength(fileUri) ?: return null val fileLength = cr.getFileLength(fileUri) ?: return null
println("fileLength:$fileLength")
if (fileLength == 0L) return null if (fileLength == 0L) return null
return DownloadedFileInfoResult(fileLength, info.totalBytes, fileUri) return DownloadedFileInfoResult(fileLength, info.totalBytes, fileUri)
} else { } else {
println("STUFF:$base")
val file = base?.gotoDir(info.relativePath, false)?.findFile(info.displayName) val file = base?.gotoDir(info.relativePath, false)?.findFile(info.displayName)
println("file:$file")
// val normalPath = context.getNormalPath(getFile(info.relativePath), info.displayName) // val normalPath = context.getNormalPath(getFile(info.relativePath), info.displayName)
// val dFile = File(normalPath) // val dFile = File(normalPath)
@ -1612,20 +1635,20 @@ object VideoDownloadManager {
.mapIndexed { index, any -> DownloadQueueResumePackage(index, any) } .mapIndexed { index, any -> DownloadQueueResumePackage(index, any) }
.toTypedArray() .toTypedArray()
setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue) setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue)
} catch (t : Throwable) { } catch (t: Throwable) {
logError(t) logError(t)
} }
} }
/*fun isMyServiceRunning(context: Context, serviceClass: Class<*>): Boolean { /*fun isMyServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager?
for (service in manager!!.getRunningServices(Int.MAX_VALUE)) { for (service in manager!!.getRunningServices(Int.MAX_VALUE)) {
if (serviceClass.name == service.service.className) { if (serviceClass.name == service.service.className) {
return true return true
}
} }
return false }
}*/ return false
}*/
fun downloadEpisode( fun downloadEpisode(
context: Context?, context: Context?,

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
style="@style/BlackButton"
android:id="@+id/download_big_button"
android:layout_width="match_parent"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:visibility="visible" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<include
android:layout_gravity="center"
layout="@layout/download_button_view"
android:layout_width="35dp"
android:layout_height="35dp" />
<TextView
android:id="@+id/result_movie_download_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:letterSpacing="0.09"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
tools:text="Downloading" />
<TextView
android:id="@+id/result_movie_download_text_precentage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:letterSpacing="0.09"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="68%"
tools:visibility="visible" />
</LinearLayout>
</FrameLayout>

View File

@ -1,118 +1,95 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:nextFocusRight="@id/download_child_episode_download" android:id="@+id/download_child_episode_holder"
android:nextFocusLeft="@id/nav_rail_view" android:layout_width="match_parent"
android:id="@+id/download_child_episode_holder" android:layout_height="50dp"
android:layout_width="match_parent" android:layout_marginBottom="5dp"
android:layout_height="50dp" android:foreground="@drawable/outline_drawable"
app:cardCornerRadius="@dimen/rounded_image_radius" android:nextFocusLeft="@id/nav_rail_view"
app:cardBackgroundColor="@color/transparent" android:nextFocusRight="@id/download_child_episode_download"
app:cardElevation="0dp" app:cardBackgroundColor="@color/transparent"
android:foreground="@drawable/outline_drawable" app:cardCornerRadius="@dimen/rounded_image_radius"
android:layout_marginBottom="5dp"> app:cardElevation="0dp">
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/download_child_episode_progress" android:id="@+id/download_child_episode_progress"
android:layout_marginBottom="-1.5dp" style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:progressTint="?attr/colorPrimary" android:layout_width="match_parent"
android:progressBackgroundTint="?attr/colorPrimary" android:layout_height="5dp"
style="@android:style/Widget.Material.ProgressBar.Horizontal" android:layout_gravity="bottom"
android:layout_width="match_parent" android:layout_marginBottom="-1.5dp"
tools:progress="50" android:progressBackgroundTint="?attr/colorPrimary"
android:layout_gravity="bottom" android:progressTint="?attr/colorPrimary"
android:layout_height="5dp" /> tools:progress="50" />
<GridLayout <GridLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="match_parent"> android:foreground="?android:attr/selectableItemBackgroundBorderless">
<ImageView <ImageView
android:id="@+id/download_child_episode_play" android:id="@+id/download_child_episode_play"
android:visibility="gone" android:layout_gravity="center_vertical"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_gravity="center_vertical" android:contentDescription="@string/episode_play_img_des"
android:src="@drawable/ic_baseline_play_arrow_24" android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/episode_play_img_des" /> android:visibility="gone" />
<LinearLayout <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="50dp"
android:orientation="vertical">
<TextView
android:id="@+id/download_child_episode_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:orientation="vertical" android:layout_marginStart="10dp"
android:layout_height="wrap_content" android:layout_marginEnd="10dp"
android:layout_marginEnd="50dp"
android:layout_width="match_parent"> android:ellipsize="marquee"
android:gravity="center_vertical"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="?attr/textColor"
tools:text="Episode 1 Episode 1 Episode 1 Episode 1 Episode 1 Episode 1 Episode 1" />
<TextView <TextView
android:id="@+id/download_child_episode_text" android:id="@+id/download_child_episode_text_extra"
android:layout_marginStart="10dp" android:layout_width="wrap_content"
android:layout_marginEnd="10dp" android:layout_height="match_parent"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:gravity="center_vertical" android:layout_marginStart="10dp"
tools:text="Episode 1 Episode 1 Episode 1 Episode 1 Episode 1 Episode 1 Episode 1" android:layout_marginEnd="10dp"
android:gravity="center_vertical"
android:scrollHorizontally="true" android:textColor="?attr/grayTextColor"
android:ellipsize="marquee" tools:text="128MB / 237MB" />
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:textColor="?attr/textColor"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<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="?attr/grayTextColor"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout> </LinearLayout>
<FrameLayout
android:layout_marginStart="-50dp"
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/download_child_episode_progress_downloaded"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:layout_width="40dp"
android:layout_height="40dp"
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:nextFocusRight="@id/download_child_episode_holder"
android:nextFocusLeft="@id/download_child_episode_holder"
android:id="@+id/download_child_episode_download"
android:visibility="visible"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="10dp"
android:layout_width="50dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
app:tint="?attr/textColor"
android:contentDescription="@string/download" />
</FrameLayout>
</GridLayout> </GridLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end">
<com.lagradost.fetchbutton.ui.PieFetchButton
android:id="@+id/download_child_episode_download"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:padding="10dp" />
</FrameLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -1,103 +1,66 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:id="@+id/episode_holder"
android:layout_height="wrap_content" android:layout_width="match_parent"
app:cardCornerRadius="@dimen/rounded_image_radius" android:layout_height="wrap_content"
app:cardBackgroundColor="?attr/boxItemBackground" android:layout_marginStart="10dp"
android:id="@+id/episode_holder" android:layout_marginTop="10dp"
android:foreground="@drawable/outline_drawable" android:layout_marginEnd="10dp"
android:layout_marginStart="10dp" android:foreground="@drawable/outline_drawable"
android:layout_marginEnd="10dp" app:cardBackgroundColor="?attr/boxItemBackground"
android:layout_marginTop="10dp"> app:cardCornerRadius="@dimen/rounded_image_radius">
<LinearLayout <LinearLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:orientation="horizontal" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_height="wrap_content"> android:orientation="horizontal">
<!--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">
<ImageView <ImageView
android:id="@+id/download_header_poster" android:id="@+id/download_header_poster"
tools:src="@drawable/example_poster" android:layout_width="match_parent"
android:scaleType="centerCrop" android:layout_height="match_parent"
android:layout_width="match_parent" android:contentDescription="@string/episode_poster_img_des"
android:layout_height="match_parent" android:scaleType="centerCrop"
android:contentDescription="@string/episode_poster_img_des" /> tools:src="@drawable/example_poster" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<LinearLayout <LinearLayout
android:layout_marginStart="15dp" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_width="match_parent" android:layout_marginStart="15dp"
android:layout_marginEnd="70dp" android:layout_marginEnd="50dp"
android:layout_height="wrap_content"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/download_header_title" android:id="@+id/download_header_title"
tools:text="Perfect Run"
android:textStyle="bold"
android:textColor="?attr/textColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/download_header_info"
tools:text="1 episode | 285MB"
android:textColor="?attr/grayTextColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView
android:layout_marginStart="-50dp"
android:layout_height="match_parent"
android:padding="50dp"
android:layout_width="50dp"
android:id="@+id/download_header_goto_child"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_baseline_keyboard_arrow_right_24"
android:contentDescription="@string/download" />
<FrameLayout
android:layout_marginStart="-50dp"
android:layout_gravity="end"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:textColor="?attr/textColor"
android:textStyle="bold"
tools:text="Perfect Run" />
<androidx.core.widget.ContentLoadingProgressBar <TextView
android:layout_marginEnd="10dp" android:id="@+id/download_header_info"
android:layout_marginStart="10dp" android:layout_width="wrap_content"
android:layout_width="40dp" android:layout_height="wrap_content"
android:layout_height="40dp" android:textColor="?attr/grayTextColor"
android:id="@+id/download_header_progress_downloaded" tools:text="1 episode | 285MB" />
android:indeterminate="false" </LinearLayout>
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="center_vertical"
android:progress="0"
android:visibility="visible" />
<ImageView
android:visibility="visible"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:padding="10dp"
android:layout_width="50dp"
android:id="@+id/download_header_episode_download"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download"
app:tint="?attr/white" />
</FrameLayout>
</LinearLayout> </LinearLayout>
<com.lagradost.fetchbutton.ui.PieFetchButton
android:id="@+id/download_header_download"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|end"
android:background="?selectableItemBackgroundBorderless"
android:padding="10dp" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -569,90 +569,12 @@
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:layout_width="match_parent" />--> android:layout_width="match_parent" />-->
<com.lagradost.cloudstream3.ui.DownloadButton
<FrameLayout android:id="@+id/result_download_movie"
android:id="@+id/result_movie_progress_downloaded_holder" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="wrap_content"> android:layout_gravity="center"
app:aria2c_layout="@layout/download_button_layout" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_download_movie"
style="@style/BlackButton"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:clickable="true"
android:focusable="true"
android:nextFocusUp="@id/result_play_movie"
android:nextFocusDown="@id/result_season_button"
android:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/result_movie_progress_downloaded"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:layout_margin="5dp"
android:background="@drawable/circle_shape"
android:indeterminate="false"
android:max="100"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:progress="30"
android:progressDrawable="@drawable/circular_progress_bar_filled"
android:visibility="visible" />
<ImageView
android:id="@+id/result_movie_download_icon"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/download"
android:src="@drawable/ic_baseline_play_arrow_24"
android:visibility="visible"
app:tint="?attr/white" />
<TextView
android:id="@+id/result_movie_download_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:letterSpacing="0.09"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
tools:text="Downloading" />
<TextView
android:id="@+id/result_movie_download_text_precentage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:letterSpacing="0.09"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="68%" />
</LinearLayout>
</FrameLayout>
<!--<androidx.core.widget.ContentLoadingProgressBar <!--<androidx.core.widget.ContentLoadingProgressBar
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -418,109 +418,25 @@
</com.google.android.material.button.MaterialButton> </com.google.android.material.button.MaterialButton>
<com.lagradost.cloudstream3.ui.DownloadButton
<FrameLayout android:id="@+id/result_download_movie"
android:nextFocusRight="@id/result_bookmark_button"
android:id="@+id/result_movie_progress_downloaded_holder"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_weight="1" android:layout_weight="1"
android:minWidth="250dp"> android:minWidth="250dp"
android:layout_height="wrap_content" />
<com.google.android.material.button.MaterialButton
android:id="@+id/result_download_movie"
style="@style/BlackButton"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@id/result_play_movie"
android:nextFocusUp="@id/result_cast_items"
android:nextFocusDown="@id/result_resume_series_button_play"
android:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/result_movie_progress_downloaded"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="end|center_vertical"
android:layout_margin="5dp"
android:background="@drawable/circle_shape"
android:indeterminate="false"
android:max="100"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:progress="30"
android:progressDrawable="@drawable/circular_progress_bar_filled"
android:visibility="visible" />
<ImageView
android:id="@+id/result_movie_download_icon"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/download"
android:src="@drawable/ic_baseline_play_arrow_24"
android:visibility="visible"
app:tint="?attr/white" />
<TextView
android:id="@+id/result_movie_download_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:letterSpacing="0.09"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
tools:text="Downloading" />
<TextView
android:id="@+id/result_movie_download_text_precentage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:letterSpacing="0.09"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:textAllCaps="false"
android:textColor="?attr/textColor"
android:textSize="15sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="68%" />
</LinearLayout>
</FrameLayout>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/result_movie_progress_downloaded_holder"
android:nextFocusDown="@id/result_resume_series_button_play"
android:id="@+id/result_bookmark_button" android:id="@+id/result_bookmark_button"
style="@style/BlackButton" style="@style/BlackButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:layout_weight="1" android:layout_weight="1"
android:minWidth="250dp" android:minWidth="250dp"
android:nextFocusLeft="@id/result_download_movie"
android:nextFocusDown="@id/result_resume_series_button_play"
android:text="@string/type_none" android:text="@string/type_none"
android:visibility="visible" /> android:visibility="visible" />
</LinearLayout> </LinearLayout>

View File

@ -95,35 +95,13 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar <com.lagradost.fetchbutton.ui.PieFetchButton
android:layout_marginEnd="10dp" android:background="?selectableItemBackgroundBorderless"
android:layout_marginStart="10dp" android:id="@+id/result_episode_download"
android:layout_width="40dp" android:layout_gravity="center"
android:layout_height="40dp" android:padding="10dp"
android:id="@+id/result_episode_progress_downloaded" android:layout_width="50dp"
android:indeterminate="false" android:layout_height="50dp">
android:progressDrawable="@drawable/circular_progress_bar" </com.lagradost.fetchbutton.ui.PieFetchButton>
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" />
<!--
android:nextFocusRight="@id/episode_holder"-->
<ImageView
android:nextFocusLeft="@id/episode_holder"
app:tint="?attr/white"
android:id="@+id/result_episode_download"
android:visibility="visible"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="10dp"
android:layout_width="50dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download" />
</FrameLayout> </FrameLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -1,151 +1,130 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:nextFocusRight="@id/result_episode_download" android:id="@+id/episode_holder_large"
android:id="@+id/episode_holder_large" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginBottom="10dp"
app:cardCornerRadius="@dimen/rounded_image_radius" android:nextFocusRight="@id/result_episode_download"
app:cardBackgroundColor="?attr/boxItemBackground" app:cardBackgroundColor="?attr/boxItemBackground"
android:layout_marginBottom="10dp"> app:cardCornerRadius="@dimen/rounded_image_radius">
<LinearLayout <LinearLayout
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:layout_width="match_parent"
android:padding="10dp" android:layout_height="wrap_content"
android:orientation="vertical" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent" android:orientation="vertical"
android:layout_height="wrap_content"> android:padding="10dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:orientation="horizontal" android:layout_height="wrap_content"
android:layout_height="wrap_content"> android:orientation="horizontal">
<!--app:cardCornerRadius="@dimen/roundedImageRadius"--> <!--app:cardCornerRadius="@dimen/roundedImageRadius"-->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="126dp" android:layout_width="126dp"
android:layout_height="72dp" android:layout_height="72dp"
android:foreground="@drawable/outline_drawable"> android:foreground="@drawable/outline_drawable">
<ImageView <ImageView
android:nextFocusRight="@id/result_episode_download" android:id="@+id/episode_poster"
android:id="@+id/episode_poster" android:layout_width="match_parent"
tools:src="@drawable/example_poster" android:layout_height="match_parent"
android:foreground="?android:attr/selectableItemBackgroundBorderless" android:contentDescription="@string/episode_poster_img_des"
android:scaleType="centerCrop" android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:layout_width="match_parent" android:nextFocusRight="@id/result_episode_download"
android:layout_height="match_parent" android:scaleType="centerCrop"
android:contentDescription="@string/episode_poster_img_des" /> tools:src="@drawable/example_poster" />
<ImageView <ImageView
android:src="@drawable/play_button" android:layout_width="36dp"
android:layout_gravity="center" android:layout_height="36dp"
android:layout_width="36dp" android:layout_gravity="center"
android:layout_height="36dp" android:contentDescription="@string/play_episode"
android:contentDescription="@string/play_episode" /> android:src="@drawable/play_button" />
<androidx.core.widget.ContentLoadingProgressBar <androidx.core.widget.ContentLoadingProgressBar
android:layout_marginBottom="-1.5dp" android:id="@+id/episode_progress"
android:id="@+id/episode_progress" style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:progressTint="?attr/colorPrimary" android:layout_width="match_parent"
android:progressBackgroundTint="?attr/colorPrimary" android:layout_height="5dp"
style="@android:style/Widget.Material.ProgressBar.Horizontal" android:layout_gravity="bottom"
android:layout_width="match_parent" android:layout_marginBottom="-1.5dp"
tools:progress="50" android:progressBackgroundTint="?attr/colorPrimary"
android:layout_gravity="bottom" android:progressTint="?attr/colorPrimary"
android:layout_height="5dp" /> tools:progress="50" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<LinearLayout <LinearLayout
android:layout_marginStart="15dp" android:layout_width="match_parent"
android:orientation="vertical" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_width="match_parent" android:layout_marginStart="15dp"
android:layout_marginEnd="50dp" android:layout_marginEnd="50dp"
android:layout_height="wrap_content"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:orientation="horizontal" android:layout_width="wrap_content"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_height="wrap_content"> android:orientation="horizontal">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:layout_gravity="start" android:id="@+id/episode_filler"
style="@style/SmallBlackButton" style="@style/SmallBlackButton"
android:layout_marginEnd="10dp" android:layout_gravity="start"
android:text="@string/filler" android:layout_marginEnd="10dp"
android:id="@+id/episode_filler" /> android:text="@string/filler" />
<TextView <TextView
android:layout_gravity="center_vertical" android:id="@+id/episode_text"
android:id="@+id/episode_text" android:layout_width="wrap_content"
tools:text="1. Jobless" android:layout_height="wrap_content"
android:textStyle="bold" android:layout_gravity="center_vertical"
android:textColor="?attr/textColor" android:textColor="?attr/textColor"
android:layout_width="wrap_content" android:textStyle="bold"
android:layout_height="wrap_content" /> tools:text="1. Jobless" />
</LinearLayout> </LinearLayout>
<TextView <TextView
android:id="@+id/episode_rating" android:id="@+id/episode_rating"
tools:text="Rated: 8.8" android:layout_width="wrap_content"
android:textColor="?attr/grayTextColor" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:textColor="?attr/grayTextColor"
android:layout_height="wrap_content" /> tools:text="Rated: 8.8" />
</LinearLayout> </LinearLayout>
<FrameLayout <FrameLayout
android:layout_marginStart="-50dp" android:layout_width="wrap_content"
android:layout_gravity="end" android:layout_height="match_parent"
android:layout_width="wrap_content" android:layout_gravity="end"
android:layout_height="match_parent"> android:layout_marginStart="-50dp">
<androidx.core.widget.ContentLoadingProgressBar <com.lagradost.fetchbutton.ui.PieFetchButton
android:layout_marginEnd="10dp" android:background="?selectableItemBackgroundBorderless"
android:layout_marginStart="10dp" android:id="@+id/result_episode_download"
android:layout_width="40dp" android:layout_gravity="center"
android:layout_height="40dp" android:padding="5dp"
android:id="@+id/result_episode_progress_downloaded" android:layout_width="50dp"
android:indeterminate="false" android:layout_height="50dp">
android:progressDrawable="@drawable/circular_progress_bar" </com.lagradost.fetchbutton.ui.PieFetchButton>
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:nextFocusLeft="@id/episode_poster"
android:id="@+id/result_episode_download"
android:visibility="visible"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="10dp"
android:layout_width="50dp"
android:background="?selectableItemBackgroundBorderless"
android:src="@drawable/ic_baseline_play_arrow_24"
android:contentDescription="@string/download"
app:tint="?attr/white" />
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
<TextView <TextView
android:maxLines="4" android:id="@+id/episode_descript"
android:ellipsize="end" android:layout_width="match_parent"
android:paddingTop="10dp" android:layout_height="wrap_content"
android:paddingBottom="10dp" android:ellipsize="end"
android:id="@+id/episode_descript" android:maxLines="4"
android:textColor="?attr/grayTextColor" android:paddingTop="10dp"
tools:text="Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart. Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart." android:paddingBottom="10dp"
android:layout_width="match_parent" android:textColor="?attr/grayTextColor"
android:layout_height="wrap_content" /> tools:text="Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart. Jon and Daenerys arrive in Winterfell and are met with skepticism. Sam learns about the fate of his family. Cersei gives Euron the reward he aims for. Theon follows his heart." />
</LinearLayout> </LinearLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -22,6 +22,10 @@
<color name="white">#FFF</color> <color name="white">#FFF</color>
<color name="black">#000</color> <color name="black">#000</color>
<color name="whiteText">#FFF</color>
<color name="blackText">#000</color>
<color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1--> <color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1-->
<color name="amoledModeLight">#121213</color> <color name="amoledModeLight">#121213</color>

View File

@ -49,11 +49,14 @@
<item name="android:windowAllowReturnTransitionOverlap">true</item> <item name="android:windowAllowReturnTransitionOverlap">true</item>
<item name="android:windowAllowEnterTransitionOverlap">true</item> <item name="android:windowAllowEnterTransitionOverlap">true</item>
<!--<item name="preferenceTheme">@style/PreferencesTheme</item>--> <!--<item name="preferenceTheme">@style/PreferencesTheme</item>-->
<!-- DEF STYLE --> <!-- DEF STYLE -->
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorOnPrimary">@color/whiteText</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="textColor">@color/textColor</item> <item name="textColor">@color/textColor</item>
<item name="grayTextColor">@color/grayTextColor</item> <item name="grayTextColor">@color/grayTextColor</item>
@ -65,6 +68,10 @@
<item name="white">#FFF</item> <item name="white">#FFF</item>
<item name="preferenceTheme">@style/CustomPreferenceThemeOverlay</item> <item name="preferenceTheme">@style/CustomPreferenceThemeOverlay</item>
<item name="aria2c_icon_color">?attr/white</item>
<item name="aria2c_fill_color">?attr/white</item>
<item name="aria2c_outline_color">?attr/white</item>
<item name="aria2c_icon_scale">0.7</item>
</style> </style>
<style name="ListViewStyle" parent="Widget.AppCompat.ListView"> <style name="ListViewStyle" parent="Widget.AppCompat.ListView">
@ -100,7 +107,7 @@
<item name="android:colorPrimary">@color/colorPrimary</item> <item name="android:colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="colorOnPrimary">@color/colorAccent</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorAccent</item> <item name="android:colorAccent">@color/colorAccent</item>
</style> </style>
@ -110,7 +117,7 @@
<item name="android:colorPrimary">@color/colorPrimaryBlue</item> <item name="android:colorPrimary">@color/colorPrimaryBlue</item>
<item name="colorPrimaryDark">#4855A2</item> <item name="colorPrimaryDark">#4855A2</item>
<item name="colorAccent">#5A6BCB</item> <item name="colorAccent">#5A6BCB</item>
<item name="colorOnPrimary">#5A6BCB</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryBlue</item> <item name="android:colorAccent">@color/colorPrimaryBlue</item>
</style> </style>
@ -120,7 +127,7 @@
<item name="android:colorPrimary">@color/colorPrimaryPurple</item> <item name="android:colorPrimary">@color/colorPrimaryPurple</item>
<item name="colorPrimaryDark">#4704A3</item> <item name="colorPrimaryDark">#4704A3</item>
<item name="colorAccent">#7125DB</item> <item name="colorAccent">#7125DB</item>
<item name="colorOnPrimary">#7125DB</item> <item name="colorOnPrimary">@color/whiteText</item>
<item name="android:colorAccent">@color/colorPrimaryPurple</item> <item name="android:colorAccent">@color/colorPrimaryPurple</item>
</style> </style>
@ -129,7 +136,7 @@
<item name="android:colorPrimary">@color/colorPrimaryGreen</item> <item name="android:colorPrimary">@color/colorPrimaryGreen</item>
<item name="colorPrimaryDark">#007363</item> <item name="colorPrimaryDark">#007363</item>
<item name="colorAccent">#39C1AE</item> <item name="colorAccent">#39C1AE</item>
<item name="colorOnPrimary">#39C1AE</item> <item name="colorOnPrimary">@color/blackText</item>
<item name="android:colorAccent">@color/colorPrimaryGreen</item> <item name="android:colorAccent">@color/colorPrimaryGreen</item>
</style> </style>
@ -138,7 +145,7 @@
<item name="android:colorPrimary">@color/colorPrimaryGreenApple</item> <item name="android:colorPrimary">@color/colorPrimaryGreenApple</item>
<item name="colorPrimaryDark">#319B5A</item> <item name="colorPrimaryDark">#319B5A</item>
<item name="colorAccent">#51C57E</item> <item name="colorAccent">#51C57E</item>
<item name="colorOnPrimary">#51C57E</item> <item name="colorOnPrimary">@color/blackText</item>
<item name="android:colorAccent">@color/colorPrimaryGreenApple</item> <item name="android:colorAccent">@color/colorPrimaryGreenApple</item>
</style> </style>
@ -147,7 +154,7 @@
<item name="android:colorPrimary">@color/colorPrimaryRed</item> <item name="android:colorPrimary">@color/colorPrimaryRed</item>
<item name="colorPrimaryDark">#B62B2B</item> <item name="colorPrimaryDark">#B62B2B</item>
<item name="colorAccent">@color/colorPrimaryRed</item> <!--#F53B3B--> <item name="colorAccent">@color/colorPrimaryRed</item> <!--#F53B3B-->
<item name="colorOnPrimary">@color/colorPrimaryRed</item> <!--#EC3838--> <item name="colorOnPrimary">@color/whiteText</item> <!--#EC3838-->
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryRed</item> <item name="android:colorAccent">@color/colorPrimaryRed</item>
</style> </style>
@ -157,7 +164,7 @@
<item name="android:colorPrimary">@color/colorPrimaryBanana</item> <item name="android:colorPrimary">@color/colorPrimaryBanana</item>
<item name="colorPrimaryDark">#9B7D31</item> <item name="colorPrimaryDark">#9B7D31</item>
<item name="colorAccent">#C5B251</item> <item name="colorAccent">#C5B251</item>
<item name="colorOnPrimary">#C5A851</item> <item name="colorOnPrimary">@color/blackText</item>
<item name="android:colorAccent">@color/colorPrimaryBanana</item> <item name="android:colorAccent">@color/colorPrimaryBanana</item>
</style> </style>
@ -166,7 +173,7 @@
<item name="android:colorPrimary">@color/colorPrimaryParty</item> <item name="android:colorPrimary">@color/colorPrimaryParty</item>
<item name="colorPrimaryDark">#C1495B</item> <item name="colorPrimaryDark">#C1495B</item>
<item name="colorAccent">#FD798C</item> <item name="colorAccent">#FD798C</item>
<item name="colorOnPrimary">#BF5968</item> <item name="colorOnPrimary">@color/blackText</item>
<item name="android:colorAccent">@color/colorPrimaryParty</item> <item name="android:colorAccent">@color/colorPrimaryParty</item>
</style> </style>
@ -175,7 +182,7 @@
<item name="android:colorPrimary">@color/colorPrimaryPink</item> <item name="android:colorPrimary">@color/colorPrimaryPink</item>
<item name="colorPrimaryDark">#DD1280</item> <item name="colorPrimaryDark">#DD1280</item>
<item name="colorAccent">#FF4DAE</item> <item name="colorAccent">#FF4DAE</item>
<item name="colorOnPrimary">#DD1280</item> <item name="colorOnPrimary">@color/blackText</item>
<item name="android:colorAccent">@color/colorPrimaryPink</item> <item name="android:colorAccent">@color/colorPrimaryPink</item>
</style> </style>
@ -184,7 +191,7 @@
<item name="android:colorPrimary">@color/colorPrimaryCarnationPink</item> <item name="android:colorPrimary">@color/colorPrimaryCarnationPink</item>
<item name="colorPrimaryDark">#83366f</item> <item name="colorPrimaryDark">#83366f</item>
<item name="colorAccent">#BD5DA5</item> <item name="colorAccent">#BD5DA5</item>
<item name="colorOnPrimary">#BD5DA5</item> <item name="colorOnPrimary">@color/blackText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryCarnationPink</item> <item name="android:colorAccent">@color/colorPrimaryCarnationPink</item>
</style> </style>
@ -194,7 +201,7 @@
<item name="android:colorPrimary">@color/colorPrimaryMaroon</item> <item name="android:colorPrimary">@color/colorPrimaryMaroon</item>
<item name="colorPrimaryDark">#370C0C</item> <item name="colorPrimaryDark">#370C0C</item>
<item name="colorAccent">#451010</item> <item name="colorAccent">#451010</item>
<item name="colorOnPrimary">#451010</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryMaroon</item> <item name="android:colorAccent">@color/colorPrimaryMaroon</item>
</style> </style>
@ -204,7 +211,7 @@
<item name="android:colorPrimary">@color/colorPrimaryDarkGreen</item> <item name="android:colorPrimary">@color/colorPrimaryDarkGreen</item>
<item name="colorPrimaryDark">#003d00</item> <item name="colorPrimaryDark">#003d00</item>
<item name="colorAccent">#004500</item> <item name="colorAccent">#004500</item>
<item name="colorOnPrimary">#004500</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryDarkGreen</item> <item name="android:colorAccent">@color/colorPrimaryDarkGreen</item>
</style> </style>
@ -214,7 +221,7 @@
<item name="android:colorPrimary">@color/colorPrimaryNavyBlue</item> <item name="android:colorPrimary">@color/colorPrimaryNavyBlue</item>
<item name="colorPrimaryDark">#000073</item> <item name="colorPrimaryDark">#000073</item>
<item name="colorAccent">#000080</item> <item name="colorAccent">#000080</item>
<item name="colorOnPrimary">#000080</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryNavyBlue</item> <item name="android:colorAccent">@color/colorPrimaryNavyBlue</item>
</style> </style>
@ -224,7 +231,7 @@
<item name="android:colorPrimary">@color/colorPrimaryGrey</item> <item name="android:colorPrimary">@color/colorPrimaryGrey</item>
<item name="colorPrimaryDark">#484848</item> <item name="colorPrimaryDark">#484848</item>
<item name="colorAccent">#515151</item> <item name="colorAccent">#515151</item>
<item name="colorOnPrimary">#515151</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryGrey</item> <item name="android:colorAccent">@color/colorPrimaryGrey</item>
</style> </style>
@ -234,7 +241,7 @@
<item name="android:colorPrimary">@color/colorPrimaryWhite</item> <item name="android:colorPrimary">@color/colorPrimaryWhite</item>
<item name="colorPrimaryDark">#CCCCCC</item> <item name="colorPrimaryDark">#CCCCCC</item>
<item name="colorAccent">#FFFFFF</item> <item name="colorAccent">#FFFFFF</item>
<item name="colorOnPrimary">#FFFFFF</item> <item name="colorOnPrimary">@color/blackText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryWhite</item> <item name="android:colorAccent">@color/colorPrimaryWhite</item>
</style> </style>
@ -244,7 +251,7 @@
<item name="android:colorPrimary">@color/colorPrimaryBrown</item> <item name="android:colorPrimary">@color/colorPrimaryBrown</item>
<item name="colorPrimaryDark">#582700</item> <item name="colorPrimaryDark">#582700</item>
<item name="colorAccent">#622C00</item> <item name="colorAccent">#622C00</item>
<item name="colorOnPrimary">#622C00</item> <item name="colorOnPrimary">@color/whiteText</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/colorPrimaryBrown</item> <item name="android:colorAccent">@color/colorPrimaryBrown</item>
</style> </style>