added new download card again...

This commit is contained in:
Denis Çerri 2023-01-23 17:59:13 +01:00
parent 19eab5e054
commit 7f813ebd96
No known key found for this signature in database
GPG key ID: 95C43D517D830350
47 changed files with 1511 additions and 728 deletions

View file

@ -320,7 +320,7 @@
<PersistentState>
<option name="values">
<map>
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/keyboard_double_arrow_down/baseline_keyboard_double_arrow_down_24.xml" />
<entry key="url" value="jar:file:/C:/Program%20Files/Android/Android%20Studio/plugins/android/lib/android.jar!/images/material/icons/materialicons/history/baseline_history_24.xml" />
</map>
</option>
</PersistentState>
@ -330,8 +330,7 @@
</option>
<option name="values">
<map>
<entry key="color" value="00a6ff" />
<entry key="outputName" value="ic_concurrent_downloads" />
<entry key="outputName" value="ic_history" />
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
</map>
</option>

View file

@ -1,6 +1,13 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
@ -109,5 +116,8 @@
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -2,12 +2,18 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "c8effa77edcd8cf5e46a79fd1a791535",
"identityHash": "94d37ae85390ef5198651df2480a02bf",
"entities": [
{
"tableName": "results",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
@ -49,12 +55,6 @@
"columnName": "playlistTitle",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
@ -68,18 +68,18 @@
},
{
"tableName": "formats",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER NOT NULL, `format` TEXT NOT NULL, `format_id` TEXT NOT NULL, `ext` TEXT NOT NULL, `filesize` INTEGER NOT NULL, `format_note` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemId` INTEGER NOT NULL, `format_id` TEXT NOT NULL, `container` TEXT NOT NULL, `filesize` INTEGER NOT NULL, `format_note` TEXT NOT NULL, `encoding` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "itemId",
"columnName": "itemId",
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "format",
"columnName": "format",
"affinity": "TEXT",
"fieldPath": "itemId",
"columnName": "itemId",
"affinity": "INTEGER",
"notNull": true
},
{
@ -89,8 +89,8 @@
"notNull": true
},
{
"fieldPath": "ext",
"columnName": "ext",
"fieldPath": "container",
"columnName": "container",
"affinity": "TEXT",
"notNull": true
},
@ -107,10 +107,10 @@
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
"fieldPath": "encoding",
"columnName": "encoding",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
@ -124,8 +124,14 @@
},
{
"tableName": "history",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
@ -179,12 +185,6 @@
"columnName": "website",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
@ -198,8 +198,14 @@
},
{
"tableName": "downloads",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioFormat` TEXT NOT NULL, `videoFormat` TEXT NOT NULL, `audioFormatId` TEXT NOT NULL, `videoFormatId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `ext` TEXT NOT NULL, `filesize` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `audioFormatId` TEXT NOT NULL, `videoFormatId` TEXT NOT NULL, `customTemplateId` INTEGER NOT NULL, `formatDesc` TEXT NOT NULL, `removeAudio` INTEGER NOT NULL DEFAULT 0, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `embedSubs` INTEGER NOT NULL, `addChapters` INTEGER NOT NULL, `SaveThumb` INTEGER NOT NULL, `ext` TEXT NOT NULL, `filesize` TEXT NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `workID` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
@ -236,18 +242,6 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioFormat",
"columnName": "audioFormat",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFormat",
"columnName": "videoFormat",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "audioFormatId",
"columnName": "audioFormatId",
@ -345,12 +339,6 @@
"columnName": "workID",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
@ -364,8 +352,14 @@
},
{
"tableName": "commandTemplates",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`title` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
@ -377,12 +371,6 @@
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
@ -398,7 +386,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c8effa77edcd8cf5e46a79fd1a791535')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '94d37ae85390ef5198651df2480a02bf')"
]
}
}

View file

@ -21,7 +21,7 @@ import androidx.work.WorkManager
import com.deniscerri.ytdlnis.DownloaderService.LocalBinder
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding
import com.deniscerri.ytdlnis.ui.DownloadsFragment
import com.deniscerri.ytdlnis.ui.HistoryFragment
import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.ui.MoreFragment
import com.deniscerri.ytdlnis.ui.settings.SettingsActivity
@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var context: Context
private lateinit var homeFragment: HomeFragment
private lateinit var downloadsFragment: DownloadsFragment
private lateinit var historyFragment: HistoryFragment
var downloaderService: DownloaderService? = null
private lateinit var workManager: WorkManager
@ -84,7 +84,7 @@ class MainActivity : AppCompatActivity() {
workManager = WorkManager.getInstance(context)
homeFragment = HomeFragment()
downloadsFragment = DownloadsFragment()
historyFragment = HistoryFragment()
moreFragment = MoreFragment()
initFragments()
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
@ -97,13 +97,13 @@ class MainActivity : AppCompatActivity() {
}
replaceFragment(homeFragment)
}
R.id.downloads -> {
if (lastFragment === downloadsFragment) {
downloadsFragment.scrollToTop()
R.id.history -> {
if (lastFragment === historyFragment) {
historyFragment.scrollToTop()
} else {
this.title = getString(R.string.downloads)
}
replaceFragment(downloadsFragment)
replaceFragment(historyFragment)
}
R.id.more -> {
if (lastFragment === moreFragment) {
@ -151,7 +151,7 @@ class MainActivity : AppCompatActivity() {
if (Intent.ACTION_SEND == action && type != null) {
Log.e(TAG, action)
homeFragment = HomeFragment()
downloadsFragment = DownloadsFragment()
historyFragment = HistoryFragment()
moreFragment = MoreFragment()
if (type.equals("application/txt", ignoreCase = true)) {
try {
@ -185,9 +185,9 @@ class MainActivity : AppCompatActivity() {
private fun initFragments() {
fm.beginTransaction()
.replace(R.id.frame_layout, homeFragment)
.add(R.id.frame_layout, downloadsFragment)
.add(R.id.frame_layout, historyFragment)
.add(R.id.frame_layout, moreFragment)
.hide(downloadsFragment)
.hide(historyFragment)
.hide(moreFragment)
.commit()
lastFragment = homeFragment

View file

@ -101,13 +101,13 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
}
card.setOnClickListener {
onItemClickListener.onCardClick(item.id!!)
onItemClickListener.onCardClick(item.id)
}
}
interface OnItemClickListener {
fun onCardClick(itemID: Int)
fun onCardClick(itemID: Long)
}
companion object {

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.adapter
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Color
import android.graphics.ColorMatrix
@ -28,7 +29,7 @@ import java.text.SimpleDateFormat
import java.util.*
class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<HistoryItem?, HistoryAdapter.ViewHolder>(AsyncDifferConfig.Builder(DIFF_CALLBACK).build()) {
private val checkedItems: ArrayList<Int>
private val checkedItems: ArrayList<Long>
private val onItemClickListener: OnItemClickListener
private val activity: Activity
@ -48,16 +49,16 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cardView = LayoutInflater.from(parent.context)
.inflate(R.layout.downloads_card, parent, false)
.inflate(R.layout.history_card, parent, false)
return ViewHolder(cardView, onItemClickListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val video = getItem(position)
val item = getItem(position)
val card = holder.cardView
// THUMBNAIL ----------------------------------
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
val imageURL = video!!.thumb
val imageURL = item!!.thumb
val uiHandler = Handler(Looper.getMainLooper())
if (imageURL.isNotEmpty()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
@ -67,28 +68,28 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
// TITLE ----------------------------------
val videoTitle = card.findViewById<TextView>(R.id.downloads_title)
var title = video.title
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
var title = item.title
if (title.length > 100) {
title = title.substring(0, 40) + "..."
}
videoTitle.text = title
itemTitle.text = title
// Bottom Info ----------------------------------
val bottomInfo = card.findViewById<TextView>(R.id.downloads_info_bottom)
var info = video.author
if (video.duration.isNotEmpty()) {
if (video.author.isNotEmpty()) info += ""
info += video.duration
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 = video.time
val time = item.time
val downloadedTime: String
if (time == 0L) {
downloadedTime = activity.getString(R.string.currently_downloading) + " " + video.type
downloadedTime = activity.getString(R.string.currently_downloading) + " " + item.type
} else {
val cal = Calendar.getInstance()
val date = Date(time * 1000L)
@ -108,7 +109,7 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
var filePresent = true
//IS IN THE FILE SYSTEM?
val path = video.downloadPath
val path = item.downloadPath
val file = File(path)
if (!file.exists() && path.isNotEmpty()) {
filePresent = false
@ -119,15 +120,15 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
})
thumbnail.alpha = 0.7f
}
if (video.type.isNotEmpty()) {
if (video.type == "audio") {
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(position)) {
if (checkedItems.contains(item.id)) {
card.isChecked = true
card.strokeWidth = 5
} else {
@ -136,49 +137,54 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
}
val finalFilePresent = filePresent
card.setOnLongClickListener {
checkCard(card, position)
checkCard(card, item.id)
true
}
card.setOnClickListener {
if (checkedItems.size > 0) {
checkCard(card, video.id!!)
checkCard(card, item.id)
} else {
onItemClickListener.onCardClick(video.id!!, finalFilePresent)
onItemClickListener.onCardClick(item.id, finalFilePresent)
}
}
}
private fun checkCard(card: MaterialCardView, videoID: Int) {
private fun checkCard(card: MaterialCardView, itemID: Long) {
if (card.isChecked) {
card.strokeWidth = 0
checkedItems.removeAt(videoID)
checkedItems.remove(itemID)
} else {
card.strokeWidth = 5
checkedItems.add(videoID)
checkedItems.add(itemID)
}
card.isChecked = !card.isChecked
onItemClickListener.onCardSelect(videoID, card.isChecked)
onItemClickListener.onCardSelect(itemID, card.isChecked)
}
interface OnItemClickListener {
fun onCardClick(position: Int, isPresent: Boolean)
fun onCardSelect(position: Int, isChecked: Boolean)
fun onCardClick(itemID: Long, isPresent: Boolean)
fun onCardSelect(itemID: Long, isChecked: Boolean)
fun onButtonClick(position: Int)
}
fun clearCheckedVideos() {
val size = checkedItems.size
for (i in 0 until size) {
val position = checkedItems[i]
notifyItemChanged(position)
@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 {
return oldItem.id === newItem.id
val ranged = arrayListOf(oldItem.id, newItem.id)
return ranged[0] == ranged[1]
}
override fun areContentsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {

View file

@ -179,7 +179,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
}
override fun areContentsTheSame(oldItem: ResultItem, newItem: ResultItem): Boolean {
return oldItem.url == newItem.url
return oldItem.url == newItem.url && oldItem.title == newItem.title && oldItem.author == newItem.author
}
}
}

View file

@ -14,7 +14,7 @@ interface CommandTemplateDao {
fun getAllTemplates() : List<CommandTemplate>
@Query("SELECT * FROM commandTemplates WHERE id=:id LIMIT 1")
fun getTemplateById(id: Int) : CommandTemplate
fun getTemplate(id: Long) : CommandTemplate
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: CommandTemplate)

View file

@ -24,8 +24,8 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getDownloadById(id: Int) : DownloadItem
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: DownloadItem)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: DownloadItem) : Long
@Query("DELETE FROM downloads")
suspend fun deleteAll()
@ -35,4 +35,7 @@ interface DownloadDao {
@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(item: DownloadItem)
@Query("SELECT * FROM downloads ORDER BY id DESC LIMIT 1")
fun getLatest() : DownloadItem
}

View file

@ -8,7 +8,7 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
@Dao
interface FormatDao {
@Query("SELECT * FROM formats WHERE itemId=:itemId")
fun getFormatsByItemId(itemId: Int) : List<Format>
fun getFormatsByItemId(itemId: Long) : List<Format>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: Format)
@ -20,6 +20,6 @@ interface FormatDao {
suspend fun deleteAll()
@Query("DELETE FROM formats WHERE itemId=:itemId")
suspend fun deleteFromatsByItemId(itemId: Int)
suspend fun deleteFromatsByItemId(itemId: Long)
}

View file

@ -23,7 +23,7 @@ interface HistoryDao {
suspend fun insert(item: HistoryItem)
@Query("DELETE FROM history WHERE id=:id")
suspend fun delete(id: Int)
suspend fun delete(id: Long)
@Query("DELETE FROM history")
suspend fun deleteAll()

View file

@ -24,4 +24,7 @@ interface ResultDao {
@Query("DELETE FROM results")
suspend fun deleteAll()
@Query("SELECT * FROM results WHERE url=:url LIMIT 1")
fun getResultByURL(url: String) : ResultItem
}

View file

@ -6,9 +6,8 @@ import androidx.room.PrimaryKey
@Entity(tableName = "commandTemplates")
data class CommandTemplate(
@PrimaryKey(autoGenerate = true)
var id: Long,
val title: String,
val content: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
)

View file

@ -6,33 +6,30 @@ import androidx.room.PrimaryKey
@Entity(tableName = "downloads")
data class DownloadItem(
val url: String,
var title: String,
var author: String,
val thumb: String,
val duration: String,
val type: String,
var audioFormat: String,
var videoFormat: String,
var audioFormatId: String,
var videoFormatId: String,
var customTemplateId: Int,
val formatDesc: String,
@ColumnInfo(defaultValue = "0")
val removeAudio: Boolean,
val downloadPath: String,
val website: String,
val downloadSize: String,
val playlistTitle: String,
val embedSubs: Boolean,
val addChapters: Boolean,
val SaveThumb: Boolean,
var ext: String,
var filesize: String,
@ColumnInfo(defaultValue = "Queued")
var status: String,
val workID: Int
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
var id: Long,
val url: String,
var title: String,
var author: String,
val thumb: String,
val duration: String,
var type: String,
var audioFormatId: String,
var videoFormatId: String,
var customTemplateId: Long,
var formatDesc: String,
@ColumnInfo(defaultValue = "0")
val removeAudio: Boolean,
val downloadPath: String,
val website: String,
val downloadSize: String,
val playlistTitle: String,
val embedSubs: Boolean,
val addChapters: Boolean,
val SaveThumb: Boolean,
var ext: String,
var filesize: String,
@ColumnInfo(defaultValue = "Queued")
var status: String,
val workID: Int
)

View file

@ -6,18 +6,17 @@ import com.google.gson.annotations.SerializedName
@Entity(tableName = "formats")
data class Format(
var itemId: Int,
@SerializedName(value = "format", alternate = ["type"])
val format: String,
@PrimaryKey(autoGenerate = true)
var id: Long,
var itemId: Long,
@SerializedName(value = "format_id", alternate = ["itag"])
val format_id: String,
@SerializedName(value = "ext", alternate = ["container", ""])
val ext: String,
@SerializedName(value = "filesize", alternate = ["clen"])
@SerializedName(value = "ext", alternate = ["container"])
val container: String,
@SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"])
val filesize: Long,
@SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"])
val format_note: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
val format_note: String,
@SerializedName(value = "encoding", alternate = ["vcodec", "acodec", "video_ext"])
val encoding: String
)

View file

@ -5,6 +5,8 @@ import androidx.room.PrimaryKey
@Entity(tableName = "history")
data class HistoryItem(
@PrimaryKey(autoGenerate = true)
var id: Long,
val url: String,
val title: String,
val author: String,
@ -14,7 +16,4 @@ data class HistoryItem(
val time: Long,
val downloadPath: String,
val website: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
)

View file

@ -5,14 +5,13 @@ import androidx.room.PrimaryKey
@Entity(tableName = "results")
data class ResultItem(
val url: String,
var title: String,
var author: String,
val duration: String,
val thumb: String,
val website: String,
var playlistTitle: String
){
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
var id: Long,
val url: String,
var title: String,
var author: String,
val duration: String,
val thumb: String,
val website: String,
var playlistTitle: String
)

View file

@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.database.repository
import android.util.Log
import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -9,11 +10,11 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads()
enum class status {
Active, Queued, Errored
Active, Queued, Errored, Processing
}
suspend fun insert(item: DownloadItem){
downloadDao.insert(item)
suspend fun insert(item: DownloadItem) : Long {
return downloadDao.insert(item)
}
suspend fun delete(item: DownloadItem){
@ -37,4 +38,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
update(item);
}
fun getLatest() : DownloadItem {
return downloadDao.getLatest()
}
}

View file

@ -53,7 +53,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
val fileUtil = FileUtil()
items.value?.forEach { item ->
if (!fileUtil.exists(item.downloadPath)){
historyDao.delete(item.id!!)
historyDao.delete(item.id)
}
}
}

View file

@ -107,7 +107,7 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
private suspend fun addFormats(formats : ArrayList<Format>, id: Long){
formats.forEach { f ->
if (f.filesize == 0L) return@forEach
f.itemId = id.toInt()
f.itemId = id
formatDao.insert(f)
}
}
@ -122,11 +122,15 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
}
fun getFormats(item: ResultItem): List<Format> {
return formatDao.getFormatsByItemId(item.id!!)
return formatDao.getFormatsByItemId(item.id)
}
fun getTemplates() : List<CommandTemplate> {
return commandTemplateDao.getAllTemplates()
}
fun getItemByURL(url: String): ResultItem {
return resultDao.getResultByURL(url)
}
}

View file

@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -27,8 +28,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
fun insertDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
repository.insert(item)
fun insertDownload(item: DownloadItem) : LiveData<Long> {
val result = MutableLiveData<Long>()
viewModelScope.launch(Dispatchers.IO){
val id = repository.insert(item)
result.postValue(id)
}
return result
}
fun deleteDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO) {
@ -38,4 +44,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
fun updateDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
repository.update(item);
}
fun getLatest() : DownloadItem {
return repository.getLatest()
}
}

View file

@ -95,29 +95,38 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
}
fun getFormats(item: ResultItem, type: String) : List<Format> {
val list = repository.getFormats(item)
var list = repository.getFormats(item)
val formats = mutableListOf<Format>()
if (list.isEmpty()){
when(type){
"audio" -> {
val audioFormats = getApplication<App>().resources.getStringArray(R.array.audio_formats)
audioFormats.forEach { formats.add(Format(item.id!!, it, "", "", 0, it)) }
}
"video" -> {
val videoFormats = getApplication<App>().resources.getStringArray(R.array.video_formats)
videoFormats.forEach { formats.add(Format(item.id!!, it, "", "", 0, it)) }
}
}
return formats
}
val audioFormats = getApplication<App>().resources.getStringArray(R.array.audio_formats)
val videoFormats = getApplication<App>().resources.getStringArray(R.array.video_formats)
when(type){
"audio" -> return list.filter { it.format.contains("audio", ignoreCase = true) }
"video" -> return list.filter { !it.format.contains("audio", ignoreCase = true) }
"audio" -> {
list = list.filter { it.format_note.contains("audio", ignoreCase = true) }
if (list.isEmpty()) {
audioFormats.forEach { formats.add(Format(0, item.id, it, "", 0, it, "")) }
return formats
}
return list
}
"video" -> {
list = list.filter { !it.format_note.contains("audio", ignoreCase = true) }
if (list.isEmpty()) {
videoFormats.forEach { formats.add(Format(0, item.id, it, "", 0, it, "")) }
return formats
}
return list
}
}
val templates = repository.getTemplates()
templates.forEach {
formats.add(Format(item.id!!, it.title, it.id.toString(), "", 0, it.content))
formats.add(Format(0, item.id, it.title, "", 0, it.content, ""))
}
return formats
}
fun getItemByURL(url: String) : ResultItem {
return repository.getItemByURL(url)
}
}

View file

@ -24,7 +24,7 @@ import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
import com.deniscerri.ytdlnis.databinding.FragmentHistoryBinding
import com.deniscerri.ytdlnis.util.FileUtil
import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout
@ -39,7 +39,7 @@ import java.io.File
/**
* A fragment representing a list of Items.
*/
class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
OnClickListener, OnLongClickListener {
private lateinit var historyViewModel : HistoryViewModel
@ -66,14 +66,14 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
private var _binding : FragmentDownloadsBinding? = null
private var _binding : FragmentHistoryBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentDownloadsBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_downloads, container, false)
_binding = FragmentHistoryBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_history, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
@ -237,7 +237,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
sortChip.setOnClickListener {
sortSheet = BottomSheetDialog(requireContext())
sortSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
sortSheet!!.setContentView(R.layout.downloads_sort_sheet)
sortSheet!!.setContentView(R.layout.history_sort_sheet)
val newest = sortSheet!!.findViewById<TextView>(R.id.newest)
val oldest = sortSheet!!.findViewById<TextView>(R.id.oldest)
newest!!.setOnClickListener {
@ -309,13 +309,13 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
override fun onClick(v: View) {
when (v.id) {
R.id.bottomsheet_remove_button -> {
removeItem(v.tag as Int)
removeItem(v.tag as Long)
}
R.id.bottom_sheet_link -> {
openLinkIntent(v.tag as Int)
openLinkIntent(v.tag as Long)
}
R.id.bottomsheet_open_file_button -> {
openFileIntent(v.tag as Int)
openFileIntent(v.tag as Long)
}
R.id.delete_selected_fab -> {
removeSelectedItems()
@ -326,7 +326,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
override fun onLongClick(v: View): Boolean {
val id = v.id
if (id == R.id.bottom_sheet_link) {
copyLinkToClipBoard(v.tag as Int)
copyLinkToClipBoard(v.tag as Long)
return true
}
return false
@ -347,13 +347,13 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel.delete(item, deleteFile[0])
}
selectedObjects = ArrayList()
historyAdapter!!.clearCheckedVideos()
historyAdapter!!.clearCheckeditems()
deleteFab!!.visibility = GONE
}
deleteDialog.show()
}
private fun removeItem(id: Int) {
private fun removeItem(id: Long) {
if (bottomSheet != null) bottomSheet!!.hide()
val deleteFile = booleanArrayOf(false)
val v = findItem(id)
@ -370,7 +370,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteDialog.show()
}
private fun copyLinkToClipBoard(id: Int) {
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)
@ -380,7 +380,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
.show()
}
private fun openLinkIntent(id: Int) {
private fun openLinkIntent(id: Long) {
val url = findItem(id)?.url
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(url)
@ -388,7 +388,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
startActivity(i)
}
private fun openFileIntent(id: Int) {
private fun openFileIntent(id: Long) {
val downloadPath = findItem(id)!!.downloadPath
val file = File(downloadPath)
val uri = FileProvider.getUriForFile(
@ -403,11 +403,11 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
startActivity(i)
}
override fun onCardClick(id: Int, isFilePresent: Boolean) {
override fun onCardClick(videoID: Long, isPresent: Boolean) {
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.downloads_bottom_sheet)
val video = findItem(id)
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)
@ -415,16 +415,16 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
val link = bottomSheet!!.findViewById<Button>(R.id.bottom_sheet_link)
val url = video.url
link!!.text = url
link.tag = id
link.tag = videoID
link.setOnClickListener(this)
link.setOnLongClickListener(this)
val remove = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_remove_button)
remove!!.tag = id
remove!!.tag = videoID
remove.setOnClickListener(this)
val openFile = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_open_file_button)
openFile!!.tag = id
openFile!!.tag = videoID
openFile.setOnClickListener(this)
if (!isFilePresent) openFile.visibility = GONE
if (!isPresent) openFile.visibility = GONE
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
@ -432,9 +432,9 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
)
}
override fun onCardSelect(id: Int, add: Boolean) {
val item = findItem(id)
if (add) selectedObjects!!.add(item!!)
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
@ -454,7 +454,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
// }
}
private fun findItem(id : Int): HistoryItem? {
private fun findItem(id : Long): HistoryItem? {
return downloadsList?.find { it?.id == id }
}

View file

@ -4,12 +4,11 @@ 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
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.util.Log
import android.view.*
import android.view.View.GONE
@ -17,7 +16,6 @@ import android.view.View.VISIBLE
import android.widget.*
import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.text.trimmedLength
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
@ -28,9 +26,12 @@ import com.deniscerri.ytdlnis.adapter.HomeAdapter
import com.deniscerri.ytdlnis.database.DatabaseManager
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentDownloadsBinding
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.service.DownloadInfo
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.facebook.shimmer.ShimmerFrameLayout
@ -43,7 +44,6 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.runBlocking
import java.text.DecimalFormat
import java.util.*
import kotlin.math.log10
@ -65,6 +65,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var downloadInfo: DownloadInfo? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel
private var downloading = false
private var fragmentView: View? = null
@ -86,13 +87,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null
private var _binding : FragmentDownloadsBinding? = null
private var _binding : FragmentHomeBinding? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentDownloadsBinding.inflate(inflater, container, false)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_home, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
@ -117,6 +118,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadQueue = ArrayList()
resultsList = mutableListOf()
selectedObjects = ArrayList()
@ -141,7 +144,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
homeAdapter!!.submitList(it)
resultsList = it
if(it.size > 1){
if (it[0].playlistTitle.isNotEmpty() || it[0].playlistTitle != "ytdlnis-TRENDING"){
if (it[0].playlistTitle.isNotEmpty() || it[0].playlistTitle != getString(R.string.trendingPlaylist)){
downloadAllFabCoordinator!!.visibility = VISIBLE
}
}
@ -205,68 +208,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}
}
//
// private fun initCards() {
// val uiHandler = Handler(Looper.getMainLooper())
// try {
// val thread = Thread {
// databaseManager = DatabaseManager(context)
// resultsList = databaseManager!!.results
// var playlistTitle = ""
// try {
// playlistTitle = resultsList.get(0).playlistTitle
// } catch (ignored: Exception) {
// }
// if (resultsList.size == 0 || playlistTitle == getString(R.string.trendingPlaylist) && !downloading) {
// try {
// databaseManager!!.clearResults()
// uiHandler.post {
// shimmerCards!!.startShimmer()
// shimmerCards!!.visibility = View.VISIBLE
// }
// infoUtil = InfoUtil(context!!)
// resultsList = infoUtil!!.getTrending(context!!)
// databaseManager!!.addToResults(resultsList)
// } catch (e: Exception) {
// Log.e(TAG, e.toString())
// }
// } else {
// if (!downloading) {
// homeAdapter!!.add(resultsList)
// for (i in resultsList.indices) {
// val tmp = resultsList.get(i)
// if (tmp!!.isDownloading) {
// updateDownloadingStatusOnResult(tmp, "audio", false)
// updateDownloadingStatusOnResult(tmp, "video", false)
// }
// }
// }
// }
// uiHandler.post {
// if (homeAdapter!!.itemCount != resultsList.size) {
// homeAdapter!!.add(resultsList)
// }
// shimmerCards!!.stopShimmer()
// shimmerCards!!.visibility = View.GONE
// }
// databaseManager!!.close()
// if (resultsList != null) {
// uiHandler.post { scrollToTop() }
// if (resultsList!!.size > 1 && resultsList!![1]!!.isPlaylistItem == 1) {
// uiHandler.post { downloadAllFabCoordinator!!.visibility = View.VISIBLE }
// }
// }
// }
// thread.start()
// } catch (e: Exception) {
// Log.e(TAG, e.toString())
// uiHandler.post {
// shimmerCards!!.stopShimmer()
// shimmerCards!!.visibility = View.GONE
// }
// }
// }
private fun initMenu() {
val onActionExpandListener: MenuItem.OnActionExpandListener =
object : MenuItem.OnActionExpandListener {
@ -407,7 +348,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear()
// selectedObjects!!.add(item!!)
showConfigureSingleDownloadCard(item!!, type)
//showConfigureSingleDownloadCard(createDownloadItem(item!!, type), item)
createDownloadItem(item!!, type){
showSingleDownloadSheet(it)
}
} else {
// downloadQueue!!.add(vid)
// updateDownloadingStatusOnResult(vid, type, true)
@ -418,6 +362,38 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}
}
private fun showSingleDownloadSheet(item : DownloadItem){
val bottomSheet = DownloadBottomSheetDialog(item)
bottomSheet.show(parentFragmentManager, null)
}
private fun createDownloadItem(item: ResultItem, type: String, itemCreated: (DownloadItem) -> Unit) : DownloadItem {
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
val downloadItem = DownloadItem(0,
item.url,
item.title,
item.author,
item.thumb,
item.duration,
type,
"", "", 0, "", false,
"", item.website, "", item.playlistTitle, embedSubs, addChapters, saveThumb, "",
"", DownloadRepository.status.Processing.toString(), 0
)
downloadViewModel.insertDownload(downloadItem).observe(viewLifecycleOwner) {
downloadItem.id = it
itemCreated(downloadItem)
}
return downloadItem
}
fun updateDownloadStatusOnResult(v: ResultItem?, type: String?, downloaded: Boolean) {
// if (v != null) {
@ -576,275 +552,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
// }
// }
}
private fun showConfigureSingleDownloadCard(item: ResultItem, type: String) {
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
var downloadItem = DownloadItem(
item.url,
item.title,
item.author,
item.thumb,
item.duration,
type,
"", "", "", "", 0, "", false,
"", item.website, "", item.playlistTitle, embedSubs, addChapters, saveThumb, "",
"", "", 0
)
val editor = sharedPreferences.edit()
try {
bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.home_download_single_bottom_sheet)
val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
title!!.editText!!.setText(item.title)
title.editText!!.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.title = p0.toString()
item.title = p0.toString()
resultViewModel.update(item)
}
})
val author = bottomSheet!!.findViewById<TextInputLayout>(R.id.author_textinput)
author!!.editText!!.setText(item.author)
author.editText!!.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.author = p0.toString()
item.author = p0.toString()
resultViewModel.update(item)
}
})
var formats = resultViewModel.getFormats(item, type)
Log.e(TAG, formats.toString())
val formatTitles = formats.map {
if (it.format_note.contains("AUDIO_QUALITY_"))
it.format_note.replace("AUDIO_QUALITY_", "") +
if (it.filesize == 0L) "" else " / " + convertFileSize(it.filesize)
else it.format_note +
if (it.filesize == 0L) "" else " / " + convertFileSize(it.filesize)}
val format = bottomSheet!!.findViewById<TextInputLayout>(R.id.format)
val autoCompleteTextView = bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_dropdown_item_1line, formatTitles))
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
when(type){
"audio" -> {
downloadItem.audioFormat = formats[index].format
downloadItem.audioFormatId = formats[index].format_id
}
"video" -> {
downloadItem.videoFormat = formats[index].format
downloadItem.videoFormatId = formats[index].format_id
}
"command" -> {
downloadItem.customTemplateId = formats[index].format_id.toInt()
}
}
}
lateinit var selectedContainer : String
val containers = when (type){
"audio" -> requireContext().resources.getStringArray(R.array.audio_containers)
"video" -> requireContext().resources.getStringArray(R.array.video_containers)
else -> null
}
val container = bottomSheet!!.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView = bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.container_textview)
if (containers == null){
containerAutoCompleteTextView!!.setText(getString(R.string.custom_command), false)
containerAutoCompleteTextView.isClickable = false
containerAutoCompleteTextView.isLongClickable = false
}else{
// val containerTitles = containers.map {
// if (it.format_note.contains("AUDIO_QUALITY_"))
// it.format_note.replace("AUDIO_QUALITY_", "") + " / " + convertFileSize(it.filesize)
// else it.format_note + " / " + convertFileSize(it.filesize) }
selectedContainer = when(type){
"audio" -> formats.find { downloadItem.audioFormat == it.format }?.ext ?: sharedPreferences.getString("audio_format", "mp3")!!
else -> formats.find { downloadItem.videoFormat == it.format }?.ext ?: sharedPreferences.getString("video_format", "DEFAULT")!!
}
containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.ext = containers[index]
}
}
// when(type) {
// "audio" -> {
//
// }
// }
// if (type == "audio") {
//
// val audioFormats = context!!.resources.getStringArray(R.array.music_formats)
// val audioFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.audio_format)
// val autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.audio_format_textview)
// val preference = sharedPreferences.getString("audio_format", "mp3")
// autoCompleteTextView!!.setText(preference, false)
// (audioFormat!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.audioFormat = audioFormats[index]
// }
// editor.putString("audio_format", audioFormats[index])
// editor.apply()
// }
// } else {
// bottomSheet!!.setContentView(R.layout.home_download_video_bottom_sheet)
// val title = bottomSheet!!.findViewById<TextInputLayout>(R.id.title_textinput)
// if (selectedObjects!!.size > 1) {
// title!!.editText!!.setText(getString(R.string.mutliple_titles))
// title.editText!!.isClickable = false
// title.editText!!.isLongClickable = false
// } else {
// title!!.editText!!.setText(selectedObjects!![0]!!.title)
// title.editText!!.addTextChangedListener(object : TextWatcher {
// override fun beforeTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun onTextChanged(
// charSequence: CharSequence,
// i: Int,
// i1: Int,
// i2: Int
// ) {
// }
//
// override fun afterTextChanged(editable: Editable) {
// val index = resultsList!!.indexOf(selectedObjects!![0])
// resultsList!![index]!!.title = editable.toString()
// }
// })
// }
// val videoFormats = context!!.resources.getStringArray(R.array.video_containers)
// val videoQualities = context!!.resources.getStringArray(R.array.video_formats)
// val videoFormat = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_format)
// var autoCompleteTextView =
// bottomSheet!!.findViewById<AutoCompleteTextView>(R.id.video_format_textview)
// var preference = sharedPreferences.getString("video_format", "webm")
// autoCompleteTextView!!.setText(preference, false)
// (videoFormat!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.videoFormat = videoFormats[index]
// }
// editor.putString("video_format", videoFormats[index])
// editor.apply()
// }
// val videoQuality = bottomSheet!!.findViewById<TextInputLayout>(R.id.video_quality)
// autoCompleteTextView = bottomSheet!!.findViewById(R.id.video_quality_textview)
// preference = sharedPreferences.getString("video_quality", "Best Quality")
// autoCompleteTextView!!.setText(preference, false)
// (videoQuality!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
// AdapterView.OnItemClickListener { adapterView: AdapterView<*>?, view: View?, index: Int, l: Long ->
// for (i in selectedObjects!!.indices) {
// val vid = findVideo(
// selectedObjects!![i]!!.getURL()
// )
// vid!!.videoQuality = videoQualities[index]
// }
// editor.putString("video_quality", videoQualities[index])
// editor.apply()
// }
// val embedSubs = bottomSheet!!.findViewById<Chip>(R.id.embed_subtitles)
// embedSubs!!.isChecked = sharedPreferences.getBoolean("embed_subtitles", false)
// embedSubs.setOnClickListener { view: View? ->
// if (embedSubs.isChecked) {
// editor.putBoolean("embed_subtitles", true)
// } else {
// editor.putBoolean("embed_subtitles", false)
// }
// editor.apply()
// }
// val addChapters = bottomSheet!!.findViewById<Chip>(R.id.add_chapters)
// addChapters!!.isChecked = sharedPreferences.getBoolean("add_chapters", false)
// addChapters.setOnClickListener { view: View? ->
// if (addChapters.isChecked) {
// editor.putBoolean("add_chapters", true)
// } else {
// editor.putBoolean("add_chapters", false)
// }
// editor.apply()
// }
// val saveThumbnail = bottomSheet!!.findViewById<Chip>(R.id.save_thumbnail)
// saveThumbnail!!.isChecked = sharedPreferences.getBoolean("write_thumbnail", false)
// saveThumbnail.setOnClickListener { view: View? ->
// if (saveThumbnail.isChecked) {
// editor.putBoolean("write_thumbnail", true)
// } else {
// editor.putBoolean("write_thumbnail", false)
// }
// editor.apply()
// }
// }
// val cancel = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_cancel_button)
// cancel!!.setOnClickListener { view: View? -> bottomSheet!!.cancel() }
// val download = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_download_button)
// download!!.setOnClickListener { view: View? ->
// 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()
// }
// bottomSheet!!.cancel()
// }
bottomSheet!!.show()
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
} catch (e: Exception) {
Log.e(TAG, e.printStackTrace().toString());
}
}
private fun convertFileSize(s: Long): String{
if (s <= 0) return "0"
val units = arrayOf("B", "kB", "MB", "GB", "TB")
val digitGroups = (log10(s.toDouble()) / log10(1024.0)).toInt()
return DecimalFormat("#,##0.#").format(s / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups]
}
companion object {
private const val TAG = "HomeFragment"
}

View file

@ -0,0 +1,154 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.app.Activity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.ui.HomeFragment
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.textfield.TextInputLayout
class DownloadAudioFragment(item: DownloadItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var fileUtil : FileUtil
private val downloadItem = item
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
fileUtil = FileUtil()
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val resultItem = resultViewModel.getItemByURL(downloadItem.url)
val type = downloadItem.type
val sharedPreferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
try {
val title = view.findViewById<TextInputLayout>(R.id.title_textinput)
title!!.editText!!.setText(downloadItem.title)
title.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.title = p0.toString()
resultItem.title = p0.toString()
resultViewModel.update(resultItem)
}
})
val author = view.findViewById<TextInputLayout>(R.id.author_textinput)
author!!.editText!!.setText(downloadItem.author)
author.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.author = p0.toString()
resultItem.author = p0.toString()
resultViewModel.update(resultItem)
}
})
val saveDir = view.findViewById<TextInputLayout>(R.id.outputPath)
saveDir!!.editText!!.setText(
sharedPreferences.getString(
"music_path",
getString(R.string.music_path)
)
)
saveDir.editText!!.isFocusable = false;
saveDir.editText!!.isClickable = true;
saveDir.editText!!.setOnClickListener {
Toast.makeText(context, "TEST", Toast.LENGTH_SHORT).show()
}
val formats = resultViewModel.getFormats(resultItem, type)
val formatTitles = formats.map {
if (it.format_note.contains("AUDIO_QUALITY_"))
it.format_note.replace("AUDIO_QUALITY_", "") +
if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
else it.format_note +
if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
}
val format = view.findViewById<TextInputLayout>(R.id.format)
val autoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
formatTitles
)
)
if (formatTitles.isNotEmpty()){
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
}
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.formatDesc = formats[index].format_note
downloadItem.audioFormatId = formats[index].format_id
}
val containers = requireContext().resources.getStringArray(R.array.audio_containers)
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.container_textview)
container?.isEnabled = true
containerAutoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
containers
)
)
val selectedContainer: String = formats.find { downloadItem.formatDesc == it.format_note }?.container
?: sharedPreferences.getString("audio_format", "mp3")!!
containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.ext = containers[index]
}
}catch (e : Exception){
e.printStackTrace()
}
}
}

View file

@ -0,0 +1,91 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.core.view.get
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.ViewModelProvider
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.tabs.TabLayout
class DownloadBottomSheetDialog(item: DownloadItem) : BottomSheetDialogFragment() {
private lateinit var tabLayout: TabLayout
private lateinit var viewPager2: ViewPager2
private lateinit var fragmentAdapter : DownloadFragmentAdapter
private val downloadItem = item
private lateinit var downloadViewModel: DownloadViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
}
@SuppressLint("RestrictedApi")
override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
val view = LayoutInflater.from(context).inflate(R.layout.download_bottom_sheet, null)
dialog.setContentView(view)
tabLayout = view.findViewById(R.id.download_tablayout)
viewPager2 = view.findViewById(R.id.download_viewpager)
val fragmentManager = parentFragmentManager
fragmentAdapter = DownloadFragmentAdapter(downloadItem, fragmentManager, lifecycle)
viewPager2.adapter = fragmentAdapter
when(downloadItem.type) {
"audio" -> {
tabLayout.selectTab(tabLayout.getTabAt(0))
viewPager2.setCurrentItem(0, false)
}
"video" -> {
tabLayout.selectTab(tabLayout.getTabAt(1))
viewPager2.setCurrentItem(1, false)
}
"command" -> {
tabLayout.selectTab(tabLayout.getTabAt(2))
viewPager2.setCurrentItem(2, false)
}
}
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
viewPager2.currentItem = tab!!.position
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
tabLayout.selectTab(tabLayout.getTabAt(position))
}
})
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
downloadViewModel.deleteDownload(downloadItem)
}
fun dismissSelf(){
this.dismiss()
}
}

View file

@ -0,0 +1,32 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
class DownloadCommandFragment(item: DownloadItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private val downloadItem = item
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
return fragmentView
}
}

View file

@ -0,0 +1,31 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import androidx.annotation.NonNull
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem
class DownloadFragmentAdapter (item : DownloadItem, fragmentManager : FragmentManager, lifecycle : Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {
private val downloadItem = item
override fun getItemCount(): Int {
return 3
}
override fun createFragment(position: Int): Fragment {
when (position) {
0 -> {
downloadItem.type = "audio"
return DownloadAudioFragment(downloadItem)
}
1 -> {
downloadItem.type = "video"
return DownloadVideoFragment(downloadItem)
}
}
downloadItem.type = "command"
return DownloadCommandFragment(downloadItem)
}
}

View file

@ -0,0 +1,187 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.app.Activity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.chip.Chip
import com.google.android.material.textfield.TextInputLayout
class DownloadVideoFragment(item: DownloadItem) : Fragment() {
private var _binding : FragmentHomeBinding? = null
private var fragmentView: View? = null
private var activity: Activity? = null
private var mainActivity: MainActivity? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel
private lateinit var fileUtil : FileUtil
private val downloadItem = item
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false)
activity = getActivity()
mainActivity = activity as MainActivity?
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
fileUtil = FileUtil()
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val resultItem = resultViewModel.getItemByURL(downloadItem.url)
val type = downloadItem.type
val sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val editor = sharedPreferences.edit()
try {
val title = view.findViewById<TextInputLayout>(R.id.title_textinput)
title!!.editText!!.setText(downloadItem.title)
title.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.title = p0.toString()
resultItem.title = p0.toString()
resultViewModel.update(resultItem)
downloadViewModel.updateDownload(downloadItem)
}
})
val author = view.findViewById<TextInputLayout>(R.id.author_textinput)
author!!.editText!!.setText(downloadItem.author)
author.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(p0: Editable?) {
downloadItem.author = p0.toString()
resultItem.author = p0.toString()
resultViewModel.update(resultItem)
downloadViewModel.updateDownload(downloadItem)
}
})
val saveDir = view.findViewById<TextInputLayout>(R.id.outputPath)
saveDir!!.editText!!.setText(
sharedPreferences.getString(
"video_path",
getString(R.string.video_path)
)
)
saveDir.editText!!.isFocusable = false;
saveDir.editText!!.isClickable = true;
saveDir.editText!!.setOnClickListener {
Toast.makeText(context, "TEST", Toast.LENGTH_SHORT).show()
}
val formats = resultViewModel.getFormats(resultItem, type)
val formatTitles = formats.map {
if (it.format_note.contains("AUDIO_QUALITY_"))
it.format_note.replace("AUDIO_QUALITY_", "") +
if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
else it.format_note +
if (it.filesize == 0L) "" else " / " + fileUtil.convertFileSize(it.filesize)
}
val format = view.findViewById<TextInputLayout>(R.id.format)
val autoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.format_textview)
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
formatTitles
)
)
if (formatTitles.isNotEmpty()) {
autoCompleteTextView!!.setText(formatTitles[formats.lastIndex], false)
}
(format!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.formatDesc = formats[index].format_note
downloadItem.videoFormatId = formats[index].format_id
}
val containers = requireContext().resources.getStringArray(R.array.video_containers)
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.container_textview)
container?.isEnabled = true
containerAutoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
containers
)
)
val selectedContainer: String =
formats.find { downloadItem.formatDesc == it.format_note }?.container
?: sharedPreferences.getString("video_format", "DEFAULT")!!
containerAutoCompleteTextView!!.setText(selectedContainer, false)
(container!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
downloadItem.ext = containers[index]
}
val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
embedSubs!!.isChecked = sharedPreferences.getBoolean("embed_subtitles", false)
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
addChapters!!.isChecked = sharedPreferences.getBoolean("add_chapters", false)
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
saveThumbnail!!.isChecked = sharedPreferences.getBoolean("write_thumbnail", false)
val cancel = view.findViewById<Button>(R.id.bottomsheet_cancel_button)
cancel!!.setOnClickListener {
parentFragmentManager.popBackStack()
}
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
download!!.setOnClickListener {
// 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()
// }
parentFragmentManager.popBackStack()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}

View file

@ -11,6 +11,9 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
import java.io.File
import java.net.URLDecoder
import java.nio.charset.StandardCharsets
import java.text.DecimalFormat
import kotlin.math.log10
import kotlin.math.pow
class FileUtil() {
@ -118,4 +121,11 @@ class FileUtil() {
}
originDir.delete()
}
fun convertFileSize(s: Long): String{
if (s <= 0) return "0"
val units = arrayOf("B", "kB", "MB", "GB", "TB")
val digitGroups = (log10(s.toDouble()) / log10(1024.0)).toInt()
return DecimalFormat("#,##0.#").format(s / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups]
}
}

View file

@ -262,7 +262,7 @@ class InfoUtil(context: Context) {
val downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
val isPlaylist = 0
video = ResultItemWithFormats(
ResultItem(
ResultItem(0,
url,
title,
author,
@ -299,7 +299,7 @@ class InfoUtil(context: Context) {
}
video = ResultItemWithFormats(
ResultItem(
ResultItem(0,
url,
title,
author,
@ -514,8 +514,9 @@ class InfoUtil(context: Context) {
formats.add(Gson().fromJson(format, Format::class.java))
}
}
Log.e(TAG, formats.toString())
items.add(ResultItemWithFormats(
ResultItem(
ResultItem(0,
url,
title,
author!!,

View file

@ -97,12 +97,9 @@ class DownloadWorker(
request.addOption("-x")
var audioQualityId : String = downloadItem.audioFormatId
if (audioQualityId == "0") audioQualityId = "ba"
var format = downloadItem.audioFormat
if (format.isEmpty()){
format = sharedPreferences.getString("audio_format", "")!!
}
var ext = downloadItem.ext
request.addOption("-f", audioQualityId)
request.addOption("--audio-format", format)
request.addOption("--audio-format", ext)
request.addOption("--embed-metadata")
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
@ -142,15 +139,13 @@ class DownloadWorker(
if (embedSubs) {
request.addOption("--embed-subs", "")
}
var videoQualityId = downloadItem.videoFormatId
val audioQualityId = downloadItem.audioFormatId
if (videoQualityId.isEmpty()) videoQualityId = "bestvideo"
val formatArgument = StringBuilder(videoQualityId)
if (videoQualityId != "worst"){
if (audioQualityId != "0") formatArgument.append("+", audioQualityId, "/best")
}
var videoFormatID = downloadItem.videoFormatId
val audioFormatID = downloadItem.audioFormatId
if (videoFormatID.isEmpty()) videoFormatID = "bestvideo"
val formatArgument = StringBuilder(videoFormatID)
if (audioFormatID != "0") formatArgument.append("+", audioFormatID, "/best")
request.addOption("-f", formatArgument.toString())
var format = downloadItem.videoFormat
var format = downloadItem.ext
if (format.isNotEmpty()) {
format = sharedPreferences.getString("video_format", "")!!
if (format != "DEFAULT") request.addOption("--merge-output-format", format)
@ -166,7 +161,7 @@ class DownloadWorker(
}
"command" -> {
val commandRegex = "\"([^\"]*)\"|(\\S+)"
val command = commandTemplateDao.getTemplateById(downloadItem.customTemplateId)
val command = commandTemplateDao.getTemplate(downloadItem.customTemplateId)
val m = Pattern.compile(commandRegex).matcher(command.content)
while (m.find()) {
if (m.group(1) != null) {
@ -198,7 +193,7 @@ class DownloadWorker(
val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000
val historyItem = HistoryItem(downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, downloadItem.downloadPath, downloadItem.website)
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, downloadItem.downloadPath, downloadItem.website)
runBlocking {
historyDao.insert(historyItem)
}

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View file

@ -19,6 +19,7 @@
android:id="@+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="match_parent">
<TextView
android:id="@+id/bottom_sheet_title"
android:layout_width="wrap_content"
android:textSize="20sp"
android:paddingTop="10dp"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:text="@string/download" />
<TextView
android:id="@+id/bottom_sheet_subtitle"
android:layout_width="wrap_content"
android:textSize="15sp"
android:paddingBottom="10dp"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:text="@string/configure_download" />
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="@android:color/transparent"
app:tabMode="scrollable"
app:tabGravity="center"
android:id="@+id/download_tablayout" >
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/audio" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/video" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/run_command" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/download_viewpager" />
</LinearLayout>

View file

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:errorEnabled="true"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="40"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content"
android:hint="@string/container">
<AutoCompleteTextView
android:id="@+id/container_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/audio_containers"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/video_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/save_dir"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="10dp"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:id="@+id/bottomsheet_schedule_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_clock" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="end"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:id="@+id/bottomsheet_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_cancel" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:id="@+id/bottomsheet_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
app:icon="@drawable/ic_down"
android:autoLink="all"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:errorEnabled="true"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="40"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content"
android:hint="@string/container">
<AutoCompleteTextView
android:id="@+id/container_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/audio_containers"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/video_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/save_dir"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/adjust_commands"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:paddingBottom="5dp"
android:textSize="15sp"
android:layout_height="wrap_content"
android:text="@string/adjust_templates" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/commands_chip_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:selectionRequired="false"
app:singleLine="true"
app:singleSelection="false">
<com.google.android.material.chip.Chip
android:id="@+id/newTemplate"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_template"
app:chipIcon="@drawable/ic_plus" />
<com.google.android.material.chip.Chip
android:id="@+id/editSelected"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit_selected"
app:chipIcon="@drawable/ic_edit" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="10dp"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:id="@+id/bottomsheet_schedule_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_clock" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="end"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:id="@+id/bottomsheet_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_cancel" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:id="@+id/bottomsheet_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
app:icon="@drawable/ic_down"
android:autoLink="all"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,216 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:errorEnabled="true"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="40"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content"
android:hint="@string/container">
<AutoCompleteTextView
android:id="@+id/container_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/audio_containers"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:hint="@string/format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/video_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/save_dir"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:id="@+id/adjust_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:paddingBottom="5dp"
android:textSize="15sp"
android:layout_height="wrap_content"
android:text="@string/adjust_video" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
app:singleLine="true"
android:layout_height="wrap_content">
<com.google.android.material.chip.Chip
android:id="@+id/embed_subtitles"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/embed_subtitles"/>
<com.google.android.material.chip.Chip
android:id="@+id/add_chapters"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/add_chapter"/>
<com.google.android.material.chip.Chip
android:id="@+id/save_thumbnail"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/save_thumb"/>
<com.google.android.material.chip.Chip
android:id="@+id/remove_audio"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/remove_audio"/>
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="10dp"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:id="@+id/bottomsheet_schedule_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_clock" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="end"
android:orientation="horizontal">
<Button
style="@style/Widget.Material3.Button.OutlinedButton.Icon"
android:id="@+id/bottomsheet_cancel_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
android:layout_marginEnd="10dp"
app:icon="@drawable/ic_cancel" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:id="@+id/bottomsheet_download_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download"
app:icon="@drawable/ic_down"
android:autoLink="all"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -18,7 +18,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/downloads"
app:title="@string/history"
app:menu="@menu/downloads_menu"
android:theme="@style/Toolbar"
/>
@ -130,25 +130,25 @@
android:layout_margin="10dp"
android:orientation="vertical">
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
<include layout="@layout/downloads_card_shimmer" />
<include layout="@layout/history_card_shimmer" />
@ -178,11 +178,11 @@
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/downloads_no_results"
<include layout="@layout/history_no_results"
android:visibility="gone" />
<include layout="@layout/downloads_bottom_sheet"
<include layout="@layout/history_item_details_bottom_sheet"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -9,7 +9,6 @@
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput"
android:layout_width="match_parent"
@ -27,33 +26,35 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="15dp"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/author_textinput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="40"
app:errorEnabled="true"
android:hint="@string/author"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_weight="30"
android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content"
android:hint="@string/container">
@ -67,30 +68,29 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:paddingStart="10dp"
android:layout_weight="65"
android:layout_height="wrap_content"
android:hint="@string/format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/audio_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/format"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:hint="@string/format">
<AutoCompleteTextView
android:id="@+id/format_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/video_formats"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath_textinput"
android:id="@+id/outputPath"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true"
@ -100,12 +100,12 @@
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:inputType="text"
android:layout_height="wrap_content"
/>
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.chip.ChipGroup
android:id="@+id/downloadChipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:selectionRequired="true"
@ -144,108 +144,9 @@
</com.google.android.material.chip.ChipGroup>
<LinearLayout
android:id="@+id/adjust_commands"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="10dp"
android:textSize="15sp"
android:layout_height="wrap_content"
android:text="@string/adjust_templates" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/commands_chip_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:selectionRequired="false"
app:singleLine="true"
app:singleSelection="false">
<com.google.android.material.chip.Chip
android:id="@+id/newTemplate"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/new_template"
app:chipIcon="@drawable/ic_plus" />
<com.google.android.material.chip.Chip
android:id="@+id/editSelected"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit_selected"
app:chipIcon="@drawable/ic_edit" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
<LinearLayout
android:id="@+id/adjust_video"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:paddingTop="5dp"
android:paddingBottom="10dp"
android:textSize="15sp"
android:layout_height="wrap_content"
android:text="@string/adjust_video" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/chipGroup"
android:layout_width="wrap_content"
app:singleLine="true"
android:layout_height="wrap_content">
<com.google.android.material.chip.Chip
android:id="@+id/embed_subtitles"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/embed_subtitles"/>
<com.google.android.material.chip.Chip
android:id="@+id/add_chapters"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/add_chapter"/>
<com.google.android.material.chip.Chip
android:id="@+id/save_thumbnail"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/save_thumb"/>
<com.google.android.material.chip.Chip
android:id="@+id/remove_audio"
style="@style/Widget.Material3.Chip.Filter.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/remove_audio"/>
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</LinearLayout>

View file

@ -4,6 +4,9 @@
<item android:title="@string/home"
android:icon="@drawable/ic_home"
android:id="@+id/home"/>
<item android:title="@string/history"
android:icon="@drawable/ic_history"
android:id="@+id/history"/>
<item android:title="@string/downloads"
android:icon="@drawable/ic_downloads"
android:id="@+id/downloads"/>

View file

@ -165,4 +165,6 @@
<string name="defaultValue">Default</string>
<string name="format">Format</string>
<string name="container">Container</string>
<string name="template">Template</string>
<string name="history">History</string>
</resources>