added ability to edit command template & delete. Fixed logs not scrolling. Implemented import/export templates from clipboard. Implemented Error Items Log Button

This commit is contained in:
Denis Çerri 2023-03-04 22:57:10 +01:00
parent 5fed145313
commit 7d5f32b4bf
No known key found for this signature in database
GPG key ID: 95C43D517D830350
30 changed files with 331 additions and 110 deletions

View file

@ -6,6 +6,7 @@ plugins {
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id "org.jetbrains.kotlin.plugin.serialization" version "1.7.21"
}
def properties = new Properties()
@ -175,5 +176,4 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
}

View file

@ -23,6 +23,7 @@ import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.squareup.picasso.Picasso
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
@ -106,7 +107,13 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
when(item.status){
DownloadRepository.Status.Cancelled.toString() -> actionButton.setIconResource(R.drawable.ic_refresh)
DownloadRepository.Status.Queued.toString() -> actionButton.setIconResource(R.drawable.ic_baseline_delete_outline_24)
else -> actionButton.setIconResource(R.drawable.ic_baseline_file_open_24)
else -> {
actionButton.setIconResource(R.drawable.ic_baseline_file_open_24)
val logFile = File(activity.filesDir.absolutePath + """/logs/${item.id} - ${item.title}##${item.type}##${item.format.format_id}.log""")
if (!logFile.exists()){
actionButton.visibility = View.GONE
}
}
}

View file

@ -53,14 +53,19 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
// onItemClickListener.onSelected(item!!)
// }
title.setOnClickListener {
card.setOnClickListener {
onItemClickListener.onItemClick(item!!)
}
card.setOnLongClickListener {
onItemClickListener.onDelete(item!!); true
}
}
interface OnItemClickListener {
fun onItemClick(commandTemplate: CommandTemplate)
fun onSelected(commandTemplate: CommandTemplate)
fun onDelete(commandTemplate: CommandTemplate)
}
companion object {

View file

@ -17,7 +17,10 @@ interface CommandTemplateDao {
fun getAllTemplatesLiveData() : LiveData<List<CommandTemplate>>
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
fun getAllShortcuts() : LiveData<List<TemplateShortcut>>
fun getAllShortcutsLiveData() : LiveData<List<TemplateShortcut>>
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
fun getAllShortcuts() : List<TemplateShortcut>
@Query("SELECT COUNT(id) FROM commandTemplates")
fun getTotalNumber() : Int

View file

@ -16,13 +16,13 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status='Queued'")
fun getQueuedDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Cancelled'")
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
fun getCancelledDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Error'")
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
fun getErroredDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE status='Processing'")
@Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC")
fun getProcessingDownloads() : LiveData<List<DownloadItem>>
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")

View file

@ -1,13 +1,14 @@
package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
@Serializable
@Entity(tableName = "commandTemplates")
data class CommandTemplate(
@PrimaryKey(autoGenerate = true)
var id: Long,
val title: String,
val content: String
var title: String,
var content: String
)

View file

@ -0,0 +1,9 @@
package com.deniscerri.ytdlnis.database.models
import kotlinx.serialization.Serializable
@Serializable
data class CommandTemplateExport(
val templates: List<CommandTemplate>,
val shortcuts: List<TemplateShortcut>
)

View file

@ -1,9 +1,10 @@
package com.deniscerri.ytdlnis.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Serializable
@Serializable
@Entity(tableName = "templateShortcuts")
data class TemplateShortcut(
@PrimaryKey(autoGenerate = true)

View file

@ -7,7 +7,7 @@ import com.deniscerri.ytdlnis.database.models.TemplateShortcut
class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
val items : LiveData<List<CommandTemplate>> = commandDao.getAllTemplatesLiveData()
val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcuts()
val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcutsLiveData()
fun getAll() : List<CommandTemplate> {
return commandDao.getAllTemplates();
@ -25,6 +25,10 @@ class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
commandDao.delete(item.id)
}
fun getAllShortCuts() : List<TemplateShortcut> {
return commandDao.getAllShortcuts();
}
suspend fun insertShortcut(item: TemplateShortcut){
commandDao.insertShortcut(item.content)
}

View file

@ -1,9 +1,15 @@
package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.widget.Toast
import androidx.core.content.ContextCompat.getSystemService
import androidx.lifecycle.*
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.CommandTemplateExport
import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.repository.CommandTemplateRepository
@ -11,13 +17,20 @@ import com.deniscerri.ytdlnis.database.repository.HistoryRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySortType
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.gson.Gson
import com.google.gson.JsonObject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class CommandTemplateViewModel(application: Application) : AndroidViewModel(application) {
class CommandTemplateViewModel(private val application: Application) : AndroidViewModel(application) {
private val repository: CommandTemplateRepository
val items: LiveData<List<CommandTemplate>>
val shortcuts : LiveData<List<TemplateShortcut>>
private val jsonFormat = Json { prettyPrint = true }
init {
val dao = DBManager.getInstance(application).commandTemplateDao
@ -58,12 +71,53 @@ class CommandTemplateViewModel(application: Application) : AndroidViewModel(appl
repository.update(item)
}
fun importFromClipboard() = viewModelScope.launch(Dispatchers.IO) {
suspend fun importFromClipboard() : Int {
val allTemplates = repository.getAll()
val allShortcuts = repository.getAllShortCuts()
var count = 0
val clipboard = application.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip = clipboard.primaryClip!!.getItemAt(0).text.toString()
try{
jsonFormat.decodeFromString<CommandTemplateExport>(clip).run {
templates.filterNot {
allTemplates.contains(it)
}.run {
this.forEach {
repository.insert(it.copy(id=0))
count++
}
}
shortcuts.filterNot {
allShortcuts.contains(it)
}.run{
this.forEach {
repository.insertShortcut(it.copy(id=0))
count++
}
}
}
}catch (e: Exception){
e.printStackTrace()
}
return count
}
fun exportToClipboard() {
fun exportToClipboard() = viewModelScope.launch(Dispatchers.IO) {
val allTemplates = repository.getAll()
val allShortcuts = repository.getAllShortCuts()
val output = jsonFormat.encodeToString(
CommandTemplateExport(
templates = allTemplates,
shortcuts = allShortcuts
)
)
val clipboard: ClipboardManager =
application.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setText(output)
}
}

View file

@ -105,7 +105,14 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
}
fun addSearchQueryToHistory(query: String) = viewModelScope.launch(Dispatchers.IO) {
searchHistoryRepository.insert(query)
val allQueries = searchHistoryRepository.getAll()
allQueries.filterNot {
it.query == query
}.run {
this.forEach {
searchHistoryRepository.insert(it.query)
}
}
}
fun deleteAllSearchQueryHistory() = viewModelScope.launch(Dispatchers.IO){

View file

@ -121,10 +121,10 @@ class ShareActivity : AppCompatActivity() {
private fun showDownloadSheet(it: ResultItem){
if (sharedPreferences.getBoolean("download_card", true)){
val bottomSheet = DownloadBottomSheetDialog(it, DownloadViewModel.Type.video)
val bottomSheet = DownloadBottomSheetDialog(it, DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!))
bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
}else{
val downloadItem = downloadViewModel.createDownloadItemFromResult(it, DownloadViewModel.Type.video)
val downloadItem = downloadViewModel.createDownloadItemFromResult(it, DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!))
downloadViewModel.queueDownloads(listOf(downloadItem))
this.finish()
}

View file

@ -144,7 +144,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
}else if (resultViewModel.itemCount.value!! == 1){
if (sharedPreferences!!.getBoolean("download_card", true)){
if(it.size == 1 && !firstBoot && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){
showSingleDownloadSheet(it[0], DownloadViewModel.Type.video)
showSingleDownloadSheet(it[0], DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!))
}
}
}else{

View file

@ -109,16 +109,18 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
when(type) {
Type.audio -> {
tabLayout.selectTab(tabLayout.getTabAt(0))
tabLayout.getTabAt(0)!!.select()
viewPager2.setCurrentItem(0, false)
}
Type.video -> {
tabLayout.selectTab(tabLayout.getTabAt(1))
tabLayout.getTabAt(1)!!.select()
viewPager2.setCurrentItem(1, false)
}
else -> {
tabLayout.selectTab(tabLayout.getTabAt(2))
viewPager2.setCurrentItem(2, false)
tabLayout.getTabAt(2)!!.select()
viewPager2.postDelayed( {
viewPager2.setCurrentItem(2, false)
}, 200)
}
}

View file

@ -20,6 +20,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
@ -68,8 +69,8 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
lifecycleScope.launch {
val sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
try {
val templates = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll()
val templates : MutableList<CommandTemplate> = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll().toMutableList()
}
val id = sharedPreferences.getLong("commandTemplate", templates[0].id)
val chosenCommand = templates.find { it.id == id }
@ -117,21 +118,10 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
}
}
val templateTitles = templates.map {it.title}
val commandTemplates = view.findViewById<TextInputLayout>(R.id.template)
val autoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.template_textview)
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
templateTitles
)
)
if (templateTitles.isNotEmpty()) {
autoCompleteTextView!!.setText(downloadItem.format.format_id, false)
}
requireView().findViewById<AutoCompleteTextView>(R.id.template_textview)
populateCommandTemplates(templates, autoCompleteTextView)
(commandTemplates!!.editText as AutoCompleteTextView?)!!.onItemClickListener =
AdapterView.OnItemClickListener { _: AdapterView<*>?, _: View?, index: Int, _: Long ->
@ -168,29 +158,40 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
val newTemplate : Chip = view.findViewById(R.id.newTemplate)
newTemplate.setOnClickListener {
uiUtil.showCreationSheet(requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
uiUtil.showCommandTemplateCreationOrUpdatingSheet(null, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
templates.add(it)
chosenCommandView.editText!!.setText(it.content)
downloadItem.format = Format(
it.title,
"",
"",
"",
"",
0,
it.content
)
populateCommandTemplates(templates, autoCompleteTextView)
}
}
//
// val embedSubs = view.findViewById<Chip>(R.id.embed_subtitles)
// embedSubs!!.isChecked = embedSubs.isChecked
// embedSubs.setOnClickListener {
// downloadItem.embedSubs = embedSubs.isChecked
// }
//
// val addChapters = view.findViewById<Chip>(R.id.add_chapters)
// addChapters!!.isChecked = addChapters.isChecked
// addChapters.setOnClickListener{
// downloadItem.addChapters = addChapters.isChecked
// }
//
// val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
// saveThumbnail!!.isChecked = saveThumbnail.isChecked
// saveThumbnail.setOnClickListener {
// downloadItem.SaveThumb = saveThumbnail.isChecked
// }
val editSelected : Chip = view.findViewById(R.id.editSelected)
editSelected.setOnClickListener {
var current = templates.find { it.title == autoCompleteTextView.text.toString() }
if (current == null) current = CommandTemplate(0, "", chosenCommandView.editText!!.text.toString())
uiUtil.showCommandTemplateCreationOrUpdatingSheet(current, requireActivity(), viewLifecycleOwner, commandTemplateViewModel) {
templates.add(it)
chosenCommandView.editText!!.setText(it.content)
downloadItem.format = Format(
it.title,
"",
"",
"",
"",
0,
it.content
)
}
}
} catch (e: Exception) {
e.printStackTrace()
@ -198,6 +199,22 @@ class DownloadCommandFragment(private val resultItem: ResultItem) : Fragment() {
}
}
private fun populateCommandTemplates(templates: List<CommandTemplate>, autoCompleteTextView: AutoCompleteTextView?){
val templateTitles = templates.map {it.title}
autoCompleteTextView?.setAdapter(
ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
templateTitles
)
)
if (templateTitles.isNotEmpty()) {
autoCompleteTextView!!.setText(downloadItem.format.format_id, false)
}
}
private var commandPathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->

View file

@ -2,6 +2,7 @@ package com.deniscerri.ytdlnis.ui.downloadqueue
import android.app.Activity
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
@ -23,6 +24,7 @@ import com.deniscerri.ytdlnis.adapter.GenericDownloadAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
import com.deniscerri.ytdlnis.ui.more.downloadLogs.DownloadLogActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.bottomsheet.BottomSheetDialog
@ -85,7 +87,11 @@ class ErroredDownloadsFragment() : Fragment(), GenericDownloadAdapter.OnItemClic
}
override fun onActionButtonClick(itemID: Long) {
TODO("Not yet implemented")
val item = items.find { it.id == itemID } ?: return
val file = File(requireContext().filesDir.absolutePath + """/logs/${item.id} - ${item.title}##${item.type}##${item.format.format_id}.log""")
val intent = Intent(requireContext(), DownloadLogActivity::class.java)
intent.putExtra("path", file.absolutePath)
startActivity(intent)
}
override fun onCardClick(itemID: Long) {

View file

@ -1,32 +1,27 @@
package com.deniscerri.ytdlnis.ui.more
import android.content.ClipboardManager
import android.content.Context
import android.content.DialogInterface
import android.os.*
import android.text.Editable
import android.text.TextWatcher
import android.view.MenuItem
import android.view.ViewGroup
import android.view.Window
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.content.res.AppCompatResources
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.TemplatesAdapter
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CommandTemplatesActivity : AppCompatActivity(), TemplatesAdapter.OnItemClickListener {
@ -68,9 +63,19 @@ class CommandTemplatesActivity : AppCompatActivity(), TemplatesAdapter.OnItemCli
topAppBar.setOnMenuItemClickListener { m: MenuItem ->
val itemId = m.itemId
if (itemId == R.id.export_clipboard) {
commandTemplateViewModel.exportToClipboard()
lifecycleScope.launch(Dispatchers.IO){
commandTemplateViewModel.exportToClipboard()
}
}else if (itemId == R.id.import_clipboard){
commandTemplateViewModel.importFromClipboard()
lifecycleScope.launch{
withContext(Dispatchers.IO){
val count = commandTemplateViewModel.importFromClipboard()
runOnUiThread{
Toast.makeText(this@CommandTemplatesActivity, "$count items imported!", Toast.LENGTH_LONG).show()
}
}
}
}
true
}
@ -79,7 +84,7 @@ class CommandTemplatesActivity : AppCompatActivity(), TemplatesAdapter.OnItemCli
private fun initChips() {
val new = findViewById<Chip>(R.id.newTemplate)
new.setOnClickListener {
uiUtil.showCreationSheet(this@CommandTemplatesActivity, this, commandTemplateViewModel) {}
uiUtil.showCommandTemplateCreationOrUpdatingSheet(null,this@CommandTemplatesActivity, this, commandTemplateViewModel) {}
}
val shortcuts = findViewById<Chip>(R.id.shortcuts)
shortcuts.setOnClickListener {
@ -92,10 +97,21 @@ class CommandTemplatesActivity : AppCompatActivity(), TemplatesAdapter.OnItemCli
}
override fun onItemClick(commandTemplate: CommandTemplate) {
TODO("Not yet implemented")
uiUtil.showCommandTemplateCreationOrUpdatingSheet(commandTemplate,this@CommandTemplatesActivity, this, commandTemplateViewModel) {}
}
override fun onSelected(commandTemplate: CommandTemplate) {
TODO("Not yet implemented")
}
override fun onDelete(commandTemplate: CommandTemplate) {
val deleteDialog = MaterialAlertDialogBuilder(this)
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + commandTemplate.title + "\"!")
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
commandTemplateViewModel.delete(commandTemplate)
}
deleteDialog.show()
}
}

View file

@ -36,15 +36,13 @@ class DownloadLogActivity : AppCompatActivity() {
}
content = findViewById(R.id.content)
content.movementMethod = ScrollingMovementMethod()
contentScrollView = findViewById(R.id.content_scrollview)
copyLog = findViewById(R.id.copy_log)
copyLog.setOnClickListener {
val clipboard: ClipboardManager =
getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clip: ClipData = ClipData.newPlainText("Download Log", content.text)
clipboard.setPrimaryClip(clip)
clipboard.setText(content.text)
}
val path = intent.getStringExtra("path")
@ -60,8 +58,10 @@ class DownloadLogActivity : AppCompatActivity() {
observer = object : FileObserver(file.absolutePath, MODIFY) {
override fun onEvent(event: Int, p: String?) {
runOnUiThread{
content.text = File(path).readText()
val newText = File(path).readText()
content.text = newText
content.scrollTo(0, content.height)
contentScrollView.fullScroll(View.FOCUS_DOWN)
}
}
}
@ -70,7 +70,8 @@ class DownloadLogActivity : AppCompatActivity() {
observer = object : FileObserver(file, MODIFY) {
override fun onEvent(event: Int, p: String?) {
runOnUiThread{
content.text = File(path).readText()
val newText = File(path).readText()
content.text = newText
content.scrollTo(0, content.height)
contentScrollView.fullScroll(View.FOCUS_DOWN)
}

View file

@ -1,6 +1,7 @@
package com.deniscerri.ytdlnis.ui.more.downloadLogs
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.*
import android.view.MenuItem
@ -13,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.DownloadLogsAdapter
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File
@ -22,6 +24,7 @@ class DownloadLogListActivity : AppCompatActivity(), DownloadLogsAdapter.OnItemC
private lateinit var noResults: RelativeLayout
private lateinit var fileList: MutableList<File>
private lateinit var topAppBar: MaterialToolbar
private lateinit var logFolder : File
var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) {
@ -43,7 +46,7 @@ class DownloadLogListActivity : AppCompatActivity(), DownloadLogsAdapter.OnItemC
noResults = findViewById(R.id.no_results)
noResults.visibility = View.GONE
val logFolder = File(filesDir.absolutePath + "/logs")
logFolder = File(filesDir.absolutePath + "/logs")
updateList(logFolder)
if(Build.VERSION.SDK_INT < 29){
@ -73,9 +76,18 @@ class DownloadLogListActivity : AppCompatActivity(), DownloadLogsAdapter.OnItemC
val itemId = m.itemId
if (itemId == R.id.remove_logs) {
try{
logFolder.listFiles()!!.forEach {
it.delete()
val deleteDialog = MaterialAlertDialogBuilder(this)
deleteDialog.setTitle(getString(R.string.confirm_delete_history))
deleteDialog.setMessage(getString(R.string.confirm_delete_logs_desc))
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
logFolder.listFiles()!!.forEach {
it.delete()
}.run {
updateList(logFolder)
}
}
deleteDialog.show()
}catch (e: Exception){
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
}
@ -113,6 +125,14 @@ class DownloadLogListActivity : AppCompatActivity(), DownloadLogsAdapter.OnItemC
}
override fun onDeleteClick(file: File) {
file.delete()
val deleteDialog = MaterialAlertDialogBuilder(this)
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + file.name + "\"!")
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
file.delete().run {
updateList(logFolder)
}
}
deleteDialog.show()
}
}

View file

@ -23,6 +23,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var videoPath: Preference? = null
private var commandPath: Preference? = null
private var incognito: SwitchPreferenceCompat? = null
private var preferredDownloadType : ListPreference? = null
private var downloadCard: SwitchPreferenceCompat? = null
private var apiKey: EditTextPreference? = null
private var concurrentFragments: SeekBarPreference? = null
@ -64,6 +65,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
videoPath = findPreference("video_path")
commandPath = findPreference("command_path")
incognito = findPreference("incognito")
preferredDownloadType = findPreference("preferred_download_type")
downloadCard = findPreference("download_card")
apiKey = findPreference("api_key")
concurrentFragments = findPreference("concurrent_fragments")
@ -111,6 +113,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.putString("command_path", getString(R.string.command_path))
}
editor.putBoolean("incognito", incognito!!.isChecked)
editor.putString("preferred_download_type", preferredDownloadType!!.value)
editor.putBoolean("download_card", downloadCard!!.isChecked)
editor.putString("api_key", apiKey!!.text)
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
@ -186,6 +189,13 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.apply()
true
}
preferredDownloadType!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
editor.putString("preferred_download_type", newValue.toString())
editor.apply()
true
}
downloadCard!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
val enable = newValue as Boolean

View file

@ -8,7 +8,6 @@ import android.content.Intent
import android.net.Uri
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.Window
@ -18,7 +17,6 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.content.FileProvider
import androidx.lifecycle.LifecycleOwner
import com.deniscerri.ytdlnis.R
@ -55,7 +53,7 @@ class UiUtil(private val fileUtil: FileUtil) {
}
fun showCreationSheet(context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel, newTemplate: (newTemplate: CommandTemplate) -> Unit){
fun showCommandTemplateCreationOrUpdatingSheet(item: CommandTemplate?, context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel, newTemplate: (newTemplate: CommandTemplate) -> Unit){
val bottomSheet = BottomSheetDialog(context)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.create_command_template)
@ -66,7 +64,15 @@ class UiUtil(private val fileUtil: FileUtil) {
val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!!
val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!!
ok.isEnabled = false
if (item != null){
title.editText!!.setText(item.title)
content.editText!!.setText(item.content)
bottomSheet.findViewById<TextView>(R.id.bottom_sheet_subtitle)!!.text = content.resources.getString(R.string.update_template)
ok.text = content.resources.getString(R.string.update)
ok.isEnabled = true
}else{
ok.isEnabled = false
}
title.editText!!.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
@ -91,6 +97,10 @@ class UiUtil(private val fileUtil: FileUtil) {
}
})
if (content.editText!!.text.isEmpty()){
}
content.setEndIconOnClickListener {
if(content.editText!!.text.isEmpty()){
val clipboard: ClipboardManager =
@ -118,9 +128,16 @@ class UiUtil(private val fileUtil: FileUtil) {
}
ok.setOnClickListener {
val t = CommandTemplate(0, title.editText!!.text.toString(), content.editText!!.text.toString())
commandTemplateViewModel.insert(t)
newTemplate(t)
if (item == null){
val t = CommandTemplate(0, title.editText!!.text.toString(), content.editText!!.text.toString())
commandTemplateViewModel.insert(t)
newTemplate(t)
}else{
item.title = title.editText!!.text.toString()
item.content = content.editText!!.text.toString()
commandTemplateViewModel.update(item)
newTemplate(item)
}
bottomSheet.cancel()
}

View file

@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?android:textColorPrimary" android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19L3,19L3,5h18v14zM8,15c0,-1.66 1.34,-3 3,-3 0.35,0 0.69,0.07 1,0.18L12,6h5v2h-3v7.03c-0.02,1.64 -1.35,2.97 -3,2.97 -1.66,0 -3,-1.34 -3,-3z"/>
<path android:fillColor="?attr/colorAccent" android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19L3,19L3,5h18v14zM8,15c0,-1.66 1.34,-3 3,-3 0.35,0 0.69,0.07 1,0.18L12,6h5v2h-3v7.03c-0.02,1.64 -1.35,2.97 -3,2.97 -1.66,0 -3,-1.34 -3,-3z"/>
</vector>

View file

@ -23,27 +23,34 @@
</com.google.android.material.appbar.AppBarLayout>
<ScrollView
<LinearLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/content_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fillViewport="true"
android:paddingBottom="150dp"
>
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="vertical">
<TextView
android:id="@+id/content"
android:padding="10dp"
android:scrollHorizontally="true"
android:fontFamily="monospace"
android:textSize="15sp"
android:layout_width="wrap_content"
android:textIsSelectable="true"
android:layout_height="wrap_content" />
<ScrollView
android:id="@+id/content_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="150dp"
>
</ScrollView>
<TextView
android:id="@+id/content"
android:padding="10dp"
android:fontFamily="monospace"
android:textSize="15sp"
android:gravity="bottom"
android:layout_width="match_parent"
android:textIsSelectable="true"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/copy_log"

View file

@ -6,6 +6,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground"
android:padding="15dp">
<TextView

View file

@ -46,7 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="all"
android:text="@string/ok"
android:text="@string/create"
app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View file

@ -9,6 +9,8 @@
<TextView
android:id="@+id/title"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
@ -17,7 +19,8 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
android:focusable="true" />
<Button
android:id="@+id/delete"

View file

@ -75,4 +75,17 @@
<item>tr</item>
<item>vi</item>
</string-array>
<string-array name="download_types">
<item>@string/audio</item>
<item>@string/video</item>
<item>@string/command</item>
</string-array>
<string-array name="download_types_values">
<item>audio</item>
<item>video</item>
<item>command</item>
</string-array>
</resources>

View file

@ -197,4 +197,10 @@
<string name="create_shortcut">Create Shortcut</string>
<string name="error_restarting_download">Error trying to restart the download</string>
<string name="delete_temp_file_too">Delete temporary files from the device</string>
<string name="update_template">Update Template</string>
<string name="create">Create</string>
<string name="update">Update</string>
<string name="preferred_download_type">Preferred Download Type</string>
<string name="preferred_download_type_summary">Default tab the download card will open to</string>
<string name="confirm_delete_logs_desc">Delete the entire log list, permanently.</string>
</resources>

View file

@ -45,6 +45,16 @@
app:summary="@string/download_card_summary"
app:title="@string/show_download_card" />
<ListPreference
android:dependency="download_card"
android:defaultValue="video"
android:entries="@array/download_types"
android:entryValues="@array/download_types_values"
android:icon="@drawable/ic_download_type"
app:key="preferred_download_type"
app:summary="@string/preferred_download_type_summary"
app:title="@string/preferred_download_type" />
<EditTextPreference
android:icon="@drawable/ic_key"
app:key="api_key"

View file

@ -40,6 +40,7 @@ plugins {
id 'com.android.application' version '7.4.1' apply false
id 'com.android.library' version '7.4.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.21' apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.7.21" apply false
}