implemented work calls after processing downloads

pull/44/head
Denis Çerri 3 years ago
parent 5e47c98ec5
commit 544a8eab14
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -25,6 +25,7 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
import com.deniscerri.ytdlnis.service.IDownloaderListener
import com.deniscerri.ytdlnis.service.IDownloaderService
import com.deniscerri.ytdlnis.ui.DownloadFragment
import com.deniscerri.ytdlnis.ui.HistoryFragment
import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.MoreFragment
@ -46,6 +47,7 @@ class MainActivity : AppCompatActivity() {
lateinit var context: Context
private lateinit var homeFragment: HomeFragment
private lateinit var historyFragment: HistoryFragment
private lateinit var downloadFragment: DownloadFragment
private lateinit var workManager: WorkManager
override fun onCreate(savedInstanceState: Bundle?) {
@ -61,6 +63,7 @@ class MainActivity : AppCompatActivity() {
homeFragment = HomeFragment()
historyFragment = HistoryFragment()
downloadFragment = DownloadFragment()
moreFragment = MoreFragment()
initFragments()
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
@ -77,10 +80,18 @@ class MainActivity : AppCompatActivity() {
if (lastFragment === historyFragment) {
historyFragment.scrollToTop()
} else {
this.title = getString(R.string.downloads)
this.title = getString(R.string.history)
}
replaceFragment(historyFragment)
}
R.id.downloads -> {
if (lastFragment === downloadFragment) {
downloadFragment.scrollToTop()
} else {
this.title = getString(R.string.downloads)
}
replaceFragment(downloadFragment)
}
R.id.more -> {
if (lastFragment === moreFragment) {
val intent = Intent(context, SettingsActivity::class.java)
@ -118,6 +129,7 @@ class MainActivity : AppCompatActivity() {
Log.e(TAG, action)
homeFragment = HomeFragment()
historyFragment = HistoryFragment()
downloadFragment = DownloadFragment()
moreFragment = MoreFragment()
if (type.equals("application/txt", ignoreCase = true)) {
try {
@ -152,9 +164,11 @@ class MainActivity : AppCompatActivity() {
fm.beginTransaction()
.replace(R.id.frame_layout, homeFragment)
.add(R.id.frame_layout, historyFragment)
.add(R.id.frame_layout, downloadFragment)
.add(R.id.frame_layout, moreFragment)
.hide(historyFragment)
.hide(moreFragment)
.hide(downloadFragment)
.commit()
lastFragment = homeFragment
listeners = ArrayList()

@ -0,0 +1,195 @@
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.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
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<HistoryItem?, ActiveDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val checkedItems: ArrayList<Long>
private val onItemClickListener: OnItemClickListener
private val activity: Activity
init {
checkedItems = ArrayList()
this.onItemClickListener = onItemClickListener
this.activity = activity
}
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
val cardView: MaterialCardView
init {
cardView = itemView.findViewById(R.id.downloads_card_view)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.history_card, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.cardView
// THUMBNAIL ----------------------------------
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
val imageURL = item!!.thumb
val uiHandler = Handler(Looper.getMainLooper())
if (imageURL.isNotEmpty()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
} else {
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
}
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
// TITLE ----------------------------------
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
var title = item.title
if (title.length > 100) {
title = title.substring(0, 40) + "..."
}
itemTitle.text = title
// Bottom Info ----------------------------------
val bottomInfo = card.findViewById<TextView>(R.id.downloads_info_bottom)
var info = item.author
if (item.duration.isNotEmpty()) {
if (item.author.isNotEmpty()) info += ""
info += item.duration
}
bottomInfo.text = info
// TIME DOWNLOADED ----------------------------------
val datetime = card.findViewById<TextView>(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<LinearLayout>(R.id.downloads_download_button_layout)
val btn = buttonLayout.findViewById<MaterialButton>(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.isNotEmpty()) {
if (item.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)
}
}
}
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()
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<HistoryItem> = object : DiffUtil.ItemCallback<HistoryItem>() {
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): 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
}
}
}
}

@ -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
@ -111,6 +112,7 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
//IS IN THE FILE SYSTEM?
val path = item.downloadPath
val file = File(path)
Log.e("aa", file.absolutePath + " " + file.isFile)
if (!file.exists() && path.isNotEmpty()) {
filePresent = false
thumbnail.colorFilter = ColorMatrixColorFilter(object : ColorMatrix() {

@ -0,0 +1,195 @@
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.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.squareup.picasso.Picasso
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<HistoryItem?, QueuedDownloadAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val checkedItems: ArrayList<Long>
private val onItemClickListener: OnItemClickListener
private val activity: Activity
init {
checkedItems = ArrayList()
this.onItemClickListener = onItemClickListener
this.activity = activity
}
class ViewHolder(itemView: View, onItemClickListener: OnItemClickListener?) : RecyclerView.ViewHolder(itemView) {
val cardView: MaterialCardView
init {
cardView = itemView.findViewById(R.id.downloads_card_view)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.history_card, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.cardView
// THUMBNAIL ----------------------------------
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
val imageURL = item!!.thumb
val uiHandler = Handler(Looper.getMainLooper())
if (imageURL.isNotEmpty()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
} else {
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
}
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
// TITLE ----------------------------------
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
var title = item.title
if (title.length > 100) {
title = title.substring(0, 40) + "..."
}
itemTitle.text = title
// Bottom Info ----------------------------------
val bottomInfo = card.findViewById<TextView>(R.id.downloads_info_bottom)
var info = item.author
if (item.duration.isNotEmpty()) {
if (item.author.isNotEmpty()) info += ""
info += item.duration
}
bottomInfo.text = info
// TIME DOWNLOADED ----------------------------------
val datetime = card.findViewById<TextView>(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<LinearLayout>(R.id.downloads_download_button_layout)
val btn = buttonLayout.findViewById<MaterialButton>(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.isNotEmpty()) {
if (item.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)
}
}
}
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()
}
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback<HistoryItem> = object : DiffUtil.ItemCallback<HistoryItem>() {
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): 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
}
}
}
}

@ -22,7 +22,7 @@ interface DownloadDao {
fun getProcessingDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE workID=:workID")
fun getDownloadByWorkId(workID: Int) : DownloadItem
fun getDownloadByWorkId(workID: Long) : DownloadItem
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getDownloadById(id: Long) : DownloadItem

@ -26,5 +26,5 @@ data class DownloadItem(
val SaveThumb: Boolean,
@ColumnInfo(defaultValue = "Queued")
var status: String,
val workID: Int
var workID: Long
)

@ -27,7 +27,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.update(item)
}
fun getDownloadByWorkID(workID: Int) : DownloadItem{
fun getDownloadByWorkID(workID: Long) : DownloadItem{
return downloadDao.getDownloadByWorkId(workID)
}

@ -0,0 +1,282 @@
package com.deniscerri.ytdlnis.ui
import android.app.Activity
import android.content.*
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.*
import android.view.View.*
import android.widget.*
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity
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.models.HistoryItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding
import com.deniscerri.ytdlnis.util.FileUtil
import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import java.io.File
/**
* A fragment representing a list of Items.
*/
class DownloadFragment : Fragment(), ActiveDownloadAdapter.OnItemClickListener, QueuedDownloadAdapter.OnItemClickListener,
OnClickListener, OnLongClickListener {
private lateinit var downloadViewModel : DownloadViewModel
private var downloading = false
private var fragmentView: View? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private var fragmentContext: Context? = null
private var layoutinflater: LayoutInflater? = null
private var shimmerCards: ShimmerFrameLayout? = null
private var topAppBar: MaterialToolbar? = null
private var activeRecyclerView: RecyclerView? = null
private var othersRecyclerView: RecyclerView? = null
private var downloadsAdapter: ActiveDownloadAdapter? = null
private var queuedDownloadsAdapter: QueuedDownloadAdapter? = null
private var bottomSheet: BottomSheetDialog? = null
private var sortSheet: BottomSheetDialog? = null
private var uiHandler: Handler? = null
private var noResults: RelativeLayout? = null
private var websiteGroup: ChipGroup? = null
private var downloadsList: List<DownloadItem?>? = null
private var allDownloadsList: List<DownloadItem?>? = null
private var selectedObjects: ArrayList<DownloadItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
private var _binding : FragmentHistoryBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHistoryBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_downloads, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
topAppBar = view.findViewById(R.id.downloads_toolbar)
noResults = view.findViewById(R.id.no_results)
websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil()
deleteFab?.tag = "deleteSelected"
deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
downloadsList = mutableListOf()
allDownloadsList = mutableListOf()
downloadsAdapter =
ActiveDownloadAdapter(
this,
requireActivity()
)
activeRecyclerView = view.findViewById(R.id.recyclerviewactivedownloads)
activeRecyclerView?.layoutManager = LinearLayoutManager(context)
activeRecyclerView?.adapter = downloadsAdapter
queuedDownloadsAdapter =
QueuedDownloadAdapter(
this,
requireActivity()
)
othersRecyclerView = view.findViewById(R.id.recyclerviewotherdownloads)
othersRecyclerView?.layoutManager = LinearLayoutManager(context)
othersRecyclerView?.adapter = queuedDownloadsAdapter
noResults?.visibility = GONE
shimmerCards?.visibility = GONE
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadViewModel.activeDownloads.observe(viewLifecycleOwner){
// update active
}
downloadViewModel.queuedDownloads.observe(viewLifecycleOwner){
// update queued
}
}
fun scrollToTop() {
activeRecyclerView!!.scrollToPosition(0)
Handler(Looper.getMainLooper()).post {
(topAppBar!!.parent as AppBarLayout).setExpanded(
true,
true
)
}
}
override fun onLongClick(v: View): Boolean {
val id = v.id
if (id == R.id.bottom_sheet_link) {
copyLinkToClipBoard(v.tag as Long)
return true
}
return false
}
private fun removeSelectedItems() {
// if (bottomSheet != null) bottomSheet!!.hide()
// val deleteFile = booleanArrayOf(false)
// val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
// deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
// deleteDialog.setMultiChoiceItems(
// arrayOf(getString(R.string.delete_files_too)),
// booleanArrayOf(false)
// ) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
// deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
// deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
// for (item in selectedObjects!!){
// historyViewModel.delete(item, deleteFile[0])
// }
// selectedObjects = ArrayList()
// historyAdapter!!.clearCheckeditems()
// deleteFab!!.visibility = GONE
// }
// deleteDialog.show()
}
private fun removeItem(id: Long) {
// if (bottomSheet != null) bottomSheet!!.hide()
// val deleteFile = booleanArrayOf(false)
// val v = findItem(id)
// val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
// deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + v!!.title + "\"!")
// deleteDialog.setMultiChoiceItems(
// arrayOf(getString(R.string.delete_file_too)),
// booleanArrayOf(false)
// ) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
// deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
// deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
// historyViewModel.delete(v, deleteFile[0])
// }
// deleteDialog.show()
}
private fun copyLinkToClipBoard(id: Long) {
val url = findItem(id)?.url
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.url), url)
clipboard.setPrimaryClip(clip)
if (bottomSheet != null) bottomSheet!!.hide()
Toast.makeText(context, getString(R.string.link_copied_to_clipboard), Toast.LENGTH_SHORT)
.show()
}
private fun openLinkIntent(id: Long) {
val url = findItem(id)?.url
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(url)
if (bottomSheet != null) bottomSheet!!.hide()
startActivity(i)
}
private fun openFileIntent(id: Long) {
val downloadPath = findItem(id)!!.downloadPath
val file = File(downloadPath)
val uri = FileProvider.getUriForFile(
fragmentContext!!,
fragmentContext!!.packageName + ".fileprovider",
file
)
val mime = mainActivity!!.contentResolver.getType(uri)
val i = Intent(Intent.ACTION_VIEW)
i.setDataAndType(uri, mime)
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(i)
}
override fun onCardClick(videoID: Long, isPresent: Boolean) {
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.history_item_details_bottom_sheet)
val video = findItem(videoID)
val title = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_title)
title!!.text = video!!.title
val author = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_author)
author!!.text = video.author
val link = bottomSheet!!.findViewById<Button>(R.id.bottom_sheet_link)
val url = video.url
link!!.text = url
link.tag = videoID
link.setOnClickListener(this)
link.setOnLongClickListener(this)
val remove = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_remove_button)
remove!!.tag = videoID
remove.setOnClickListener(this)
val openFile = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_open_file_button)
openFile!!.tag = videoID
openFile.setOnClickListener(this)
if (!isPresent) openFile.visibility = GONE
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
override fun onCardSelect(videoID: Long, isChecked: Boolean) {
val item = findItem(videoID)
if (isChecked) selectedObjects!!.add(item!!)
else selectedObjects!!.remove(item)
if (selectedObjects!!.size > 1) {
deleteFab!!.visibility = VISIBLE
} else {
deleteFab!!.visibility = GONE
}
}
override fun onButtonClick(position: Int) {
// val vid = downloadsObjects!![position]
// try {
// //mainActivity!!.removeItemFromDownloadQueue(vid, vid!!.downloadedType)
// } catch (e: Exception) {
// val info = DownloadInfo()
// info.video = vid
// info.downloadType = vid!!.downloadedType
// }
}
private fun findItem(id : Long): DownloadItem? {
return downloadsList?.find { it?.id == id }
}
companion object {
private const val TAG = "downloadsFragment"
}
override fun onClick(p0: View?) {
TODO("Not yet implemented")
}
}

@ -42,7 +42,6 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
OnClickListener, OnLongClickListener {
private lateinit var historyViewModel : HistoryViewModel
private var downloading = false
private var fragmentView: View? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
@ -58,8 +57,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
private var noResults: RelativeLayout? = null
private var selectionChips: LinearLayout? = null
private var websiteGroup: ChipGroup? = null
private var downloadsList: List<HistoryItem?>? = null
private var allDownloadsList: List<HistoryItem?>? = null
private var historyList: List<HistoryItem?>? = null
private var allhistoryList: List<HistoryItem?>? = null
private var selectedObjects: ArrayList<HistoryItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
@ -83,10 +82,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
shimmerCards = view.findViewById(R.id.shimmer_downloads_framelayout)
topAppBar = view.findViewById(R.id.downloads_toolbar)
noResults = view.findViewById(R.id.downloads_no_results)
selectionChips = view.findViewById(R.id.downloads_selection_chips)
shimmerCards = view.findViewById(R.id.shimmer_history_framelayout)
topAppBar = view.findViewById(R.id.history_toolbar)
noResults = view.findViewById(R.id.no_results)
selectionChips = view.findViewById(R.id.history_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil()
@ -94,18 +93,17 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
downloadsList = mutableListOf()
allDownloadsList = mutableListOf()
historyList = mutableListOf()
allhistoryList = mutableListOf()
historyAdapter =
HistoryAdapter(
this,
requireActivity()
)
recyclerView = view.findViewById(R.id.recyclerviewdownloadss)
recyclerView = view.findViewById(R.id.recyclerviewhistorys)
recyclerView?.layoutManager = LinearLayoutManager(context)
recyclerView?.adapter = historyAdapter
@ -116,7 +114,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java]
historyViewModel.allItems.observe(viewLifecycleOwner) {
allDownloadsList = it
allhistoryList = it
if(it.isEmpty()){
noResults!!.visibility = VISIBLE
selectionChips!!.visibility = GONE
@ -130,7 +128,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel.getFilteredList().observe(viewLifecycleOwner) {
historyAdapter!!.submitList(it)
downloadsList = it
historyList = it
}
initMenu()
@ -158,14 +156,14 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
return true
}
}
topAppBar!!.menu.findItem(R.id.search_downloads)
topAppBar!!.menu.findItem(R.id.search_history)
.setOnActionExpandListener(onActionExpandListener)
val searchView = topAppBar!!.menu.findItem(R.id.search_downloads).actionView as SearchView?
val searchView = topAppBar!!.menu.findItem(R.id.search_history).actionView as SearchView?
searchView!!.inputType = InputType.TYPE_CLASS_TEXT
searchView.queryHint = getString(R.string.search_history_hint)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
topAppBar!!.menu.findItem(R.id.search_downloads).collapseActionView()
topAppBar!!.menu.findItem(R.id.search_history).collapseActionView()
historyViewModel.setQueryFilter(query)
return true
}
@ -178,8 +176,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
topAppBar!!.setOnClickListener { scrollToTop() }
topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
when (m.itemId) {
R.id.remove_downloads -> {
if(allDownloadsList!!.isEmpty()){
R.id.remove_history -> {
if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -192,8 +190,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteDialog.show()
}
}
R.id.remove_deleted_downloads -> {
if(allDownloadsList!!.isEmpty()){
R.id.remove_deleted_history -> {
if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -207,7 +205,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
}
}
R.id.remove_duplicates -> {
if(allDownloadsList!!.isEmpty()){
if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -283,7 +281,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
if (!websites.contains(item.website)) websites.add(item.website)
}
websiteGroup!!.removeAllViews()
//val websites = downloadsRecyclerViewAdapter!!.websites
//val websites = historyRecyclerViewAdapter!!.websites
for (i in websites.indices) {
val w = websites[i]
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, websiteGroup, false) as Chip
@ -441,7 +439,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
}
override fun onButtonClick(position: Int) {
// val vid = downloadsObjects!![position]
// val vid = historyObjects!![position]
// try {
// //mainActivity!!.removeItemFromDownloadQueue(vid, vid!!.downloadedType)
// } catch (e: Exception) {
@ -452,10 +450,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
}
private fun findItem(id : Long): HistoryItem? {
return downloadsList?.find { it?.id == id }
return historyList?.find { it?.id == id }
}
companion object {
private const val TAG = "downloadsFragment"
private const val TAG = "historyFragment"
}
}

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@ -22,12 +23,10 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.service.DownloadInfo
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil
@ -37,14 +36,10 @@ import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.ChipGroup
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import java.util.*
import kotlin.collections.ArrayList
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var progressBar: LinearProgressIndicator? = null
private var inputQuery: String? = null
private var inputQueries: LinkedList<String?>? = null
private var inputQueriesLength = 0
@ -56,7 +51,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var homeFabs: CoordinatorLayout? = null
private var infoUtil: InfoUtil? = null
private var downloadQueue: ArrayList<ResultItem>? = null
private var downloadInfo: DownloadInfo? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel
@ -70,17 +64,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var shimmerCards: ShimmerFrameLayout? = null
private var topAppBar: MaterialToolbar? = null
private var recyclerView: RecyclerView? = null
private var bottomSheet: BottomSheetDialog? = null
private var uiHandler: Handler? = null
private var noResults: RelativeLayout? = null
private var selectionChips: LinearLayout? = null
private var websiteGroup: ChipGroup? = null
private var resultsList: List<ResultItem?>? = null
private var selectedObjects: ArrayList<ResultItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
private var firstBoot = true
private var sharedPreferences: SharedPreferences? = null
private var _binding : FragmentHomeBinding? = null
override fun onCreateView(
@ -99,15 +88,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
fragmentContext = context
layoutinflater = LayoutInflater.from(context)
shimmerCards = view.findViewById(R.id.shimmer_downloads_framelayout)
topAppBar = view.findViewById(R.id.downloads_toolbar)
noResults = view.findViewById(R.id.downloads_no_results)
selectionChips = view.findViewById(R.id.downloads_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil()
deleteFab?.tag = "deleteSelected"
deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
@ -118,6 +100,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
resultsList = mutableListOf()
selectedObjects = ArrayList()
sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
//initViews
shimmerCards = view.findViewById(R.id.shimmer_results_framelayout)
topAppBar = view.findViewById(R.id.home_toolbar)
@ -143,9 +127,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}else{
downloadAllFabCoordinator!!.visibility = GONE
}
}else if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
}else if (sharedPreferences!!.getBoolean("download_card", true)){
if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(it[0], "audio")
}
}
firstBoot = false
}
@ -338,9 +324,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } catch (ignored: Exception) {
// }
// }
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
if (sharedPreferences.getBoolean("download_card", true)) {
if (sharedPreferences!!.getBoolean("download_card", true)) {
// selectedObjects!!.clear()
// selectedObjects!!.add(item!!)
//showConfigureSingleDownloadCard(createDownloadItem(item!!, type), item)

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.view.LayoutInflater
import android.view.View
@ -15,10 +16,15 @@ import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.findFragment
import androidx.lifecycle.ViewModelProvider
import androidx.viewpager2.widget.ViewPager2
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.work.DownloadWorker
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
@ -99,27 +105,20 @@ class DownloadBottomSheetDialog(item: DownloadItem) : BottomSheetDialogFragment(
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
download!!.setOnClickListener {
downloadItem.status = DownloadRepository.status.Queued.toString()
val workID = SystemClock.uptimeMillis()
downloadItem.workID = workID
downloadViewModel.insertDownload(downloadItem)
dismiss()
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.downloadedType = type
// updateDownloadingStatusOnResult(vid, type, true)
// homeAdapter!!.notifyItemChanged(resultsList!!.indexOf(vid))
// downloadQueue!!.add(vid)
// }
// selectedObjects = ArrayList()
// homeAdapter!!.clearCheckedVideos()
// downloadFabs!!.visibility = View.GONE
// if (isStoragePermissionGranted) {
// mainActivity!!.startDownloadService(downloadQueue, listener)
// downloadQueue!!.clear()
// }
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
.setInputData(Data.Builder().putLong("workID", workID).build())
.build()
WorkManager.getInstance(requireContext()).beginUniqueWork(
downloadItem.id.toString(),
ExistingWorkPolicy.KEEP,
workRequest
).enqueue()
dismiss()
}
}

@ -3,7 +3,9 @@ package com.deniscerri.ytdlnis.util
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.provider.DocumentsContract
import android.util.Log
import android.webkit.MimeTypeMap
import android.widget.Toast
import com.deniscerri.ytdlnis.R
@ -11,6 +13,8 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import java.io.File
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.text.DecimalFormat
import kotlin.math.log10
import kotlin.math.pow
@ -54,21 +58,16 @@ class FileUtil() {
return formattedPath.toString()
}
fun scanMedia(video: DownloadItem, context: Context) : String {
fun scanMedia(destDir: String, context: Context) : String {
val files = ArrayList<File>()
val title: String = video.title.replace("[^a-zA-Z0-9]".toRegex(), "")
var pathTmp: String
val path = File(video.downloadPath)
val path = File(formatPath(destDir))
try {
for (file in path.listFiles()!!) {
if (file.isFile) {
pathTmp = file.absolutePath.replace("[^a-zA-Z0-9]".toRegex(), "")
if (pathTmp.contains(title)) {
files.add(file)
}
}
}
val paths = arrayOfNulls<String>(files.size)
for (i in files.indices) paths[i] = files[i].absolutePath
@ -80,19 +79,29 @@ class FileUtil() {
return context.getString(R.string.unfound_file);
}
fun moveFile(originDir : File, context:Context, destDir : Uri, progress: (p: Int) -> Unit){
fun moveFile(originDir: File, context: Context, destDir: String, progress: (p: Int) -> Unit){
originDir.listFiles()?.forEach {
val mimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
if (it.name.equals("rList")){
it.delete()
return@forEach
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val f = File(formatPath(destDir)+"/"+it.name)
if (!f.exists()) f.mkdir()
Files.move(it.toPath(), f.toPath(), StandardCopyOption.REPLACE_EXISTING)
progress(100)
}else{
val mimeType =
MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
val destination = Uri.parse(destDir).run {
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
}
val destUri = DocumentsContract.createDocument(
context.contentResolver,
destDir,
destination,
mimeType,
it.name
) ?: return@forEach
@ -120,7 +129,9 @@ class FileUtil() {
it.delete()
}
}
originDir.delete()
scanMedia(destDir, context)
}
fun convertFileSize(s: Long): String{

@ -15,7 +15,6 @@ import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
class NotificationUtil(var context: Context) {
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
private val commandDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID)
private val fileTransferNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, FILE_TRANSFER_CHANNEL_ID)
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
fun createNotificationChannel() {
@ -39,43 +38,6 @@ class NotificationUtil(var context: Context) {
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
//file transfers
name = context.getString(R.string.file_transfer_notification_channel_name)
description = context.getString(R.string.file_transfer_notification_channel_description)
channel = NotificationChannel(FILE_TRANSFER_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
}
}
fun createFileTransferNotification(
pendingIntent: PendingIntent?,
title: String?,
) : Notification {
val intent = Intent(context, CancelDownloadNotificationReceiver::class.java)
return fileTransferNotificationBuilder
.setContentTitle(title)
.setCategory(Notification.CATEGORY_PROGRESS)
.setSmallIcon(R.drawable.ic_app_icon)
.setContentText("")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
.setContentIntent(pendingIntent)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.build()
}
fun updateFileTransferNotification(
id: Int,
progress: Int
) {
try {
fileTransferNotificationBuilder.setProgress(100, progress, false)
notificationManager.notify(id, fileTransferNotificationBuilder.build())
} catch (ignored: Exception) {
}
}
@ -83,7 +45,6 @@ class NotificationUtil(var context: Context) {
when(channel) {
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
FILE_TRANSFER_CHANNEL_ID -> { return fileTransferNotificationBuilder }
}
return downloadNotificationBuilder
}
@ -102,7 +63,7 @@ class NotificationUtil(var context: Context) {
intent.putExtra("workID", workID)
val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
context,
0,
workID,
intent,
PendingIntent.FLAG_IMMUTABLE
)

@ -5,6 +5,7 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Looper
import android.os.SystemClock
import android.provider.DocumentsContract
import android.util.Log
@ -26,6 +27,7 @@ import com.yausername.youtubedl_android.YoutubeDLRequest
import kotlinx.coroutines.runBlocking
import java.io.File
import java.util.regex.Pattern
import kotlin.math.abs
class DownloadWorker(
@ -33,7 +35,7 @@ class DownloadWorker(
workerParams: WorkerParameters
) : Worker(context, workerParams) {
override fun doWork(): Result {
workID = inputData.getInt("workID", SystemClock.uptimeMillis().toInt())
workID = inputData.getLong("workID", SystemClock.uptimeMillis())
val notificationUtil = NotificationUtil(context)
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
@ -47,8 +49,8 @@ class DownloadWorker(
}
val intent = Intent(context, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, downloadItem.title, workID, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
val foregroundInfo = ForegroundInfo(workID, notification)
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, downloadItem.title, workID.toInt(), NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
val foregroundInfo = ForegroundInfo(workID.toInt(), notification)
setForegroundAsync(foregroundInfo)
@ -123,10 +125,6 @@ class DownloadWorker(
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
}
"video" -> {
if (downloadLocation == context.getString(R.string.video_path)){
tempFileDir = File(context.getString(R.string.video_path))
}
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
if (addChapters) {
request.addOption("--sponsorblock-mark", "all")
@ -136,17 +134,19 @@ class DownloadWorker(
request.addOption("--embed-subs", "")
}
var videoFormatID = downloadItem.format.format_id
if (videoFormatID.isEmpty()) videoFormatID = "bestvideo"
val formatArgument = StringBuilder(videoFormatID)
formatArgument.append("+bestaudio/best")
request.addOption("-f", formatArgument.toString())
var format = downloadItem.format.container
Log.e(TAG, videoFormatID)
var formatArgument = "bestvideo+bestaudio/best"
if (videoFormatID.isNotEmpty()) {
if (videoFormatID == "Best Quality") videoFormatID = "bestvideo"
else if (videoFormatID == "Worst Quality") videoFormatID = "worst"
formatArgument = videoFormatID + if (downloadItem.removeAudio) "" else "+bestaudio"
}
request.addOption("-f", formatArgument)
val format = downloadItem.format.container
if(format.isNotEmpty()){
format = sharedPreferences.getString("video_format", "")!!
if (format != "DEFAULT") request.addOption("--merge-output-format", format)
request.addOption("--merge-output-format", format)
}
if (format != "webm") {
if (format != "webm" && format != "DEFAULT") {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) {
request.addOption("--embed-thumbnail")
@ -173,13 +173,14 @@ class DownloadWorker(
setProgressAsync(workDataOf("progress" to progress.toInt()))
val title: String = downloadItem.title
notificationUtil.updateDownloadNotification(
workID,
downloadItem.workID.toInt(),
line, progress.toInt(), 0, title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
)
}
}.onSuccess {
//move file from internal to set download directory
moveFile(tempFileDir.absoluteFile, downloadLocation){ progress ->
setProgressAsync(workDataOf("progress" to progress))
}
@ -187,22 +188,31 @@ class DownloadWorker(
val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, downloadItem.downloadPath, downloadItem.website, downloadItem.format)
val fileUtil = FileUtil()
val finalPath = fileUtil.formatPath(downloadItem.downloadPath) + downloadItem.author + " - " + downloadItem.title + "." + downloadItem.format.container
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPath, downloadItem.website, downloadItem.format)
runBlocking {
historyDao.insert(historyItem)
}
}
runBlocking {
dao.delete(downloadItem.id)
}
}.onFailure {
tempFileDir.delete()
if (BuildConfig.DEBUG) {
Looper.prepare()
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
Log.e(TAG, context.getString(R.string.failed_download), it)
}
notificationUtil.updateDownloadNotification(
workID,
downloadItem.workID.toInt(),
context.getString(R.string.failed_download), 0, 0, downloadItem.title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
)
downloadItem.status = DownloadRepository.status.Errored.toString()
runBlocking {
dao.update(downloadItem)
}
return Result.failure()
}
@ -227,13 +237,13 @@ class DownloadWorker(
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
}
val fileUtil = FileUtil()
fileUtil.moveFile(originDir, context, destDir){ p ->
fileUtil.moveFile(originDir, context, downLocation){ p ->
progress(p)
}
}
companion object {
var workID: Int = 0
var workID: Long = 0
const val TAG = "DownloadWorker"
}

@ -1,56 +0,0 @@
package com.deniscerri.ytdlnis.work
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.SystemClock
import android.provider.DocumentsContract
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
import java.io.File
class FileTransferWorker(
private val context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result {
val originDir = File(inputData.getString(originDir)!!)
val downLocation = inputData.getString(downLocation)
val fileTitle = inputData.getString(title) ?: ""
val destDir = Uri.parse(downLocation).run {
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
}
val fileUtil = FileUtil()
val notificationUtil = NotificationUtil(context)
val intent = Intent(context, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val title = "Moving $fileTitle to ${fileUtil.formatPath(downLocation!!)}"
val id : Int = SystemClock.uptimeMillis().toInt()
val notification = notificationUtil.createFileTransferNotification(pendingIntent, title)
val foregroundInfo = ForegroundInfo(id, notification)
setForeground(foregroundInfo)
fileUtil.moveFile(originDir, context, destDir){ progress ->
setProgressAsync(workDataOf("progress" to progress))
notificationUtil.updateFileTransferNotification(id, progress)
}
return Result.success()
}
companion object {
const val downLocation = "downLocation"
const val originDir = "originDir"
const val title = "title"
}
}

@ -123,7 +123,7 @@
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
app:singleLine="true"
app:singleLine="false"
android:layout_height="wrap_content">
<com.google.android.material.chip.Chip

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/downloadscoordinator"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/downloads_appbarlayout"
app:liftOnScroll="false"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/downloads_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/downloads"
android:theme="@style/Toolbar"
/>
</com.google.android.material.appbar.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="10dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerviewactivedownloads"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:orientation="vertical"
android:paddingBottom="150dp"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
</androidx.recyclerview.widget.RecyclerView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerviewotherdownloads"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:orientation="vertical"
android:paddingBottom="150dp"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager">
</androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/delete_selected_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/delete_selected_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:visibility="gone"
android:layout_gravity="bottom|end"
android:text="@string/delete_selected"
app:icon="@drawable/ic_delete_all"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/history_no_results"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

@ -3,23 +3,23 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/downloadscoordinator"
android:id="@+id/historycoordinator"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/downloads_appbarlayout"
android:id="@+id/history_appbarlayout"
app:liftOnScroll="false"
android:background="@android:color/transparent"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/downloads_toolbar"
android:id="@+id/history_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/history"
app:menu="@menu/downloads_menu"
app:menu="@menu/history_menu"
android:theme="@style/Toolbar"
/>
@ -39,7 +39,7 @@
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/downloads_selection_chips"
android:id="@+id/history_selection_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
@ -101,7 +101,7 @@
</HorizontalScrollView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerviewdownloadss"
android:id="@+id/recyclerviewhistorys"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/chips_recycler_horizontalscrollview"
@ -119,12 +119,12 @@
android:visibility="gone"
android:layout_height="match_parent"
android:layout_below="@+id/chips_recycler_horizontalscrollview"
android:id="@+id/shimmer_downloads_framelayout"
android:id="@+id/shimmer_history_framelayout"
android:orientation="vertical">
<LinearLayout
android:id="@+id/shimmer_downloads_linear_layout"
android:id="@+id/shimmer_history_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/downloads_no_results"
android:id="@+id/no_results"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"

@ -4,20 +4,20 @@
tools:context=".MainActivity" >
<item
android:id="@+id/search_downloads"
android:id="@+id/search_history"
android:title="@string/search"
android:icon="@drawable/ic_search"
app:showAsAction="collapseActionView|always"
app:actionViewClass="androidx.appcompat.widget.SearchView"/>
<item
android:id="@+id/remove_downloads"
android:id="@+id/remove_history"
android:icon="@drawable/ic_delete_all"
android:title="@string/remove_all"
app:showAsAction="never" />
<item
android:id="@+id/remove_deleted_downloads"
android:id="@+id/remove_deleted_history"
android:icon="@drawable/ic_delete_all"
android:title="@string/remove_deleted"
app:showAsAction="never" />
Loading…
Cancel
Save