6.14.6 commit
This commit is contained in:
parent
cd31132516
commit
9093c59068
|
@ -26,8 +26,8 @@ android {
|
||||||
vectorDrawables.useSupportLibrary false
|
vectorDrawables.useSupportLibrary false
|
||||||
vectorDrawables.generatedDensities = []
|
vectorDrawables.generatedDensities = []
|
||||||
|
|
||||||
versionCode 3020304
|
versionCode 3020305
|
||||||
versionName "6.14.5"
|
versionName "6.14.6"
|
||||||
|
|
||||||
applicationId "ac.mdiq.podcini.R"
|
applicationId "ac.mdiq.podcini.R"
|
||||||
def commit = ""
|
def commit = ""
|
||||||
|
|
|
@ -363,7 +363,7 @@ class PlaybackService : MediaLibraryService() {
|
||||||
val item_ = realm.query(Episode::class).query("id == $0", item!!.id).first().find()
|
val item_ = realm.query(Episode::class).query("id == $0", item!!.id).first().find()
|
||||||
if (item_ != null) {
|
if (item_ != null) {
|
||||||
item = upsert(item_) {
|
item = upsert(item_) {
|
||||||
it.playState = PlayState.PLAYED.code
|
if (it.playState < PlayState.PLAYED.code || it.playState == PlayState.IGNORED.code) it.playState = PlayState.PLAYED.code
|
||||||
val media = it.media
|
val media = it.media
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
media.startPosition = playable.startPosition
|
media.startPosition = playable.startPosition
|
||||||
|
|
|
@ -1,152 +0,0 @@
|
||||||
package ac.mdiq.podcini.preferences.fragments
|
|
||||||
|
|
||||||
import ac.mdiq.podcini.BuildConfig
|
|
||||||
import ac.mdiq.podcini.R
|
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
|
||||||
import ac.mdiq.podcini.ui.compose.CustomTheme
|
|
||||||
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.*
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.platform.ComposeView
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory
|
|
||||||
|
|
||||||
class AboutFragment : PreferenceFragmentCompat() {
|
|
||||||
@SuppressLint("CommitTransaction")
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
addPreferencesFromResource(R.xml.preferences_about)
|
|
||||||
|
|
||||||
findPreference<Preference>("about_version")!!.summary = String.format("%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH)
|
|
||||||
findPreference<Preference>("about_version")!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
||||||
val clip = ClipData.newPlainText(getString(R.string.bug_report_title), findPreference<Preference>("about_version")!!.summary)
|
|
||||||
clipboard.setPrimaryClip(clip)
|
|
||||||
if (Build.VERSION.SDK_INT <= 32) Snackbar.make(requireView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>("about_help")!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>("about_privacy_policy")!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/blob/main/PrivacyPolicy.md")
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>("about_licenses")!!.onPreferenceClickListener =
|
|
||||||
Preference.OnPreferenceClickListener {
|
|
||||||
parentFragmentManager.beginTransaction().replace(R.id.settingsContainer, LicensesFragment()).addToBackStack(getString(R.string.translators)).commit()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.about_pref)
|
|
||||||
}
|
|
||||||
|
|
||||||
class LicensesFragment : Fragment() {
|
|
||||||
private val licenses = mutableStateListOf<LicenseItem>()
|
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
||||||
val composeView = ComposeView(requireContext()).apply { setContent { CustomTheme(requireContext()) { MainView() } } }
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
licenses.clear()
|
|
||||||
val stream = requireContext().assets.open("licenses.xml")
|
|
||||||
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
|
||||||
val libraryList = docBuilder.parse(stream).getElementsByTagName("library")
|
|
||||||
for (i in 0 until libraryList.length) {
|
|
||||||
val lib = libraryList.item(i).attributes
|
|
||||||
licenses.add(LicenseItem(lib.getNamedItem("name").textContent,
|
|
||||||
String.format("By %s, %s license", lib.getNamedItem("author").textContent, lib.getNamedItem("license").textContent), lib.getNamedItem("website").textContent, lib.getNamedItem("licenseText").textContent))
|
|
||||||
}
|
|
||||||
}.invokeOnCompletion { throwable -> if (throwable!= null) Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show() }
|
|
||||||
return composeView
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MainView() {
|
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
val textColor = MaterialTheme.colorScheme.onSurface
|
|
||||||
var showDialog by remember { mutableStateOf(false) }
|
|
||||||
var curLicenseIndex by remember { mutableIntStateOf(-1) }
|
|
||||||
if (showDialog) Dialog(onDismissRequest = { showDialog = false }) {
|
|
||||||
Surface(shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
|
|
||||||
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
|
||||||
Text(licenses[curLicenseIndex].title, color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
|
||||||
Row {
|
|
||||||
Button(onClick = { openInBrowser(requireContext(), licenses[curLicenseIndex].licenseUrl) }) { Text("View website") }
|
|
||||||
Spacer(Modifier.weight(1f))
|
|
||||||
Button(onClick = { showLicenseText(licenses[curLicenseIndex].licenseTextFile) }) { Text("View license") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LazyColumn(state = lazyListState, modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 20.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
|
||||||
itemsIndexed(licenses) { index, item ->
|
|
||||||
Column(Modifier.clickable(onClick = {
|
|
||||||
curLicenseIndex = index
|
|
||||||
showDialog = true
|
|
||||||
})) {
|
|
||||||
Text(item.title, color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
|
||||||
Text(item.subtitle, color = textColor, style = MaterialTheme.typography.bodySmall)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showLicenseText(licenseTextFile: String) {
|
|
||||||
try {
|
|
||||||
val reader = BufferedReader(InputStreamReader(requireContext().assets.open(licenseTextFile), "UTF-8"))
|
|
||||||
val licenseText = StringBuilder()
|
|
||||||
var line = ""
|
|
||||||
while ((reader.readLine()?.also { line = it }) != null) licenseText.append(line).append("\n")
|
|
||||||
MaterialAlertDialogBuilder(requireContext()).setMessage(licenseText).show()
|
|
||||||
} catch (e: IOException) { e.printStackTrace() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.licenses)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LicenseItem(val title: String, val subtitle: String, val licenseUrl: String, val licenseTextFile: String)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import ac.mdiq.podcini.net.sync.model.EpisodeAction
|
||||||
import ac.mdiq.podcini.net.sync.model.EpisodeAction.Companion.readFromJsonObject
|
import ac.mdiq.podcini.net.sync.model.EpisodeAction.Companion.readFromJsonObject
|
||||||
import ac.mdiq.podcini.net.sync.model.SyncServiceException
|
import ac.mdiq.podcini.net.sync.model.SyncServiceException
|
||||||
import ac.mdiq.podcini.preferences.ExportWriter
|
import ac.mdiq.podcini.preferences.ExportWriter
|
||||||
import ac.mdiq.podcini.preferences.OpmlTransporter.*
|
import ac.mdiq.podcini.preferences.OpmlTransporter.OpmlWriter
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodeByGuidOrUrl
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisodeByGuidOrUrl
|
||||||
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
|
import ac.mdiq.podcini.storage.database.Episodes.getEpisodes
|
||||||
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
|
import ac.mdiq.podcini.storage.database.Feeds.getFeedList
|
||||||
|
@ -20,6 +20,7 @@ import ac.mdiq.podcini.storage.utils.FileNameGenerator.generateFileName
|
||||||
import ac.mdiq.podcini.storage.utils.FilesUtils.getDataFolder
|
import ac.mdiq.podcini.storage.utils.FilesUtils.getDataFolder
|
||||||
import ac.mdiq.podcini.ui.activity.OpmlImportActivity
|
import ac.mdiq.podcini.ui.activity.OpmlImportActivity
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
|
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
import android.app.ProgressDialog
|
import android.app.ProgressDialog
|
||||||
|
@ -32,16 +33,33 @@ import android.os.Bundle
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.app.ShareCompat.IntentBuilder
|
import androidx.core.app.ShareCompat.IntentBuilder
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
@ -57,6 +75,7 @@ import java.nio.channels.FileChannel
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.Throws
|
||||||
|
|
||||||
class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
|
@ -102,77 +121,120 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
private var progressDialog: ProgressDialog? = null
|
private var progressDialog: ProgressDialog? = null
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {}
|
||||||
addPreferencesFromResource(R.xml.preferences_import_export)
|
|
||||||
setupStorageScreen()
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
(activity as PreferenceActivity).supportActionBar?.setTitle(R.string.import_export_pref)
|
||||||
progressDialog = ProgressDialog(context)
|
progressDialog = ProgressDialog(context)
|
||||||
progressDialog!!.isIndeterminate = true
|
progressDialog!!.isIndeterminate = true
|
||||||
progressDialog!!.setMessage(requireContext().getString(R.string.please_wait))
|
progressDialog!!.setMessage(requireContext().getString(R.string.please_wait))
|
||||||
|
return ComposeView(requireContext()).apply {
|
||||||
|
setContent {
|
||||||
|
CustomTheme(requireContext()) {
|
||||||
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(16.dp).verticalScroll(scrollState)) {
|
||||||
|
Text(stringResource(R.string.database), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
exportDatabase()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.database_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.database_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
override fun onStart() {
|
importDatabase()
|
||||||
super.onStart()
|
})) {
|
||||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.import_export_pref)
|
Text(stringResource(R.string.database_import_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.database_import_summary), color = textColor)
|
||||||
}
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
|
|
||||||
private fun dateStampFilename(fname: String): String {
|
Text(stringResource(R.string.media_files), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
|
||||||
return String.format(fname, SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()))
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
exportMediaFiles()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.media_files_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.media_files_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
importMediaFiles()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.media_files_import_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.media_files_import_summary), color = textColor)
|
||||||
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
|
|
||||||
private fun setupStorageScreen() {
|
Text(stringResource(R.string.preferences), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
|
||||||
findPreference<Preference>(IExport.prefOpmlExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
exportPreferences()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.preferences_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.preferences_export_summary), color = textColor)
|
||||||
|
}
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
importPreferences()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.preferences_import_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.preferences_import_summary), color = textColor)
|
||||||
|
}
|
||||||
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
|
|
||||||
|
Text(stringResource(R.string.opml), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher, OpmlWriter())
|
openExportPathPicker(Export.OPML, chooseOpmlExportPathLauncher, OpmlWriter())
|
||||||
true
|
})) {
|
||||||
|
Text(stringResource(R.string.opml_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.opml_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
findPreference<Preference>(IExport.prefHtmlExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher, HtmlWriter())
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefProgressExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
openExportPathPicker(Export.PROGRESS, chooseProgressExportPathLauncher, EpisodesProgressWriter())
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefProgressImport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
importEpisodeProgress()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefOpmlImport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
try {
|
try {
|
||||||
chooseOpmlImportPathLauncher.launch("*/*")
|
chooseOpmlImportPathLauncher.launch("*/*")
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Log.e(TAG, "No activity found. Should never happen...")
|
Log.e(TAG, "No activity found. Should never happen...")
|
||||||
}
|
}
|
||||||
true
|
})) {
|
||||||
|
Text(stringResource(R.string.opml_import_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.opml_import_summary), color = textColor)
|
||||||
}
|
}
|
||||||
findPreference<Preference>(IExport.prefDatabaseImport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
importDatabase()
|
|
||||||
true
|
Text(stringResource(R.string.progress), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
openExportPathPicker(Export.PROGRESS, chooseProgressExportPathLauncher, EpisodesProgressWriter())
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.progress_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.progress_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
findPreference<Preference>(IExport.prefDatabaseExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
exportDatabase()
|
importEpisodeProgress()
|
||||||
true
|
})) {
|
||||||
|
Text(stringResource(R.string.progress_import_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.progress_import_summary), color = textColor)
|
||||||
}
|
}
|
||||||
findPreference<Preference>(IExport.prefPrefImport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
importPreferences()
|
|
||||||
true
|
Text(stringResource(R.string.html), color = textColor, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.padding(top = 10.dp))
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
|
openExportPathPicker(Export.HTML, chooseHtmlExportPathLauncher, HtmlWriter())
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.html_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.html_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
findPreference<Preference>(IExport.prefPrefExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, top = 10.dp).clickable(onClick = {
|
||||||
exportPreferences()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefMediaFilesImport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
importMediaFiles()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefMediaFilesExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
exportMediaFiles()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(IExport.prefFavoritesExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher, FavoritesWriter())
|
openExportPathPicker(Export.FAVORITES, chooseFavoritesExportPathLauncher, FavoritesWriter())
|
||||||
true
|
})) {
|
||||||
|
Text(stringResource(R.string.favorites_export_label), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.favorites_export_summary), color = textColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dateStampFilename(fname: String): String {
|
||||||
|
return String.format(fname, SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date()))
|
||||||
|
}
|
||||||
|
|
||||||
private fun exportWithWriter(exportWriter: ExportWriter, uri: Uri?, exportType: Export) {
|
private fun exportWithWriter(exportWriter: ExportWriter, uri: Uri?, exportType: Export) {
|
||||||
val context: Context? = activity
|
val context: Context? = activity
|
||||||
|
@ -1112,22 +1174,6 @@ class ImportExportPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EnumEntryName")
|
|
||||||
private enum class IExport {
|
|
||||||
prefOpmlExport,
|
|
||||||
prefOpmlImport,
|
|
||||||
prefProgressExport,
|
|
||||||
prefProgressImport,
|
|
||||||
prefHtmlExport,
|
|
||||||
prefPrefImport,
|
|
||||||
prefPrefExport,
|
|
||||||
prefMediaFilesImport,
|
|
||||||
prefMediaFilesExport,
|
|
||||||
prefDatabaseImport,
|
|
||||||
prefDatabaseExport,
|
|
||||||
prefFavoritesExport,
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val TAG: String = ImportExportPreferencesFragment::class.simpleName ?: "Anonymous"
|
private val TAG: String = ImportExportPreferencesFragment::class.simpleName ?: "Anonymous"
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,78 @@
|
||||||
package ac.mdiq.podcini.preferences.fragments
|
package ac.mdiq.podcini.preferences.fragments
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.BuildConfig
|
||||||
import ac.mdiq.podcini.R
|
import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.ui.activity.BugReportActivity
|
import ac.mdiq.podcini.ui.activity.BugReportActivity
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity.Companion.getTitleOfPage
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity.Companion.getTitleOfPage
|
||||||
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity.Screens
|
||||||
|
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
|
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffColorFilter
|
import android.graphics.PorterDuffColorFilter
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import com.bytehamster.lib.preferencesearch.SearchPreference
|
import com.bytehamster.lib.preferencesearch.SearchPreference
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
|
||||||
class MainPreferencesFragment : PreferenceFragmentCompat() {
|
class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
@ -79,7 +136,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference<Preference>(Prefs.prefScreenImportExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
findPreference<Preference>(Prefs.prefScreenImportExport.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
(activity as PreferenceActivity).openScreen(R.xml.preferences_import_export)
|
(activity as PreferenceActivity).openScreen(Screens.preferences_import_export)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference<Preference>(Prefs.notifications.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
findPreference<Preference>(Prefs.notifications.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
@ -97,10 +154,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference<Preference>(Prefs.prefAbout.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
findPreference<Preference>(Prefs.prefAbout.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
parentFragmentManager.beginTransaction()
|
parentFragmentManager.beginTransaction().replace(R.id.settingsContainer, AboutFragment()).addToBackStack(getString(R.string.about_pref)).commit()
|
||||||
.replace(R.id.settingsContainer, AboutFragment())
|
|
||||||
.addToBackStack(getString(R.string.about_pref))
|
|
||||||
.commit()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
findPreference<Preference>(Prefs.prefDocumentation.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
findPreference<Preference>(Prefs.prefDocumentation.name)!!.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||||
|
@ -131,7 +185,7 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
config.index(R.xml.preferences_user_interface).addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
|
config.index(R.xml.preferences_user_interface).addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
|
||||||
config.index(R.xml.preferences_playback).addBreadcrumb(getTitleOfPage(R.xml.preferences_playback))
|
config.index(R.xml.preferences_playback).addBreadcrumb(getTitleOfPage(R.xml.preferences_playback))
|
||||||
config.index(R.xml.preferences_downloads).addBreadcrumb(getTitleOfPage(R.xml.preferences_downloads))
|
config.index(R.xml.preferences_downloads).addBreadcrumb(getTitleOfPage(R.xml.preferences_downloads))
|
||||||
config.index(R.xml.preferences_import_export).addBreadcrumb(getTitleOfPage(R.xml.preferences_import_export))
|
// config.index(R.xml.preferences_import_export).addBreadcrumb(getTitleOfPage(R.xml.preferences_import_export))
|
||||||
config.index(R.xml.preferences_autodownload)
|
config.index(R.xml.preferences_autodownload)
|
||||||
.addBreadcrumb(getTitleOfPage(R.xml.preferences_downloads))
|
.addBreadcrumb(getTitleOfPage(R.xml.preferences_downloads))
|
||||||
.addBreadcrumb(R.string.automation)
|
.addBreadcrumb(R.string.automation)
|
||||||
|
@ -139,9 +193,136 @@ class MainPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
config.index(R.xml.preferences_synchronization).addBreadcrumb(getTitleOfPage(R.xml.preferences_synchronization))
|
config.index(R.xml.preferences_synchronization).addBreadcrumb(getTitleOfPage(R.xml.preferences_synchronization))
|
||||||
config.index(R.xml.preferences_notifications).addBreadcrumb(getTitleOfPage(R.xml.preferences_notifications))
|
config.index(R.xml.preferences_notifications).addBreadcrumb(getTitleOfPage(R.xml.preferences_notifications))
|
||||||
// config.index(R.xml.feed_settings).addBreadcrumb(getTitleOfPage(R.xml.feed_settings))
|
// config.index(R.xml.feed_settings).addBreadcrumb(getTitleOfPage(R.xml.feed_settings))
|
||||||
config.index(R.xml.preferences_swipe)
|
// config.index(R.xml.preferences_swipe)
|
||||||
.addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
|
// .addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
|
||||||
.addBreadcrumb(getTitleOfPage(R.xml.preferences_swipe))
|
// .addBreadcrumb(getTitleOfPage(R.xml.preferences_swipe))
|
||||||
|
}
|
||||||
|
|
||||||
|
class AboutFragment : PreferenceFragmentCompat() {
|
||||||
|
@SuppressLint("CommitTransaction")
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
(activity as PreferenceActivity).supportActionBar?.setTitle(R.string.about_pref)
|
||||||
|
return ComposeView(requireContext()).apply {
|
||||||
|
setContent {
|
||||||
|
CustomTheme(requireContext()) {
|
||||||
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||||
|
Image(painter = painterResource(R.drawable.teaser), contentDescription = "")
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, top = 5.dp, bottom = 5.dp)) {
|
||||||
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_star), contentDescription = "", tint = textColor)
|
||||||
|
Column(Modifier.padding(start = 10.dp).clickable(onClick = {
|
||||||
|
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
val clip = ClipData.newPlainText(getString(R.string.bug_report_title), findPreference<Preference>("about_version")!!.summary)
|
||||||
|
clipboard.setPrimaryClip(clip)
|
||||||
|
if (Build.VERSION.SDK_INT <= 32) Snackbar.make(requireView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.podcini_version), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(String.format("%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH), color = textColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, top = 5.dp, bottom = 5.dp)) {
|
||||||
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_questionmark), contentDescription = "", tint = textColor)
|
||||||
|
Column(Modifier.padding(start = 10.dp).clickable(onClick = {
|
||||||
|
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/")
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.online_help), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.online_help_sum), color = textColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, top = 5.dp, bottom = 5.dp)) {
|
||||||
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_info), contentDescription = "", tint = textColor)
|
||||||
|
Column(Modifier.padding(start = 10.dp).clickable(onClick = {
|
||||||
|
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/blob/main/PrivacyPolicy.md")
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.privacy_policy), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text("Podcini PrivacyPolicy", color = textColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 10.dp, top = 5.dp, bottom = 5.dp)) {
|
||||||
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_info), contentDescription = "", tint = textColor)
|
||||||
|
Column(Modifier.padding(start = 10.dp).clickable(onClick = {
|
||||||
|
parentFragmentManager.beginTransaction().replace(R.id.settingsContainer, LicensesFragment()).addToBackStack(getString(R.string.translators)).commit()
|
||||||
|
})) {
|
||||||
|
Text(stringResource(R.string.licenses), color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(stringResource(R.string.licenses_summary), color = textColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LicensesFragment : Fragment() {
|
||||||
|
private val licenses = mutableStateListOf<LicenseItem>()
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
val composeView = ComposeView(requireContext()).apply { setContent { CustomTheme(requireContext()) { MainView() } } }
|
||||||
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
licenses.clear()
|
||||||
|
val stream = requireContext().assets.open("licenses.xml")
|
||||||
|
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
val libraryList = docBuilder.parse(stream).getElementsByTagName("library")
|
||||||
|
for (i in 0 until libraryList.length) {
|
||||||
|
val lib = libraryList.item(i).attributes
|
||||||
|
licenses.add(LicenseItem(lib.getNamedItem("name").textContent,
|
||||||
|
String.format("By %s, %s license", lib.getNamedItem("author").textContent, lib.getNamedItem("license").textContent), lib.getNamedItem("website").textContent, lib.getNamedItem("licenseText").textContent))
|
||||||
|
}
|
||||||
|
}.invokeOnCompletion { throwable -> if (throwable!= null) Toast.makeText(context, throwable.message, Toast.LENGTH_LONG).show() }
|
||||||
|
return composeView
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainView() {
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
var curLicenseIndex by remember { mutableIntStateOf(-1) }
|
||||||
|
if (showDialog) Dialog(onDismissRequest = { showDialog = false }) {
|
||||||
|
Surface(shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
|
Text(licenses[curLicenseIndex].title, color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Row {
|
||||||
|
Button(onClick = { openInBrowser(requireContext(), licenses[curLicenseIndex].licenseUrl) }) { Text("View website") }
|
||||||
|
Spacer(Modifier.weight(1f))
|
||||||
|
Button(onClick = { showLicenseText(licenses[curLicenseIndex].licenseTextFile) }) { Text("View license") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LazyColumn(state = lazyListState, modifier = Modifier.fillMaxWidth().padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 20.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
itemsIndexed(licenses) { index, item ->
|
||||||
|
Column(Modifier.clickable(onClick = {
|
||||||
|
curLicenseIndex = index
|
||||||
|
showDialog = true
|
||||||
|
})) {
|
||||||
|
Text(item.title, color = textColor, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
|
||||||
|
Text(item.subtitle, color = textColor, style = MaterialTheme.typography.bodySmall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLicenseText(licenseTextFile: String) {
|
||||||
|
try {
|
||||||
|
val reader = BufferedReader(InputStreamReader(requireContext().assets.open(licenseTextFile), "UTF-8"))
|
||||||
|
val licenseText = StringBuilder()
|
||||||
|
var line = ""
|
||||||
|
while ((reader.readLine()?.also { line = it }) != null) licenseText.append(line).append("\n")
|
||||||
|
MaterialAlertDialogBuilder(requireContext()).setMessage(licenseText).show()
|
||||||
|
} catch (e: IOException) { e.printStackTrace() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.licenses)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LicenseItem(val title: String, val subtitle: String, val licenseUrl: String, val licenseTextFile: String)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EnumEntryName")
|
@Suppress("EnumEntryName")
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
package ac.mdiq.podcini.preferences.fragments
|
|
||||||
|
|
||||||
import ac.mdiq.podcini.R
|
|
||||||
import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.showSettingDialog
|
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
|
||||||
import ac.mdiq.podcini.ui.fragment.*
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
|
|
||||||
|
|
||||||
class SwipePreferencesFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
addPreferencesFromResource(R.xml.preferences_swipe)
|
|
||||||
|
|
||||||
findPreference<Preference>(Prefs.prefSwipeQueue.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
// SwipeActionsDialog(requireContext(), QueuesFragment.TAG).show(object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {}
|
|
||||||
// })
|
|
||||||
showSettingDialog(this, QueuesFragment.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(Prefs.prefSwipeEpisodes.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
// SwipeActionsDialog(requireContext(), AllEpisodesFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {}
|
|
||||||
// })
|
|
||||||
showSettingDialog(this, AllEpisodesFragment.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(Prefs.prefSwipeDownloads.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
// SwipeActionsDialog(requireContext(), DownloadsFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {}
|
|
||||||
// })
|
|
||||||
showSettingDialog(this, DownloadsFragment.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(Prefs.prefSwipeFeed.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
// SwipeActionsDialog(requireContext(), FeedEpisodesFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {}
|
|
||||||
// })
|
|
||||||
showSettingDialog(this, FeedEpisodesFragment.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
findPreference<Preference>(Prefs.prefSwipeHistory.name)?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
// SwipeActionsDialog(requireContext(), HistoryFragment.TAG).show (object : SwipeActionsDialog.Callback {
|
|
||||||
// override fun onCall() {}
|
|
||||||
// })
|
|
||||||
showSettingDialog(this, HistoryFragment.TAG)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
(activity as PreferenceActivity).supportActionBar?.setTitle(R.string.swipeactions_label)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EnumEntryName")
|
|
||||||
private enum class Prefs {
|
|
||||||
prefSwipeQueue,
|
|
||||||
// prefSwipeStatistics,
|
|
||||||
prefSwipeEpisodes,
|
|
||||||
prefSwipeDownloads,
|
|
||||||
prefSwipeFeed,
|
|
||||||
prefSwipeHistory
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,6 +7,9 @@ import ac.mdiq.podcini.preferences.UserPreferences.fullNotificationButtons
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting
|
import ac.mdiq.podcini.preferences.UserPreferences.setShowRemainTimeSetting
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity.Companion.getTitleOfPage
|
||||||
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity.Screens
|
||||||
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity.SwipePreferencesFragment
|
||||||
import ac.mdiq.podcini.ui.fragment.NavDrawerFragment.Companion.navMap
|
import ac.mdiq.podcini.ui.fragment.NavDrawerFragment.Companion.navMap
|
||||||
import ac.mdiq.podcini.util.EventFlow
|
import ac.mdiq.podcini.util.EventFlow
|
||||||
import ac.mdiq.podcini.util.FlowEvent
|
import ac.mdiq.podcini.util.FlowEvent
|
||||||
|
@ -91,19 +94,8 @@ class UserInterfacePreferencesFragment : PreferenceFragmentCompat() {
|
||||||
showFullNotificationButtonsDialog()
|
showFullNotificationButtonsDialog()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
// findPreference<Preference>(UserPreferences.PREF_FILTER_FEED)?.onPreferenceClickListener =
|
|
||||||
// (Preference.OnPreferenceClickListener {
|
|
||||||
// SubscriptionsFilterDialog().show(childFragmentManager, "filter")
|
|
||||||
// true
|
|
||||||
// })
|
|
||||||
|
|
||||||
// findPreference<Preference>(UserPreferences.Prefs.prefDrawerFeedOrder.name)?.onPreferenceClickListener = (Preference.OnPreferenceClickListener {
|
|
||||||
//// FeedSortDialog.showDialog(requireContext())
|
|
||||||
// FeedSortDialog().show(childFragmentManager, "FeedSortDialog")
|
|
||||||
// true
|
|
||||||
// })
|
|
||||||
findPreference<Preference>(PREF_SWIPE)?.setOnPreferenceClickListener {
|
findPreference<Preference>(PREF_SWIPE)?.setOnPreferenceClickListener {
|
||||||
(activity as PreferenceActivity).openScreen(R.xml.preferences_swipe)
|
(activity as PreferenceActivity).openScreen(Screens.preferences_swipe)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= 26) findPreference<Preference>(UserPreferences.Prefs.prefExpandNotify.name)!!.isVisible = false
|
if (Build.VERSION.SDK_INT >= 26) findPreference<Preference>(UserPreferences.Prefs.prefExpandNotify.name)!!.isVisible = false
|
||||||
|
|
|
@ -632,21 +632,6 @@ class SwipeActions(private val fragment: Fragment, private val tag: String) : De
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSettingDialog(fragment: Fragment, tag: String) {
|
|
||||||
val composeView = ComposeView(fragment.requireContext()).apply {
|
|
||||||
setContent {
|
|
||||||
val showDialog = remember { mutableStateOf(true) }
|
|
||||||
CustomTheme(fragment.requireContext()) {
|
|
||||||
SwipeActionsDialog(tag, onDismissRequest = {
|
|
||||||
showDialog.value = false
|
|
||||||
(fragment.view as? ViewGroup)?.removeView(this@apply)
|
|
||||||
}) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(fragment.view as? ViewGroup)?.addView(composeView)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SwipeActionsDialog(tag: String, onDismissRequest: () -> Unit, callback: ()->Unit) {
|
fun SwipeActionsDialog(tag: String, onDismissRequest: () -> Unit, callback: ()->Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
|
@ -5,6 +5,13 @@ import ac.mdiq.podcini.databinding.SettingsActivityBinding
|
||||||
import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
|
import ac.mdiq.podcini.preferences.ThemeSwitcher.getTheme
|
||||||
import ac.mdiq.podcini.preferences.fragments.*
|
import ac.mdiq.podcini.preferences.fragments.*
|
||||||
import ac.mdiq.podcini.preferences.fragments.SynchronizationPreferencesFragment
|
import ac.mdiq.podcini.preferences.fragments.SynchronizationPreferencesFragment
|
||||||
|
import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.SwipeActionsDialog
|
||||||
|
import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
|
import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.DownloadsFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.HistoryFragment
|
||||||
|
import ac.mdiq.podcini.ui.fragment.QueuesFragment
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import ac.mdiq.podcini.util.EventFlow
|
import ac.mdiq.podcini.util.EventFlow
|
||||||
import ac.mdiq.podcini.util.FlowEvent
|
import ac.mdiq.podcini.util.FlowEvent
|
||||||
|
@ -13,10 +20,24 @@ import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.ComposeView
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult
|
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult
|
||||||
|
@ -53,33 +74,42 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
||||||
if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) openScreen(R.xml.preferences_autodownload)
|
if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) openScreen(R.xml.preferences_autodownload)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPreferenceScreen(screen: Int): PreferenceFragmentCompat? {
|
|
||||||
var prefFragment: PreferenceFragmentCompat? = null
|
|
||||||
|
|
||||||
when (screen) {
|
|
||||||
R.xml.preferences_user_interface -> prefFragment = UserInterfacePreferencesFragment()
|
|
||||||
R.xml.preferences_downloads -> prefFragment = DownloadsPreferencesFragment()
|
|
||||||
R.xml.preferences_import_export -> prefFragment = ImportExportPreferencesFragment()
|
|
||||||
R.xml.preferences_autodownload -> prefFragment = AutoDownloadPreferencesFragment()
|
|
||||||
R.xml.preferences_synchronization -> prefFragment = SynchronizationPreferencesFragment()
|
|
||||||
R.xml.preferences_playback -> prefFragment = PlaybackPreferencesFragment()
|
|
||||||
R.xml.preferences_notifications -> prefFragment = NotificationPreferencesFragment()
|
|
||||||
R.xml.preferences_swipe -> prefFragment = SwipePreferencesFragment()
|
|
||||||
}
|
|
||||||
return prefFragment
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("CommitTransaction")
|
@SuppressLint("CommitTransaction")
|
||||||
fun openScreen(screen: Int): PreferenceFragmentCompat? {
|
fun openScreen(screen: Int): PreferenceFragmentCompat {
|
||||||
val fragment = getPreferenceScreen(screen)
|
val fragment = when (screen) {
|
||||||
|
R.xml.preferences_user_interface -> UserInterfacePreferencesFragment()
|
||||||
|
R.xml.preferences_downloads -> DownloadsPreferencesFragment()
|
||||||
|
// R.xml.preferences_import_export -> ImportExportPreferencesFragment()
|
||||||
|
R.xml.preferences_autodownload -> AutoDownloadPreferencesFragment()
|
||||||
|
R.xml.preferences_synchronization -> SynchronizationPreferencesFragment()
|
||||||
|
R.xml.preferences_playback -> PlaybackPreferencesFragment()
|
||||||
|
R.xml.preferences_notifications -> NotificationPreferencesFragment()
|
||||||
|
// R.xml.preferences_swipe -> SwipePreferencesFragment()
|
||||||
|
else -> UserInterfacePreferencesFragment()
|
||||||
|
}
|
||||||
|
|
||||||
if (screen == R.xml.preferences_notifications && Build.VERSION.SDK_INT >= 26) {
|
if (screen == R.xml.preferences_notifications && Build.VERSION.SDK_INT >= 26) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
} else
|
} else
|
||||||
supportFragmentManager.beginTransaction().replace(binding.settingsContainer.id, fragment!!).addToBackStack(getString(getTitleOfPage(screen))).commit()
|
supportFragmentManager.beginTransaction().replace(binding.settingsContainer.id, fragment).addToBackStack(getString(getTitleOfPage(screen))).commit()
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openScreen(screen: Screens): PreferenceFragmentCompat {
|
||||||
|
val fragment = when (screen) {
|
||||||
|
Screens.preferences_user_interface -> UserInterfacePreferencesFragment()
|
||||||
|
Screens.preferences_downloads -> DownloadsPreferencesFragment()
|
||||||
|
Screens.preferences_import_export -> ImportExportPreferencesFragment()
|
||||||
|
Screens.preferences_autodownload -> AutoDownloadPreferencesFragment()
|
||||||
|
Screens.preferences_synchronization -> SynchronizationPreferencesFragment()
|
||||||
|
Screens.preferences_playback -> PlaybackPreferencesFragment()
|
||||||
|
Screens.preferences_notifications -> NotificationPreferencesFragment()
|
||||||
|
Screens.preferences_swipe -> SwipePreferencesFragment()
|
||||||
|
}
|
||||||
|
supportFragmentManager.beginTransaction().replace(binding.settingsContainer.id, fragment).addToBackStack(getString(screen.titleRes)).commit()
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +179,49 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
||||||
s.show()
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SwipePreferencesFragment : PreferenceFragmentCompat() {
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {}
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
|
(activity as PreferenceActivity).supportActionBar?.setTitle(R.string.swipeactions_label)
|
||||||
|
return ComposeView(requireContext()).apply {
|
||||||
|
setContent {
|
||||||
|
CustomTheme(requireContext()) {
|
||||||
|
val textColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
|
||||||
|
for (e in Prefs.entries) {
|
||||||
|
val showDialog = remember { mutableStateOf(false) }
|
||||||
|
if (showDialog.value) SwipeActionsDialog(e.tag, onDismissRequest = { showDialog.value = false }) {}
|
||||||
|
Text(stringResource(e.res), color = textColor, style = MaterialTheme.typography.headlineMedium, modifier = Modifier.padding(bottom = 10.dp).clickable(onClick = {
|
||||||
|
showDialog.value = true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Suppress("EnumEntryName")
|
||||||
|
private enum class Prefs(val res: Int, val tag: String) {
|
||||||
|
prefSwipeQueue(R.string.queue_label, QueuesFragment.TAG),
|
||||||
|
prefSwipeEpisodes(R.string.episodes_label, AllEpisodesFragment.TAG),
|
||||||
|
prefSwipeDownloads(R.string.downloads_label, DownloadsFragment.TAG),
|
||||||
|
prefSwipeFeed(R.string.individual_subscription, FeedEpisodesFragment.TAG),
|
||||||
|
prefSwipeHistory(R.string.playback_history_label, HistoryFragment.TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("EnumEntryName")
|
||||||
|
enum class Screens(val titleRes: Int) {
|
||||||
|
preferences_swipe(R.string.swipeactions_label),
|
||||||
|
preferences_downloads(R.string.downloads_pref),
|
||||||
|
preferences_autodownload(R.string.pref_automatic_download_title),
|
||||||
|
preferences_playback(R.string.playback_pref),
|
||||||
|
preferences_import_export(R.string.import_export_pref),
|
||||||
|
preferences_user_interface(R.string.user_interface_label),
|
||||||
|
preferences_synchronization(R.string.synchronization_pref),
|
||||||
|
preferences_notifications(R.string.notification_pref_fragment);
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val FRAGMENT_TAG = "tag_preferences"
|
private const val FRAGMENT_TAG = "tag_preferences"
|
||||||
const val OPEN_AUTO_DOWNLOAD_SETTINGS: String = "OpenAutoDownloadSettings"
|
const val OPEN_AUTO_DOWNLOAD_SETTINGS: String = "OpenAutoDownloadSettings"
|
||||||
|
@ -158,11 +231,11 @@ class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener {
|
||||||
R.xml.preferences_downloads -> R.string.downloads_pref
|
R.xml.preferences_downloads -> R.string.downloads_pref
|
||||||
R.xml.preferences_autodownload -> R.string.pref_automatic_download_title
|
R.xml.preferences_autodownload -> R.string.pref_automatic_download_title
|
||||||
R.xml.preferences_playback -> R.string.playback_pref
|
R.xml.preferences_playback -> R.string.playback_pref
|
||||||
R.xml.preferences_import_export -> R.string.import_export_pref
|
// R.xml.preferences_import_export -> R.string.import_export_pref
|
||||||
R.xml.preferences_user_interface -> R.string.user_interface_label
|
R.xml.preferences_user_interface -> R.string.user_interface_label
|
||||||
R.xml.preferences_synchronization -> R.string.synchronization_pref
|
R.xml.preferences_synchronization -> R.string.synchronization_pref
|
||||||
R.xml.preferences_notifications -> R.string.notification_pref_fragment
|
R.xml.preferences_notifications -> R.string.notification_pref_fragment
|
||||||
R.xml.preferences_swipe -> R.string.swipeactions_label
|
// R.xml.preferences_swipe -> R.string.swipeactions_label
|
||||||
else -> R.string.settings_label
|
else -> R.string.settings_label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -661,9 +661,9 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
||||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(16.dp).height(16.dp))
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(16.dp).height(16.dp))
|
||||||
val curContext = LocalContext.current
|
val curContext = LocalContext.current
|
||||||
val dateSizeText = " · " + formatDateTimeFlex(vm.episode.getPubDate()) +
|
val dateSizeText = " · " + formatDateTimeFlex(vm.episode.getPubDate()) +
|
||||||
if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else "" +
|
" · " + getDurationStringLong(vm.durationState) +
|
||||||
" · " + getDurationStringLong(vm.durationState) + " · " +
|
(if ((vm.episode.media?.size ?: 0) > 0) " · " + Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else "") +
|
||||||
if ((vm.episode.media?.size ?: 0) > 0) Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else ""
|
(if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else "")
|
||||||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -679,11 +679,11 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: MutableList<EpisodeVM>, feed:
|
||||||
if (vm.episode.media?.getMediaType() == MediaType.VIDEO)
|
if (vm.episode.media?.getMediaType() == MediaType.VIDEO)
|
||||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(16.dp).height(16.dp))
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_videocam), tint = textColor, contentDescription = "isVideo", modifier = Modifier.width(16.dp).height(16.dp))
|
||||||
val dateSizeText = " · " + getDurationStringLong(vm.durationState) +
|
val dateSizeText = " · " + getDurationStringLong(vm.durationState) +
|
||||||
if ((vm.episode.media?.size ?: 0) > 0) " · " + Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else ""
|
(if ((vm.episode.media?.size ?: 0) > 0) " · " + Formatter.formatShortFileSize(curContext, vm.episode.media?.size ?: 0) else "")
|
||||||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
}
|
}
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
val dateSizeText = formatDateTimeFlex(vm.episode.getPubDate()) + if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else ""
|
val dateSizeText = formatDateTimeFlex(vm.episode.getPubDate()) + (if (vm.viewCount > 0) " · " + formatNumber(vm.viewCount) else "")
|
||||||
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
Text(dateSizeText, color = textColor, style = MaterialTheme.typography.bodySmall, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||||
if (vm.viewCount > 0)
|
if (vm.viewCount > 0)
|
||||||
Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_people_alt_24), tint = textColor, contentDescription = "people", modifier = Modifier.width(16.dp).height(16.dp))
|
Icon(imageVector = ImageVector.vectorResource(R.drawable.baseline_people_alt_24), tint = textColor, contentDescription = "people", modifier = Modifier.width(16.dp).height(16.dp))
|
||||||
|
|
|
@ -244,7 +244,7 @@ fun RenameOrCreateSyntheticFeed(feed_: Feed? = null, onDismissRequest: () -> Uni
|
||||||
Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
|
Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
|
||||||
Text(stringResource(R.string.rename_feed_label), color = textColor, style = MaterialTheme.typography.bodyLarge)
|
Text(stringResource(R.string.rename_feed_label), color = textColor, style = MaterialTheme.typography.bodyLarge)
|
||||||
var name by remember { mutableStateOf("") }
|
var name by remember { mutableStateOf("") }
|
||||||
TextField(value = name, onValueChange = { if (it.isEmpty() || it.toIntOrNull() != null) name = it }, label = { Text(stringResource(R.string.new_namee)) })
|
TextField(value = name, onValueChange = { name = it }, label = { Text(stringResource(R.string.new_namee)) })
|
||||||
var hasVideo by remember { mutableStateOf(true) }
|
var hasVideo by remember { mutableStateOf(true) }
|
||||||
var isYoutube by remember { mutableStateOf(false) }
|
var isYoutube by remember { mutableStateOf(false) }
|
||||||
if (feed_ == null) {
|
if (feed_ == null) {
|
||||||
|
@ -259,6 +259,7 @@ fun RenameOrCreateSyntheticFeed(feed_: Feed? = null, onDismissRequest: () -> Uni
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
Button({ onDismissRequest() }) { Text(stringResource(R.string.cancel_label)) }
|
Button({ onDismissRequest() }) { Text(stringResource(R.string.cancel_label)) }
|
||||||
|
Spacer(Modifier.weight(1f))
|
||||||
Button({
|
Button({
|
||||||
val feed = feed_ ?: createSynthetic(0, name, hasVideo)
|
val feed = feed_ ?: createSynthetic(0, name, hasVideo)
|
||||||
if (feed_ == null) {
|
if (feed_ == null) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import ac.mdiq.podcini.ui.compose.CustomTheme
|
||||||
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.ARGUMENT_FEED_ID
|
import ac.mdiq.podcini.ui.fragment.FeedEpisodesFragment.Companion.ARGUMENT_FEED_ID
|
||||||
import ac.mdiq.podcini.ui.fragment.HistoryFragment.Companion.getNumberOfPlayed
|
import ac.mdiq.podcini.ui.fragment.HistoryFragment.Companion.getNumberOfPlayed
|
||||||
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
import ac.mdiq.podcini.ui.utils.ThemeUtils
|
||||||
|
import ac.mdiq.podcini.util.IntentUtils.openInBrowser
|
||||||
import ac.mdiq.podcini.util.Logd
|
import ac.mdiq.podcini.util.Logd
|
||||||
import android.R.attr
|
import android.R.attr
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
@ -121,7 +122,7 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
||||||
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
Column {
|
Column {
|
||||||
for (f in feeds) {
|
for (f in feeds) {
|
||||||
Row(verticalAlignment = Alignment.Top, modifier = Modifier.padding(bottom = 5.dp).clickable {
|
Row(verticalAlignment = Alignment.Top, modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp).clickable {
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putLong(ARGUMENT_FEED_ID, f.id)
|
args.putLong(ARGUMENT_FEED_ID, f.id)
|
||||||
(activity as MainActivity).loadFragment(FeedEpisodesFragment.TAG, args)
|
(activity as MainActivity).loadFragment(FeedEpisodesFragment.TAG, args)
|
||||||
|
@ -134,6 +135,10 @@ class NavDrawerFragment : Fragment(), OnSharedPreferenceChangeListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.weight(1f))
|
Spacer(Modifier.weight(1f))
|
||||||
|
Text("Currently launching on Google Play, please kindly support with closed testing. Thank you!", color = MaterialTheme.colorScheme.tertiary,
|
||||||
|
modifier = Modifier.clickable(onClick = {
|
||||||
|
openInBrowser(requireContext(), "https://github.com/XilinJia/Podcini/discussions/120")
|
||||||
|
}))
|
||||||
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
HorizontalDivider(modifier = Modifier.fillMaxWidth().height(1.dp))
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().clickable {
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().clickable {
|
||||||
startActivity(Intent(activity, PreferenceActivity::class.java))
|
startActivity(Intent(activity, PreferenceActivity::class.java))
|
||||||
|
|
|
@ -18,9 +18,7 @@ object IntentUtils {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun isCallable(context: Context, intent: Intent?): Boolean {
|
fun isCallable(context: Context, intent: Intent?): Boolean {
|
||||||
val list = context.packageManager.queryIntentActivities(intent!!, PackageManager.MATCH_DEFAULT_ONLY)
|
val list = context.packageManager.queryIntentActivities(intent!!, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
for (info in list) {
|
for (info in list) if (info.activityInfo.exported) return true
|
||||||
if (info.activityInfo.exported) return true
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ImageView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:srcCompat="@drawable/teaser"
|
|
||||||
android:importantForAccessibility="no"/>
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PreferenceScreen
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:layout="@layout/about_teaser"/>
|
|
||||||
<Preference
|
|
||||||
android:key="about_version"
|
|
||||||
android:title="@string/podcini_version"
|
|
||||||
android:icon="@drawable/ic_star"
|
|
||||||
android:summary="1.7.2 (asd8qs)"/>
|
|
||||||
<Preference
|
|
||||||
android:key="about_help"
|
|
||||||
android:icon="@drawable/ic_questionmark"
|
|
||||||
android:summary="@string/online_help_sum"
|
|
||||||
android:title="@string/online_help"/>
|
|
||||||
<Preference
|
|
||||||
android:key="about_privacy_policy"
|
|
||||||
android:icon="@drawable/ic_questionmark"
|
|
||||||
android:summary="Podcini PrivacyPolicy.md"
|
|
||||||
android:title="@string/privacy_policy"/>
|
|
||||||
<Preference
|
|
||||||
android:key="about_licenses"
|
|
||||||
android:icon="@drawable/ic_info"
|
|
||||||
android:summary="@string/licenses_summary"
|
|
||||||
android:title="@string/licenses"/>
|
|
||||||
<!-- <Preference-->
|
|
||||||
<!-- android:key="about_contributors"-->
|
|
||||||
<!-- android:icon="@drawable/ic_settings"-->
|
|
||||||
<!-- android:summary="@string/contributors_summary"-->
|
|
||||||
<!-- android:title="@string/contributors"/>-->
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
|
|
@ -1,77 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PreferenceScreen
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch">
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/database">
|
|
||||||
<Preference
|
|
||||||
android:key="prefDatabaseExport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/database_export_label"
|
|
||||||
android:summary="@string/database_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefDatabaseImport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/database_import_label"
|
|
||||||
android:summary="@string/database_import_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/media_files">
|
|
||||||
<Preference
|
|
||||||
android:key="prefMediaFilesExport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/media_files_export_label"
|
|
||||||
android:summary="@string/media_files_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefMediaFilesImport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/media_files_import_label"
|
|
||||||
android:summary="@string/media_files_import_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/preferences">
|
|
||||||
<Preference
|
|
||||||
android:key="prefPrefExport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/preferences_export_label"
|
|
||||||
android:summary="@string/preferences_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefPrefImport"
|
|
||||||
search:keywords="@string/import_export_search_keywords"
|
|
||||||
android:title="@string/preferences_import_label"
|
|
||||||
android:summary="@string/preferences_import_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/opml">
|
|
||||||
<Preference
|
|
||||||
android:key="prefOpmlExport"
|
|
||||||
android:title="@string/opml_export_label"
|
|
||||||
android:summary="@string/opml_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefOpmlImport"
|
|
||||||
android:title="@string/opml_import_label"
|
|
||||||
android:summary="@string/opml_import_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/progress">
|
|
||||||
<Preference
|
|
||||||
android:key="prefProgressExport"
|
|
||||||
android:title="@string/progress_export_label"
|
|
||||||
android:summary="@string/progress_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefProgressImport"
|
|
||||||
android:title="@string/progress_import_label"
|
|
||||||
android:summary="@string/progress_import_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/html">
|
|
||||||
<Preference
|
|
||||||
android:key="prefHtmlExport"
|
|
||||||
android:title="@string/html_export_label"
|
|
||||||
android:summary="@string/html_export_summary"/>
|
|
||||||
<Preference
|
|
||||||
android:key="prefFavoritesExport"
|
|
||||||
android:title="@string/favorites_export_label"
|
|
||||||
android:summary="@string/favorites_export_summary"/>
|
|
||||||
</PreferenceCategory>
|
|
||||||
</PreferenceScreen>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="prefSwipeQueue"
|
|
||||||
android:title="@string/queue_label"/>
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="prefSwipeEpisodes"
|
|
||||||
android:title="@string/episodes_label"/>
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="prefSwipeDownloads"
|
|
||||||
android:title="@string/downloads_label"/>
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="prefSwipeHistory"
|
|
||||||
android:title="@string/playback_history_label"/>
|
|
||||||
|
|
||||||
<!-- <Preference-->
|
|
||||||
<!-- android:key="prefSwipeStatistics"-->
|
|
||||||
<!-- android:title="@string/statistics_label"/>-->
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:key="prefSwipeFeed"
|
|
||||||
android:title="@string/individual_subscription"/>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
# 6.14.6
|
||||||
|
|
||||||
|
* fixed issue of unable to input in rename feed
|
||||||
|
* fixed issue of marking played after playing even when the episode has been marked as Again or Forever
|
||||||
|
* fixed duration not shown in narrow episode lists
|
||||||
|
* some preferences fragments are in Compose
|
||||||
|
* temp message on NavDrawer for closed testing
|
||||||
|
|
||||||
# 6.14.5
|
# 6.14.5
|
||||||
|
|
||||||
* minor adjustments in episode lists layout
|
* minor adjustments in episode lists layout
|
||||||
|
|
Loading…
Reference in New Issue