From 8ff441172e8c9a4ce2b15a84ab20079ce38ca201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C3=87erri?= <64997243+deniscerri@users.noreply.github.com> Date: Sun, 26 Feb 2023 19:24:51 +0100 Subject: [PATCH] implemented download queue --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 10 + .../com/deniscerri/ytdlnis/MainActivity.kt | 4 +- .../ytdlnis/adapter/ActiveDownloadAdapter.kt | 166 ++++++---------- .../ytdlnis/adapter/QueuedDownloadAdapter.kt | 141 +++++--------- .../ytdlnis/ui/DownloadQueueActivity.kt | 176 +++++++++++++++++ .../com/deniscerri/ytdlnis/ui/HomeFragment.kt | 13 -- .../deniscerri/ytdlnis/work/DownloadWorker.kt | 4 +- .../main/res/layout/active_download_card.xml | 179 ++++++++++++++++++ .../res/layout/activity_command_templates.xml | 42 ++++ .../res/layout/activity_download_queue.xml | 99 ++++++++++ .../main/res/layout/queued_download_card.xml | 169 +++++++++++++++++ app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/more_preferences.xml | 9 + 15 files changed, 796 insertions(+), 222 deletions(-) create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/ui/DownloadQueueActivity.kt create mode 100644 app/src/main/res/layout/active_download_card.xml create mode 100644 app/src/main/res/layout/activity_command_templates.xml create mode 100644 app/src/main/res/layout/activity_download_queue.xml create mode 100644 app/src/main/res/layout/queued_download_card.xml diff --git a/app/build.gradle b/app/build.gradle index b0c1397c..2eec33c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,7 +120,7 @@ dependencies { implementation "androidx.appcompat:appcompat:$appCompatVer" implementation "androidx.constraintlayout:constraintlayout:2.1.4" - implementation 'com.google.android.material:material:1.9.0-alpha02' + implementation 'com.google.android.material:material:1.7.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.core:core:1.9.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cb071837..3e366f27 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -65,6 +65,16 @@ + + + + + + { if (lastFragment === historyFragment) { - historyFragment.scrollToTop() + val intent = Intent(context, DownloadQueueActivity::class.java) + startActivity(intent) } else { this.title = getString(R.string.downloads) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ActiveDownloadAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ActiveDownloadAdapter.kt index d6786dd8..cc82153b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ActiveDownloadAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ActiveDownloadAdapter.kt @@ -1,56 +1,48 @@ package com.deniscerri.ytdlnis.adapter -import android.annotation.SuppressLint import android.app.Activity import android.graphics.Color -import android.graphics.ColorMatrix -import android.graphics.ColorMatrixColorFilter import android.os.Handler import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView -import android.widget.LinearLayout import android.widget.TextView -import androidx.core.content.ContextCompat import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.deniscerri.ytdlnis.R -import com.deniscerri.ytdlnis.database.models.HistoryItem -import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel +import com.deniscerri.ytdlnis.database.models.DownloadItem +import com.deniscerri.ytdlnis.util.FileUtil import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView +import com.google.android.material.progressindicator.LinearProgressIndicator import com.squareup.picasso.Picasso -import java.io.File -import java.text.DateFormat -import java.text.SimpleDateFormat -import java.util.* -class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { - private val checkedItems: ArrayList +class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { private val onItemClickListener: OnItemClickListener private val activity: Activity + private val fileUtil: FileUtil init { - checkedItems = ArrayList() this.onItemClickListener = onItemClickListener this.activity = activity + fileUtil = FileUtil() } class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) { val cardView: MaterialCardView init { - cardView = itemView.findViewById(R.id.downloads_card_view) + cardView = itemView.findViewById(R.id.active_download_card_view) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val cardView = LayoutInflater.from(parent.context) - .inflate(R.layout.history_card, parent, false) + .inflate(R.layout.active_download_card, parent, false) return ViewHolder(cardView, onItemClickListener) } @@ -58,7 +50,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: val item = getItem(position) val card = holder.cardView // THUMBNAIL ---------------------------------- - val thumbnail = card.findViewById(R.id.downloads_image_view) + val thumbnail = card.findViewById(R.id.image_view) val imageURL = item!!.thumb val uiHandler = Handler(Looper.getMainLooper()) if (imageURL.isNotEmpty()) { @@ -68,126 +60,76 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity: } thumbnail.setColorFilter(Color.argb(95, 0, 0, 0)) + // PROGRESS BAR ---------------------------------------------------- + val progressBar = card.findViewById(R.id.progress) + progressBar.tag = "${item.id}##progress" + progressBar.progress = 0 + progressBar.isIndeterminate = true + // TITLE ---------------------------------- - val itemTitle = card.findViewById(R.id.downloads_title) + val itemTitle = card.findViewById(R.id.title) var title = item.title if (title.length > 100) { title = title.substring(0, 40) + "..." } itemTitle.text = title - // Bottom Info ---------------------------------- - val bottomInfo = card.findViewById(R.id.downloads_info_bottom) + // Author ---------------------------------- + val author = card.findViewById(R.id.author) var info = item.author if (item.duration.isNotEmpty()) { if (item.author.isNotEmpty()) info += " • " info += item.duration } - bottomInfo.text = info + author.text = info - // TIME DOWNLOADED ---------------------------------- - val datetime = card.findViewById(R.id.downloads_info_time) - val time = item.time - val downloadedTime: String - if (time == 0L) { - downloadedTime = activity.getString(R.string.currently_downloading) + " " + item.type - } else { - val cal = Calendar.getInstance() - val date = Date(time * 1000L) - cal.time = date - val day = cal[Calendar.DAY_OF_MONTH] - val month = cal.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) - val year = cal[Calendar.YEAR] - val formatter: DateFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) - val timeString = formatter.format(date) - downloadedTime = "$day $month $year - $timeString" - } - datetime.text = downloadedTime - - // BUTTON ---------------------------------- - val buttonLayout = card.findViewById(R.id.downloads_download_button_layout) - val btn = buttonLayout.findViewById(R.id.downloads_download_button_type) - var filePresent = true - - //IS IN THE FILE SYSTEM? - val path = item.downloadPath - val file = File(path) - if (!file.exists() && path.isNotEmpty()) { - filePresent = false - thumbnail.colorFilter = ColorMatrixColorFilter(object : ColorMatrix() { - init { - setSaturation(0f) - } - }) - thumbnail.alpha = 0.7f - } - if (item.type == DownloadViewModel.Type.audio) { - if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music) - } else { - if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video) - } - if (btn.hasOnClickListeners()) btn.setOnClickListener(null) - if (checkedItems.contains(item.id)) { - card.isChecked = true - card.strokeWidth = 5 - } else { - card.isChecked = false - card.strokeWidth = 0 - } - val finalFilePresent = filePresent - card.setOnLongClickListener { - checkCard(card, item.id) - true - } - card.setOnClickListener { - if (checkedItems.size > 0) { - checkCard(card, item.id) + val formatNote = card.findViewById(R.id.format_note) + formatNote.text = item.format.format_note + val codec = card.findViewById(R.id.codec) + val codecText = + if (item.format.encoding != "") { + item.format.encoding.uppercase() + }else if (item.format.vcodec != "none" && item.format.vcodec != ""){ + item.format.vcodec.uppercase() } else { - onItemClickListener.onCardClick(item.id, finalFilePresent) + item.format.acodec.uppercase() } + if (codecText == "" || codecText == "none"){ + codec.visibility = View.GONE + }else{ + codec.visibility = View.VISIBLE + codec.text = codecText + } + + val fileSize = card.findViewById(R.id.file_size) + fileSize.text = fileUtil.convertFileSize(item.format.filesize) + + //OUTPUT + val output = card.findViewById(R.id.output) + output.tag = "${item.id}##output" + + + // CANCEL BUTTON ---------------------------------- + val cancelButton = card.findViewById(R.id.active_download_cancel) + if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null) + + cancelButton.setOnClickListener { + onItemClickListener.onCancelClick(item.id) } } - - private fun checkCard(card: MaterialCardView, itemID: Long) { - if (card.isChecked) { - card.strokeWidth = 0 - checkedItems.remove(itemID) - } else { - card.strokeWidth = 5 - checkedItems.add(itemID) - } - card.isChecked = !card.isChecked - onItemClickListener.onCardSelect(itemID, card.isChecked) - } - interface OnItemClickListener { - fun onCardClick(itemID: Long, isPresent: Boolean) - fun onCardSelect(itemID: Long, isChecked: Boolean) - fun onButtonClick(position: Int) - } - - @SuppressLint("NotifyDataSetChanged") - fun clearCheckeditems() { - for (i in 0 until itemCount){ - val item = getItem(i) - if (checkedItems.find { it == item?.id } != null){ - checkedItems.remove(item?.id) - notifyItemChanged(i) - } - } - - checkedItems.clear() + fun onCancelClick(itemID: Long) } companion object { - private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean { + private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { val ranged = arrayListOf(oldItem.id, newItem.id) return ranged[0] == ranged[1] } - override fun areContentsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean { - return oldItem.time == newItem.time + override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { + return oldItem.id == newItem.id } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/QueuedDownloadAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/QueuedDownloadAdapter.kt index a2264328..c3674386 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/QueuedDownloadAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/QueuedDownloadAdapter.kt @@ -7,6 +7,7 @@ import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.os.Handler import android.os.Looper +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -19,38 +20,40 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.deniscerri.ytdlnis.R +import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel +import com.deniscerri.ytdlnis.util.FileUtil import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView import com.squareup.picasso.Picasso +import org.w3c.dom.Text import java.io.File import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* -class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { - private val checkedItems: ArrayList +class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) { private val onItemClickListener: OnItemClickListener private val activity: Activity + private var fileUtil: FileUtil init { - checkedItems = ArrayList() this.onItemClickListener = onItemClickListener this.activity = activity + fileUtil = FileUtil() } class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) { val cardView: MaterialCardView - init { - cardView = itemView.findViewById(R.id.downloads_card_view) + cardView = itemView.findViewById(R.id.queued_download_card_view) } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val cardView = LayoutInflater.from(parent.context) - .inflate(R.layout.history_card, parent, false) + .inflate(R.layout.queued_download_card, parent, false) return ViewHolder(cardView, onItemClickListener) } @@ -58,7 +61,7 @@ class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: val item = getItem(position) val card = holder.cardView // THUMBNAIL ---------------------------------- - val thumbnail = card.findViewById(R.id.downloads_image_view) + val thumbnail = card.findViewById(R.id.image_view) val imageURL = item!!.thumb val uiHandler = Handler(Looper.getMainLooper()) if (imageURL.isNotEmpty()) { @@ -69,29 +72,48 @@ class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: thumbnail.setColorFilter(Color.argb(95, 0, 0, 0)) // TITLE ---------------------------------- - val itemTitle = card.findViewById(R.id.downloads_title) + val itemTitle = card.findViewById(R.id.title) var title = item.title if (title.length > 100) { title = title.substring(0, 40) + "..." } itemTitle.text = title - // Bottom Info ---------------------------------- - val bottomInfo = card.findViewById(R.id.downloads_info_bottom) + // Author ---------------------------------- + val author = card.findViewById(R.id.author) var info = item.author if (item.duration.isNotEmpty()) { if (item.author.isNotEmpty()) info += " • " info += item.duration } - bottomInfo.text = info + author.text = info - // TIME DOWNLOADED ---------------------------------- - val datetime = card.findViewById(R.id.downloads_info_time) - val time = item.time - val downloadedTime: String + val formatNote = card.findViewById(R.id.format_note) + formatNote.text = item.format.format_note + val codec = card.findViewById(R.id.codec) + val codecText = + if (item.format.encoding != "") { + item.format.encoding.uppercase() + }else if (item.format.vcodec != "none" && item.format.vcodec != ""){ + item.format.vcodec.uppercase() + } else { + item.format.acodec.uppercase() + } + if (codecText == "" || codecText == "none"){ + codec.visibility = View.GONE + }else{ + codec.visibility = View.VISIBLE + codec.text = codecText + } + + val fileSize = card.findViewById(R.id.file_size) + fileSize.text = fileUtil.convertFileSize(item.format.filesize) + val downloadStartTime = card.findViewById(R.id.time) + val time = item.downloadStartTime if (time == 0L) { - downloadedTime = activity.getString(R.string.currently_downloading) + " " + item.type + downloadStartTime.visibility = View.GONE } else { + downloadStartTime.visibility = View.VISIBLE val cal = Calendar.getInstance() val date = Date(time * 1000L) cal.time = date @@ -100,94 +122,31 @@ class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: val year = cal[Calendar.YEAR] val formatter: DateFormat = SimpleDateFormat("HH:mm", Locale.getDefault()) val timeString = formatter.format(date) - downloadedTime = "$day $month $year - $timeString" + downloadStartTime.text = "$day $month $year - $timeString" } - datetime.text = downloadedTime - // BUTTON ---------------------------------- - val buttonLayout = card.findViewById(R.id.downloads_download_button_layout) - val btn = buttonLayout.findViewById(R.id.downloads_download_button_type) - var filePresent = true + // CANCEL BUTTON ---------------------------------- + val cancelButton = card.findViewById(R.id.queued_download_cancel) + if (cancelButton.hasOnClickListeners()) cancelButton.setOnClickListener(null) - //IS IN THE FILE SYSTEM? - val path = item.downloadPath - val file = File(path) - if (!file.exists() && path.isNotEmpty()) { - filePresent = false - thumbnail.colorFilter = ColorMatrixColorFilter(object : ColorMatrix() { - init { - setSaturation(0f) - } - }) - thumbnail.alpha = 0.7f - } - if (item.type == DownloadViewModel.Type.audio) { - if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_music) - } else { - if (filePresent) btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video_downloaded) else btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video) - } - if (btn.hasOnClickListeners()) btn.setOnClickListener(null) - if (checkedItems.contains(item.id)) { - card.isChecked = true - card.strokeWidth = 5 - } else { - card.isChecked = false - card.strokeWidth = 0 - } - val finalFilePresent = filePresent - card.setOnLongClickListener { - checkCard(card, item.id) - true - } - card.setOnClickListener { - if (checkedItems.size > 0) { - checkCard(card, item.id) - } else { - onItemClickListener.onCardClick(item.id, finalFilePresent) - } + cancelButton.setOnClickListener { + onItemClickListener.onQueuedCancelClick(item.id) } + } - - private fun checkCard(card: MaterialCardView, itemID: Long) { - if (card.isChecked) { - card.strokeWidth = 0 - checkedItems.remove(itemID) - } else { - card.strokeWidth = 5 - checkedItems.add(itemID) - } - card.isChecked = !card.isChecked - onItemClickListener.onCardSelect(itemID, card.isChecked) - } - interface OnItemClickListener { - fun onCardClick(itemID: Long, isPresent: Boolean) - fun onCardSelect(itemID: Long, isChecked: Boolean) - fun onButtonClick(position: Int) - } - - @SuppressLint("NotifyDataSetChanged") - fun clearCheckeditems() { - for (i in 0 until itemCount){ - val item = getItem(i) - if (checkedItems.find { it == item?.id } != null){ - checkedItems.remove(item?.id) - notifyItemChanged(i) - } - } - - checkedItems.clear() + fun onQueuedCancelClick(itemID: Long) } companion object { - private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean { + private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { val ranged = arrayListOf(oldItem.id, newItem.id) return ranged[0] == ranged[1] } - override fun areContentsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean { - return oldItem.time == newItem.time + override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { + return oldItem.url == newItem.url && oldItem.format.format_id == newItem.format.format_id } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/DownloadQueueActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/DownloadQueueActivity.kt new file mode 100644 index 00000000..c3a6a94f --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/DownloadQueueActivity.kt @@ -0,0 +1,176 @@ +package com.deniscerri.ytdlnis.ui + +import android.content.Context +import android.os.Bundle +import android.util.Log +import android.view.MenuItem +import android.view.View +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.work.WorkManager +import com.deniscerri.ytdlnis.R +import com.deniscerri.ytdlnis.adapter.ActiveDownloadAdapter +import com.deniscerri.ytdlnis.adapter.QueuedDownloadAdapter +import com.deniscerri.ytdlnis.database.models.DownloadItem +import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel +import com.deniscerri.ytdlnis.util.NotificationUtil +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.progressindicator.LinearProgressIndicator +import com.yausername.youtubedl_android.YoutubeDL +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.File + + +class DownloadQueueActivity : AppCompatActivity(), ActiveDownloadAdapter.OnItemClickListener, QueuedDownloadAdapter.OnItemClickListener { + private lateinit var activeRecyclerView: RecyclerView + private lateinit var activeDownloads: ActiveDownloadAdapter + private lateinit var queuedRecyclerView: RecyclerView + private lateinit var queuedDownloads: QueuedDownloadAdapter + private lateinit var noResults: RelativeLayout + private lateinit var downloadViewModel: DownloadViewModel + private lateinit var topAppBar: MaterialToolbar + private lateinit var notificationUtil: NotificationUtil + + private lateinit var runningLayout: LinearLayout + private lateinit var queuedLayout: LinearLayout + var context: Context? = null + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_download_queue) + context = baseContext + val view : View = window.decorView.findViewById(android.R.id.content) + + + topAppBar = findViewById(R.id.logs_toolbar) + topAppBar.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() } + + activeDownloads = + ActiveDownloadAdapter( + this, + this@DownloadQueueActivity + ) + + queuedDownloads = + QueuedDownloadAdapter( + this, + this@DownloadQueueActivity + ) + + + activeRecyclerView = findViewById(R.id.active_recyclerview) + activeRecyclerView.layoutManager = LinearLayoutManager(context) + activeRecyclerView.adapter = activeDownloads + + queuedRecyclerView = findViewById(R.id.queued_recyclerview) + queuedRecyclerView.layoutManager = LinearLayoutManager(context) + queuedRecyclerView.adapter = queuedDownloads + + noResults = findViewById(R.id.no_results) + noResults.visibility = View.GONE + + runningLayout = findViewById(R.id.running_layout) + queuedLayout = findViewById(R.id.queued_layout) + + downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] + notificationUtil = NotificationUtil(this) + + + //active downloads livedata + downloadViewModel.activeDownloads.observe(this) { + activeDownloads.submitList(it) + if (it.isEmpty()){ + if (queuedLayout.visibility == View.GONE) noResults.visibility = View.VISIBLE + runningLayout.visibility = View.GONE + }else{ + noResults.visibility = View.GONE + runningLayout.visibility = View.VISIBLE + + it.forEach{item -> + WorkManager.getInstance(this) + .getWorkInfosForUniqueWorkLiveData(item.id.toString()) + .observe(this){ list -> + list.forEach {work -> + if (work == null) return@observe + val id = work.progress.getLong("id", 0L) + if(id == 0L) return@observe + + val progress = work.progress.getInt("progress", 0) + val output = work.progress.getString("output") + + val progressBar = view.findViewWithTag("$id##progress") + val outputText = view.findViewWithTag("$id##output") + + runOnUiThread { + progressBar.setProgressCompat(progress, true) + outputText.text = output + } + } + } + } + } + } + + //queued downloads livedata + downloadViewModel.queuedDownloads.observe(this) { + queuedDownloads.submitList(it) + if (it.isEmpty()){ + if (runningLayout.visibility == View.GONE) noResults.visibility = View.VISIBLE + queuedLayout.visibility = View.GONE + }else{ + noResults.visibility = View.GONE + queuedLayout.visibility = View.VISIBLE + } + } + } + + private fun initMenu(logFolder: File) { + topAppBar.setOnMenuItemClickListener { m: MenuItem -> + val itemId = m.itemId + if (itemId == R.id.remove_logs) { + try{ + logFolder.listFiles()!!.forEach { + it.delete() + } + }catch (e: Exception){ + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + } + true + } + } + + + companion object { + private const val TAG = "DownloadQueueActivity" + } + + override fun onCancelClick(itemID: Long) { + cancelDownload(itemID) + } + + override fun onQueuedCancelClick(itemID: Long) { + lifecycleScope.launch{ + withContext(Dispatchers.IO){ + downloadViewModel.deleteDownload(downloadViewModel.getItemByID(itemID)) + } + } + cancelDownload(itemID) + } + + private fun cancelDownload(itemID: Long){ + val id = itemID.toInt() + YoutubeDL.getInstance().destroyProcessById(id.toString()) + WorkManager.getInstance(this).cancelUniqueWork(id.toString()) + notificationUtil.cancelDownloadNotification(id) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 760588bb..1f382ecc 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -184,15 +184,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi } try { Handler(Looper.getMainLooper()).post { - // DOWNLOAD ALL BUTTON if (resultsList!!.size > 1 || inputQueriesLength > 1) { downloadAllFabCoordinator!!.visibility = VISIBLE } -// databaseManager = DatabaseManager(context) -// databaseManager!!.clearResults() -// for (v in resultsList!!) v!!.isPlaylistItem = 1 -// databaseManager!!.addToResults(resultsList) } } catch (ignored: Exception) { } @@ -202,14 +197,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi resultViewModel.checkTrending() } - WorkManager.getInstance(requireContext()) - .getWorkInfosByTagLiveData("download") - .observe(viewLifecycleOwner){ list -> - list.forEach { - //Toast.makeText(context, """${it.progress.getInt("progress", 0)} ${it.progress.getString("output")}""", Toast.LENGTH_SHORT).show() - } - } - // fragmentView!!.post{ // if (shimmerCards!!.visibility == VISIBLE) return@post // val clipboard = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt index 5b488d76..8494009c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -209,10 +209,8 @@ class DownloadWorker( "Format: ${downloadItem.format}\n\n") } - YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line -> - setProgressAsync(workDataOf("progress" to progress.toInt())) - setProgressAsync(workDataOf("output" to line)) + setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line, "id" to downloadItem.id)) val title: String = downloadItem.title notificationUtil.updateDownloadNotification( downloadItem.id.toInt(), diff --git a/app/src/main/res/layout/active_download_card.xml b/app/src/main/res/layout/active_download_card.xml new file mode 100644 index 00000000..447e093a --- /dev/null +++ b/app/src/main/res/layout/active_download_card.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_command_templates.xml b/app/src/main/res/layout/activity_command_templates.xml new file mode 100644 index 00000000..dc343f64 --- /dev/null +++ b/app/src/main/res/layout/activity_command_templates.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_download_queue.xml b/app/src/main/res/layout/activity_download_queue.xml new file mode 100644 index 00000000..9342b836 --- /dev/null +++ b/app/src/main/res/layout/activity_download_queue.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/queued_download_card.xml b/app/src/main/res/layout/queued_download_card.xml new file mode 100644 index 00000000..ddf93874 --- /dev/null +++ b/app/src/main/res/layout/queued_download_card.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index a22e401e..8ac5d0a1 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,5 +4,5 @@ #000000 #323232 #A6A6A6 - @android:color/background_light + @android:color/background_dark diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 032595a4..b707589e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -181,4 +181,6 @@ Log Downloads Create a log file for each download New Update + Download Queue + Running \ No newline at end of file diff --git a/app/src/main/res/xml/more_preferences.xml b/app/src/main/res/xml/more_preferences.xml index d21694c7..2ec4e2f1 100644 --- a/app/src/main/res/xml/more_preferences.xml +++ b/app/src/main/res/xml/more_preferences.xml @@ -41,6 +41,15 @@ + + + + +