fixed terminal mode somewhat & implemented language preference

This commit is contained in:
Denis Çerri 2023-02-18 21:59:15 +01:00
parent 7783b2846d
commit 5482105a18
No known key found for this signature in database
GPG key ID: 95C43D517D830350
24 changed files with 291 additions and 144 deletions

View file

@ -320,7 +320,7 @@
<PersistentState>
<option name="values">
<map>
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/text_format/baseline_text_format_24.xml" />
<entry key="url" value="file:/$USER_HOME$/AppData/Local/Android/Sdk/icons/material/materialicons/terminal/baseline_terminal_24.xml" />
</map>
</option>
</PersistentState>
@ -330,7 +330,7 @@
</option>
<option name="values">
<map>
<entry key="outputName" value="ic_textformat" />
<entry key="outputName" value="ic_terminal" />
<entry key="sourceFile" value="C:\Users\denis\Desktop\adaptiveproduct_youtube_foreground_color_108 (1).svg" />
</map>
</option>

View file

@ -14,8 +14,10 @@
<application
android:name=".App"
android:allowBackup="false"
android:localeConfig="@xml/locales_config"
android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher"
android:configChanges="layoutDirection|locale"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
@ -27,6 +29,7 @@
tools:ignore="DataExtractionRules">
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
android:configChanges="layoutDirection|locale"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -46,6 +49,7 @@
</activity>
<activity android:name=".receiver.ShareActivity"
android:configChanges="layoutDirection|locale"
android:exported="true">
<!-- <intent-filter>-->
@ -62,6 +66,7 @@
</activity>
<activity
android:name=".ui.settings.SettingsActivity"
android:configChanges="layoutDirection|locale"
android:parentActivityName=".MainActivity"
android:exported="true">
<intent-filter>
@ -71,6 +76,7 @@
</activity>
<activity
android:name=".ui.CustomCommandActivity"
android:configChanges="layoutDirection|locale"
android:theme="@style/AppDefaultTheme"
android:parentActivityName=".MainActivity"
android:label="@string/run_command"

View file

@ -2,6 +2,8 @@ package com.deniscerri.ytdlnis
import android.app.Application
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.preference.PreferenceManager
import androidx.work.Configuration
import androidx.work.WorkManager
@ -11,9 +13,11 @@ import com.yausername.aria2c.Aria2c
import com.yausername.ffmpeg.FFmpeg
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLException
import java.util.*
import java.util.concurrent.Executors
class App : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
@ -40,6 +44,9 @@ class App : Application() {
Toast.makeText(this@App, e.message, Toast.LENGTH_SHORT).show()
e.printStackTrace()
}
val locale = getSharedPreferences("root_preferences", MODE_PRIVATE)
.getString("app_language", Locale.getDefault().language)!!
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(locale))
}
@Throws(YoutubeDLException::class)
private fun initLibraries() {

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION_CODES
@ -15,7 +16,9 @@ import android.view.MenuItem
import android.view.View
import android.view.WindowInsets
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.app.ActivityCompat
import androidx.core.os.LocaleListCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
@ -49,6 +52,7 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityMainBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setContentView(binding.root)
context = baseContext
@ -253,6 +257,10 @@ class MainActivity : AppCompatActivity() {
}
dialog.show()
}
override fun onConfigurationChanged(newConfig: Configuration) {
startActivity(Intent(this, MainActivity::class.java))
super.onConfigurationChanged(newConfig)
}
companion object {
private const val TAG = "MainActivity"

View file

@ -103,7 +103,7 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
DownloadViewModel.Type.video -> {
btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_video)
}
DownloadViewModel.Type.command -> {
else -> {
btn.icon = ContextCompat.getDrawable(activity, R.drawable.ic_baseline_keyboard_arrow_right_24)
}
}

View file

@ -38,7 +38,8 @@ class Converters {
return when(string){
"audio" -> DownloadViewModel.Type.audio
"video" -> DownloadViewModel.Type.video
else -> DownloadViewModel.Type.command
"command" -> DownloadViewModel.Type.command
else -> DownloadViewModel.Type.terminal
}
}

View file

@ -52,4 +52,7 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE url=:url AND format=:format AND (status='Error' OR status='Cancelled') LIMIT 1")
fun getUnfinishedByURLAndFormat(url: String, format: String) : DownloadItem
@Query("SELECT * FROM downloads WHERE type='terminal' ORDER BY id DESC LIMIT 1")
fun getTerminalDownload() : DownloadItem
}

View file

@ -64,4 +64,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
0L
}
}
fun getTerminalDownload() : Long {
return downloadDao.getTerminalDownload().id
}
}

View file

@ -37,7 +37,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private var bestVideoFormat : Format
private var bestAudioFormat : Format
enum class Type {
audio, video, command
audio, video, command, terminal
}
init {
@ -92,7 +92,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val downloadPath = when(type){
Type.audio -> sharedPreferences.getString("music_path", getApplication<App>().resources.getString(R.string.music_path))
Type.video -> sharedPreferences.getString("video_path", getApplication<App>().resources.getString(R.string.video_path))
Type.command -> sharedPreferences.getString("command_path", getApplication<App>().resources.getString(R.string.command_path))
else -> sharedPreferences.getString("command_path", getApplication<App>().resources.getString(R.string.command_path))
}
val audioPreferences = AudioPreferences(embedThumb)
@ -111,18 +111,19 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
private fun getFormat(resultItem: ResultItem, type: Type) : Format {
private fun getFormat(resultItem: ResultItem?, type: Type) : Format {
when(type) {
Type.audio -> {
return try {
resultItem.formats.last { it.format_note.contains("audio", ignoreCase = true) }
resultItem!!.formats.last { it.format_note.contains("audio", ignoreCase = true) }
}catch (e: Exception){
bestAudioFormat
}
}
Type.video -> {
return try {
resultItem.formats.last { !it.format_note.contains("audio", ignoreCase = true) }
resultItem!!.formats.last { !it.format_note.contains("audio", ignoreCase = true) }
}catch (e: Exception){
bestVideoFormat
}
@ -203,4 +204,39 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
}
fun startTerminalDownload(command: String) = viewModelScope.launch(Dispatchers.IO) {
val downloadPath = sharedPreferences.getString("command_path", getApplication<App>().resources.getString(R.string.command_path))
val downloadItem = DownloadItem(0,
"",
"",
"",
"",
"",
Type.terminal,
Format("", "", 0, command),
downloadPath!!, "", "", "", AudioPreferences(), VideoPreferences(), "", false, DownloadRepository.Status.Processing.toString(), 0
)
downloadItem.status = DownloadRepository.Status.Queued.toString()
Log.e("aaaaaaaa", downloadItem.toString())
val id = repository.insert(downloadItem)
downloadItem.id = id
val workRequest = OneTimeWorkRequestBuilder<DownloadWorker>()
.setInputData(Data.Builder().putLong("id", downloadItem.id).build())
.addTag("terminal")
.build()
val context = getApplication<App>().applicationContext
WorkManager.getInstance(context).beginUniqueWork(
downloadItem.id.toString(),
ExistingWorkPolicy.KEEP,
workRequest
).enqueue()
}
fun getTerminalDownload(): Long {
return repository.getTerminalDownload();
}
}

View file

@ -6,19 +6,34 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.EditText
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.service.IDownloaderService
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.yausername.youtubedl_android.YoutubeDL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
class CustomCommandActivity : AppCompatActivity() {
private var topAppBar: MaterialToolbar? = null
private lateinit var downloadViewModel: DownloadViewModel
private lateinit var notificationUtil: NotificationUtil
private lateinit var workManager: WorkManager
private var isDownloadServiceRunning = false
private var output: TextView? = null
private var input: EditText? = null
@ -27,80 +42,6 @@ class CustomCommandActivity : AppCompatActivity() {
private var iDownloaderService: IDownloaderService? = null
private var scrollView: ScrollView? = null
var context: Context? = null
// private val serviceConnection: ServiceConnection = object : ServiceConnection {
// override fun onServiceConnected(className: ComponentName, service: IBinder) {
// downloaderService = (service as LocalBinder).service
// iDownloaderService = service
// isDownloadServiceRunning = true
// try {
// val listeners = ArrayList<IDownloaderListener>()
// listeners.add(listener)
// iDownloaderService!!.addActivity(this@CustomCommandActivity, listeners)
// listener.onDownloadStart(iDownloaderService!!.info)
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
//
// override fun onServiceDisconnected(componentName: ComponentName) {
// downloaderService = null
// iDownloaderService = null
// isDownloadServiceRunning = false
// }
// }
// var listener: IDownloaderListener = object : IDownloaderListener {
// override fun onDownloadStart(info: DownloadInfo) {
// input!!.isEnabled = false
// output!!.text = ""
// swapFabs()
// }
//
// override fun onDownloadProgress(info: DownloadInfo) {
// val newInfo = info.outputLine
// if (newInfo.contains("[download]")) {
// val temp = output!!.text.toString()
// output!!.text =
// temp.substring(0, temp.lastIndexOf(System.getProperty("line.separator")!!) - 2)
// }
// output!!.append(
// """${info.outputLine}
//"""
// )
// output!!.scrollTo(0, output!!.height)
// scrollView!!.fullScroll(View.FOCUS_DOWN)
// }
//
// @SuppressLint("SetTextI18n")
// override fun onDownloadError(info: DownloadInfo) {
// output!!.append(
// """
//
// ${info.outputLine}
// """.trimIndent()
// )
// scrollView!!.scrollTo(0, scrollView!!.maxScrollAmount)
// input!!.setText("yt-dlp ")
// input!!.isEnabled = true
// swapFabs()
// }
//
// @SuppressLint("SetTextI18n")
// override fun onDownloadEnd(info: DownloadInfo) {
// output!!.append(info.outputLine)
// scrollView!!.scrollTo(0, scrollView!!.maxScrollAmount)
// // MEDIA SCAN
// MediaScannerConnection.scanFile(context, arrayOf("/storage"), null, null)
// input!!.setText("yt-dlp ")
// input!!.isEnabled = true
// swapFabs()
// }
//
// override fun onDownloadCancel(downloadInfo: DownloadInfo) {}
// override fun onDownloadCancelAll(downloadInfo: DownloadInfo) {}
// override fun onDownloadServiceEnd() {
// stopDownloadService()
// }
// }
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -115,19 +56,18 @@ class CustomCommandActivity : AppCompatActivity() {
input!!.requestFocus()
fab = findViewById(R.id.command_fab)
fab!!.setOnClickListener {
if (isStoragePermissionGranted) {
startDownloadService(
input!!.text.toString(),
NotificationUtil.COMMAND_DOWNLOAD_NOTIFICATION_ID
)
}
startDownload(
input!!.text.toString()
)
}
cancelFab = findViewById(R.id.cancel_command_fab)
cancelFab!!.setOnClickListener {
cancelDownloadService()
swapFabs()
cancelDownload()
input!!.isEnabled = true
}
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
notificationUtil = NotificationUtil(this)
workManager = WorkManager.getInstance(this)
handleIntent(intent)
}
@ -147,53 +87,32 @@ class CustomCommandActivity : AppCompatActivity() {
}
}
private fun swapFabs() {
val cancel = cancelFab!!.visibility
val start = fab!!.visibility
cancelFab!!.visibility = start
fab!!.visibility = cancel
}
private fun startDownloadService(command: String?, id: Int) {
private fun startDownload(command: String?) {
if (isDownloadServiceRunning) return
//val serviceIntent = Intent(context, DownloaderService::class.java)
// serviceIntent.putExtra("command", command)
// serviceIntent.putExtra("id", id)
//context!!.applicationContext.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
downloadViewModel.startTerminalDownload(command!!)
output!!.text = ""
workManager.getWorkInfosByTagLiveData("terminal")
.observe(this){ list ->
list.forEach {
if(it.progress.getString("output") != null){
output?.append("\n" + it.progress.getString("output") + "\n")
output?.scrollTo(0, output!!.height)
scrollView?.fullScroll(View.FOCUS_DOWN)
}
}
}
}
fun stopDownloadService() {
// if (!isDownloadServiceRunning) return
// iDownloaderService!!.removeActivity(this)
// context!!.applicationContext.unbindService(serviceConnection)
// downloaderService!!.stopForeground(true)
// downloaderService!!.stopSelf()
// isDownloadServiceRunning = false
}
fun cancelDownloadService() {
if (!isDownloadServiceRunning) return
iDownloaderService!!.cancelDownload(false)
stopDownloadService()
}
private val isStoragePermissionGranted: Boolean
get() = if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
== PackageManager.PERMISSION_GRANTED
) {
true
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
1
)
false
private fun cancelDownload() {
lifecycleScope.launch {
val id = withContext(Dispatchers.IO){ downloadViewModel.getTerminalDownload(); }
YoutubeDL.getInstance().destroyProcessById(id.toString())
WorkManager.getInstance(this@CustomCommandActivity).cancelUniqueWork(id.toString())
notificationUtil.cancelDownloadNotification(id.toInt())
}
}
companion object {
private const val TAG = "CustomCommandActivity"

View file

@ -72,7 +72,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
tabLayout.selectTab(tabLayout.getTabAt(1))
viewPager2.setCurrentItem(1, false)
}
Type.command -> {
else -> {
tabLayout.selectTab(tabLayout.getTabAt(2))
viewPager2.setCurrentItem(2, false)
}

View file

@ -73,7 +73,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
tabLayout.selectTab(tabLayout.getTabAt(1))
viewPager2.setCurrentItem(1, false)
}
Type.command -> {
else -> {
tabLayout.selectTab(tabLayout.getTabAt(2))
viewPager2.setCurrentItem(2, false)
}

View file

@ -1,12 +1,16 @@
package com.deniscerri.ytdlnis.ui.settings
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import android.os.LocaleList
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentManager
import com.deniscerri.ytdlnis.R
import com.google.android.material.appbar.MaterialToolbar
import java.util.*
class SettingsActivity : AppCompatActivity() {
private var fm: FragmentManager? = null
@ -23,4 +27,34 @@ class SettingsActivity : AppCompatActivity() {
.replace(R.id.settings_frame_layout, SettingsFragment())
.commit()
}
fun setLocale(l: String){
val locale = Locale(l)
Locale.setDefault(locale)
val config: Configuration = this.resources.configuration
if (Build.VERSION.SDK_INT >= 24){
Locale.setDefault(locale)
config.setLocales(LocaleList(locale))
this.resources.updateConfiguration(config, this.resources.displayMetrics)
onConfigurationChanged(config)
}else{
Locale.setDefault(locale)
config.locale = locale
config.setLayoutDirection(locale)
this.resources.updateConfiguration(config, this.resources.displayMetrics)
onConfigurationChanged(config)
}
restartActivity()
}
private fun restartActivity() {
// val intent = intent
// finish()
// startActivity(Intent(this, MainActivity::class.java))
recreate()
}
}

View file

@ -2,17 +2,23 @@ package com.deniscerri.ytdlnis.ui.settings
import android.app.Activity
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.preference.*
import com.deniscerri.ytdlnis.BuildConfig
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.UpdateUtil
import java.util.*
class SettingsFragment : PreferenceFragmentCompat() {
private var language: ListPreference? = null
private var musicPath: Preference? = null
private var videoPath: Preference? = null
private var commandPath: Preference? = null
@ -51,6 +57,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
val preferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val editor = preferences.edit()
language = findPreference("app_language")
musicPath = findPreference("music_path")
videoPath = findPreference("video_path")
commandPath = findPreference("command_path")
@ -76,6 +83,20 @@ class SettingsFragment : PreferenceFragmentCompat() {
updateApp = findPreference("update_app")
version = findPreference("version")
version!!.summary = BuildConfig.VERSION_NAME
val values = resources.getStringArray(R.array.language_values)
if (Build.VERSION.SDK_INT >= 34){
language!!.isVisible = false
}else{
val entries = mutableListOf<String>()
values.forEach {
entries.add(Locale(it).getDisplayName(Locale(it)))
}
language!!.entries = entries.toTypedArray()
}
val lang = if (values.contains(Locale.getDefault().language)) Locale.getDefault().language else "en"
editor.putString("app_language", lang)
if (preferences.getString("music_path", "")!!.isEmpty()) {
editor.putString("music_path", getString(R.string.music_path))
}
@ -111,6 +132,17 @@ class SettingsFragment : PreferenceFragmentCompat() {
val preferences =
requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
val editor = preferences.edit()
language!!.summary = Locale(preferences.getString("app_language", "")!!).getDisplayLanguage(Locale(preferences.getString("app_language", "")!!))
language!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
editor.putString("app_language", newValue.toString())
language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString()))
editor.apply()
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString()))
(requireActivity() as SettingsActivity).setLocale(newValue.toString())
true
}
musicPath!!.summary = fileUtil?.formatPath(preferences.getString("music_path", "")!!)
musicPath!!.onPreferenceClickListener =
Preference.OnPreferenceClickListener {

View file

@ -67,13 +67,13 @@ class DownloadWorker(
val url = downloadItem.url
val request = YoutubeDLRequest(url)
var request = YoutubeDLRequest(url)
val type = downloadItem.type
val downloadLocation = downloadItem.downloadPath
val tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.title}##${downloadItem.type}""")
var tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.title}##${downloadItem.type}""")
tempFolder.append("##${downloadItem.format.format_id}")
val tempFileDir = File(tempFolder.toString())
var tempFileDir = File(tempFolder.toString())
tempFileDir.delete()
tempFileDir.mkdir()
@ -190,6 +190,32 @@ class DownloadWorker(
}
}
}
DownloadViewModel.Type.terminal -> {
if(downloadItem.format.format_note.startsWith("yt-dlp ")){
downloadItem.format.format_note = downloadItem.format.format_note.substring(6).trim();
}
val commandRegex = "\"([^\"]*)\"|(\\S+)"
request = YoutubeDLRequest(emptyList())
tempFolder = StringBuilder(context.cacheDir.absolutePath + """/${downloadItem.format.format_id}##terminal""")
tempFileDir = File(tempFolder.toString())
tempFileDir.delete()
tempFileDir.mkdir()
val m = Pattern.compile(commandRegex).matcher(downloadItem.format.format_note)
while (m.find()) {
if (m.group(1) != null) {
request.addOption(m.group(1)!!)
} else {
request.addOption(m.group(2)!!)
}
}
request.addOption("-o", tempFileDir.absolutePath + "/${downloadItem.customFileNameTemplate}.%(ext)s")
}
}
runCatching {

View file

@ -0,0 +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:colorAccent" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/>
</vector>

View file

@ -0,0 +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:colorAccent" android:pathData="M20,4H4C2.89,4 2,4.9 2,6v12c0,1.1 0.89,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.11,4 20,4zM20,18H4V8h16V18zM18,17h-6v-2h6V17zM7.5,17l-1.41,-1.41L8.67,13l-2.59,-2.59L7.5,9l4,4L7.5,17z"/>
</vector>

View file

@ -64,8 +64,7 @@
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/custom_command_toolbar"
android:elevation="0dp"
app:layout_scrollFlags="scroll|enterAlways|snap"
app:title="@string/custom_command"
app:title="@string/terminal"
android:layout_width="match_parent"
app:navigationIcon="@drawable/ic_back"
android:layout_height="match_parent"/>

View file

@ -142,4 +142,22 @@
<string name="sponsorblock_fillers">Pjesë Shtesë</string>
<string name="sponsorblock_reminders">Përkujtuesit e Abonimit</string>
<string name="select_sponsorblock_filtering">Zgjidh Filtrimin e SponsorBlock</string>
<string name="save_dir">Direktoria</string>
<string name="new_template">Shabllon i ri</string>
<string name="adjust_templates">Cakto Shabllonët</string>
<string name="edit_selected">Redakto të zgjedhurin</string>
<string name="adjust_video">Modifiko Videon</string>
<string name="schedule">Planifiko</string>
<string name="concurrent_downloads">Shkarkimet e njëkohësishme</string>
<string name="concurrent_downloads_summary">Numri i shkarkimeve qe do ekzekutohen në të njëjtën kohë</string>
<string name="defaultValue">Siç është</string>
<string name="container">Formati</string>
<string name="template">Shablloni</string>
<string name="logs">Shënimet</string>
<string name="commands">Komandat</string>
<string name="date_added">Data e shtuar</string>
<string name="update_ytdl_nightly">Shkarko versionin beta të yt-dlp</string>
<string name="adjust_audio">Modifiko Audion</string>
<string name="file_name_template">Formati i Emrit të Skedarit</string>
<string name="language">Gjuha</string>
</resources>

View file

@ -58,4 +58,16 @@
<item>0.0</item>
<item>100.0</item>
</array>
<string-array name="language_values">
<item>en</item>
<item>de</item>
<item>fr</item>
<item>ja</item>
<item>nb</item>
<item>pl</item>
<item>ru</item>
<item>sq</item>
<item>tr</item>
</string-array>
</resources>

View file

@ -155,7 +155,6 @@
<string name="adjust_templates">Adjust templates</string>
<string name="edit_selected">Edit selected</string>
<string name="adjust_video">Adjust video</string>
<string name="remove_audio">Remove Audio</string>
<string name="schedule">Schedule</string>
<string name="concurrent_downloads">Concurrent Downloads</string>
<string name="concurrent_downloads_summary">Number of downloads executing at the same time</string>
@ -168,4 +167,7 @@
<string name="update_ytdl_nightly">Download Nightly Version of yt-dlp</string>
<string name="adjust_audio">Adjust Audio</string>
<string name="file_name_template">Filename Template</string>
<string name="language">Language</string>
<string name="command_templates">Command Templates</string>
<string name="terminal">Terminal</string>
</resources>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/>
<locale android:name="de"/>
<locale android:name="fr"/>
<locale android:name="ja"/>
<locale android:name="nb-NO"/>
<locale android:name="pl"/>
<locale android:name="ru"/>
<locale android:name="sq-AL"/>
<locale android:name="tr-TR"/>
</locale-config>

View file

@ -17,10 +17,11 @@
android:data="https://github.com/deniscerri/ytdlnis" />
</Preference>
<Preference
app:icon="@drawable/ic_baseline_keyboard_arrow_right_24"
app:icon="@drawable/ic_terminal"
app:key="run_command"
app:title="@string/custom_command">
app:title="@string/terminal">
<intent android:action="ytdlnis.page.CustomCommandActivity" />
</Preference>
@ -32,9 +33,18 @@
<intent android:action="ytdlnis.page.DownloadLogsActivity" />
</Preference>
<Preference
app:icon="@drawable/ic_baseline_keyboard_arrow_right_24"
app:key="command_templates"
app:allowDividerBelow="true"
app:title="@string/command_templates">
<intent android:action="ytdlnis.page.DownloadLogsActivity" />
</Preference>
<Preference
app:icon="@drawable/ic_settings"
app:key="open_settings"
app:allowDividerAbove="true"
app:title="@string/settings">
<intent android:action="ytdlnis.settings.SettingsActivity" />
</Preference>

View file

@ -1,6 +1,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ListPreference
android:entries="@array/language_values"
android:entryValues="@array/language_values"
android:icon="@drawable/ic_language"
app:key="app_language"
app:summary="en"
app:title="@string/language" />
<PreferenceCategory android:title="@string/directories">
<Preference
app:icon="@drawable/ic_music_downloaded"