ask permission for auto update preferences on fresh install

pull/1153/head
deniscerri 4 months ago
parent e61a644bd0
commit b22bc0b155
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -12,7 +12,7 @@ def properties = new Properties()
def versionMajor = 1 def versionMajor = 1
def versionMinor = 8 def versionMinor = 8
def versionPatch = 8 def versionPatch = 8
def versionBuild = 0 // bump for dogfood builds, public betas, etc. def versionBuild = 1 // bump for dogfood builds, public betas, etc.
//WHEN RELEASING BETA KEEP THE SAME VERSION AS LATEST STABLE, SO USERS CAN DOWNGRADE //WHEN RELEASING BETA KEEP THE SAME VERSION AS LATEST STABLE, SO USERS CAN DOWNGRADE
def isBeta = false def isBeta = false

@ -12,13 +12,22 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.view.Gravity
import android.view.View import android.view.View
import android.view.WindowInsets import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.children
import androidx.core.view.forEach import androidx.core.view.forEach
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
@ -31,6 +40,8 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.afollestad.materialdialogs.utils.MDUtil.textChanged
import com.anggrayudi.storage.file.getAbsolutePath import com.anggrayudi.storage.file.getAbsolutePath
import com.deniscerri.ytdl.core.RuntimeManager import com.deniscerri.ytdl.core.RuntimeManager
import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.DBManager
@ -53,12 +64,17 @@ import com.deniscerri.ytdl.util.ThemeUtil
import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.UiUtil
import com.deniscerri.ytdl.util.UpdateUtil import com.deniscerri.ytdl.util.UpdateUtil
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.elevation.SurfaceColors import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -72,6 +88,8 @@ import java.io.InputStreamReader
import java.io.Reader import java.io.Reader
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.Locale
import kotlin.sequences.forEach
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -112,7 +130,6 @@ class MainActivity : BaseActivity() {
} }
askPermissions() askPermissions()
checkUpdate()
navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
navController = navHostFragment.findNavController() navController = navHostFragment.findNavController()
@ -270,26 +287,7 @@ class MainActivity : BaseActivity() {
val intent = intent val intent = intent
handleIntents(intent) handleIntents(intent)
if (preferences.getBoolean("auto_update_ytdlp", false)){ askAutoUpdatePreferences()
CoroutineScope(SupervisorJob()).launch(Dispatchers.IO) {
kotlin.runCatching {
if(DBManager.getInstance(this@MainActivity).downloadDao.getDownloadsCountByStatus(listOf("Active", "Queued")) == 0){
if (UpdateUtil(this@MainActivity).updateYTDL().status == UpdateUtil.YTDLPUpdateStatus.DONE) {
val version = RuntimeManager.getInstance().version(context)
val snack = Snackbar.make(findViewById(R.id.frame_layout),
this@MainActivity.getString(R.string.ytld_update_success) + " [${version}]",
Snackbar.LENGTH_LONG)
navigationBarView?.apply {
snack.setAnchorView(this)
}
snack.show()
}
}
}
}
}
} }
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
@ -485,8 +483,67 @@ class MainActivity : BaseActivity() {
} }
} }
private fun askAutoUpdatePreferences() {
if (preferences.getBoolean("asked_auto_update_preferences", false)) {
callAutoUpdates()
return
}
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(context.getString(R.string.update))
builder.setIcon(R.drawable.ic_info)
val view = layoutInflater.inflate(R.layout.dialog_ask_update_preferences, null)
val updateAppLayout = view.findViewById<View>(R.id.update_app)
val updateAppSwitch = updateAppLayout.findViewById<MaterialSwitch>(R.id.preference_switch)
updateAppLayout.findViewById<MaterialButton>(R.id.preference_icon).apply {
icon = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_update_app)
isVisible = true
}
updateAppLayout.findViewById<TextView>(R.id.preference_title).text = getString(R.string.update_app)
updateAppLayout.findViewById<TextView>(R.id.preference_summary).apply {
text = getString(R.string.update_app_summary)
isVisible = true
}
updateAppSwitch.isChecked = true
updateAppLayout.setOnClickListener {
updateAppSwitch.isChecked = !updateAppSwitch.isChecked
}
val updateYTDLLayout = view.findViewById<View>(R.id.update_ytdl)
val updateYTDLSwitch = updateYTDLLayout.findViewById<MaterialSwitch>(R.id.preference_switch)
updateYTDLLayout.findViewById<MaterialButton>(R.id.preference_icon).apply {
icon = ContextCompat.getDrawable(this@MainActivity, R.drawable.ic_update)
isVisible = true
}
updateYTDLLayout.findViewById<TextView>(R.id.preference_title).text = getString(R.string.auto_update_ytdlp)
updateYTDLLayout.findViewById<TextView>(R.id.preference_summary).apply {
text = getString(R.string.auto_update_ytdlp_summary)
isVisible = true
}
updateYTDLSwitch.isChecked = true
updateYTDLLayout.setOnClickListener {
updateYTDLSwitch.isChecked = !updateYTDLSwitch.isChecked
}
builder.setView(view)
builder.setCancelable(false)
builder.setPositiveButton(
context.getString(R.string.ok)
) { _: DialogInterface?, _: Int ->
preferences.edit(commit = true) {
putBoolean("update_app", updateAppSwitch.isChecked)
putBoolean("auto_update_ytdlp", updateYTDLSwitch.isChecked)
putBoolean("asked_auto_update_preferences", true)
}
callAutoUpdates()
}
val dialog = builder.create()
dialog.show()
}
private fun checkUpdate() { private fun callAutoUpdates() {
if (preferences.getBoolean("update_app", false)) { if (preferences.getBoolean("update_app", false)) {
val updateUtil = UpdateUtil(this) val updateUtil = UpdateUtil(this)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -496,14 +553,36 @@ class MainActivity : BaseActivity() {
settingsViewModel.backup() settingsViewModel.backup()
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, this@MainActivity, preferences) UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, this@MainActivity, updateUtil, this@MainActivity, preferences)
} }
} }
} }
} }
if (preferences.getBoolean("auto_update_ytdlp", false)){
CoroutineScope(SupervisorJob()).launch {
try {
val hasActiveQueuedDownloads = DBManager.getInstance(this@MainActivity).downloadDao.getDownloadsCountByStatus(listOf("Active", "Queued")) > 0
if (hasActiveQueuedDownloads) return@launch
val updateRes = withContext(Dispatchers.IO) {
UpdateUtil(this@MainActivity).updateYTDL()
}
if (updateRes.status == UpdateUtil.YTDLPUpdateStatus.DONE) {
val version = RuntimeManager.getInstance().version(context)
val message = this@MainActivity.getString(R.string.ytld_update_success) + " [${version}]"
Snackbar.make(findViewById(R.id.frame_layout), message, Snackbar.LENGTH_LONG).apply {
anchorView = navigationBarView
show()
}
}
} catch (err: Exception) {}
}
}
} }
companion object { companion object {
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
} }

@ -60,7 +60,7 @@ object AdvancedSettingsModule : SettingModule {
Pair(it, items[itemValues.indexOf(it)]) Pair(it, items[itemValues.indexOf(it)])
}.toMutableList() }.toMutableList()
showFormatImportanceDialog(context,title.toString(), itms) { new -> showFormatImportanceDialog(host.getHostContext(),title.toString(), itms) { new ->
prefs.edit(commit = true) { prefs.edit(commit = true) {
putString("format_importance_audio", new.joinToString(",") { it.first }) putString("format_importance_audio", new.joinToString(",") { it.first })
} }

@ -158,7 +158,7 @@ object GeneralSettingsModule : SettingModule {
val itemTouchHelper = ItemTouchHelper(itemTouchCallback) val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(optionsRecycler) itemTouchHelper.attachToRecyclerView(optionsRecycler)
MaterialAlertDialogBuilder(context) MaterialAlertDialogBuilder(host.getHostContext())
.setTitle(R.string.navigation_bar) .setTitle(R.string.navigation_bar)
.setView(binding) .setView(binding)
.setPositiveButton(R.string.ok) { _, _ -> .setPositiveButton(R.string.ok) { _, _ ->
@ -178,7 +178,7 @@ object GeneralSettingsModule : SettingModule {
(pref as ListPreference).apply { (pref as ListPreference).apply {
summary = entry summary = entry
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
val dialog = MaterialAlertDialogBuilder(context) val dialog = MaterialAlertDialogBuilder(host.getHostContext())
dialog.setTitle(context.getString(R.string.app_icon_change)) dialog.setTitle(context.getString(R.string.app_icon_change))
dialog.setNegativeButton(context.getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } dialog.setNegativeButton(context.getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
dialog.setPositiveButton(context.getString(R.string.ok)) { _: DialogInterface?, _: Int -> dialog.setPositiveButton(context.getString(R.string.ok)) { _: DialogInterface?, _: Int ->

@ -108,7 +108,7 @@ object UpdateSettingsModule : SettingModule {
settingsViewModel.backup() settingsViewModel.backup()
} }
} }
UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, host.getHostContext(), preferences) UiUtil.showNewAppUpdateDialog(res.getOrNull()!!, host.getHostContext(), updateUtil, host.hostLifecycleOwner, preferences)
} }
} }
true true

@ -45,12 +45,15 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.FileProvider
import androidx.core.view.children import androidx.core.view.children
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.afollestad.materialdialogs.utils.MDUtil.textChanged import com.afollestad.materialdialogs.utils.MDUtil.textChanged
@ -94,6 +97,7 @@ import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonConfiguration import io.noties.markwon.MarkwonConfiguration
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -2689,74 +2693,28 @@ object UiUtil {
} }
@SuppressLint("UnspecifiedRegisterReceiverFlag") @SuppressLint("UnspecifiedRegisterReceiverFlag")
fun showNewAppUpdateDialog(v: GithubRelease, context: Activity, preferences: SharedPreferences) { fun showNewAppUpdateDialog(v: GithubRelease, context: Activity, updateUtil: UpdateUtil, lifecycleOwner: LifecycleOwner, preferences: SharedPreferences) {
if (context.isFinishing || context.isDestroyed) return if (context.isFinishing || context.isDestroyed) return
var positiveButton: Button? = null
var tmpDownloadJob: Job? = null
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val skippedVersions = preferences.getString("skip_updates", "")?.split(",")?.distinct()?.toMutableList() ?: mutableListOf() val skippedVersions = preferences.getString("skip_updates", "")?.split(",")?.distinct()?.toMutableList() ?: mutableListOf()
val updateDialog = MaterialAlertDialogBuilder(context) val updateDialog = MaterialAlertDialogBuilder(context)
.setTitle(v.tag_name) .setTitle(v.tag_name)
.setMessage(v.body) .setMessage(v.body)
.setCancelable(false)
.setIcon(R.drawable.ic_update_app) .setIcon(R.drawable.ic_update_app)
.setNeutralButton(R.string.ignore){ d: DialogInterface?, _:Int -> .setNeutralButton(R.string.ignore){ d: DialogInterface?, _:Int ->
tmpDownloadJob?.cancel()
skippedVersions.add(v.tag_name) skippedVersions.add(v.tag_name)
preferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply() preferences.edit().putString("skip_updates", skippedVersions.joinToString(",")).apply()
d?.dismiss() d?.dismiss()
} }
.setNegativeButton(context.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> } .setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int ->
.setPositiveButton(context.getString(R.string.update)) { d: DialogInterface?, _: Int -> tmpDownloadJob?.cancel()
runCatching {
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
if (releaseVersion == null){
Toast.makeText(context, R.string.couldnt_find_apk, Toast.LENGTH_SHORT).show()
return@runCatching
}
val uri = Uri.parse(releaseVersion.browser_download_url)
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.mkdirs()
val downloadID = downloadManager.enqueue(
DownloadManager.Request(uri)
.setAllowedNetworkTypes(
DownloadManager.Request.NETWORK_WIFI or
DownloadManager.Request.NETWORK_MOBILE
)
.setAllowedOverRoaming(true)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setTitle(releaseVersion.name)
.setDescription(context.getString(R.string.downloading_update))
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, releaseVersion.name)
)
val onDownloadComplete: BroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
context?.unregisterReceiver(this)
FileUtil.openFileIntent(context!!,
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS)?.absolutePath +
File.separator + releaseVersion.name)
}
}
if (Build.VERSION.SDK_INT >= 26) {
if (Build.VERSION.SDK_INT >= 33) {
context.registerReceiver(onDownloadComplete, IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE),
Context.RECEIVER_NOT_EXPORTED
)
}else{
context.registerReceiver(onDownloadComplete, IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
}
}
d?.dismiss()
}
} }
.setPositiveButton(R.string.update, null)
val view = updateDialog.show() val view = updateDialog.show()
val textView = view.findViewById<TextView>(android.R.id.message) val textView = view.findViewById<TextView>(android.R.id.message)
textView!!.movementMethod = LinkMovementMethod.getInstance() textView!!.movementMethod = LinkMovementMethod.getInstance()
@ -2770,5 +2728,41 @@ object UiUtil {
} }
}).build() }).build()
mw.setMarkdown(textView, v.body) mw.setMarkdown(textView, v.body)
positiveButton = view.getButton(android.app.AlertDialog.BUTTON_POSITIVE)
positiveButton?.setOnClickListener {
positiveButton.isEnabled = false
positiveButton.text = "0%"
val lifecycleScope = lifecycleOwner.lifecycleScope
tmpDownloadJob = lifecycleScope.launch {
val fileResp = updateUtil.downloadReleaseApk(context, v) { progress ->
lifecycleScope.launch {
withContext(Dispatchers.Main) {
positiveButton.text = "$progress%"
}
}
}
fileResp.onFailure {
lifecycleScope.launch {
withContext(Dispatchers.Main) {
view.dismiss()
Snackbar.make(context.findViewById(R.id.frame_layout), it.message ?: context.getString(R.string.errored), Snackbar.LENGTH_LONG).show()
}
}
}
fileResp.onSuccess { file ->
view.dismiss()
val contentUri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", file)
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(contentUri, "application/vnd.android.package-archive")
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
}
context.startActivity(intent)
}
}
}
} }
} }

@ -2,20 +2,27 @@ package com.deniscerri.ytdl.util
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Build
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.deniscerri.ytdl.BuildConfig import com.deniscerri.ytdl.BuildConfig
import com.deniscerri.ytdl.R import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.core.RuntimeManager import com.deniscerri.ytdl.core.RuntimeManager
import com.deniscerri.ytdl.core.models.YTDLRequest import com.deniscerri.ytdl.core.models.YTDLRequest
import com.deniscerri.ytdl.core.packages.PackageBase.Companion.sharedClient
import com.deniscerri.ytdl.core.packages.PackageBase.PackageRelease
import com.deniscerri.ytdl.database.models.GithubRelease import com.deniscerri.ytdl.database.models.GithubRelease
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import okhttp3.Request
import java.io.File
import java.io.InputStreamReader import java.io.InputStreamReader
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import kotlin.coroutines.cancellation.CancellationException
class UpdateUtil(var context: Context) { class UpdateUtil(var context: Context) {
@ -151,6 +158,51 @@ class UpdateUtil(var context: Context) {
} }
suspend fun downloadReleaseApk(context: Context, release: GithubRelease, onProgress: (Int) -> Unit) : Result<File> {
return withContext(Dispatchers.IO) {
try {
val tempApk = File(context.cacheDir, "YTDLnis-${release.tag_name}_interally_downloaded.apk")
val releaseVersion = release.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
//download
val request = Request.Builder()
.url(releaseVersion!!.browser_download_url)
.build()
val response = sharedClient.newCall(request).execute()
if (!response.isSuccessful) {
return@withContext Result.failure(Throwable(response.body.string()))
}
val body = response.body
val totalBytes = body.contentLength()
var bytesDownloaded = 0L
body.byteStream().use { inputStream ->
tempApk.outputStream().use { outputStream ->
val buffer = ByteArray(8192) // 8KB buffer
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
if (!isActive) throw CancellationException("Download cancelled by user")
outputStream.write(buffer, 0, bytesRead)
bytesDownloaded += bytesRead
// Calculate percentage
val progress = ((bytesDownloaded * 100) / totalBytes).toInt()
onProgress(progress)
}
}
}
Result.success(tempApk)
} catch (e: Exception) {
Result.failure(e)
}
}
}
companion object { companion object {
var updatingYTDL = false var updatingYTDL = false
var updatingApp = false var updatingApp = false

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:paddingTop="20dp"
android:layout_height="wrap_content">
<include
android:id="@+id/update_app"
layout="@layout/preference_search_result_switch" />
<include
android:id="@+id/update_ytdl"
layout="@layout/preference_search_result_switch" />
</LinearLayout>

@ -7,7 +7,7 @@
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingVertical="16dp" android:paddingVertical="8dp"
android:minHeight="48dp"> android:minHeight="48dp">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton

@ -7,7 +7,7 @@
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingVertical="16dp" android:paddingVertical="8dp"
android:minHeight="48dp"> android:minHeight="48dp">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton

@ -3,11 +3,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal" android:orientation="horizontal"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingVertical="16dp" android:paddingVertical="8dp"
android:minHeight="48dp" android:minHeight="48dp"
android:gravity="center_vertical"> android:gravity="center_vertical">
@ -41,7 +42,8 @@
android:textSize="16sp" android:textSize="16sp"
android:ellipsize="end" android:ellipsize="end"
android:textStyle="bold" android:textStyle="bold"
android:maxLines="2" /> android:maxLines="2"
tools:text="Preference Title" />
<TextView <TextView
android:id="@+id/preference_summary" android:id="@+id/preference_summary"
@ -52,7 +54,10 @@
android:textSize="14sp" android:textSize="14sp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="10" android:maxLines="10"
android:visibility="gone" /> android:visibility="gone"
tools:text="Preference Description"
tools:visibility="visible"
/>
</LinearLayout> </LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch

@ -515,4 +515,5 @@
<string name="bundled">Bundled</string> <string name="bundled">Bundled</string>
<string name="use_app_language_for_metadata_summary">If enabled, metadata fields will follow your app language and even affect format selection by preferring dubbed versions instead of original</string> <string name="use_app_language_for_metadata_summary">If enabled, metadata fields will follow your app language and even affect format selection by preferring dubbed versions instead of original</string>
<string name="installed">Installed</string> <string name="installed">Installed</string>
<string name="auto_update_ytdlp_summary">It\'s recommended to keep this enabled to keep the application working properly as yt-dlp updates pretty often! The data is extracted from yt-dlp\'s GitHub Repository.</string>
</resources> </resources>

@ -51,7 +51,7 @@
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch" android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false" app:defaultValue="true"
android:icon="@drawable/ic_update_app" android:icon="@drawable/ic_update_app"
android:key="update_app" android:key="update_app"
app:summary="@string/update_app_summary" app:summary="@string/update_app_summary"

Loading…
Cancel
Save