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> <PersistentState>
<option name="values"> <option name="values">
<map> <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> </map>
</option> </option>
</PersistentState> </PersistentState>
@ -330,8 +330,7 @@
</option> </option>
<option name="values"> <option name="values">
<map> <map>
<entry key="color" value="00a6ff" /> <entry key="outputName" value="ic_history" />
<entry key="outputName" value="ic_concurrent_downloads" />
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" /> <entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
</map> </map>
</option> </option>

View file

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

View file

@ -2,12 +2,18 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "c8effa77edcd8cf5e46a79fd1a791535", "identityHash": "94d37ae85390ef5198651df2480a02bf",
"entities": [ "entities": [
{ {
"tableName": "results", "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": [ "fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "url", "fieldPath": "url",
"columnName": "url", "columnName": "url",
@ -49,12 +55,6 @@
"columnName": "playlistTitle", "columnName": "playlistTitle",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -68,18 +68,18 @@
}, },
{ {
"tableName": "formats", "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": [ "fields": [
{ {
"fieldPath": "itemId", "fieldPath": "id",
"columnName": "itemId", "columnName": "id",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "format", "fieldPath": "itemId",
"columnName": "format", "columnName": "itemId",
"affinity": "TEXT", "affinity": "INTEGER",
"notNull": true "notNull": true
}, },
{ {
@ -89,8 +89,8 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "ext", "fieldPath": "container",
"columnName": "ext", "columnName": "container",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
}, },
@ -107,10 +107,10 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "id", "fieldPath": "encoding",
"columnName": "id", "columnName": "encoding",
"affinity": "INTEGER", "affinity": "TEXT",
"notNull": false "notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@ -124,8 +124,14 @@
}, },
{ {
"tableName": "history", "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": [ "fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "url", "fieldPath": "url",
"columnName": "url", "columnName": "url",
@ -179,12 +185,6 @@
"columnName": "website", "columnName": "website",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -198,8 +198,14 @@
}, },
{ {
"tableName": "downloads", "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": [ "fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "url", "fieldPath": "url",
"columnName": "url", "columnName": "url",
@ -236,18 +242,6 @@
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
}, },
{
"fieldPath": "audioFormat",
"columnName": "audioFormat",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "videoFormat",
"columnName": "videoFormat",
"affinity": "TEXT",
"notNull": true
},
{ {
"fieldPath": "audioFormatId", "fieldPath": "audioFormatId",
"columnName": "audioFormatId", "columnName": "audioFormatId",
@ -345,12 +339,6 @@
"columnName": "workID", "columnName": "workID",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -364,8 +352,14 @@
}, },
{ {
"tableName": "commandTemplates", "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": [ "fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{ {
"fieldPath": "title", "fieldPath": "title",
"columnName": "title", "columnName": "title",
@ -377,12 +371,6 @@
"columnName": "content", "columnName": "content",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -398,7 +386,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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.DownloaderService.LocalBinder
import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.databinding.ActivityMainBinding 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.HomeFragment
import com.deniscerri.ytdlnis.ui.MoreFragment import com.deniscerri.ytdlnis.ui.MoreFragment
import com.deniscerri.ytdlnis.ui.settings.SettingsActivity import com.deniscerri.ytdlnis.ui.settings.SettingsActivity
@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
lateinit var context: Context lateinit var context: Context
private lateinit var homeFragment: HomeFragment private lateinit var homeFragment: HomeFragment
private lateinit var downloadsFragment: DownloadsFragment private lateinit var historyFragment: HistoryFragment
var downloaderService: DownloaderService? = null var downloaderService: DownloaderService? = null
private lateinit var workManager: WorkManager private lateinit var workManager: WorkManager
@ -84,7 +84,7 @@ class MainActivity : AppCompatActivity() {
workManager = WorkManager.getInstance(context) workManager = WorkManager.getInstance(context)
homeFragment = HomeFragment() homeFragment = HomeFragment()
downloadsFragment = DownloadsFragment() historyFragment = HistoryFragment()
moreFragment = MoreFragment() moreFragment = MoreFragment()
initFragments() initFragments()
binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem -> binding.bottomNavigationView.setOnItemSelectedListener { item: MenuItem ->
@ -97,13 +97,13 @@ class MainActivity : AppCompatActivity() {
} }
replaceFragment(homeFragment) replaceFragment(homeFragment)
} }
R.id.downloads -> { R.id.history -> {
if (lastFragment === downloadsFragment) { if (lastFragment === historyFragment) {
downloadsFragment.scrollToTop() historyFragment.scrollToTop()
} else { } else {
this.title = getString(R.string.downloads) this.title = getString(R.string.downloads)
} }
replaceFragment(downloadsFragment) replaceFragment(historyFragment)
} }
R.id.more -> { R.id.more -> {
if (lastFragment === moreFragment) { if (lastFragment === moreFragment) {
@ -151,7 +151,7 @@ class MainActivity : AppCompatActivity() {
if (Intent.ACTION_SEND == action && type != null) { if (Intent.ACTION_SEND == action && type != null) {
Log.e(TAG, action) Log.e(TAG, action)
homeFragment = HomeFragment() homeFragment = HomeFragment()
downloadsFragment = DownloadsFragment() historyFragment = HistoryFragment()
moreFragment = MoreFragment() moreFragment = MoreFragment()
if (type.equals("application/txt", ignoreCase = true)) { if (type.equals("application/txt", ignoreCase = true)) {
try { try {
@ -185,9 +185,9 @@ class MainActivity : AppCompatActivity() {
private fun initFragments() { private fun initFragments() {
fm.beginTransaction() fm.beginTransaction()
.replace(R.id.frame_layout, homeFragment) .replace(R.id.frame_layout, homeFragment)
.add(R.id.frame_layout, downloadsFragment) .add(R.id.frame_layout, historyFragment)
.add(R.id.frame_layout, moreFragment) .add(R.id.frame_layout, moreFragment)
.hide(downloadsFragment) .hide(historyFragment)
.hide(moreFragment) .hide(moreFragment)
.commit() .commit()
lastFragment = homeFragment lastFragment = homeFragment

View file

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

View file

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

View file

@ -24,8 +24,8 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1") @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
fun getDownloadById(id: Int) : DownloadItem fun getDownloadById(id: Int) : DownloadItem
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: DownloadItem) suspend fun insert(item: DownloadItem) : Long
@Query("DELETE FROM downloads") @Query("DELETE FROM downloads")
suspend fun deleteAll() suspend fun deleteAll()
@ -35,4 +35,7 @@ interface DownloadDao {
@Update(onConflict = OnConflictStrategy.REPLACE) @Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun update(item: DownloadItem) 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 @Dao
interface FormatDao { interface FormatDao {
@Query("SELECT * FROM formats WHERE itemId=:itemId") @Query("SELECT * FROM formats WHERE itemId=:itemId")
fun getFormatsByItemId(itemId: Int) : List<Format> fun getFormatsByItemId(itemId: Long) : List<Format>
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: Format) suspend fun insert(item: Format)
@ -20,6 +20,6 @@ interface FormatDao {
suspend fun deleteAll() suspend fun deleteAll()
@Query("DELETE FROM formats WHERE itemId=:itemId") @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) suspend fun insert(item: HistoryItem)
@Query("DELETE FROM history WHERE id=:id") @Query("DELETE FROM history WHERE id=:id")
suspend fun delete(id: Int) suspend fun delete(id: Long)
@Query("DELETE FROM history") @Query("DELETE FROM history")
suspend fun deleteAll() suspend fun deleteAll()

View file

@ -24,4 +24,7 @@ interface ResultDao {
@Query("DELETE FROM results") @Query("DELETE FROM results")
suspend fun deleteAll() 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") @Entity(tableName = "commandTemplates")
data class CommandTemplate( data class CommandTemplate(
@PrimaryKey(autoGenerate = true)
var id: Long,
val title: String, val title: String,
val content: String val content: String
){ )
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}

View file

@ -6,33 +6,30 @@ import androidx.room.PrimaryKey
@Entity(tableName = "downloads") @Entity(tableName = "downloads")
data class DownloadItem( 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) @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") @Entity(tableName = "formats")
data class Format( data class Format(
var itemId: Int, @PrimaryKey(autoGenerate = true)
@SerializedName(value = "format", alternate = ["type"]) var id: Long,
val format: String, var itemId: Long,
@SerializedName(value = "format_id", alternate = ["itag"]) @SerializedName(value = "format_id", alternate = ["itag"])
val format_id: String, val format_id: String,
@SerializedName(value = "ext", alternate = ["container", ""]) @SerializedName(value = "ext", alternate = ["container"])
val ext: String, val container: String,
@SerializedName(value = "filesize", alternate = ["clen"]) @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"])
val filesize: Long, val filesize: Long,
@SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"]) @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"])
val format_note: String val format_note: String,
){ @SerializedName(value = "encoding", alternate = ["vcodec", "acodec", "video_ext"])
@PrimaryKey(autoGenerate = true) val encoding: String
var id: Int? = null )
}

View file

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

View file

@ -5,14 +5,13 @@ import androidx.room.PrimaryKey
@Entity(tableName = "results") @Entity(tableName = "results")
data class ResultItem( 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) @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 package com.deniscerri.ytdlnis.database.repository
import android.util.Log
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import com.deniscerri.ytdlnis.database.dao.DownloadDao import com.deniscerri.ytdlnis.database.dao.DownloadDao
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
@ -9,11 +10,11 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads() val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads()
enum class status { enum class status {
Active, Queued, Errored Active, Queued, Errored, Processing
} }
suspend fun insert(item: DownloadItem){ suspend fun insert(item: DownloadItem) : Long {
downloadDao.insert(item) return downloadDao.insert(item)
} }
suspend fun delete(item: DownloadItem){ suspend fun delete(item: DownloadItem){
@ -37,4 +38,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
update(item); update(item);
} }
fun getLatest() : DownloadItem {
return downloadDao.getLatest()
}
} }

View file

@ -53,7 +53,7 @@ class HistoryRepository(private val historyDao: HistoryDao) {
val fileUtil = FileUtil() val fileUtil = FileUtil()
items.value?.forEach { item -> items.value?.forEach { item ->
if (!fileUtil.exists(item.downloadPath)){ 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){ private suspend fun addFormats(formats : ArrayList<Format>, id: Long){
formats.forEach { f -> formats.forEach { f ->
if (f.filesize == 0L) return@forEach if (f.filesize == 0L) return@forEach
f.itemId = id.toInt() f.itemId = id
formatDao.insert(f) formatDao.insert(f)
} }
} }
@ -122,11 +122,15 @@ class ResultRepository(private val resultDao: ResultDao, private val formatDao:
} }
fun getFormats(item: ResultItem): List<Format> { fun getFormats(item: ResultItem): List<Format> {
return formatDao.getFormatsByItemId(item.id!!) return formatDao.getFormatsByItemId(item.id)
} }
fun getTemplates() : List<CommandTemplate> { fun getTemplates() : List<CommandTemplate> {
return commandTemplateDao.getAllTemplates() 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 android.app.Application
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.DownloadItem 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){ fun insertDownload(item: DownloadItem) : LiveData<Long> {
repository.insert(item) 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) { 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){ fun updateDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){
repository.update(item); 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> { fun getFormats(item: ResultItem, type: String) : List<Format> {
val list = repository.getFormats(item) var list = repository.getFormats(item)
val formats = mutableListOf<Format>() val formats = mutableListOf<Format>()
if (list.isEmpty()){ val audioFormats = getApplication<App>().resources.getStringArray(R.array.audio_formats)
when(type){ val videoFormats = getApplication<App>().resources.getStringArray(R.array.video_formats)
"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
}
when(type){ when(type){
"audio" -> return list.filter { it.format.contains("audio", ignoreCase = true) } "audio" -> {
"video" -> return list.filter { !it.format.contains("audio", ignoreCase = true) } 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() val templates = repository.getTemplates()
templates.forEach { 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 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.models.HistoryItem
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel 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.deniscerri.ytdlnis.util.FileUtil
import com.facebook.shimmer.ShimmerFrameLayout import com.facebook.shimmer.ShimmerFrameLayout
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
@ -39,7 +39,7 @@ import java.io.File
/** /**
* A fragment representing a list of Items. * A fragment representing a list of Items.
*/ */
class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener, class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener,
OnClickListener, OnLongClickListener { OnClickListener, OnLongClickListener {
private lateinit var historyViewModel : HistoryViewModel private lateinit var historyViewModel : HistoryViewModel
@ -66,14 +66,14 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
private var deleteFab: ExtendedFloatingActionButton? = null private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null private var fileUtil: FileUtil? = null
private var _binding : FragmentDownloadsBinding? = null private var _binding : FragmentHistoryBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
_binding = FragmentDownloadsBinding.inflate(inflater, container, false) _binding = FragmentHistoryBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_downloads, container, false) fragmentView = inflater.inflate(R.layout.fragment_history, container, false)
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity? mainActivity = activity as MainActivity?
@ -237,7 +237,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
sortChip.setOnClickListener { sortChip.setOnClickListener {
sortSheet = BottomSheetDialog(requireContext()) sortSheet = BottomSheetDialog(requireContext())
sortSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE) 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 newest = sortSheet!!.findViewById<TextView>(R.id.newest)
val oldest = sortSheet!!.findViewById<TextView>(R.id.oldest) val oldest = sortSheet!!.findViewById<TextView>(R.id.oldest)
newest!!.setOnClickListener { newest!!.setOnClickListener {
@ -309,13 +309,13 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.bottomsheet_remove_button -> { R.id.bottomsheet_remove_button -> {
removeItem(v.tag as Int) removeItem(v.tag as Long)
} }
R.id.bottom_sheet_link -> { R.id.bottom_sheet_link -> {
openLinkIntent(v.tag as Int) openLinkIntent(v.tag as Long)
} }
R.id.bottomsheet_open_file_button -> { R.id.bottomsheet_open_file_button -> {
openFileIntent(v.tag as Int) openFileIntent(v.tag as Long)
} }
R.id.delete_selected_fab -> { R.id.delete_selected_fab -> {
removeSelectedItems() removeSelectedItems()
@ -326,7 +326,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
override fun onLongClick(v: View): Boolean { override fun onLongClick(v: View): Boolean {
val id = v.id val id = v.id
if (id == R.id.bottom_sheet_link) { if (id == R.id.bottom_sheet_link) {
copyLinkToClipBoard(v.tag as Int) copyLinkToClipBoard(v.tag as Long)
return true return true
} }
return false return false
@ -347,13 +347,13 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
historyViewModel.delete(item, deleteFile[0]) historyViewModel.delete(item, deleteFile[0])
} }
selectedObjects = ArrayList() selectedObjects = ArrayList()
historyAdapter!!.clearCheckedVideos() historyAdapter!!.clearCheckeditems()
deleteFab!!.visibility = GONE deleteFab!!.visibility = GONE
} }
deleteDialog.show() deleteDialog.show()
} }
private fun removeItem(id: Int) { private fun removeItem(id: Long) {
if (bottomSheet != null) bottomSheet!!.hide() if (bottomSheet != null) bottomSheet!!.hide()
val deleteFile = booleanArrayOf(false) val deleteFile = booleanArrayOf(false)
val v = findItem(id) val v = findItem(id)
@ -370,7 +370,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
deleteDialog.show() deleteDialog.show()
} }
private fun copyLinkToClipBoard(id: Int) { private fun copyLinkToClipBoard(id: Long) {
val url = findItem(id)?.url val url = findItem(id)?.url
val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.url), url) val clip = ClipData.newPlainText(getString(R.string.url), url)
@ -380,7 +380,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
.show() .show()
} }
private fun openLinkIntent(id: Int) { private fun openLinkIntent(id: Long) {
val url = findItem(id)?.url val url = findItem(id)?.url
val i = Intent(Intent.ACTION_VIEW) val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(url) i.data = Uri.parse(url)
@ -388,7 +388,7 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
startActivity(i) startActivity(i)
} }
private fun openFileIntent(id: Int) { private fun openFileIntent(id: Long) {
val downloadPath = findItem(id)!!.downloadPath val downloadPath = findItem(id)!!.downloadPath
val file = File(downloadPath) val file = File(downloadPath)
val uri = FileProvider.getUriForFile( val uri = FileProvider.getUriForFile(
@ -403,11 +403,11 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
startActivity(i) startActivity(i)
} }
override fun onCardClick(id: Int, isFilePresent: Boolean) { override fun onCardClick(videoID: Long, isPresent: Boolean) {
bottomSheet = BottomSheetDialog(fragmentContext!!) bottomSheet = BottomSheetDialog(fragmentContext!!)
bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet!!.setContentView(R.layout.downloads_bottom_sheet) bottomSheet!!.setContentView(R.layout.history_item_details_bottom_sheet)
val video = findItem(id) val video = findItem(videoID)
val title = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_title) val title = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_title)
title!!.text = video!!.title title!!.text = video!!.title
val author = bottomSheet!!.findViewById<TextView>(R.id.bottom_sheet_author) 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 link = bottomSheet!!.findViewById<Button>(R.id.bottom_sheet_link)
val url = video.url val url = video.url
link!!.text = url link!!.text = url
link.tag = id link.tag = videoID
link.setOnClickListener(this) link.setOnClickListener(this)
link.setOnLongClickListener(this) link.setOnLongClickListener(this)
val remove = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_remove_button) val remove = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_remove_button)
remove!!.tag = id remove!!.tag = videoID
remove.setOnClickListener(this) remove.setOnClickListener(this)
val openFile = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_open_file_button) val openFile = bottomSheet!!.findViewById<Button>(R.id.bottomsheet_open_file_button)
openFile!!.tag = id openFile!!.tag = videoID
openFile.setOnClickListener(this) openFile.setOnClickListener(this)
if (!isFilePresent) openFile.visibility = GONE if (!isPresent) openFile.visibility = GONE
bottomSheet!!.show() bottomSheet!!.show()
bottomSheet!!.window!!.setLayout( bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -432,9 +432,9 @@ class DownloadsFragment : Fragment(), HistoryAdapter.OnItemClickListener,
) )
} }
override fun onCardSelect(id: Int, add: Boolean) { override fun onCardSelect(videoID: Long, isChecked: Boolean) {
val item = findItem(id) val item = findItem(videoID)
if (add) selectedObjects!!.add(item!!) if (isChecked) selectedObjects!!.add(item!!)
else selectedObjects!!.remove(item) else selectedObjects!!.remove(item)
if (selectedObjects!!.size > 1) { if (selectedObjects!!.size > 1) {
deleteFab!!.visibility = VISIBLE 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 } return downloadsList?.find { it?.id == id }
} }

View file

@ -4,12 +4,11 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.text.Editable
import android.text.InputType import android.text.InputType
import android.text.TextWatcher
import android.util.Log import android.util.Log
import android.view.* import android.view.*
import android.view.View.GONE import android.view.View.GONE
@ -17,7 +16,6 @@ import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.text.trimmedLength
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager 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.DatabaseManager
import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem 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.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.service.DownloadInfo
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.InfoUtil
import com.facebook.shimmer.ShimmerFrameLayout 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.floatingactionbutton.FloatingActionButton
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.runBlocking
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import kotlin.math.log10 import kotlin.math.log10
@ -65,6 +65,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var downloadInfo: DownloadInfo? = null private var downloadInfo: DownloadInfo? = null
private lateinit var resultViewModel : ResultViewModel private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel
private var downloading = false private var downloading = false
private var fragmentView: View? = null private var fragmentView: View? = null
@ -86,13 +87,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
private var deleteFab: ExtendedFloatingActionButton? = null private var deleteFab: ExtendedFloatingActionButton? = null
private var fileUtil: FileUtil? = null private var fileUtil: FileUtil? = null
private var _binding : FragmentDownloadsBinding? = null private var _binding : FragmentHomeBinding? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
_binding = FragmentDownloadsBinding.inflate(inflater, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
fragmentView = inflater.inflate(R.layout.fragment_home, container, false) fragmentView = inflater.inflate(R.layout.fragment_home, container, false)
activity = getActivity() activity = getActivity()
mainActivity = activity as MainActivity? mainActivity = activity as MainActivity?
@ -117,6 +118,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
selectedObjects = ArrayList() selectedObjects = ArrayList()
downloading = mainActivity!!.isDownloadServiceRunning() downloading = mainActivity!!.isDownloadServiceRunning()
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
downloadQueue = ArrayList() downloadQueue = ArrayList()
resultsList = mutableListOf() resultsList = mutableListOf()
selectedObjects = ArrayList() selectedObjects = ArrayList()
@ -141,7 +144,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
homeAdapter!!.submitList(it) homeAdapter!!.submitList(it)
resultsList = it resultsList = it
if(it.size > 1){ 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 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() { private fun initMenu() {
val onActionExpandListener: MenuItem.OnActionExpandListener = val onActionExpandListener: MenuItem.OnActionExpandListener =
object : MenuItem.OnActionExpandListener { object : MenuItem.OnActionExpandListener {
@ -407,7 +348,10 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
if (sharedPreferences.getBoolean("download_card", true)) { if (sharedPreferences.getBoolean("download_card", true)) {
// selectedObjects!!.clear() // selectedObjects!!.clear()
// selectedObjects!!.add(item!!) // selectedObjects!!.add(item!!)
showConfigureSingleDownloadCard(item!!, type) //showConfigureSingleDownloadCard(createDownloadItem(item!!, type), item)
createDownloadItem(item!!, type){
showSingleDownloadSheet(it)
}
} else { } else {
// downloadQueue!!.add(vid) // downloadQueue!!.add(vid)
// updateDownloadingStatusOnResult(vid, type, true) // 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) { fun updateDownloadStatusOnResult(v: ResultItem?, type: String?, downloaded: Boolean) {
// if (v != null) { // 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 { companion object {
private const val TAG = "HomeFragment" 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.io.File
import java.net.URLDecoder import java.net.URLDecoder
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.text.DecimalFormat
import kotlin.math.log10
import kotlin.math.pow
class FileUtil() { class FileUtil() {
@ -118,4 +121,11 @@ class FileUtil() {
} }
originDir.delete() 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 downloadedVideo = databaseManager!!.checkDownloaded(url, "video")
val isPlaylist = 0 val isPlaylist = 0
video = ResultItemWithFormats( video = ResultItemWithFormats(
ResultItem( ResultItem(0,
url, url,
title, title,
author, author,
@ -299,7 +299,7 @@ class InfoUtil(context: Context) {
} }
video = ResultItemWithFormats( video = ResultItemWithFormats(
ResultItem( ResultItem(0,
url, url,
title, title,
author, author,
@ -514,8 +514,9 @@ class InfoUtil(context: Context) {
formats.add(Gson().fromJson(format, Format::class.java)) formats.add(Gson().fromJson(format, Format::class.java))
} }
} }
Log.e(TAG, formats.toString())
items.add(ResultItemWithFormats( items.add(ResultItemWithFormats(
ResultItem( ResultItem(0,
url, url,
title, title,
author!!, author!!,

View file

@ -97,12 +97,9 @@ class DownloadWorker(
request.addOption("-x") request.addOption("-x")
var audioQualityId : String = downloadItem.audioFormatId var audioQualityId : String = downloadItem.audioFormatId
if (audioQualityId == "0") audioQualityId = "ba" if (audioQualityId == "0") audioQualityId = "ba"
var format = downloadItem.audioFormat var ext = downloadItem.ext
if (format.isEmpty()){
format = sharedPreferences.getString("audio_format", "")!!
}
request.addOption("-f", audioQualityId) request.addOption("-f", audioQualityId)
request.addOption("--audio-format", format) request.addOption("--audio-format", ext)
request.addOption("--embed-metadata") request.addOption("--embed-metadata")
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
@ -142,15 +139,13 @@ class DownloadWorker(
if (embedSubs) { if (embedSubs) {
request.addOption("--embed-subs", "") request.addOption("--embed-subs", "")
} }
var videoQualityId = downloadItem.videoFormatId var videoFormatID = downloadItem.videoFormatId
val audioQualityId = downloadItem.audioFormatId val audioFormatID = downloadItem.audioFormatId
if (videoQualityId.isEmpty()) videoQualityId = "bestvideo" if (videoFormatID.isEmpty()) videoFormatID = "bestvideo"
val formatArgument = StringBuilder(videoQualityId) val formatArgument = StringBuilder(videoFormatID)
if (videoQualityId != "worst"){ if (audioFormatID != "0") formatArgument.append("+", audioFormatID, "/best")
if (audioQualityId != "0") formatArgument.append("+", audioQualityId, "/best")
}
request.addOption("-f", formatArgument.toString()) request.addOption("-f", formatArgument.toString())
var format = downloadItem.videoFormat var format = downloadItem.ext
if (format.isNotEmpty()) { if (format.isNotEmpty()) {
format = sharedPreferences.getString("video_format", "")!! format = sharedPreferences.getString("video_format", "")!!
if (format != "DEFAULT") request.addOption("--merge-output-format", format) if (format != "DEFAULT") request.addOption("--merge-output-format", format)
@ -166,7 +161,7 @@ class DownloadWorker(
} }
"command" -> { "command" -> {
val commandRegex = "\"([^\"]*)\"|(\\S+)" val commandRegex = "\"([^\"]*)\"|(\\S+)"
val command = commandTemplateDao.getTemplateById(downloadItem.customTemplateId) val command = commandTemplateDao.getTemplate(downloadItem.customTemplateId)
val m = Pattern.compile(commandRegex).matcher(command.content) val m = Pattern.compile(commandRegex).matcher(command.content)
while (m.find()) { while (m.find()) {
if (m.group(1) != null) { if (m.group(1) != null) {
@ -198,7 +193,7 @@ class DownloadWorker(
val incognito = sharedPreferences.getBoolean("incognito", false) val incognito = sharedPreferences.getBoolean("incognito", false)
if (!incognito) { if (!incognito) {
val unixtime = System.currentTimeMillis() / 1000 val unixtime = System.currentTimeMillis() / 1000
val historyItem = HistoryItem(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 { runBlocking {
historyDao.insert(historyItem) 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:id="@+id/bottomNavigationView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" 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_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap" app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/downloads" app:title="@string/history"
app:menu="@menu/downloads_menu" app:menu="@menu/downloads_menu"
android:theme="@style/Toolbar" android:theme="@style/Toolbar"
/> />
@ -130,25 +130,25 @@
android:layout_margin="10dp" android:layout_margin="10dp"
android:orientation="vertical"> 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> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<include layout="@layout/downloads_no_results" <include layout="@layout/history_no_results"
android:visibility="gone" /> android:visibility="gone" />
<include layout="@layout/downloads_bottom_sheet" <include layout="@layout/history_item_details_bottom_sheet"
android:visibility="gone" /> android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -9,7 +9,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/title_textinput" android:id="@+id/title_textinput"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -27,33 +26,35 @@
</com.google.android.material.textfield.TextInputLayout> </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 <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="15dp"
android:orientation="horizontal"> 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 <com.google.android.material.textfield.TextInputLayout
android:id="@+id/downloadContainer" android:id="@+id/downloadContainer"
style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu" style="@style/Widget.Material3.TextInputLayout.FilledBox.ExposedDropdownMenu"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="30" android:layout_weight="45"
android:paddingStart="10dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/container"> android:hint="@string/container">
@ -67,30 +68,29 @@
</com.google.android.material.textfield.TextInputLayout> </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> </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 <com.google.android.material.textfield.TextInputLayout
android:id="@+id/outputPath_textinput" android:id="@+id/outputPath"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:errorEnabled="true" app:errorEnabled="true"
@ -100,12 +100,12 @@
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:inputType="text" android:inputType="text"
android:layout_height="wrap_content" android:layout_height="wrap_content"/>
/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
android:id="@+id/downloadChipGroup"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:selectionRequired="true" app:selectionRequired="true"
@ -144,108 +144,9 @@
</com.google.android.material.chip.ChipGroup> </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> </LinearLayout>

View file

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

View file

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