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.databinding.ActivityMainBinding
import com.deniscerri.ytdlnis.service.IDownloaderListener import com.deniscerri.ytdlnis.service.IDownloaderListener
import com.deniscerri.ytdlnis.service.IDownloaderService import com.deniscerri.ytdlnis.service.IDownloaderService
import com.deniscerri.ytdlnis.ui.DownloadFragment
import com.deniscerri.ytdlnis.ui.HistoryFragment import com.deniscerri.ytdlnis.ui.HistoryFragment
import com.deniscerri.ytdlnis.ui.HomeFragment import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.MoreFragment import com.deniscerri.ytdlnis.ui.MoreFragment
@ -46,6 +47,7 @@ class MainActivity : AppCompatActivity() {
lateinit var context: Context lateinit var context: Context
private lateinit var homeFragment: HomeFragment private lateinit var homeFragment: HomeFragment
private lateinit var historyFragment: HistoryFragment private lateinit var historyFragment: HistoryFragment
private lateinit var downloadFragment: DownloadFragment
private lateinit var workManager: WorkManager private lateinit var workManager: WorkManager
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -61,6 +63,7 @@ class MainActivity : AppCompatActivity() {
homeFragment = HomeFragment() homeFragment = HomeFragment()
historyFragment = HistoryFragment() historyFragment = HistoryFragment()
downloadFragment = DownloadFragment()
moreFragment = MoreFragment() moreFragment = MoreFragment()
initFragments() initFragments()
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem -> binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
@ -77,10 +80,18 @@ class MainActivity : AppCompatActivity() {
if (lastFragment === historyFragment) { if (lastFragment === historyFragment) {
historyFragment.scrollToTop() historyFragment.scrollToTop()
} else { } else {
this.title = getString(R.string.downloads) this.title = getString(R.string.history)
} }
replaceFragment(historyFragment) replaceFragment(historyFragment)
} }
R.id.downloads -> {
if (lastFragment === downloadFragment) {
downloadFragment.scrollToTop()
} else {
this.title = getString(R.string.downloads)
}
replaceFragment(downloadFragment)
}
R.id.more -> { R.id.more -> {
if (lastFragment === moreFragment) { if (lastFragment === moreFragment) {
val intent = Intent(context, SettingsActivity::class.java) val intent = Intent(context, SettingsActivity::class.java)
@ -118,6 +129,7 @@ class MainActivity : AppCompatActivity() {
Log.e(TAG, action) Log.e(TAG, action)
homeFragment = HomeFragment() homeFragment = HomeFragment()
historyFragment = HistoryFragment() historyFragment = HistoryFragment()
downloadFragment = DownloadFragment()
moreFragment = MoreFragment() moreFragment = MoreFragment()
if (type.equals("application/txt", ignoreCase = true)) { if (type.equals("application/txt", ignoreCase = true)) {
try { try {
@ -152,9 +164,11 @@ class MainActivity : AppCompatActivity() {
fm.beginTransaction() fm.beginTransaction()
.replace(R.id.frame_layout, homeFragment) .replace(R.id.frame_layout, homeFragment)
.add(R.id.frame_layout, historyFragment) .add(R.id.frame_layout, historyFragment)
.add(R.id.frame_layout, downloadFragment)
.add(R.id.frame_layout, moreFragment) .add(R.id.frame_layout, moreFragment)
.hide(historyFragment) .hide(historyFragment)
.hide(moreFragment) .hide(moreFragment)
.hide(downloadFragment)
.commit() .commit()
lastFragment = homeFragment lastFragment = homeFragment
listeners = ArrayList() 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.graphics.ColorMatrixColorFilter
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -111,6 +112,7 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
//IS IN THE FILE SYSTEM? //IS IN THE FILE SYSTEM?
val path = item.downloadPath val path = item.downloadPath
val file = File(path) val file = File(path)
Log.e("aa", file.absolutePath + " " + file.isFile)
if (!file.exists() && path.isNotEmpty()) { if (!file.exists() && path.isNotEmpty()) {
filePresent = false filePresent = false
thumbnail.colorFilter = ColorMatrixColorFilter(object : ColorMatrix() { 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>> fun getProcessingDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE workID=:workID") @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") @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getDownloadById(id: Long) : DownloadItem fun getDownloadById(id: Long) : DownloadItem

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

@ -27,7 +27,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.update(item) downloadDao.update(item)
} }
fun getDownloadByWorkID(workID: Int) : DownloadItem{ fun getDownloadByWorkID(workID: Long) : DownloadItem{
return downloadDao.getDownloadByWorkId(workID) 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 { OnClickListener, OnLongClickListener {
private lateinit var historyViewModel : HistoryViewModel private lateinit var historyViewModel : HistoryViewModel
private var downloading = false
private var fragmentView: View? = null private var fragmentView: View? = null
private var activity: Activity? = null private var activity: Activity? = null
private var mainActivity: MainActivity? = null private var mainActivity: MainActivity? = null
@ -58,8 +57,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
private var noResults: RelativeLayout? = null private var noResults: RelativeLayout? = null
private var selectionChips: LinearLayout? = null private var selectionChips: LinearLayout? = null
private var websiteGroup: ChipGroup? = null private var websiteGroup: ChipGroup? = null
private var downloadsList: List<HistoryItem?>? = null private var historyList: List<HistoryItem?>? = null
private var allDownloadsList: List<HistoryItem?>? = null private var allhistoryList: List<HistoryItem?>? = null
private var selectedObjects: ArrayList<HistoryItem>? = null private var selectedObjects: ArrayList<HistoryItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null private var fileUtil: FileUtil? = null
@ -83,10 +82,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
fragmentContext = context fragmentContext = context
layoutinflater = LayoutInflater.from(context) layoutinflater = LayoutInflater.from(context)
shimmerCards = view.findViewById(R.id.shimmer_downloads_framelayout) shimmerCards = view.findViewById(R.id.shimmer_history_framelayout)
topAppBar = view.findViewById(R.id.downloads_toolbar) topAppBar = view.findViewById(R.id.history_toolbar)
noResults = view.findViewById(R.id.downloads_no_results) noResults = view.findViewById(R.id.no_results)
selectionChips = view.findViewById(R.id.downloads_selection_chips) selectionChips = view.findViewById(R.id.history_selection_chips)
websiteGroup = view.findViewById(R.id.website_chip_group) websiteGroup = view.findViewById(R.id.website_chip_group)
deleteFab = view.findViewById(R.id.delete_selected_fab) deleteFab = view.findViewById(R.id.delete_selected_fab)
fileUtil = FileUtil() fileUtil = FileUtil()
@ -94,18 +93,17 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteFab?.setOnClickListener(this) deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper()) uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList() selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
downloadsList = mutableListOf() historyList = mutableListOf()
allDownloadsList = mutableListOf() allhistoryList = mutableListOf()
historyAdapter = historyAdapter =
HistoryAdapter( HistoryAdapter(
this, this,
requireActivity() requireActivity()
) )
recyclerView = view.findViewById(R.id.recyclerviewdownloadss) recyclerView = view.findViewById(R.id.recyclerviewhistorys)
recyclerView?.layoutManager = LinearLayoutManager(context) recyclerView?.layoutManager = LinearLayoutManager(context)
recyclerView?.adapter = historyAdapter recyclerView?.adapter = historyAdapter
@ -116,7 +114,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java] historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java]
historyViewModel.allItems.observe(viewLifecycleOwner) { historyViewModel.allItems.observe(viewLifecycleOwner) {
allDownloadsList = it allhistoryList = it
if(it.isEmpty()){ if(it.isEmpty()){
noResults!!.visibility = VISIBLE noResults!!.visibility = VISIBLE
selectionChips!!.visibility = GONE selectionChips!!.visibility = GONE
@ -130,7 +128,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel.getFilteredList().observe(viewLifecycleOwner) { historyViewModel.getFilteredList().observe(viewLifecycleOwner) {
historyAdapter!!.submitList(it) historyAdapter!!.submitList(it)
downloadsList = it historyList = it
} }
initMenu() initMenu()
@ -158,14 +156,14 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
return true return true
} }
} }
topAppBar!!.menu.findItem(R.id.search_downloads) topAppBar!!.menu.findItem(R.id.search_history)
.setOnActionExpandListener(onActionExpandListener) .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!!.inputType = InputType.TYPE_CLASS_TEXT
searchView.queryHint = getString(R.string.search_history_hint) searchView.queryHint = getString(R.string.search_history_hint)
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { 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) historyViewModel.setQueryFilter(query)
return true return true
} }
@ -178,8 +176,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
topAppBar!!.setOnClickListener { scrollToTop() } topAppBar!!.setOnClickListener { scrollToTop() }
topAppBar!!.setOnMenuItemClickListener { m: MenuItem -> topAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
when (m.itemId) { when (m.itemId) {
R.id.remove_downloads -> { R.id.remove_history -> {
if(allDownloadsList!!.isEmpty()){ if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{ }else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!) val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -192,8 +190,8 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteDialog.show() deleteDialog.show()
} }
} }
R.id.remove_deleted_downloads -> { R.id.remove_deleted_history -> {
if(allDownloadsList!!.isEmpty()){ if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{ }else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!) val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -207,7 +205,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
} }
} }
R.id.remove_duplicates -> { R.id.remove_duplicates -> {
if(allDownloadsList!!.isEmpty()){ if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
}else{ }else{
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!) val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
@ -283,7 +281,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
if (!websites.contains(item.website)) websites.add(item.website) if (!websites.contains(item.website)) websites.add(item.website)
} }
websiteGroup!!.removeAllViews() websiteGroup!!.removeAllViews()
//val websites = downloadsRecyclerViewAdapter!!.websites //val websites = historyRecyclerViewAdapter!!.websites
for (i in websites.indices) { for (i in websites.indices) {
val w = websites[i] val w = websites[i]
val tmp = layoutinflater!!.inflate(R.layout.filter_chip, websiteGroup, false) as Chip 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) { override fun onButtonClick(position: Int) {
// val vid = downloadsObjects!![position] // val vid = historyObjects!![position]
// try { // try {
// //mainActivity!!.removeItemFromDownloadQueue(vid, vid!!.downloadedType) // //mainActivity!!.removeItemFromDownloadQueue(vid, vid!!.downloadedType)
// } catch (e: Exception) { // } catch (e: Exception) {
@ -452,10 +450,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
} }
private fun findItem(id : Long): HistoryItem? { private fun findItem(id : Long): HistoryItem? {
return downloadsList?.find { it?.id == id } return historyList?.find { it?.id == id }
} }
companion object { 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.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -22,12 +23,10 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HomeAdapter 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.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding 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.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog import com.deniscerri.ytdlnis.ui.downloadcard.DownloadMultipleBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil 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.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton 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.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener { class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
private var progressBar: LinearProgressIndicator? = null
private var inputQuery: String? = null private var inputQuery: String? = null
private var inputQueries: LinkedList<String?>? = null private var inputQueries: LinkedList<String?>? = null
private var inputQueriesLength = 0 private var inputQueriesLength = 0
@ -56,7 +51,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var homeFabs: CoordinatorLayout? = null private var homeFabs: CoordinatorLayout? = null
private var infoUtil: InfoUtil? = null private var infoUtil: InfoUtil? = null
private var downloadQueue: ArrayList<ResultItem>? = null private var downloadQueue: ArrayList<ResultItem>? = null
private var downloadInfo: DownloadInfo? = null
private lateinit var resultViewModel : ResultViewModel private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel private lateinit var downloadViewModel : DownloadViewModel
@ -70,17 +64,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var shimmerCards: ShimmerFrameLayout? = null private var shimmerCards: ShimmerFrameLayout? = null
private var topAppBar: MaterialToolbar? = null private var topAppBar: MaterialToolbar? = null
private var recyclerView: RecyclerView? = null private var recyclerView: RecyclerView? = null
private var bottomSheet: BottomSheetDialog? = null
private var uiHandler: Handler? = 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 resultsList: List<ResultItem?>? = null
private var selectedObjects: ArrayList<ResultItem>? = null private var selectedObjects: ArrayList<ResultItem>? = null
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null private var fileUtil: FileUtil? = null
private var firstBoot = true private var firstBoot = true
private var sharedPreferences: SharedPreferences? = null
private var _binding : FragmentHomeBinding? = null private var _binding : FragmentHomeBinding? = null
override fun onCreateView( override fun onCreateView(
@ -99,15 +88,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
fragmentContext = context fragmentContext = context
layoutinflater = LayoutInflater.from(context) layoutinflater = LayoutInflater.from(context)
shimmerCards = view.findViewById(R.id.shimmer_downloads_framelayout)
topAppBar = view.findViewById(R.id.downloads_toolbar) 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() fileUtil = FileUtil()
deleteFab?.tag = "deleteSelected"
deleteFab?.setOnClickListener(this)
uiHandler = Handler(Looper.getMainLooper()) uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList() selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning() downloading = mainActivity!!.isDownloadServiceRunning()
@ -118,6 +100,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
resultsList = mutableListOf() resultsList = mutableListOf()
selectedObjects = ArrayList() selectedObjects = ArrayList()
sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
//initViews //initViews
shimmerCards = view.findViewById(R.id.shimmer_results_framelayout) shimmerCards = view.findViewById(R.id.shimmer_results_framelayout)
topAppBar = view.findViewById(R.id.home_toolbar) topAppBar = view.findViewById(R.id.home_toolbar)
@ -143,9 +127,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}else{ }else{
downloadAllFabCoordinator!!.visibility = GONE 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") showSingleDownloadSheet(it[0], "audio")
} }
}
firstBoot = false firstBoot = false
} }
@ -338,9 +324,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// } catch (ignored: Exception) { // } catch (ignored: Exception) {
// } // }
// } // }
val sharedPreferences = if (sharedPreferences!!.getBoolean("download_card", true)) {
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear() // selectedObjects!!.clear()
// selectedObjects!!.add(item!!) // selectedObjects!!.add(item!!)
//showConfigureSingleDownloadCard(createDownloadItem(item!!, type), item) //showConfigureSingleDownloadCard(createDownloadItem(item!!, type), item)

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

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

@ -15,7 +15,6 @@ import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
class NotificationUtil(var context: Context) { class NotificationUtil(var context: Context) {
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID) 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 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) private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
fun createNotificationChannel() { fun createNotificationChannel() {
@ -39,43 +38,6 @@ class NotificationUtil(var context: Context) {
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance) channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description channel.description = description
notificationManager.createNotificationChannel(channel) 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) { when(channel) {
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder} DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder } COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
FILE_TRANSFER_CHANNEL_ID -> { return fileTransferNotificationBuilder }
} }
return downloadNotificationBuilder return downloadNotificationBuilder
} }
@ -102,7 +63,7 @@ class NotificationUtil(var context: Context) {
intent.putExtra("workID", workID) intent.putExtra("workID", workID)
val cancelNotificationPendingIntent = PendingIntent.getBroadcast( val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
context, context,
0, workID,
intent, intent,
PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_IMMUTABLE
) )

@ -5,6 +5,7 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Looper
import android.os.SystemClock import android.os.SystemClock
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.Log import android.util.Log
@ -26,6 +27,7 @@ import com.yausername.youtubedl_android.YoutubeDLRequest
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.io.File import java.io.File
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.math.abs
class DownloadWorker( class DownloadWorker(
@ -33,7 +35,7 @@ class DownloadWorker(
workerParams: WorkerParameters workerParams: WorkerParameters
) : Worker(context, workerParams) { ) : Worker(context, workerParams) {
override fun doWork(): Result { override fun doWork(): Result {
workID = inputData.getInt("workID", SystemClock.uptimeMillis().toInt()) workID = inputData.getLong("workID", SystemClock.uptimeMillis())
val notificationUtil = NotificationUtil(context) val notificationUtil = NotificationUtil(context)
val dbManager = DBManager.getInstance(context) val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao val dao = dbManager.downloadDao
@ -47,8 +49,8 @@ class DownloadWorker(
} }
val intent = Intent(context, MainActivity::class.java) val intent = Intent(context, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, downloadItem.title, workID, NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID) val notification = notificationUtil.createDownloadServiceNotification(pendingIntent, downloadItem.title, workID.toInt(), NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID)
val foregroundInfo = ForegroundInfo(workID, notification) val foregroundInfo = ForegroundInfo(workID.toInt(), notification)
setForegroundAsync(foregroundInfo) setForegroundAsync(foregroundInfo)
@ -123,10 +125,6 @@ class DownloadWorker(
request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s") request.addOption("-o", tempFileDir.absolutePath + "/%(uploader)s - %(title)s.%(ext)s")
} }
"video" -> { "video" -> {
if (downloadLocation == context.getString(R.string.video_path)){
tempFileDir = File(context.getString(R.string.video_path))
}
val addChapters = sharedPreferences.getBoolean("add_chapters", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false)
if (addChapters) { if (addChapters) {
request.addOption("--sponsorblock-mark", "all") request.addOption("--sponsorblock-mark", "all")
@ -136,17 +134,19 @@ class DownloadWorker(
request.addOption("--embed-subs", "") request.addOption("--embed-subs", "")
} }
var videoFormatID = downloadItem.format.format_id var videoFormatID = downloadItem.format.format_id
if (videoFormatID.isEmpty()) videoFormatID = "bestvideo" Log.e(TAG, videoFormatID)
val formatArgument = StringBuilder(videoFormatID) var formatArgument = "bestvideo+bestaudio/best"
formatArgument.append("+bestaudio/best") if (videoFormatID.isNotEmpty()) {
request.addOption("-f", formatArgument.toString()) if (videoFormatID == "Best Quality") videoFormatID = "bestvideo"
var format = downloadItem.format.container 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()){ if(format.isNotEmpty()){
format = sharedPreferences.getString("video_format", "")!! request.addOption("--merge-output-format", format)
if (format != "DEFAULT") request.addOption("--merge-output-format", format)
} }
if (format != "webm" && format != "DEFAULT") {
if (format != "webm") {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
if (embedThumb) { if (embedThumb) {
request.addOption("--embed-thumbnail") request.addOption("--embed-thumbnail")
@ -173,13 +173,14 @@ class DownloadWorker(
setProgressAsync(workDataOf("progress" to progress.toInt())) setProgressAsync(workDataOf("progress" to progress.toInt()))
val title: String = downloadItem.title val title: String = downloadItem.title
notificationUtil.updateDownloadNotification( notificationUtil.updateDownloadNotification(
workID, downloadItem.workID.toInt(),
line, progress.toInt(), 0, title, line, progress.toInt(), 0, title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
) )
} }
}.onSuccess { }.onSuccess {
//move file from internal to set download directory //move file from internal to set download directory
moveFile(tempFileDir.absoluteFile, downloadLocation){ progress -> moveFile(tempFileDir.absoluteFile, downloadLocation){ progress ->
setProgressAsync(workDataOf("progress" to progress)) setProgressAsync(workDataOf("progress" to progress))
} }
@ -187,22 +188,31 @@ class DownloadWorker(
val incognito = sharedPreferences.getBoolean("incognito", false) val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) { if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000 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 { runBlocking {
historyDao.insert(historyItem) historyDao.insert(historyItem)
} }
} }
runBlocking {
dao.delete(downloadItem.id)
}
}.onFailure { }.onFailure {
tempFileDir.delete() tempFileDir.delete()
if (BuildConfig.DEBUG) { Looper.prepare()
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show() Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
Log.e(TAG, context.getString(R.string.failed_download), it) Log.e(TAG, context.getString(R.string.failed_download), it)
}
notificationUtil.updateDownloadNotification( notificationUtil.updateDownloadNotification(
workID, downloadItem.workID.toInt(),
context.getString(R.string.failed_download), 0, 0, downloadItem.title, context.getString(R.string.failed_download), 0, 0, downloadItem.title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
) )
downloadItem.status = DownloadRepository.status.Errored.toString()
runBlocking {
dao.update(downloadItem)
}
return Result.failure() return Result.failure()
} }
@ -227,13 +237,13 @@ class DownloadWorker(
DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this)) DocumentsContract.buildChildDocumentsUriUsingTree(this, DocumentsContract.getTreeDocumentId(this))
} }
val fileUtil = FileUtil() val fileUtil = FileUtil()
fileUtil.moveFile(originDir, context, destDir){ p -> fileUtil.moveFile(originDir, context, downLocation){ p ->
progress(p) progress(p)
} }
} }
companion object { companion object {
var workID: Int = 0 var workID: Long = 0
const val TAG = "DownloadWorker" 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 <com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup" android:id="@+id/chipGroup"
android:layout_width="wrap_content" android:layout_width="wrap_content"
app:singleLine="true" app:singleLine="false"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.chip.Chip <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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" 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"> xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/downloads_appbarlayout" android:id="@+id/history_appbarlayout"
app:liftOnScroll="false" app:liftOnScroll="false"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/downloads_toolbar" android:id="@+id/history_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap" app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/history" app:title="@string/history"
app:menu="@menu/downloads_menu" app:menu="@menu/history_menu"
android:theme="@style/Toolbar" android:theme="@style/Toolbar"
/> />
@ -39,7 +39,7 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:id="@+id/downloads_selection_chips" android:id="@+id/history_selection_chips"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -101,7 +101,7 @@
</HorizontalScrollView> </HorizontalScrollView>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerviewdownloadss" android:id="@+id/recyclerviewhistorys"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/chips_recycler_horizontalscrollview" android:layout_below="@+id/chips_recycler_horizontalscrollview"
@ -119,12 +119,12 @@
android:visibility="gone" android:visibility="gone"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@+id/chips_recycler_horizontalscrollview" android:layout_below="@+id/chips_recycler_horizontalscrollview"
android:id="@+id/shimmer_downloads_framelayout" android:id="@+id/shimmer_history_framelayout"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:id="@+id/shimmer_downloads_linear_layout" android:id="@+id/shimmer_history_linear_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="10dp" android:layout_margin="10dp"

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

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