From ac6d2bd1056b4bd1943523c1ff6fcdd3326a65ad Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Wed, 9 Oct 2024 19:52:40 +0100 Subject: [PATCH] 6.9.3 commit --- app/build.gradle | 4 +- .../podcini/ui/actions/EpisodeMenuHandler.kt | 8 +- .../ui/actions/EpisodeMultiSelectHandler.kt | 166 ------ .../mdiq/podcini/ui/activity/MainActivity.kt | 2 +- .../ui/activity/ShareReceiverActivity.kt | 79 ++- .../ui/activity/VideoplayerActivity.kt | 36 +- .../ac/mdiq/podcini/ui/compose/Composables.kt | 31 +- .../ac/mdiq/podcini/ui/compose/EpisodesVM.kt | 104 +++- .../ui/fragment/AudioPlayerFragment.kt | 34 +- .../ui/fragment/EpisodeInfoFragment.kt | 42 +- .../podcini/ui/fragment/FeedInfoFragment.kt | 15 +- .../ui/fragment/FeedSettingsFragment.kt | 491 ++++-------------- .../podcini/ui/fragment/SharedLogFragment.kt | 38 +- .../ui/fragment/SubscriptionsFragment.kt | 81 +-- .../main/res/layout/audioplayer_fragment.xml | 4 +- app/src/main/res/layout/feedinfo.xml | 8 +- app/src/main/res/menu/episode_info.xml | 20 +- .../main/res/menu/feeditemlist_context.xml | 16 +- app/src/main/res/menu/mediaplayer.xml | 24 +- changelog.md | 8 + .../android/en-US/changelogs/3020268.txt | 7 + 21 files changed, 402 insertions(+), 816 deletions(-) delete mode 100644 app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectHandler.kt create mode 100644 fastlane/metadata/android/en-US/changelogs/3020268.txt diff --git a/app/build.gradle b/app/build.gradle index 52da72cd..bc109206 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,8 +31,8 @@ android { testApplicationId "ac.mdiq.podcini.tests" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 3020267 - versionName "6.9.2" + versionCode 3020268 + versionName "6.9.3" applicationId "ac.mdiq.podcini.R" def commit = "" diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMenuHandler.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMenuHandler.kt index f4e02960..b781df0d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMenuHandler.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMenuHandler.kt @@ -75,8 +75,8 @@ object EpisodeMenuHandler { setItemTitle(menu, R.id.mark_unread_item, R.string.mark_unread_label_no_media) } - setItemVisibility(menu, R.id.add_to_favorites_item, !isFavorite) - setItemVisibility(menu, R.id.remove_from_favorites_item, isFavorite) +// setItemVisibility(menu, R.id.add_to_favorites_item, !isFavorite) +// setItemVisibility(menu, R.id.remove_from_favorites_item, isFavorite) CoroutineScope(Dispatchers.Main).launch { val fileDownloaded = withContext(Dispatchers.IO) { hasMedia && selectedItem.media?.fileExists() ?: false } @@ -167,8 +167,8 @@ object EpisodeMenuHandler { } R.id.add_to_queue_item -> addToQueue(true, selectedItem) R.id.remove_from_queue_item -> removeFromQueue(selectedItem) - R.id.add_to_favorites_item -> setFavorite(selectedItem, true) - R.id.remove_from_favorites_item -> setFavorite(selectedItem, false) +// R.id.add_to_favorites_item -> setFavorite(selectedItem, true) +// R.id.remove_from_favorites_item -> setFavorite(selectedItem, false) R.id.reset_position -> { selectedItem.media?.setPosition(0) if (curState.curMediaId == (selectedItem.media?.id ?: "")) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectHandler.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectHandler.kt deleted file mode 100644 index f2ae8aa1..00000000 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/actions/EpisodeMultiSelectHandler.kt +++ /dev/null @@ -1,166 +0,0 @@ -package ac.mdiq.podcini.ui.actions - -import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.SelectQueueDialogBinding -import ac.mdiq.podcini.net.download.service.DownloadServiceInterface -import ac.mdiq.podcini.playback.base.InTheatre.curQueue -import ac.mdiq.podcini.storage.database.Episodes -import ac.mdiq.podcini.storage.database.Episodes.setPlayState -import ac.mdiq.podcini.storage.database.Queues -import ac.mdiq.podcini.storage.database.Queues.addToQueueSync -import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet -import ac.mdiq.podcini.storage.database.Queues.removeFromQueue -import ac.mdiq.podcini.storage.database.RealmDB.realm -import ac.mdiq.podcini.storage.model.Episode -import ac.mdiq.podcini.storage.model.PlayQueue -import ac.mdiq.podcini.ui.activity.MainActivity -import ac.mdiq.podcini.ui.utils.LocalDeleteModal -import ac.mdiq.podcini.util.EventFlow -import ac.mdiq.podcini.util.FlowEvent -import android.app.Activity -import android.content.DialogInterface -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.widget.RadioButton -import androidx.annotation.PluralsRes -import androidx.media3.common.util.UnstableApi -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.runBlocking -import java.lang.ref.WeakReference - - -@UnstableApi -class EpisodeMultiSelectHandler(private val activity: MainActivity, private val actionId: Int) { - private var totalNumItems = 0 - private var snackbar: Snackbar? = null - - fun handleAction(items: List) { - when (actionId) { - R.id.toggle_favorite_batch -> toggleFavorite(items) - R.id.add_to_queue_batch -> queueChecked(items) - R.id.put_in_queue_batch -> PutToQueueDialog(activity, items).show() - R.id.remove_from_queue_batch -> removeFromQueueChecked(items) - R.id.toggle_played_batch -> { - setPlayState(Episode.PlayState.UNSPECIFIED.code, false, *items.toTypedArray()) -// showMessage(R.plurals.marked_read_batch_label, items.size) - } - R.id.download_batch -> downloadChecked(items) - R.id.delete_batch -> deleteChecked(items) - else -> Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=$actionId") - } - } - - private fun queueChecked(items: List) { - // Check if an episode actually contains any media files before adding it to queue -// val toQueue = mutableListOf() -// for (episode in items) { -// if (episode.media != null) toQueue.add(episode.id) -// } - Queues.addToQueue(true, *items.toTypedArray()) - showMessage(R.plurals.added_to_queue_batch_label, items.size) - } - - private fun removeFromQueueChecked(items: List) { -// val checkedIds = getSelectedIds(items) - removeFromQueue(*items.toTypedArray()) - showMessage(R.plurals.removed_from_queue_batch_label, items.size) - } - - private fun toggleFavorite(items: List) { - for (item in items) { - Episodes.setFavorite(item, null) - } - showMessage(R.plurals.marked_favorite_batch_label, items.size) - } - - private fun downloadChecked(items: List) { - // download the check episodes in the same order as they are currently displayed - for (episode in items) { - if (episode.media != null && episode.feed != null && !episode.feed!!.isLocalFeed) DownloadServiceInterface.get()?.download(activity, episode) - } - showMessage(R.plurals.downloading_batch_label, items.size) - } - - private fun deleteChecked(items: List) { - LocalDeleteModal.deleteEpisodesWarnLocal(activity, items) - showMessage(R.plurals.deleted_multi_episode_batch_label, items.size) - } - - private fun showMessage(@PluralsRes msgId: Int, numItems: Int) { - totalNumItems += numItems - activity.runOnUiThread { - val text: String = activity.resources.getQuantityString(msgId, totalNumItems, totalNumItems) - if (snackbar != null) { - snackbar?.setText(text) - snackbar?.show() // Resets the timeout - } else snackbar = activity.showSnackbarAbovePlayer(text, Snackbar.LENGTH_LONG) - } - } - -// private fun getSelectedIds(items: List): List { -// val checkedIds = mutableListOf() -// for (i in items.indices) { -// checkedIds.add(items[i].id) -// } -// return checkedIds -// } - - class PutToQueueDialog(activity: Activity, val items: List) { - private val activityRef: WeakReference = WeakReference(activity) - - fun show() { - val activity = activityRef.get() ?: return - val binding = SelectQueueDialogBinding.inflate(LayoutInflater.from(activity)) - binding.removeCheckbox.visibility = View.VISIBLE - - val queues = realm.query(PlayQueue::class).find() - for (i in queues.indices) { - val radioButton = RadioButton(activity) - radioButton.text = queues[i].name - radioButton.textSize = 20f - radioButton.tag = i - binding.radioGroup.addView(radioButton) - } - var toQueue: PlayQueue = curQueue - binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> - val radioButton = group.findViewById(checkedId) - val selectedIndex = radioButton.tag as Int - toQueue = queues[selectedIndex] - } - val dialog = MaterialAlertDialogBuilder(activity) - .setView(binding.root) - .setTitle(R.string.put_in_queue_label) - .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> -// val queues = realm.query(PlayQueue::class).find() - if (binding.removeCheckbox.isChecked) { - val toRemove = mutableSetOf() - val toRemoveCur = mutableListOf() - items.forEach { e -> - if (curQueue.contains(e)) toRemoveCur.add(e) - } - items.forEach { e -> - for (q in queues) { - if (q.contains(e)) { - toRemove.add(e.id) - break - } - } - } - if (toRemove.isNotEmpty()) runBlocking { removeFromAllQueuesQuiet(toRemove.toList()) } - if (toRemoveCur.isNotEmpty()) EventFlow.postEvent(FlowEvent.QueueEvent.removed(toRemoveCur)) - } - items.forEach { e -> - runBlocking { addToQueueSync(false, e, toQueue) } - } - } - .setNegativeButton(R.string.cancel_label, null) - dialog.show() - } - } - - companion object { - private val TAG: String = EpisodeMultiSelectHandler::class.simpleName ?: "Anonymous" - } -} diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt index 9928f2f3..9f290111 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/MainActivity.kt @@ -399,7 +399,7 @@ class MainActivity : CastEnabledActivity() { navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0)) mainView.layoutParams = params // val playerView = findViewById(R.id.playerFragment1) - val playerView = findViewById(R.id.composeView1) + val playerView = findViewById(R.id.player1) val playerParams = playerView?.layoutParams as? MarginLayoutParams playerParams?.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, 0) playerView?.layoutParams = playerParams diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt index 795924fb..2a1a123e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/ShareReceiverActivity.kt @@ -9,6 +9,8 @@ import ac.mdiq.podcini.ui.compose.confirmAddYoutubeEpisode import ac.mdiq.podcini.util.Logd import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeServiceURL import ac.mdiq.vista.extractor.services.youtube.YoutubeParsingHelper.isYoutubeURL +import android.app.Service.START_STICKY +import android.content.Context import android.content.DialogInterface import android.content.Intent import android.net.Uri @@ -20,6 +22,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.media3.common.util.UnstableApi +import androidx.work.WorkerParameters import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.net.URL import java.net.URLDecoder @@ -49,42 +52,18 @@ class ShareReceiverActivity : AppCompatActivity() { Logd(TAG, "feedUrl: $sharedUrl") val log = ShareLog(sharedUrl!!) upsertBlk(log) {} - receiveShared(sharedUrl!!,this, true) -// val url = URL(sharedUrl) -// when { -//// plain text -// sharedUrl!!.matches(Regex("^[^\\s<>/]+\$")) -> { -// log = upsertBlk(log) {it.type = "text" } -// val intent = MainActivity.showOnlineSearch(this, sharedUrl!!) -// startActivity(intent) -// finish() -// } -//// Youtube media -//// sharedUrl!!.startsWith("https://youtube.com/watch?") || sharedUrl!!.startsWith("https://www.youtube.com/watch?") || sharedUrl!!.startsWith("https://music.youtube.com/watch?") -> { -// (isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url) -> { -// log = upsertBlk(log) {it.type = "youtube media" } -// Logd(TAG, "got youtube media") -// setContent { -// val showDialog = remember { mutableStateOf(true) } -// CustomTheme(this@ShareReceiverActivity) { -// confirmAddYoutubeEpisode(listOf(sharedUrl!!), showDialog.value, onDismissRequest = { -// showDialog.value = false -// finish() -// }) -// } -// } -// } -//// podcast or Youtube channel, Youtube playlist, or other? -// else -> { -// log = upsertBlk(log) {it.type = "podcast" } -// Logd(TAG, "Activity was started with url $sharedUrl") -// val intent = MainActivity.showOnlineFeed(this, sharedUrl!!) -//// intent.putExtra(MainActivity.Extras.started_from_share.name, getIntent().getBooleanExtra(MainActivity.Extras.started_from_share.name, false)) -// startActivity(intent) -// finish() -// } -// } + receiveShared(sharedUrl!!,this, true) { + setContent { + val showDialog = remember { mutableStateOf(true) } + CustomTheme(this) { + confirmAddYoutubeEpisode(listOf(sharedUrl!!), showDialog.value, onDismissRequest = { + showDialog.value = false +// finish() + }) + } + } + } } private fun showNoPodcastFoundError() { @@ -105,13 +84,31 @@ class ShareReceiverActivity : AppCompatActivity() { overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) } +// class UrlFetchWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { +// override fun doWork(): Result { +// val url = inputData.getString("shared_url") ?: return Result.failure() +// +// // Fetch the content from URL using OkHttp or another HTTP client +// // Ensure you're on a background thread +// +// return Result.success() +// } +// } + +// // Schedule the worker +// val workRequest = OneTimeWorkRequestBuilder() +// .setInputData(workDataOf("shared_url" to sharedUrl)) +// .build() +// +// WorkManager.getInstance(context).enqueue(workRequest) + companion object { private val TAG: String = ShareReceiverActivity::class.simpleName ?: "Anonymous" const val ARG_FEEDURL: String = "arg.feedurl" private const val RESULT_ERROR = 2 - fun receiveShared(sharedUrl: String, activity: AppCompatActivity, finish: Boolean) { + fun receiveShared(sharedUrl: String, activity: AppCompatActivity, finish: Boolean, mediaCB: ()->Unit) { val url = URL(sharedUrl) val log = realm.query(ShareLog::class).query("url == $0", sharedUrl).first().find() when { @@ -126,15 +123,7 @@ class ShareReceiverActivity : AppCompatActivity() { (isYoutubeURL(url) && url.path.startsWith("/watch")) || isYoutubeServiceURL(url) -> { if (log != null) upsertBlk(log) {it.type = "youtube media" } Logd(TAG, "got youtube media") - activity.setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(activity) { - confirmAddYoutubeEpisode(listOf(sharedUrl), showDialog.value, onDismissRequest = { - showDialog.value = false - if (finish) activity.finish() - }) - } - } + mediaCB() } // podcast or Youtube channel, Youtube playlist, or other? else -> { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt index df00a793..f09040c7 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt @@ -255,12 +255,12 @@ class VideoplayerActivity : CastEnabledActivity() { val isItemHasDownloadLink = isEpisodeMedia && (media as EpisodeMedia?)?.downloadUrl != null menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink) - menu.findItem(R.id.add_to_favorites_item).setVisible(false) - menu.findItem(R.id.remove_from_favorites_item).setVisible(false) - if (isEpisodeMedia) { - menu.findItem(R.id.add_to_favorites_item).setVisible(!videoEpisodeFragment.isFavorite) - menu.findItem(R.id.remove_from_favorites_item).setVisible(videoEpisodeFragment.isFavorite) - } +// menu.findItem(R.id.add_to_favorites_item).setVisible(false) +// menu.findItem(R.id.remove_from_favorites_item).setVisible(false) +// if (isEpisodeMedia) { +// menu.findItem(R.id.add_to_favorites_item).setVisible(!videoEpisodeFragment.isFavorite) +// menu.findItem(R.id.remove_from_favorites_item).setVisible(videoEpisodeFragment.isFavorite) +// } menu.findItem(R.id.set_sleeptimer_item).setVisible(!isSleepTimerActive()) menu.findItem(R.id.disable_sleeptimer_item).setVisible(isSleepTimerActive()) @@ -271,8 +271,8 @@ class VideoplayerActivity : CastEnabledActivity() { menu.findItem(R.id.player_show_chapters).setVisible(true) if (videoMode == VideoMode.WINDOW_VIEW) { - menu.findItem(R.id.add_to_favorites_item).setShowAsAction(SHOW_AS_ACTION_NEVER) - menu.findItem(R.id.remove_from_favorites_item).setShowAsAction(SHOW_AS_ACTION_NEVER) +// menu.findItem(R.id.add_to_favorites_item).setShowAsAction(SHOW_AS_ACTION_NEVER) +// menu.findItem(R.id.remove_from_favorites_item).setShowAsAction(SHOW_AS_ACTION_NEVER) menu.findItem(R.id.set_sleeptimer_item).setShowAsAction(SHOW_AS_ACTION_NEVER) menu.findItem(R.id.disable_sleeptimer_item).setShowAsAction(SHOW_AS_ACTION_NEVER) menu.findItem(R.id.player_switch_to_audio_only).setShowAsAction(SHOW_AS_ACTION_NEVER) @@ -305,16 +305,16 @@ class VideoplayerActivity : CastEnabledActivity() { val media = curMedia ?: return false val feedItem = (media as? EpisodeMedia)?.episodeOrFetch() when { - item.itemId == R.id.add_to_favorites_item && feedItem != null -> { - setFavorite(feedItem, true) - videoEpisodeFragment.isFavorite = true - invalidateOptionsMenu() - } - item.itemId == R.id.remove_from_favorites_item && feedItem != null -> { - setFavorite(feedItem, false) - videoEpisodeFragment.isFavorite = false - invalidateOptionsMenu() - } +// item.itemId == R.id.add_to_favorites_item && feedItem != null -> { +// setFavorite(feedItem, true) +// videoEpisodeFragment.isFavorite = true +// invalidateOptionsMenu() +// } +// item.itemId == R.id.remove_from_favorites_item && feedItem != null -> { +// setFavorite(feedItem, false) +// videoEpisodeFragment.isFavorite = false +// invalidateOptionsMenu() +// } item.itemId == R.id.disable_sleeptimer_item || item.itemId == R.id.set_sleeptimer_item -> SleepTimerDialog().show(supportFragmentManager, "SleepTimerDialog") item.itemId == R.id.audio_controls -> { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt index 1d2c693a..958351ee 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Composables.kt @@ -13,38 +13,19 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Spinner( - items: List, - selectedItem: String, - onItemSelected: (String) -> Unit -) { +fun Spinner(items: List, selectedItem: String, onItemSelected: (String) -> Unit) { var expanded by remember { mutableStateOf(false) } - val context = LocalContext.current - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = !expanded } - ) { - TextField( - readOnly = true, - value = selectedItem, - onValueChange = {}, - label = { Text("Select an item") }, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) - }, - colors = ExposedDropdownMenuDefaults.textFieldColors() - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false } - ) { + ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) { + TextField(readOnly = true, value = selectedItem, onValueChange = {}, label = { Text("Select an item") }, + modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable, true), // Material3 requirement + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, colors = ExposedDropdownMenuDefaults.textFieldColors()) + ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { items.forEach { item -> DropdownMenuItem(text = { Text(item) }, onClick = { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt index 3fdcd122..ff7c4d5b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/EpisodesVM.kt @@ -12,21 +12,25 @@ import ac.mdiq.podcini.storage.database.Episodes.setPlayState import ac.mdiq.podcini.storage.database.Feeds.addToMiscSyndicate import ac.mdiq.podcini.storage.database.Feeds.addToYoutubeSyndicate import ac.mdiq.podcini.storage.database.Queues +import ac.mdiq.podcini.storage.database.Queues.addToQueueSync +import ac.mdiq.podcini.storage.database.Queues.removeFromAllQueuesQuiet import ac.mdiq.podcini.storage.database.Queues.removeFromQueue import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.upsert import ac.mdiq.podcini.storage.model.Episode import ac.mdiq.podcini.storage.model.MediaType +import ac.mdiq.podcini.storage.model.PlayQueue import ac.mdiq.podcini.storage.model.ShareLog import ac.mdiq.podcini.storage.utils.DurationConverter import ac.mdiq.podcini.storage.utils.ImageResourceUtils import ac.mdiq.podcini.ui.actions.EpisodeActionButton -import ac.mdiq.podcini.ui.actions.EpisodeMultiSelectHandler.PutToQueueDialog import ac.mdiq.podcini.ui.actions.SwipeAction import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.fragment.EpisodeInfoFragment import ac.mdiq.podcini.ui.fragment.FeedInfoFragment import ac.mdiq.podcini.ui.utils.LocalDeleteModal +import ac.mdiq.podcini.util.EventFlow +import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd import ac.mdiq.podcini.util.MiscFormatter.formatAbbrev import ac.mdiq.vista.extractor.Vista @@ -173,6 +177,77 @@ class EpisodeVM(var episode: Episode) { } } +@Composable +fun ChooseRatingDialog(selected: List, onDismissRequest: () -> Unit) { + Dialog(onDismissRequest = onDismissRequest) { + Surface(shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { + for (rating in Episode.Rating.entries) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable { + for (item in selected) Episodes.setRating(item, rating.code) + onDismissRequest() + }) { + Icon(imageVector = ImageVector.vectorResource(id = rating.res), "") + Text(rating.name, Modifier.padding(start = 4.dp)) + } + } + } + } + } +} + +@Composable +fun PutToQueueDialog(selected: List, onDismissRequest: () -> Unit) { + val queues = realm.query(PlayQueue::class).find() + Dialog(onDismissRequest = onDismissRequest) { + Surface(shape = RoundedCornerShape(16.dp)) { + val scrollState = rememberScrollState() + Column(modifier = Modifier.verticalScroll(scrollState).padding(16.dp), verticalArrangement = Arrangement.spacedBy(1.dp)) { + var removeChecked by remember { mutableStateOf(false) } + var toQueue by remember { mutableStateOf(curQueue) } + for (q in queues) { + Row(verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = toQueue == q, onClick = { toQueue = q }) + Text(q.name,) + } + } + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(checked = removeChecked, onCheckedChange = { removeChecked = it }) + Text(text = stringResource(R.string.remove_from_other_queues), style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 10.dp)) + } + Row { + Spacer(Modifier.weight(1f)) + Button(onClick = { + if (removeChecked) { + val toRemove = mutableSetOf() + val toRemoveCur = mutableListOf() + selected.forEach { e -> + if (curQueue.contains(e)) toRemoveCur.add(e) + } + selected.forEach { e -> + for (q in queues) { + if (q.contains(e)) { + toRemove.add(e.id) + break + } + } + } + if (toRemove.isNotEmpty()) runBlocking { removeFromAllQueuesQuiet(toRemove.toList()) } + if (toRemoveCur.isNotEmpty()) EventFlow.postEvent(FlowEvent.QueueEvent.removed(toRemoveCur)) + } + selected.forEach { e -> + runBlocking { addToQueueSync(false, e, toQueue) } + } + onDismissRequest() + }) { + Text("Confirm") + } + } + } + } + } +} + @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList, refreshCB: (()->Unit)? = null, @@ -192,26 +267,11 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList, showConfirmYoutubeDialog.value = false }) - @Composable - fun ChooseRatingDialog(onDismissRequest: () -> Unit) { - Dialog(onDismissRequest = onDismissRequest) { - Surface(shape = RoundedCornerShape(16.dp)) { - Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { - for (rating in Episode.Rating.entries) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(4.dp).clickable { - for (item in selected) Episodes.setRating(item, rating.code) - onDismissRequest() - }) { - Icon(imageVector = ImageVector.vectorResource(id = rating.res), "") - Text(rating.name, Modifier.padding(start = 4.dp)) - } - } - } - } - } - } var showChooseRatingDialog by remember { mutableStateOf(false) } - if (showChooseRatingDialog) ChooseRatingDialog { showChooseRatingDialog = false } + if (showChooseRatingDialog) ChooseRatingDialog(selected) { showChooseRatingDialog = false } + + var showPutToQueueDialog by remember { mutableStateOf(false) } + if (showPutToQueueDialog) PutToQueueDialog(selected) { showPutToQueueDialog = false } @Composable fun EpisodeSpeedDial(modifier: Modifier = Modifier) { @@ -270,7 +330,8 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList, isExpanded = false selectMode = false Logd(TAG, "ic_playlist_play: ${selected.size}") - PutToQueueDialog(activity, selected).show() + showPutToQueueDialog = true +// PutToQueueDialog(activity, selected).show() }, verticalAlignment = Alignment.CenterVertically) { Icon(imageVector = ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "") Text(stringResource(id = R.string.put_in_queue_label)) } }, @@ -325,7 +386,6 @@ fun EpisodeLazyColumn(activity: MainActivity, vms: SnapshotStateList, } var refreshing by remember { mutableStateOf(false)} - PullToRefreshBox(modifier = Modifier.fillMaxWidth(), isRefreshing = refreshing, indicator = {}, onRefresh = { refreshing = true refreshCB?.invoke() diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt index cca0f4be..4b500305 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt @@ -36,6 +36,7 @@ import ac.mdiq.podcini.ui.actions.EpisodeMenuHandler import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion.videoMode import ac.mdiq.podcini.ui.activity.starter.VideoPlayerActivityStarter +import ac.mdiq.podcini.ui.compose.ChooseRatingDialog import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.dialog.MediaPlayerErrorDialog import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog @@ -136,7 +137,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var episodeDate by mutableStateOf("") private var chapterControlVisible by mutableStateOf(false) private var hasNextChapter by mutableStateOf(true) - // var imgLoc by mutableStateOf("") + var rating by mutableStateOf(currentItem?.rating ?: 0) private val currentChapter: Chapter? get() { if (currentMedia == null || currentMedia!!.getChapters().isEmpty() || displayedChapterIndex == -1) return null @@ -160,7 +161,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { controller!!.init() onCollaped() - binding.composeView1.setContent { + binding.player1.setContent { CustomTheme(requireContext()) { if (showPlayer1) PlayerUI() else Spacer(modifier = Modifier.size(0.dp)) @@ -173,7 +174,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { // else Spacer(modifier = Modifier.size(0.dp)) } } - binding.composeView2.setContent { + binding.player2.setContent { CustomTheme(requireContext()) { if (!showPlayer1) PlayerUI() else Spacer(modifier = Modifier.size(0.dp)) @@ -334,6 +335,11 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { @OptIn(ExperimentalFoundationApi::class) @Composable fun DetailUI() { + var showChooseRatingDialog by remember { mutableStateOf(false) } + if (showChooseRatingDialog) ChooseRatingDialog(listOf(currentItem!!)) { + showChooseRatingDialog = false + } + val scrollState = rememberScrollState() Column(modifier = Modifier.fillMaxWidth().verticalScroll(scrollState)) { val textColor = MaterialTheme.colorScheme.onSurface @@ -354,7 +360,16 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } }, onLongClick = { copyText(currentMedia?.getFeedTitle()?:"") })) - Text(episodeDate, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 2.dp), color = textColor, style = MaterialTheme.typography.bodyMedium) + Row(modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 2.dp), ) { + Spacer(modifier = Modifier.weight(0.2f)) + var ratingIconRes = Episode.Rating.fromCode(rating).res + Icon(painter = painterResource(ratingIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating", modifier = Modifier.width(15.dp).height(15.dp).clickable(onClick = { + showChooseRatingDialog = true + })) + Spacer(modifier = Modifier.weight(0.4f)) + Text(episodeDate, textAlign = TextAlign.Center, color = textColor, style = MaterialTheme.typography.bodyMedium) + Spacer(modifier = Modifier.weight(0.6f)) + } Text(titleText, textAlign = TextAlign.Center, color = textColor, style = MaterialTheme.typography.titleLarge, modifier = Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 5.dp) .combinedClickable(onClick = {}, onLongClick = { copyText(currentItem?.title?:"") })) fun restoreFromPreference(): Boolean { @@ -899,7 +914,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { onPlaybackServiceChanged(event) } is FlowEvent.PlayEvent -> onPlayEvent(event) - is FlowEvent.RatingEvent -> onFavoriteEvent(event) + is FlowEvent.RatingEvent -> onRatingEvent(event) is FlowEvent.PlayerErrorEvent -> MediaPlayerErrorDialog.show(activity as Activity, event) // is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) loadMediaInfo(false) is FlowEvent.SleepTimerUpdatedEvent -> if (event.isCancelled || event.wasJustEnabled()) setupOptionsMenu() @@ -911,8 +926,11 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - private fun onFavoriteEvent(event: FlowEvent.RatingEvent) { - if (curEpisode?.id == event.episode.id) EpisodeMenuHandler.onPrepareMenu(toolbar.menu, event.episode) + private fun onRatingEvent(event: FlowEvent.RatingEvent) { + if (curEpisode?.id == event.episode.id) { + rating = event.rating + EpisodeMenuHandler.onPrepareMenu(toolbar.menu, event.episode) + } } private fun setupOptionsMenu() { @@ -982,7 +1000,7 @@ class AudioPlayerFragment : Fragment(), Toolbar.OnMenuItemClickListener { fun fadePlayerToToolbar(slideOffset: Float) { val playerFadeProgress = (max(0.0, min(0.2, (slideOffset - 0.2f).toDouble())) / 0.2f).toFloat() - val player = binding.composeView1 + val player = binding.player1 player.alpha = 1 - playerFadeProgress player.visibility = if (playerFadeProgress > 0.99f) View.GONE else View.VISIBLE val toolbarFadeProgress = (max(0.0, min(0.2, (slideOffset - 0.6f).toDouble())) / 0.2f).toFloat() diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt index 3ff4414f..2f4e7510 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/EpisodeInfoFragment.kt @@ -11,6 +11,7 @@ import ac.mdiq.podcini.playback.base.InTheatre import ac.mdiq.podcini.playback.service.PlaybackService.Companion.seekTo import ac.mdiq.podcini.preferences.UsageStatistics import ac.mdiq.podcini.preferences.UserPreferences +import ac.mdiq.podcini.storage.database.RealmDB.realm import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.database.RealmDB.unmanaged import ac.mdiq.podcini.storage.database.RealmDB.upsert @@ -22,6 +23,7 @@ import ac.mdiq.podcini.storage.utils.DurationConverter import ac.mdiq.podcini.storage.utils.ImageResourceUtils import ac.mdiq.podcini.ui.actions.* import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.compose.ChooseRatingDialog import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.utils.ShownotesCleaner import ac.mdiq.podcini.ui.utils.ThemeUtils @@ -100,6 +102,15 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { private var itemLoaded = false private var episode: Episode? = null // managed + private var txtvPodcast by mutableStateOf("") + private var txtvTitle by mutableStateOf("") + private var txtvPublished by mutableStateOf("") + private var txtvSize by mutableStateOf("") + private var txtvDuration by mutableStateOf("") + private var itemLink by mutableStateOf("") + var hasMedia by mutableStateOf(true) + var rating by mutableStateOf(episode?.rating ?: 0) + private var webviewData by mutableStateOf("") private lateinit var shownotesCleaner: ShownotesCleaner @@ -188,6 +199,11 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { runOnIOScope { if (episode != null) episode = upsert(episode!!) { it.comment = commentTextState.text } } }) + var showChooseRatingDialog by remember { mutableStateOf(false) } + if (showChooseRatingDialog) ChooseRatingDialog(listOf(episode!!)) { + showChooseRatingDialog = false + } + Column { Row(modifier = Modifier.padding(start = 16.dp, end = 16.dp), verticalAlignment = Alignment.CenterVertically) { val imgLoc = if (episode != null) ImageResourceUtils.getEpisodeListImageLocation(episode!!) else null @@ -199,7 +215,12 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } Row(verticalAlignment = Alignment.CenterVertically) { - Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.weight(0.4f)) + var ratingIconRes = Episode.Rating.fromCode(rating).res + Icon(painter = painterResource(ratingIconRes), tint = MaterialTheme.colorScheme.tertiary, contentDescription = "rating", modifier = Modifier.width(15.dp).height(15.dp).clickable(onClick = { + showChooseRatingDialog = true + })) + Spacer(modifier = Modifier.weight(0.2f)) if (hasMedia) Icon(painter = painterResource(actionButton1?.getDrawable()?: R.drawable.ic_questionmark), tint = textColor, contentDescription = "butAction1", modifier = Modifier.width(24.dp).height(24.dp).clickable(onClick = { when { @@ -235,7 +256,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { }) // if (cond) CircularProgressIndicator(progress = 0.01f * dlPercent, strokeWidth = 4.dp, color = textColor) } - Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.weight(0.4f)) } if (!hasMedia) Text("noMediaLabel", color = textColor, style = MaterialTheme.typography.bodyMedium) val scrollState = rememberScrollState() @@ -367,12 +388,6 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { else EpisodeMenuHandler.onPrepareMenu(toolbar.menu, episode, R.id.open_podcast, R.id.mark_read_item, R.id.visit_website_item) } - private var txtvPodcast by mutableStateOf("") - private var txtvTitle by mutableStateOf("") - private var txtvPublished by mutableStateOf("") - private var txtvSize by mutableStateOf("") - private var txtvDuration by mutableStateOf("") - private var itemLink by mutableStateOf("") @UnstableApi private fun updateAppearance() { if (episode == null) { @@ -429,7 +444,6 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { updateButtons() } - var hasMedia by mutableStateOf(true) @UnstableApi private fun updateButtons() { // binding.circularProgressBar.visibility = View.GONE @@ -501,7 +515,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { Logd(TAG, "Received event: ${event.TAG}") when (event) { is FlowEvent.QueueEvent -> onQueueEvent(event) - is FlowEvent.RatingEvent -> onFavoriteEvent(event) + is FlowEvent.RatingEvent -> onRatingEvent(event) is FlowEvent.EpisodeEvent -> onEpisodeEvent(event) is FlowEvent.PlayerSettingsEvent -> updateButtons() is FlowEvent.EpisodePlayedEvent -> load() @@ -520,10 +534,11 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } - private fun onFavoriteEvent(event: FlowEvent.RatingEvent) { + private fun onRatingEvent(event: FlowEvent.RatingEvent) { if (episode?.id == event.episode.id) { episode = unmanaged(episode!!) - episode!!.rating = if (event.episode.isFavorite) Episode.Rating.FAVORITE.code else Episode.Rating.NEUTRAL.code + episode!!.rating = event.rating + rating = episode!!.rating // episode = event.episode prepareMenu() } @@ -587,6 +602,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } withContext(Dispatchers.Main) { // binding.progbarLoading.visibility = View.GONE + rating = episode!!.rating onFragmentLoaded() itemLoaded = true } @@ -597,7 +613,7 @@ class EpisodeInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { } fun setItem(item_: Episode) { - episode = item_ + episode = realm.query(Episode::class).query("id == ${item_.id}").first().find() } /** diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt index 29aea112..6bfe4c8e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt @@ -104,14 +104,17 @@ class FeedInfoFragment : Fragment(), Toolbar.OnMenuItemClickListener { txtvAuthor = feed.author ?: "" txtvUrl = feed.downloadUrl - binding.header.setContent { - CustomTheme(requireContext()) { - HeaderUI() - } - } +// binding.header.setContent { +// CustomTheme(requireContext()) { +// HeaderUI() +// } +// } binding.detailUI.setContent { CustomTheme(requireContext()) { - DetailUI() + Column { + HeaderUI() + DetailUI() + } } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt index aa0f8ab8..6708b38c 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt @@ -36,8 +36,10 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -93,26 +95,17 @@ class FeedSettingsFragment : Fragment() { binding.composeView.setContent { CustomTheme(requireContext()) { val textColor = MaterialTheme.colorScheme.onSurface - Column( - modifier = Modifier.padding(start = 20.dp, end = 16.dp, top = 10.dp, bottom = 10.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Column(modifier = Modifier.padding(start = 20.dp, end = 16.dp, top = 10.dp, bottom = 10.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { if ((feed?.id ?: 0) > 10) { // refresh Column { Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_refresh), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.keep_updated), - style = MaterialTheme.typography.titleLarge, - color = textColor - ) + Text(text = stringResource(R.string.keep_updated), style = MaterialTheme.typography.titleLarge, color = textColor) Spacer(modifier = Modifier.weight(1f)) var checked by remember { mutableStateOf(feed?.preferences?.keepUpdated ?: true) } - Switch( - checked = checked, - modifier = Modifier.height(24.dp), + Switch(checked = checked, modifier = Modifier.height(24.dp), onCheckedChange = { checked = it feed = upsertBlk(feed!!) { f -> @@ -121,41 +114,22 @@ class FeedSettingsFragment : Fragment() { } ) } - Text( - text = stringResource(R.string.keep_updated_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.keep_updated_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } } if ((feed?.id?:0) > 10 && feed?.hasVideoMedia == true) { // video mode Column { Row(Modifier.fillMaxWidth()) { + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) VideoModeDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) Icon(ImageVector.vectorResource(id = R.drawable.ic_delete), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.feed_video_mode_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - VideoModeDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) + Text(text = stringResource(R.string.feed_video_mode_label), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true }) ) } - Text( - text = stringResource(videoModeSummaryResId), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(videoModeSummaryResId), style = MaterialTheme.typography.bodyMedium, color = textColor) } } if (feed?.type != Feed.FeedType.YOUTUBE.name) { @@ -164,18 +138,12 @@ class FeedSettingsFragment : Fragment() { Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_stream), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.pref_stream_over_download_title), - style = MaterialTheme.typography.titleLarge, - color = textColor - ) + Text(text = stringResource(R.string.pref_stream_over_download_title), style = MaterialTheme.typography.titleLarge, color = textColor) Spacer(modifier = Modifier.weight(1f)) var checked by remember { mutableStateOf(feed?.preferences?.prefStreamOverDownload ?: false) } - Switch( - checked = checked, - modifier = Modifier.height(24.dp), + Switch(checked = checked, modifier = Modifier.height(24.dp), onCheckedChange = { checked = it feed = upsertBlk(feed!!) { f -> @@ -184,61 +152,37 @@ class FeedSettingsFragment : Fragment() { } ) } - Text( - text = stringResource(R.string.pref_stream_over_download_sum), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.pref_stream_over_download_sum), style = MaterialTheme.typography.bodyMedium, color = textColor) } } // associated queue Column { curPrefQueue = feed?.preferences?.queueTextExt ?: "Default" + var showDialog by remember { mutableStateOf(false) } + var selectedOption by remember { mutableStateOf(feed?.preferences?.queueText ?: "Default") } + if (showDialog) SetAssociatedQueue(showDialog, selectedOption = selectedOption, onDismissRequest = { showDialog = false }) Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_playlist_play), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.pref_feed_associated_queue), - style = MaterialTheme.typography.titleLarge, - color = textColor, + Text(text = stringResource(R.string.pref_feed_associated_queue), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { - val selectedOption = feed?.preferences?.queueText ?: "Default" - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - SetAssociatedQueue(showDialog.value, selectedOption = selectedOption, onDismissRequest = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) + selectedOption = feed?.preferences?.queueText ?: "Default" + showDialog = true }) ) } - Text( - text = curPrefQueue + " : " + stringResource(R.string.pref_feed_associated_queue_sum), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = curPrefQueue + " : " + stringResource(R.string.pref_feed_associated_queue_sum), style = MaterialTheme.typography.bodyMedium, color = textColor) } // auto add new to queue if (curPrefQueue != "None") { Column { Row(Modifier.fillMaxWidth()) { - Icon(ImageVector.vectorResource(id = androidx.media3.session.R.drawable.media3_icon_queue_add), - "", - tint = textColor) + Icon(ImageVector.vectorResource(id = androidx.media3.session.R.drawable.media3_icon_queue_add), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.audo_add_new_queue), - style = MaterialTheme.typography.titleLarge, - color = textColor - ) + Text(text = stringResource(R.string.audo_add_new_queue), style = MaterialTheme.typography.titleLarge, color = textColor) Spacer(modifier = Modifier.weight(1f)) var checked by remember { mutableStateOf(feed?.preferences?.autoAddNewToQueue ?: true) } - Switch( - checked = checked, - modifier = Modifier.height(24.dp), + Switch(checked = checked, modifier = Modifier.height(24.dp), onCheckedChange = { checked = it feed = upsertBlk(feed!!) { f -> @@ -247,41 +191,21 @@ class FeedSettingsFragment : Fragment() { } ) } - Text( - text = stringResource(R.string.audo_add_new_queue_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.audo_add_new_queue_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } } if (feed?.type != Feed.FeedType.YOUTUBE.name) { // auto delete Column { Row(Modifier.fillMaxWidth()) { + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) AutoDeleteDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) Icon(ImageVector.vectorResource(id = R.drawable.ic_delete), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.auto_delete_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - AutoDeleteDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) - ) + Text(text = stringResource(R.string.auto_delete_label), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true })) } - Text( - text = stringResource(autoDeleteSummaryResId), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(autoDeleteSummaryResId), style = MaterialTheme.typography.bodyMedium, color = textColor) } } // tags @@ -289,127 +213,64 @@ class FeedSettingsFragment : Fragment() { Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_tag), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.feed_tags_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, + Text(text = stringResource(R.string.feed_tags_label), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { val dialog = TagSettingsDialog.newInstance(listOf(feed!!)) dialog.show(parentFragmentManager, TagSettingsDialog.TAG) }) ) } - Text( - text = stringResource(R.string.feed_tags_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.feed_tags_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } // playback speed Column { Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_playback_speed), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.playback_speed), - style = MaterialTheme.typography.titleLarge, - color = textColor, + Text(text = stringResource(R.string.playback_speed), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { PlaybackSpeedDialog().show() }) ) } - Text( - text = stringResource(R.string.pref_feed_playback_speed_sum), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.pref_feed_playback_speed_sum), style = MaterialTheme.typography.bodyMedium, color = textColor) } // auto skip Column { Row(Modifier.fillMaxWidth()) { + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) AutoSkipDialog(showDialog.value, onDismiss = { showDialog.value = false }) Icon(ImageVector.vectorResource(id = R.drawable.ic_skip_24dp), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.pref_feed_skip), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - AutoSkipDialog(showDialog.value, onDismiss = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) - ) + Text(text = stringResource(R.string.pref_feed_skip), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true })) } - Text( - text = stringResource(R.string.pref_feed_skip_sum), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.pref_feed_skip_sum), style = MaterialTheme.typography.bodyMedium, color = textColor) } // volume adaption Column { Row(Modifier.fillMaxWidth()) { + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) VolumeAdaptionDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) Icon(ImageVector.vectorResource(id = R.drawable.ic_volume_adaption), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.feed_volume_adapdation), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - VolumeAdaptionDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) - ) + Text(text = stringResource(R.string.feed_volume_adapdation), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true })) } - Text( - text = stringResource(R.string.feed_volume_adaptation_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.feed_volume_adaptation_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } // authentication if ((feed?.id?:0) > 0 && feed?.isLocalFeed != true) { Column { Row(Modifier.fillMaxWidth()) { + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) AuthenticationDialog(showDialog.value, onDismiss = { showDialog.value = false }) Icon(ImageVector.vectorResource(id = R.drawable.ic_key), "", tint = textColor) Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.authentication_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - AuthenticationDialog(showDialog.value, - onDismiss = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) - ) + Text(text = stringResource(R.string.authentication_label), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true })) } - Text( - text = stringResource(R.string.authentication_descr), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.authentication_descr), style = MaterialTheme.typography.bodyMedium, color = textColor) } } if (isEnableAutodownload && feed?.type != Feed.FeedType.YOUTUBE.name) { @@ -417,15 +278,9 @@ class FeedSettingsFragment : Fragment() { var audoDownloadChecked by remember { mutableStateOf(feed?.preferences?.autoDownload ?: false) } Column { Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.auto_download_label), - style = MaterialTheme.typography.titleLarge, - color = textColor - ) + Text(text = stringResource(R.string.auto_download_label), style = MaterialTheme.typography.titleLarge, color = textColor) Spacer(modifier = Modifier.weight(1f)) - Switch( - checked = audoDownloadChecked, - modifier = Modifier.height(24.dp), + Switch(checked = audoDownloadChecked, modifier = Modifier.height(24.dp), onCheckedChange = { audoDownloadChecked = it feed = upsertBlk(feed!!) { f -> f.preferences?.autoDownload = audoDownloadChecked } @@ -433,32 +288,18 @@ class FeedSettingsFragment : Fragment() { ) } if (!isEnableAutodownload) { - Text( - text = stringResource(R.string.auto_download_disabled_globally), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.auto_download_disabled_globally), style = MaterialTheme.typography.bodyMedium, color = textColor) } } if (audoDownloadChecked) { // auto download policy Column (modifier = Modifier.padding(start = 20.dp)){ Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.feed_auto_download_policy), - style = MaterialTheme.typography.titleLarge, - color = textColor, + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) AutoDownloadPolicyDialog(showDialog.value, onDismissRequest = { showDialog.value = false }) + Text(text = stringResource(R.string.feed_auto_download_policy), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - AutoDownloadPolicyDialog(showDialog.value, - onDismissRequest = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) + showDialog.value = true }) ) } @@ -466,45 +307,23 @@ class FeedSettingsFragment : Fragment() { // episode cache Column (modifier = Modifier.padding(start = 20.dp)) { Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.pref_episode_cache_title), - style = MaterialTheme.typography.titleLarge, - color = textColor, - modifier = Modifier.clickable(onClick = { - val composeView = ComposeView(requireContext()).apply { - setContent { - val showDialog = remember { mutableStateOf(true) } - CustomTheme(requireContext()) { - SetEpisodesCacheDialog(showDialog.value, - onDismiss = { showDialog.value = false }) - } - } - } - (view as? ViewGroup)?.addView(composeView) - }) + val showDialog = remember { mutableStateOf(false) } + if (showDialog.value) SetEpisodesCacheDialog(showDialog.value, onDismiss = { showDialog.value = false }) + Text(text = stringResource(R.string.pref_episode_cache_title), style = MaterialTheme.typography.titleLarge, color = textColor, + modifier = Modifier.clickable(onClick = { showDialog.value = true }) ) } - Text( - text = stringResource(R.string.pref_episode_cache_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.pref_episode_cache_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } // counting played Column (modifier = Modifier.padding(start = 20.dp)) { Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.pref_auto_download_counting_played_title), - style = MaterialTheme.typography.titleLarge, - color = textColor - ) + Text(text = stringResource(R.string.pref_auto_download_counting_played_title), style = MaterialTheme.typography.titleLarge, color = textColor) Spacer(modifier = Modifier.weight(1f)) var checked by remember { mutableStateOf(feed?.preferences?.countingPlayed ?: true) } - Switch( - checked = checked, - modifier = Modifier.height(24.dp), + Switch(checked = checked, modifier = Modifier.height(24.dp), onCheckedChange = { checked = it feed = upsertBlk(feed!!) { f -> @@ -513,19 +332,12 @@ class FeedSettingsFragment : Fragment() { } ) } - Text( - text = stringResource(R.string.pref_auto_download_counting_played_summary), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.pref_auto_download_counting_played_summary), style = MaterialTheme.typography.bodyMedium, color = textColor) } // inclusive filter Column (modifier = Modifier.padding(start = 20.dp)) { Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.episode_inclusive_filters_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, + Text(text = stringResource(R.string.episode_inclusive_filters_label), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { object : AutoDownloadFilterPrefDialog(requireContext(), feed?.preferences!!.autoDownloadFilter!!, @@ -540,19 +352,12 @@ class FeedSettingsFragment : Fragment() { }) ) } - Text( - text = stringResource(R.string.episode_filters_description), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.episode_filters_description), style = MaterialTheme.typography.bodyMedium, color = textColor) } // exclusive filter Column (modifier = Modifier.padding(start = 20.dp)) { Row(Modifier.fillMaxWidth()) { - Text( - text = stringResource(R.string.episode_exclusive_filters_label), - style = MaterialTheme.typography.titleLarge, - color = textColor, + Text(text = stringResource(R.string.episode_exclusive_filters_label), style = MaterialTheme.typography.titleLarge, color = textColor, modifier = Modifier.clickable(onClick = { object : AutoDownloadFilterPrefDialog(requireContext(), feed?.preferences!!.autoDownloadFilter!!, @@ -567,20 +372,14 @@ class FeedSettingsFragment : Fragment() { }) ) } - Text( - text = stringResource(R.string.episode_filters_description), - style = MaterialTheme.typography.bodyMedium, - color = textColor - ) + Text(text = stringResource(R.string.episode_filters_description), style = MaterialTheme.typography.bodyMedium, color = textColor) } } } } } } - if (feed != null) { - toolbar.subtitle = feed!!.title - } + if (feed != null) toolbar.subtitle = feed!!.title return binding.root } @@ -617,23 +416,11 @@ class FeedSettingsFragment : Fragment() { if (showDialog) { val (selectedOption, onOptionSelected) = remember { mutableStateOf(videoMode) } Dialog(onDismissRequest = { onDismissRequest() }) { - Card( - modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column { videoModeTags.forEach { text -> - Row(Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { Checkbox(checked = (text == selectedOption), onCheckedChange = { Logd(TAG, "row clicked: $text $selectedOption") @@ -652,12 +439,7 @@ class FeedSettingsFragment : Fragment() { } } ) - Text( - text = text, - style = MaterialTheme.typography.bodyLarge.merge(), -// color = textColor, - modifier = Modifier.padding(start = 16.dp) - ) + Text(text = text, style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp)) } } } @@ -688,23 +470,11 @@ class FeedSettingsFragment : Fragment() { if (showDialog) { val (selectedOption, onOptionSelected) = remember { mutableStateOf(autoDeletePolicy) } Dialog(onDismissRequest = { onDismissRequest() }) { - Card( - modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column { FeedAutoDeleteOptions.forEach { text -> - Row(Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { Checkbox(checked = (text == selectedOption), onCheckedChange = { Logd(TAG, "row clicked: $text $selectedOption") @@ -722,12 +492,7 @@ class FeedSettingsFragment : Fragment() { } } ) - Text( - text = text, - style = MaterialTheme.typography.bodyLarge.merge(), -// color = textColor, - modifier = Modifier.padding(start = 16.dp) - ) + Text(text = text, style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp)) } } } @@ -742,23 +507,11 @@ class FeedSettingsFragment : Fragment() { if (showDialog) { val (selectedOption, onOptionSelected) = remember { mutableStateOf(feed?.preferences?.volumeAdaptionSetting ?: VolumeAdaptionSetting.OFF) } Dialog(onDismissRequest = { onDismissRequest() }) { - Card( - modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column { VolumeAdaptionSetting.entries.forEach { item -> - Row(Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp), verticalAlignment = Alignment.CenterVertically) { Checkbox(checked = (item == selectedOption), onCheckedChange = { _ -> Logd(TAG, "row clicked: $item $selectedOption") @@ -769,12 +522,7 @@ class FeedSettingsFragment : Fragment() { } } ) - Text( - text = stringResource(item.resId), - style = MaterialTheme.typography.bodyLarge.merge(), -// color = textColor, - modifier = Modifier.padding(start = 16.dp) - ) + Text(text = stringResource(item.resId), style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp)) } } } @@ -789,16 +537,8 @@ class FeedSettingsFragment : Fragment() { if (showDialog) { val (selectedOption, onOptionSelected) = remember { mutableStateOf(feed?.preferences?.autoDLPolicy ?: AutoDownloadPolicy.ONLY_NEW) } Dialog(onDismissRequest = { onDismissRequest() }) { - Card( - modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column { AutoDownloadPolicy.entries.forEach { item -> Row(Modifier @@ -817,12 +557,7 @@ class FeedSettingsFragment : Fragment() { } } ) - Text( - text = stringResource(item.resId), - style = MaterialTheme.typography.bodyLarge.merge(), -// color = textColor, - modifier = Modifier.padding(start = 16.dp) - ) + Text(text = stringResource(item.resId), style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp)) } } } @@ -836,17 +571,10 @@ class FeedSettingsFragment : Fragment() { fun SetEpisodesCacheDialog(showDialog: Boolean, onDismiss: () -> Unit) { if (showDialog) { Dialog(onDismissRequest = onDismiss) { - Card(modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column(modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { var newCache by remember { mutableStateOf((feed?.preferences?.autoDLMaxEpisodes ?: 1).toString()) } - TextField(value = newCache, - onValueChange = { if (it.isEmpty() || it.toIntOrNull() != null) newCache = it }, + TextField(value = newCache, onValueChange = { if (it.isEmpty() || it.toIntOrNull() != null) newCache = it }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), // visualTransformation = PositiveIntegerTransform(), label = { Text("Max episodes allowed") } @@ -870,18 +598,10 @@ class FeedSettingsFragment : Fragment() { var selected by remember {mutableStateOf(selectedOption)} if (showDialog) { Dialog(onDismissRequest = { onDismissRequest() }) { - Card(modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column(modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { queueSettingOptions.forEach { option -> - Row(modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { + Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { Checkbox(checked = option == selected, onCheckedChange = { isChecked -> selected = option @@ -911,6 +631,7 @@ class FeedSettingsFragment : Fragment() { } if (selected == "Custom") { if (queues == null) queues = realm.query(PlayQueue::class).find() + Logd(TAG, "queues: ${queues?.size}") Spinner(items = queues!!.map { it.name }, selectedItem = feed?.preferences?.queue?.name ?: "Default") { name -> Logd(TAG, "Queue selected: $name") val q = queues?.firstOrNull { it.name == name } @@ -929,26 +650,14 @@ class FeedSettingsFragment : Fragment() { fun AuthenticationDialog(showDialog: Boolean, onDismiss: () -> Unit) { if (showDialog) { Dialog(onDismissRequest = onDismiss) { - Card(modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column(modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { val oldName = feed?.preferences?.username?:"" var newName by remember { mutableStateOf(oldName) } - TextField(value = newName, - onValueChange = { newName = it }, - label = { Text("Username") } - ) + TextField(value = newName, onValueChange = { newName = it }, label = { Text("Username") }) val oldPW = feed?.preferences?.password?:"" var newPW by remember { mutableStateOf(oldPW) } - TextField(value = newPW, - onValueChange = { newPW = it }, - label = { Text("Password") } - ) + TextField(value = newPW, onValueChange = { newPW = it }, label = { Text("Password") }) Button(onClick = { if (newName.isNotEmpty() && oldName != newName) { feed = upsertBlk(feed!!) { @@ -971,14 +680,8 @@ class FeedSettingsFragment : Fragment() { fun AutoSkipDialog(showDialog: Boolean, onDismiss: () -> Unit) { if (showDialog) { Dialog(onDismissRequest = onDismiss) { - Card(modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column(modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { var intro by remember { mutableStateOf((feed?.preferences?.introSkip ?: 0).toString()) } TextField(value = intro, onValueChange = { if (it.isEmpty() || it.toIntOrNull() != null) intro = it }, diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SharedLogFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SharedLogFragment.kt index 1a59e41a..6e204d6d 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SharedLogFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SharedLogFragment.kt @@ -7,6 +7,7 @@ import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.model.ShareLog import ac.mdiq.podcini.ui.activity.ShareReceiverActivity.Companion.receiveShared import ac.mdiq.podcini.ui.compose.CustomTheme +import ac.mdiq.podcini.ui.compose.confirmAddYoutubeEpisode import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd @@ -43,6 +44,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope +import io.realm.kotlin.query.Sort import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -82,12 +84,13 @@ class SharedLogFragment : Fragment(), Toolbar.OnMenuItemClickListener { val showDialog = remember { mutableStateOf(false) } val dialogParam = remember { mutableStateOf(ShareLog()) } if (showDialog.value) { - DetailDialog( - status = dialogParam.value, - showDialog = showDialog.value, - onDismissRequest = { showDialog.value = false }, - ) + DetailDialog(status = dialogParam.value, showDialog = showDialog.value, onDismissRequest = { showDialog.value = false }) } + var showYTMediaConfirmDialog by remember { mutableStateOf(false) } + var sharedUrl by remember { mutableStateOf("") } + if (showYTMediaConfirmDialog) + confirmAddYoutubeEpisode(listOf(sharedUrl), showYTMediaConfirmDialog, onDismissRequest = { showYTMediaConfirmDialog = false }) + LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 6.dp, top = 5.dp, bottom = 5.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { itemsIndexed(logs) { position, log -> @@ -96,7 +99,12 @@ class SharedLogFragment : Fragment(), Toolbar.OnMenuItemClickListener { if (log.status == 1) { showDialog.value = true dialogParam.value = log - } else receiveShared(log.url!!, activity as AppCompatActivity, false) + } else { + receiveShared(log.url!!, activity as AppCompatActivity, false) { + sharedUrl = log.url!! + showYTMediaConfirmDialog = true + } + } }) { Column { Row { @@ -104,6 +112,13 @@ class SharedLogFragment : Fragment(), Toolbar.OnMenuItemClickListener { val iconColor = remember { if (log.status == 1) Color.Green else Color.Yellow } Icon(icon, "Info", tint = iconColor, modifier = Modifier.padding(end = 2.dp)) Text(formatDateTimeFlex(Date(log.id)), color = textColor) + Spacer(Modifier.weight(1f)) + var showAction by remember { mutableStateOf(log.status != 1) } + if (true || showAction) { + Icon(painter = painterResource(R.drawable.ic_delete), tint = textColor, contentDescription = null, + modifier = Modifier.width(25.dp).height(25.dp).clickable { + }) + } } Text(log.url?:"unknown", color = textColor) val statusText = remember {"" } @@ -113,15 +128,6 @@ class SharedLogFragment : Fragment(), Toolbar.OnMenuItemClickListener { Text(stringResource(R.string.download_error_tap_for_details), color = textColor) } } - var showAction by remember { mutableStateOf(log.status != 1) } - if (showAction) { - Icon(painter = painterResource(R.drawable.ic_refresh), - tint = textColor, - contentDescription = null, - modifier = Modifier.width(28.dp).height(32.dp).clickable { - - }) - } } } } @@ -154,7 +160,7 @@ class SharedLogFragment : Fragment(), Toolbar.OnMenuItemClickListener { try { val result = withContext(Dispatchers.IO) { Logd(TAG, "getDownloadLog() called") - val dlog = realm.query(ShareLog::class).find().toMutableList() + val dlog = realm.query(ShareLog::class).sort("id", Sort.DESCENDING).find().toMutableList() realm.copyFromRealm(dlog) } withContext(Dispatchers.Main) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt index 7be6c7fb..62f7a94b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/fragment/SubscriptionsFragment.kt @@ -529,9 +529,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column { FeedAutoDeleteOptions.forEach { text -> - Row( - Modifier.fillMaxWidth().padding(horizontal = 16.dp).selectable( - selected = (text == selectedOption), + Row(Modifier.fillMaxWidth().padding(horizontal = 16.dp) + .selectable(selected = (text == selectedOption), onClick = { if (text != selectedOption) { val autoDeleteAction: AutoDeleteAction = AutoDeleteAction.fromTag(text) @@ -545,11 +544,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { verticalAlignment = Alignment.CenterVertically ) { RadioButton(selected = (text == selectedOption), onClick = { }) - Text( - text = text, - style = MaterialTheme.typography.bodyLarge.merge(), - modifier = Modifier.padding(start = 16.dp) - ) + Text(text = text, style = MaterialTheme.typography.bodyLarge.merge(), modifier = Modifier.padding(start = 16.dp)) } } } @@ -641,27 +636,15 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { CustomTheme(activity) { if (showDialog.value) { Dialog(onDismissRequest = { showDialog.value = false }) { - Card( - modifier = Modifier - .wrapContentSize(align = Alignment.Center) - .padding(16.dp), - shape = RoundedCornerShape(16.dp), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.Center - ) { + Card(modifier = Modifier.wrapContentSize(align = Alignment.Center).padding(16.dp), shape = RoundedCornerShape(16.dp)) { + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.Center) { Row(Modifier.fillMaxWidth()) { Icon(ImageVector.vectorResource(id = R.drawable.ic_refresh), "") Spacer(modifier = Modifier.width(20.dp)) - Text( - text = stringResource(R.string.keep_updated), - style = MaterialTheme.typography.titleLarge - ) + Text(text = stringResource(R.string.keep_updated), style = MaterialTheme.typography.titleLarge) Spacer(modifier = Modifier.weight(1f)) var checked by remember { mutableStateOf(false) } - Switch( - checked = checked, + Switch(checked = checked, onCheckedChange = { checked = it saveFeedPreferences { pref: FeedPreferences -> @@ -670,10 +653,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } ) } - Text( - text = stringResource(R.string.keep_updated_summary), - style = MaterialTheme.typography.bodyMedium - ) + Text(text = stringResource(R.string.keep_updated_summary), style = MaterialTheme.typography.bodyMedium) } } } @@ -799,9 +779,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { val scrollState = rememberScrollState() Column(modifier = modifier.verticalScroll(scrollState), verticalArrangement = Arrangement.Bottom) { if (isExpanded) options.forEachIndexed { _, button -> - FloatingActionButton(modifier = Modifier.padding(start = 4.dp, bottom = 6.dp).height(40.dp), - containerColor = Color.LightGray, - onClick = {}) { button() } + FloatingActionButton(modifier = Modifier.padding(start = 4.dp, bottom = 6.dp).height(40.dp), containerColor = Color.LightGray, onClick = {}) { button() } } FloatingActionButton(containerColor = MaterialTheme.colorScheme.secondaryContainer, contentColor = MaterialTheme.colorScheme.secondary, @@ -826,10 +804,8 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { }) { if (if (useGrid == null) useGridLayout else useGrid!!) { val lazyGridState = rememberLazyGridState() - LazyVerticalGrid(state = lazyGridState, - columns = GridCells.Fixed(3), - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalArrangement = Arrangement.spacedBy(16.dp), + LazyVerticalGrid(state = lazyGridState, columns = GridCells.Fixed(3), + verticalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(start = 12.dp, top = 16.dp, end = 12.dp, bottom = 16.dp) ) { items(feedListFiltered.size, key = {index -> feedListFiltered[index].id}) { index -> @@ -867,8 +843,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { val textColor = MaterialTheme.colorScheme.onSurface ConstraintLayout { val (coverImage, episodeCount, error) = createRefs() - AsyncImage(model = feed.imageUrl, contentDescription = "coverImage", - placeholder = painterResource(R.mipmap.ic_launcher), + AsyncImage(model = feed.imageUrl, contentDescription = "coverImage", placeholder = painterResource(R.mipmap.ic_launcher), modifier = Modifier .constrainAs(coverImage) { top.linkTo(parent.top) @@ -881,24 +856,19 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { top.linkTo(coverImage.top) }) // TODO: need to use state - if (feed.lastUpdateFailed) Icon(painter = painterResource(R.drawable.ic_error), tint = Color.Red, - contentDescription = "error", + if (feed.lastUpdateFailed) Icon(painter = painterResource(R.drawable.ic_error), tint = Color.Red, contentDescription = "error", modifier = Modifier.constrainAs(error) { end.linkTo(parent.end) bottom.linkTo(coverImage.bottom) }) } - Text(feed.title ?: "No title", - color = textColor, - maxLines = 2, - overflow = TextOverflow.Ellipsis) + Text(feed.title ?: "No title", color = textColor, maxLines = 2, overflow = TextOverflow.Ellipsis) } } } } else { val lazyListState = rememberLazyListState() - LazyColumn(state = lazyListState, - modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp, bottom = 10.dp), + LazyColumn(state = lazyListState, modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 10.dp, bottom = 10.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { itemsIndexed(feedListFiltered, key = {index, feed -> feed.id}) { index, feed -> var isSelected by remember { mutableStateOf(false) } @@ -912,8 +882,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { Logd(TAG, "toggleSelected: selected: ${selected.size}") } Row(Modifier.background(if (isSelected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface)) { - AsyncImage(model = feed.imageUrl, contentDescription = "imgvCover", - placeholder = painterResource(R.mipmap.ic_launcher), + AsyncImage(model = feed.imageUrl, contentDescription = "imgvCover", placeholder = painterResource(R.mipmap.ic_launcher), modifier = Modifier.width(80.dp).height(80.dp) .clickable(onClick = { Logd(TAG, "icon clicked!") @@ -964,13 +933,9 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { } } if (selectMode) { - Row(modifier = Modifier.align(Alignment.TopEnd).width(150.dp).height(45.dp) - .background(Color.LightGray), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically) { - Icon(painter = painterResource(R.drawable.baseline_arrow_upward_24), - tint = Color.Black, - contentDescription = null, + Row(modifier = Modifier.align(Alignment.TopEnd).width(150.dp).height(45.dp).background(Color.LightGray), + horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically) { + Icon(painter = painterResource(R.drawable.baseline_arrow_upward_24), tint = Color.Black, contentDescription = null, modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp) .clickable(onClick = { selected.clear() @@ -980,9 +945,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { selectedSize = selected.size Logd(TAG, "selectedIds: ${selected.size}") })) - Icon(painter = painterResource(R.drawable.baseline_arrow_downward_24), - tint = Color.Black, - contentDescription = null, + Icon(painter = painterResource(R.drawable.baseline_arrow_downward_24), tint = Color.Black, contentDescription = null, modifier = Modifier.width(35.dp).height(35.dp).padding(end = 10.dp) .clickable(onClick = { selected.clear() @@ -993,9 +956,7 @@ class SubscriptionsFragment : Fragment(), Toolbar.OnMenuItemClickListener { Logd(TAG, "selectedIds: ${selected.size}") })) var selectAllRes by remember { mutableIntStateOf(R.drawable.ic_select_all) } - Icon(painter = painterResource(selectAllRes), - tint = Color.Black, - contentDescription = null, + Icon(painter = painterResource(selectAllRes), tint = Color.Black, contentDescription = null, modifier = Modifier.width(35.dp).height(35.dp) .clickable(onClick = { if (selectedSize != feedListFiltered.size) { diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml index 29ccc747..21615f3c 100644 --- a/app/src/main/res/layout/audioplayer_fragment.xml +++ b/app/src/main/res/layout/audioplayer_fragment.xml @@ -9,7 +9,7 @@ android:orientation="vertical"> diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml index 74bc0fcd..cc7449c1 100644 --- a/app/src/main/res/layout/feedinfo.xml +++ b/app/src/main/res/layout/feedinfo.xml @@ -25,10 +25,10 @@ app:navigationContentDescription="@string/toolbar_back_button_content_description" app:navigationIcon="?homeAsUpIndicator" /> - + + + + - + + + + + - + + + + + - + + + + - + + + + - - - - + + + + + + + + + + + +