diff --git a/app/build.gradle b/app/build.gradle index 8039a3ec..d4fd1817 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,8 +26,8 @@ android { vectorDrawables.useSupportLibrary false vectorDrawables.generatedDensities = [] - versionCode 3020339 - versionName "7.3.2" + versionCode 3020340 + versionName "7.3.3" ndkVersion "27.0.12077973" diff --git a/app/src/main/kotlin/ac/mdiq/podcini/preferences/ComboTransporter.kt b/app/src/main/kotlin/ac/mdiq/podcini/preferences/ComboTransporter.kt index a7da9e3d..c67acd76 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/preferences/ComboTransporter.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/preferences/ComboTransporter.kt @@ -153,7 +153,7 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { feed!!.episodes.forEach { e -> if (!e.title.isNullOrEmpty()) nameEpisodeMap[generateFileName(e.title!!)] = e } nameEpisodeMap.keys.forEach { Logd(TAG, "key: $it") } val dirFiles = srcFile.listFiles() - Logd(TAG, "srcFile: ${srcFile.name} dirFiles: ${dirFiles.size}") + Logd(TAG, "srcFile: ${srcFile.name} dirFiles: ${dirFiles?.size}") if (!dirFiles.isNullOrEmpty()) { val destDir = destRootDir.findFile(relativePath) ?: destRootDir.createDirectory(relativePath) ?: return dirFiles.forEach { file -> copyRecursive(context, file, srcFile, destDir, move) } @@ -192,7 +192,7 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { throw e } } - private fun copyRecursive(context: Context, srcFile: DocumentFile, srcRootDir: DocumentFile, destRootDir: DocumentFile, move: Boolean) { + private fun copyRecursive(context: Context, srcFile: DocumentFile, srcRootDir: DocumentFile, destRootDir: DocumentFile, move: Boolean, onlyUpdateDB: Boolean = false) { val relativePath = srcFile.uri.path?.substring(srcRootDir.uri.path!!.length+1) ?: return if (srcFile.isDirectory) { Logd(TAG, "copyRecursiveDD folder title: $relativePath") @@ -204,7 +204,7 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { val destDir = destRootDir.findFile(relativePath) ?: destRootDir.createDirectory(relativePath) ?: return val files = srcFile.listFiles() if (files.isNotEmpty()) files.forEach { file -> copyRecursive(context, file, srcFile, destDir, move) } - if (move) srcFile.delete() + if (!onlyUpdateDB && move) srcFile.delete() } else { val nameParts = relativePath.split(".") if (nameParts.size < 3) return @@ -216,7 +216,7 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { val destName = "$title.${episode.id}.$ext" val destFile = destRootDir.createFile(getMimeType(destName), destName) ?: return Logd(TAG, "copyRecursiveDD copying file to: ${destFile.uri}") - copyFile(srcFile, destFile, context, move) + if (!onlyUpdateDB) copyFile(srcFile, destFile, context, move) upsertBlk(episode) { it.fileUrl = destFile.uri.toString() it.setIsDownloaded() @@ -289,6 +289,54 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) outputStream.write(buffer, 0, bytesRead) } + private fun copyRecursive(srcFile: File, srcRootDir: File, destRootDir: File, move: Boolean, onlyUpdateDB: Boolean = false) { + val relativePath = srcFile.absolutePath.substring(srcRootDir.absolutePath.length+1) +// val relativePath = srcFile.name + if (srcFile.isDirectory) { + feed = nameFeedMap[relativePath] ?: return + Logd(TAG, "copyRecursiveFD found feed: ${feed?.title}") + nameEpisodeMap.clear() + feed!!.episodes.forEach { e -> if (!e.title.isNullOrEmpty()) nameEpisodeMap[generateFileName(e.title!!)] = e } + nameEpisodeMap.keys.forEach { Logd(TAG, "key: $it") } + val destFile = File(destRootDir, relativePath) + if (!destFile.exists()) destFile.mkdirs() + val files = srcFile.listFiles() + if (!files.isNullOrEmpty()) files.forEach { file -> copyRecursive(file, srcFile, destFile, move) } + if (!onlyUpdateDB && move) srcFile.delete() + } else { + val nameParts = relativePath.split(".") + if (nameParts.size < 3) return + val ext = nameParts[nameParts.size-1] + val title = nameParts.dropLast(2).joinToString(".") + Logd(TAG, "copyRecursiveFD file title: $title") + val episode = nameEpisodeMap[title] ?: return + Logd(TAG, "copyRecursiveFD found episode: ${episode.title}") + val destName = "$title.${episode.id}.$ext" + val destFile = File(destRootDir, destName) + if (!destFile.exists()) { + Logd(TAG, "copyRecursiveDF copying file to: ${destFile.absolutePath}") + if (!onlyUpdateDB) copyFile(srcFile, destFile, move) + upsertBlk(episode) { + it.fileUrl = Uri.fromFile(destFile).toString() + Logd(TAG, "copyRecursiveDF fileUrl: ${it.fileUrl}") + it.setIsDownloaded() + } + } + } + } + private fun copyFile(sourceFile: File, destFile: File, move: Boolean) { + try { + val inputStream = FileInputStream(sourceFile) + val outputStream = FileOutputStream(destFile) + copyStream(inputStream, outputStream) + inputStream.close() + outputStream.close() + if (move) sourceFile.delete() + } catch (e: IOException) { + Log.e("Error", "Error copying file: $e") + throw e + } + } @Throws(IOException::class) fun importFromUri(uri: Uri, context: Context, move: Boolean = false, verify : Boolean = true) { try { @@ -317,6 +365,33 @@ class MediaFilesTransporter(val mediaFilesDirName: String) { feed = null } } + @Throws(IOException::class) + fun updateDB(context: Context) { + try { + val feeds = getFeedList() + feeds.forEach { f -> if (!f.title.isNullOrEmpty()) nameFeedMap[generateFileName(f.title!!)] = f } + Logd(TAG, "importFromUri customMediaUriString: [$customMediaUriString]") + if (customMediaUriString.isNotBlank()) { + val customUri = Uri.parse(customMediaUriString) + val directory = DocumentFile.fromTreeUri(getAppContext(), customUri) ?: throw IllegalArgumentException("Invalid tree URI: $customMediaUriString") + val exportedDir = directory + val fileList = exportedDir.listFiles() + fileList.forEach { file -> copyRecursive(context, file, exportedDir, directory, false, true) } + } else { + val mediaDir = context.getExternalFilesDir("media") ?: return + val exportedDir = mediaDir + val fileList = exportedDir.listFiles() + fileList?.forEach { file -> copyRecursive(file, exportedDir, mediaDir, false, true) } + } + } catch (e: IOException) { + Log.e(TAG, Log.getStackTraceString(e)) + throw e + } finally { + nameFeedMap.clear() + nameEpisodeMap.clear() + feed = null + } + } } class DatabaseTransporter { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt index 0df68b71..b1e48ff4 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/database/RealmDB.kt @@ -101,30 +101,30 @@ object RealmDB { Logd(TAG, "migrating DB from below 37") mContext.enumerate(className = "Episode") { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? -> newObject?.run { - Logd(TAG, "start: ${getNullableValue("title", String::class)}") +// Logd(TAG, "start: ${getNullableValue("title", String::class)}") val media = oldObject.getObject(propertyName = "media") if (media != null) { set("fileUrl", media.getNullableValue("fileUrl", String::class)) set("downloadUrl", media.getNullableValue("downloadUrl", String::class)) set("mimeType", media.getNullableValue("mimeType", String::class)) - Logd(TAG, "after mimeType") +// Logd(TAG, "after mimeType") set("downloaded", media.getValue("downloaded", Boolean::class)) - Logd(TAG, "after downloaded") +// Logd(TAG, "after downloaded") set("downloadTime", media.getValue("downloadTime", Long::class)) set("duration", media.getValue("duration", Long::class)) set("position", media.getValue("position", Long::class)) set("lastPlayedTime", media.getValue("lastPlayedTime", Long::class)) set("startPosition", media.getValue("startPosition", Long::class)) - Logd(TAG, "after startPosition") +// Logd(TAG, "after startPosition") set("playedDurationWhenStarted", media.getValue("playedDurationWhenStarted", Long::class)) set("playedDuration", media.getValue("playedDuration", Long::class)) set("timeSpentOnStart", media.getValue("timeSpentOnStart", Long::class)) set("startTime", media.getValue("startTime", Long::class)) - Logd(TAG, "after startTime") +// Logd(TAG, "after startTime") set("timeSpent", media.getValue("timeSpent", Long::class)) set("size", media.getValue("size", Long::class)) set("playbackCompletionTime", media.getValue("playbackCompletionTime", Long::class)) - Logd(TAG, "after all") +// Logd(TAG, "after all") } } } @@ -139,55 +139,71 @@ object RealmDB { set("keepUpdated", pref.getValue("keepUpdated", Boolean::class)) set("username", pref.getNullableValue("username", String::class)) set("password", pref.getNullableValue("password", String::class)) - Logd(TAG, "after password") +// Logd(TAG, "after password") set("videoMode", pref.getValue("videoMode", Long::class)) set("playSpeed", pref.getValue("playSpeed", Float::class)) set("introSkip", pref.getValue("introSkip", Long::class)) set("endingSkip", pref.getValue("endingSkip", Long::class)) set("autoDelete", pref.getValue("autoDelete", Long::class)) - Logd(TAG, "after autoDelete") +// Logd(TAG, "after autoDelete") set("audioType", pref.getValue("audioType", Long::class)) set("volumeAdaption", pref.getValue("volumeAdaption", Long::class)) set("audioQuality", pref.getValue("audioQuality", Long::class)) set("videoQuality", pref.getValue("videoQuality", Long::class)) set("prefStreamOverDownload", pref.getValue("prefStreamOverDownload", Boolean::class)) set("filterString", pref.getValue("filterString", String::class)) - Logd(TAG, "after filterString") +// Logd(TAG, "after filterString") set("sortOrderCode", pref.getValue("sortOrderCode", Long::class)) val tagsSet = getValueSet("tags") tagsSet.addAll(pref.getValueSet("tags")) set("autoDownload", pref.getValue("autoDownload", Boolean::class)) set("queueId", pref.getValue("queueId", Long::class)) - Logd(TAG, "after queueId") +// Logd(TAG, "after queueId") set("autoAddNewToQueue", pref.getValue("autoAddNewToQueue", Boolean::class)) set("autoDLInclude", pref.getNullableValue("autoDLInclude", String::class)) set("autoDLExclude", pref.getNullableValue("autoDLExclude", String::class)) set("autoDLMinDuration", pref.getValue("autoDLMinDuration", Long::class)) - Logd(TAG, "after autoDLMinDuration") +// Logd(TAG, "after autoDLMinDuration") set("markExcludedPlayed", pref.getValue("markExcludedPlayed", Boolean::class)) set("autoDLMaxEpisodes", pref.getValue("autoDLMaxEpisodes", Long::class)) set("countingPlayed", pref.getValue("countingPlayed", Boolean::class)) set("autoDLPolicyCode", pref.getValue("autoDLPolicyCode", Long::class)) - Logd(TAG, "after all") +// Logd(TAG, "after all") } } } } if (oldRealm.schemaVersion() < 39) { Logd(TAG, "migrating DB from below 39") +// val oldEpisodes = oldRealm.query(className = "Episode").find() +// for (oe in oldEpisodes) { +// try { +// val fileUrl = oe.getNullableValue("fileUrl", String::class) +// if (!fileUrl.isNullOrBlank()) { +// val f = File(fileUrl) +// val uri = Uri.fromFile(f) +// val ne = newRealm.findLatest(oe) +// ne?.set("fileUrl", uri.toString()) +// } +// } catch (e: Throwable) { +// Log.e(TAG, " can't create uri from fileUrl") +// val ne = newRealm.findLatest(oe) +// ne?.set("fileUrl", "") +// } +// } mContext.enumerate(className = "Episode") { oldObject: DynamicRealmObject, newObject: DynamicMutableRealmObject? -> newObject?.run { - val fileUrl = oldObject.getNullableValue("fileUrl", String::class) - Logd(TAG, "fileUrl: $fileUrl") - if (!fileUrl.isNullOrBlank()) { - try { + try { + val fileUrl = oldObject.getNullableValue("fileUrl", String::class) + Logd(TAG, "fileUrl: $fileUrl") + if (!fileUrl.isNullOrBlank()) { val f = File(fileUrl) val uri = Uri.fromFile(f) set("fileUrl", uri.toString()) - } catch (e: Throwable) { - Log.e(TAG, " can't create uri from $fileUrl") - set("fileUrl", "") } + } catch (e: Throwable) { + Log.e(TAG, " can't create uri from fileUrl") + set("fileUrl", "") } } } diff --git a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt index 32de1c47..60fa691b 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/storage/model/Episode.kt @@ -21,7 +21,9 @@ import ac.mdiq.podcini.storage.utils.StorageUtils.getDataFolder import ac.mdiq.podcini.storage.utils.StorageUtils.getMimeType import ac.mdiq.podcini.util.Logd import ac.mdiq.vista.extractor.Vista +import ac.mdiq.vista.extractor.stream.AudioStream import ac.mdiq.vista.extractor.stream.StreamInfo +import ac.mdiq.vista.extractor.stream.VideoStream import android.content.ContentResolver import android.content.Context import android.media.MediaMetadataRetriever @@ -178,6 +180,9 @@ class Episode : RealmObject { return field } +// var audioStreamsList: RealmList = realmListOf() +// var videoStreamsList: RealmList = realmListOf() + @Ignore val isRemote = mutableStateOf(false) @@ -535,7 +540,10 @@ class Episode : RealmObject { false } } - else -> throw IllegalArgumentException("Unsupported URI scheme: ${fileuri.scheme}") + else -> { + Logd(TAG, "Unsupported URI scheme: ${fileuri.scheme}") + false + } } } 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 f7d903cb..2f357ed8 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 @@ -23,7 +23,6 @@ import ac.mdiq.podcini.storage.database.Feeds.getFeed import ac.mdiq.podcini.storage.database.Feeds.monitorFeeds import ac.mdiq.podcini.storage.database.RealmDB.runOnIOScope import ac.mdiq.podcini.storage.model.Feed -import ac.mdiq.podcini.ui.utils.starter.MainActivityStarter import ac.mdiq.podcini.ui.compose.CustomTheme import ac.mdiq.podcini.ui.dialog.RatingDialog import ac.mdiq.podcini.ui.screens.* @@ -31,6 +30,7 @@ import ac.mdiq.podcini.ui.utils.feedOnDisplay import ac.mdiq.podcini.ui.utils.setOnlineFeedUrl import ac.mdiq.podcini.ui.utils.setOnlineSearchTerms import ac.mdiq.podcini.ui.utils.setSearchTerms +import ac.mdiq.podcini.ui.utils.starter.MainActivityStarter import ac.mdiq.podcini.util.EventFlow import ac.mdiq.podcini.util.FlowEvent import ac.mdiq.podcini.util.Logd @@ -48,13 +48,13 @@ import android.os.Bundle import android.os.PowerManager import android.os.StrictMode import android.provider.Settings -import android.util.DisplayMetrics import android.util.Log import android.view.KeyEvent import android.view.MenuItem import android.view.View import android.widget.EditText import android.widget.Toast +import androidx.activity.OnBackPressedCallback import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.BorderStroke @@ -65,13 +65,11 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.core.graphics.Insets -import androidx.core.view.WindowCompat import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController import androidx.navigation.NavHostController @@ -105,16 +103,6 @@ class MainActivity : CastEnabledActivity() { var showUnrestrictedBackgroundPermissionDialog by mutableStateOf(false) -// private val isDrawerOpen: Boolean -// get() = drawerLayout?.isDrawerOpen(navDrawer) == true - -// private val screenWidth: Int -// get() { -// val displayMetrics = DisplayMetrics() -// windowManager.defaultDisplay.getMetrics(displayMetrics) -// return displayMetrics.widthPixels -// } - enum class Screens { Subscriptions, FeedEpisodes, @@ -148,11 +136,32 @@ class MainActivity : CastEnabledActivity() { lifecycleScope.launch((Dispatchers.IO)) { buildTags() } - if (savedInstanceState != null) ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(Extras.generated_view_id.name, 0)) +// if (savedInstanceState != null) ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(Extras.generated_view_id.name, 0)) - WindowCompat.setDecorFitsSystemWindows(window, false) +// WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) + onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + Logd(TAG, "handleOnBackPressed called") + when { + isDrawerOpen -> closeDrawer() + isBSExpanded -> isBSExpanded = false + mainNavController.previousBackStackEntry != null -> mainNavController.popBackStack() + else -> { + val toPage = defaultPage + if (getLastNavScreen() == toPage || AppPreferences.DefaultPages.Remember.name == toPage) { + if (getPref(AppPrefs.prefBackButtonOpensDrawer, false)) openDrawer() + else { + isEnabled = false + onBackPressedDispatcher.onBackPressed() + } + } else loadScreen(toPage, null) + } + } + } + }) + setContent { CustomTheme(this) { // if (showToast) CustomToast(message = toastMassege, onDismiss = { showToast = false }) @@ -196,25 +205,29 @@ class MainActivity : CastEnabledActivity() { lcScope = rememberCoroutineScope() val navController = rememberNavController() mainNavController = navController - val scaffoldState = rememberBottomSheetScaffoldState() - bottomSheetScaffoldState.value = scaffoldState + val sheetState = rememberBottomSheetScaffoldState(bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded)) if (showUnrestrictedBackgroundPermissionDialog) UnrestrictedBackgroundPermissionDialog { showUnrestrictedBackgroundPermissionDialog = false } - val insets = WindowInsets.systemBars.asPaddingValues() - val dynamicBottomPadding = insets.calculateBottomPadding() - Logd(TAG, "effectiveBottomPadding: $dynamicBottomPadding") + LaunchedEffect(isBSExpanded) { + if (isBSExpanded) lcScope?.launch { sheetState.bottomSheetState.expand() } + else lcScope?.launch { sheetState.bottomSheetState.partialExpand() } + } ModalNavigationDrawer(drawerState = drawerState, modifier = Modifier.fillMaxHeight(), drawerContent = { NavDrawerScreen() }) { - BottomSheetScaffold(scaffoldState = scaffoldState, sheetPeekHeight = dynamicBottomPadding + 110.dp, sheetDragHandle = {}, topBar = {}, + val insets = WindowInsets.systemBars.asPaddingValues() + var dynamicBottomPadding by remember { mutableStateOf(0.dp) } + dynamicBottomPadding = insets.calculateBottomPadding() + Logd(TAG, "effectiveBottomPadding: $dynamicBottomPadding") + BottomSheetScaffold(scaffoldState = sheetState, sheetPeekHeight = dynamicBottomPadding + 110.dp, sheetDragHandle = {}, topBar = {}, sheetSwipeEnabled = false, sheetShape = RectangleShape, sheetContent = { AudioPlayerScreen() } ) { paddingValues -> Box(modifier = Modifier.fillMaxSize().padding( - start = paddingValues.calculateStartPadding(LayoutDirection.Ltr), top = paddingValues.calculateTopPadding(), - end = paddingValues.calculateEndPadding(LayoutDirection.Ltr), - bottom = dynamicBottomPadding + 110.dp + start = paddingValues.calculateStartPadding(LocalLayoutDirection.current), + end = paddingValues.calculateEndPadding(LocalLayoutDirection.current), + bottom = paddingValues.calculateBottomPadding() - 110.dp )) { CompositionLocalProvider(LocalNavController provides navController) { NavHost(navController = navController, startDestination = Screens.Subscriptions.name) { @@ -330,7 +343,10 @@ class MainActivity : CastEnabledActivity() { override fun onAttachedToWindow() { super.onAttachedToWindow() - updateInsets() + // setPlayerVisible(audioPlayerView.visibility == View.VISIBLE) + val playerHeight = resources.getDimension(R.dimen.external_player_height).toInt() + Logd(TAG, "playerHeight: $playerHeight ${navigationBarInsets.bottom}") +// bottomSheet.peekHeight = playerHeight + navigationBarInsets.bottom } /** @@ -339,11 +355,11 @@ class MainActivity : CastEnabledActivity() { * This makes sure that we do not get ID collisions * and therefore errors when trying to restore state from another view. */ - private fun ensureGeneratedViewIdGreaterThan(minimum: Int) { - while (View.generateViewId() <= minimum) { - // Generate new IDs - } - } +// private fun ensureGeneratedViewIdGreaterThan(minimum: Int) { +// while (View.generateViewId() <= minimum) { +// // Generate new IDs +// } +// } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) @@ -395,13 +411,6 @@ class MainActivity : CastEnabledActivity() { } } - private fun updateInsets() { -// setPlayerVisible(audioPlayerView.visibility == View.VISIBLE) - val playerHeight = resources.getDimension(R.dimen.external_player_height).toInt() - Logd(TAG, "playerHeight: $playerHeight ${navigationBarInsets.bottom}") -// bottomSheet.peekHeight = playerHeight + navigationBarInsets.bottom - } - fun setPlayerVisible(visible_: Boolean?) { Logd(TAG, "setPlayerVisible $visible_") // val visible = visible_ ?: (bottomSheet.state != BottomSheetBehavior.STATE_COLLAPSED) @@ -495,21 +504,6 @@ class MainActivity : CastEnabledActivity() { } } - override fun onBackPressed() { - when { - isDrawerOpen -> closeDrawer() - isBottomSheetExpanded -> collapseBottomSheet() - mainNavController.previousBackStackEntry != null -> mainNavController.popBackStack() - else -> { - val toPage = defaultPage - if (getLastNavScreen() == toPage || AppPreferences.DefaultPages.Remember.name == toPage) { - if (getPref(AppPrefs.prefBackButtonOpensDrawer, false)) openDrawer() - else super.onBackPressed() - } else loadScreen(toPage, null) - } - } - } - private var eventSink: Job? = null private var eventStickySink: Job? = null private fun cancelFlowEvents() { @@ -557,7 +551,7 @@ class MainActivity : CastEnabledActivity() { mainNavController.navigate(Screens.FeedEpisodes.name) } } - collapseBottomSheet() + isBSExpanded = false } intent.hasExtra(Extras.fragment_feed_url.name) -> { val feedurl = intent.getStringExtra(Extras.fragment_feed_url.name) @@ -580,7 +574,7 @@ class MainActivity : CastEnabledActivity() { //// bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) // } intent.getBooleanExtra(MainActivityStarter.Extras.open_player.name, false) -> { - expandBottomSheet() + isBSExpanded = true // bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED // bottomSheetCallback.onSlide(dummyView, 1.0f) } @@ -695,11 +689,6 @@ class MainActivity : CastEnabledActivity() { companion object { private val TAG: String = MainActivity::class.simpleName ?: "Anonymous" - const val MAIN_FRAGMENT_TAG: String = "main" -// const val PREF_NAME: String = "MainActivityPrefs" - - const val REQUEST_CODE_FIRST_PERMISSION = 1001 - const val REQUEST_CODE_SECOND_PERMISSION = 1002 lateinit var mainNavController: NavHostController val LocalNavController = staticCompositionLocalOf { error("NavController not provided") } @@ -718,22 +707,7 @@ class MainActivity : CastEnabledActivity() { val isDrawerOpen: Boolean get() = drawerState.isOpen - @OptIn(ExperimentalMaterial3Api::class) - val bottomSheetScaffoldState = mutableStateOf(null) - - @OptIn(ExperimentalMaterial3Api::class) - fun expandBottomSheet() { - lcScope?.launch { bottomSheetScaffoldState.value?.bottomSheetState?.expand() } - } - - @OptIn(ExperimentalMaterial3Api::class) - fun collapseBottomSheet() { - lcScope?.launch { bottomSheetScaffoldState.value?.bottomSheetState?.partialExpand() } - } - - @OptIn(ExperimentalMaterial3Api::class) - val isBottomSheetExpanded: Boolean - get() = bottomSheetScaffoldState.value?.bottomSheetState?.hasExpandedState == true + var isBSExpanded by mutableStateOf(false) @JvmStatic fun getIntentToOpenFeed(context: Context, feedId: Long): Intent { 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 cb991b6d..e64edff8 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 @@ -4,6 +4,7 @@ import ac.mdiq.podcini.R import ac.mdiq.podcini.preferences.AppPreferences.getPref import ac.mdiq.podcini.preferences.AppPreferences.putPref import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.activity.MainActivity.Companion.isBSExpanded import android.content.Context import android.text.Spannable import android.text.SpannableString @@ -274,7 +275,7 @@ fun MediaPlayerErrorDialog(activity: Context, message: String, showDialog: Mutab }, confirmButton = { TextButton(onClick = { - MainActivity.collapseBottomSheet() + isBSExpanded = false showDialog.value = false }) { Text("OK") } }, 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 e89ca54d..e548f40a 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 @@ -995,7 +995,7 @@ fun EpisodesFilterDialog(filter: EpisodeFilter? = null, filtersDisabled: Mutable val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> window.setGravity(Gravity.BOTTOM) - window.setDimAmount(0f) +// window.setDimAmount(0f) } Surface(modifier = Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 10.dp).height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) { @@ -1147,7 +1147,7 @@ fun EpisodeSortDialog(initOrder: EpisodeSortOrder, showKeepSorted: Boolean = fal val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> window.setGravity(Gravity.BOTTOM) - window.setDimAmount(0f) +// window.setDimAmount(0f) } Surface(modifier = Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 10.dp).height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt index 5d7d2c8e..70140103 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/compose/Feeds.kt @@ -490,7 +490,7 @@ fun PlaybackSpeedFullDialog(settingCode: BooleanArray, indexDefault: Int, maxSpe val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> window.setGravity(Gravity.BOTTOM) - window.setDimAmount(0f) +// window.setDimAmount(0f) } Card(modifier = Modifier.fillMaxWidth().wrapContentHeight().padding(top = 10.dp, bottom = 10.dp), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) { Column { diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/AudioPlayerScreen.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/AudioPlayerScreen.kt index a1a3fd83..db5f4ddf 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/AudioPlayerScreen.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/AudioPlayerScreen.kt @@ -40,8 +40,7 @@ import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.utils.DurationConverter import ac.mdiq.podcini.storage.utils.DurationConverter.convertOnSpeed import ac.mdiq.podcini.ui.activity.MainActivity -import ac.mdiq.podcini.ui.activity.MainActivity.Companion.collapseBottomSheet -import ac.mdiq.podcini.ui.activity.MainActivity.Companion.expandBottomSheet +import ac.mdiq.podcini.ui.activity.MainActivity.Companion.isBSExpanded import ac.mdiq.podcini.ui.activity.VideoplayerActivity.Companion.videoMode import ac.mdiq.podcini.ui.compose.* import ac.mdiq.podcini.ui.utils.ShownotesCleaner @@ -107,8 +106,6 @@ import kotlin.math.sin class AudioPlayerVM(val context: Context, val lcScope: CoroutineScope) { internal val actMain: MainActivity? = generateSequence(context) { if (it is ContextWrapper) it.baseContext else null }.filterIsInstance().firstOrNull() - internal var isCollapsed by mutableStateOf(true) - internal var controllerFuture: ListenableFuture? = null internal var controller: ServiceStatusHandler? = null @@ -209,7 +206,7 @@ class AudioPlayerVM(val context: Context, val lcScope: CoroutineScope) { } // if (isShowPlay) setIsShowPlay(false) onPositionUpdate(event) - if (!isCollapsed) { + if (isBSExpanded) { if (curItem?.id != event.episode.id) return val newChapterIndex: Int = curItem!!.getCurrentChapterIndex(event.position) if (newChapterIndex >= 0 && newChapterIndex != displayedChapterIndex) refreshChapterData(newChapterIndex) @@ -264,7 +261,7 @@ class AudioPlayerVM(val context: Context, val lcScope: CoroutineScope) { txtvPlaybackSpeed = DecimalFormat("0.00").format(curSpeedFB.toDouble()) curPlaybackSpeed = curSpeedFB onPositionUpdate(FlowEvent.PlaybackPositionEvent(media, media.position, media.duration)) - if (isPlayingVideoLocally && curEpisode?.feed?.videoModePolicy != VideoMode.AUDIO_ONLY) collapseBottomSheet() + if (isPlayingVideoLocally && curEpisode?.feed?.videoModePolicy != VideoMode.AUDIO_ONLY) isBSExpanded = false prevItem = media } @@ -395,7 +392,7 @@ class AudioPlayerVM(val context: Context, val lcScope: CoroutineScope) { // imgLoc = ImageResourceUtils.getEpisodeListImageLocation(curMedia!!) curItem = curEpisode } - if (!isCollapsed && curMediaChanged) { + if (isBSExpanded && curMediaChanged) { Logd(TAG, "loadMediaInfo loading details ${curEpisode?.id}") lcScope.launch { withContext(Dispatchers.IO) { curEpisode?.apply { this.loadChapters(context, false) } } @@ -428,20 +425,6 @@ class AudioPlayerVM(val context: Context, val lcScope: CoroutineScope) { homeText = null } } - - internal fun onExpanded() { - Logd(TAG, "onExpanded()") -// the function can also be called from MainActivity when a select menu pops up and closes - isCollapsed = false - if (shownotesCleaner == null) shownotesCleaner = ShownotesCleaner(context) - setIsShowPlay(isShowPlay) - } - - internal fun onCollaped() { - Logd(TAG, "onCollaped()") - isCollapsed = true - setIsShowPlay(isShowPlay) - } } @OptIn(ExperimentalMaterial3Api::class) @@ -473,7 +456,7 @@ fun AudioPlayerScreen() { } vm.controller!!.init() } - vm.onCollaped() + isBSExpanded = false if (vm.shownotesCleaner == null) vm.shownotesCleaner = ShownotesCleaner(context) vm.actMain?.setPlayerVisible(curEpisode != null) if (curEpisode != null) vm.updateUi(curEpisode!!) @@ -516,6 +499,16 @@ fun AudioPlayerScreen() { } } + LaunchedEffect(isBSExpanded) { + if (isBSExpanded) { + if (vm.shownotesCleaner == null) vm.shownotesCleaner = ShownotesCleaner(context) + vm.setIsShowPlay(vm.isShowPlay) + } else { + vm.setIsShowPlay(vm.isShowPlay) + } + Logd(TAG, "isExpanded: ${isBSExpanded}") + } + @OptIn(ExperimentalFoundationApi::class) @Composable fun ControlUI() { @@ -547,7 +540,7 @@ fun AudioPlayerScreen() { modifier = Modifier.width(65.dp).height(65.dp).padding(start = 5.dp) .clickable(onClick = { Logd(TAG, "playerUi icon was clicked") - if (vm.isCollapsed) { + if (!isBSExpanded) { val media = curEpisode if (media != null) { val mediaType = media.getMediaType() @@ -555,19 +548,14 @@ fun AudioPlayerScreen() { || (media.feed?.videoModePolicy == VideoMode.AUDIO_ONLY)) { Logd(TAG, "popping as audio episode") ensureService() - vm.onExpanded() - expandBottomSheet() -// (context as MainActivity).bottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED) + isBSExpanded = true } else { Logd(TAG, "popping video context") val intent = getPlayerActivityIntent(context, mediaType) context.startActivity(intent) } } - } else { - vm.onCollaped() - collapseBottomSheet() - } + } else isBSExpanded = false })) Spacer(Modifier.weight(0.1f)) Column(horizontalAlignment = Alignment.CenterHorizontally) { @@ -735,8 +723,7 @@ fun AudioPlayerScreen() { if (showShareDialog && vm.curItem != null && vm.actMain != null) ShareDialog(vm.curItem!!, vm.actMain) {showShareDialog = false } Row(modifier = Modifier.fillMaxWidth().padding(10.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Icon(imageVector = ImageVector.vectorResource(R.drawable.ic_arrow_down), tint = textColor, contentDescription = "Collapse", modifier = Modifier.clickable { - vm.onCollaped() - collapseBottomSheet() + isBSExpanded = false }) var homeIcon by remember { mutableIntStateOf(R.drawable.baseline_home_24)} Icon(imageVector = ImageVector.vectorResource(homeIcon), tint = textColor, contentDescription = "Home", modifier = Modifier.clickable { @@ -958,9 +945,9 @@ fun AudioPlayerScreen() { } } MediaPlayerErrorDialog(context, vm.errorMessage, vm.showErrorDialog) - Box(modifier = Modifier.fillMaxWidth().then(if (vm.isCollapsed) Modifier else Modifier.statusBarsPadding().navigationBarsPadding())) { - PlayerUI(Modifier.align(if (vm.isCollapsed) Alignment.TopCenter else Alignment.BottomCenter).zIndex(1f)) - if (!vm.isCollapsed) { + Box(modifier = Modifier.fillMaxWidth().then(if (!isBSExpanded) Modifier else Modifier.statusBarsPadding().navigationBarsPadding())) { + PlayerUI(Modifier.align(if (!isBSExpanded) Alignment.TopCenter else Alignment.BottomCenter).zIndex(1f)) + if (isBSExpanded) { if (vm.cleanedNotes == null) vm.updateDetails() Column(Modifier.padding(bottom = 120.dp)) { Toolbar() @@ -970,7 +957,6 @@ fun AudioPlayerScreen() { } } - // @Subscribe(threadMode = ThreadMode.MAIN) // @Suppress("unused") // fun bufferUpdate(event: BufferUpdateEvent) { @@ -1115,7 +1101,7 @@ abstract class ServiceStatusHandler(private val activity: MainActivity) { * should be used to update the GUI or start/cancel background threads. */ private fun handleStatus() { - Log.d(TAG, "handleStatus() called status: ${status}") + Log.d(TAG, "handleStatus() called status: $status") checkMediaInfoLoaded() when (status) { PlayerStatus.PLAYING -> updatePlayButton(false) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/EpisodesScreen.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/EpisodesScreen.kt index 31aeab83..c98492f8 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/EpisodesScreen.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/EpisodesScreen.kt @@ -6,6 +6,7 @@ import ac.mdiq.podcini.net.download.DownloadStatus import ac.mdiq.podcini.preferences.AppPreferences.AppPrefs import ac.mdiq.podcini.preferences.AppPreferences.getPref import ac.mdiq.podcini.preferences.AppPreferences.putPref +import ac.mdiq.podcini.preferences.MediaFilesTransporter import ac.mdiq.podcini.storage.database.Episodes import ac.mdiq.podcini.storage.database.Episodes.getEpisodes import ac.mdiq.podcini.storage.database.RealmDB.realm @@ -301,8 +302,12 @@ class EpisodesVM(val context: Context, val lcScope: CoroutineScope) { // val items = realm.query(Episode::class).query("media.episode == nil").find() // Logd(TAG, "number of episode with null backlink: ${items.size}") nameEpisodeMap.clear() - for (e in episodes) { - var fileUrl = e.fileUrl ?: continue + MediaFilesTransporter("").updateDB(context) + val eList = realm.query(Episode::class).find() + + for (e in eList) { + var fileUrl = e.fileUrl + if (fileUrl.isNullOrBlank()) continue fileUrl = fileUrl.substring(fileUrl.lastIndexOf('/') + 1) Logd(TAG, "reconcile: fileUrl: $fileUrl") nameEpisodeMap[fileUrl] = e @@ -516,7 +521,7 @@ fun EpisodesScreen() { vm.showClearHistoryDialog.value = true expanded = false }) - if (vm.vms.isNotEmpty() && vm.spinnerTexts[vm.curIndex] == QuickAccess.Downloaded.name) + if (vm.spinnerTexts[vm.curIndex] == QuickAccess.Downloaded.name) DropdownMenuItem(text = { Text(stringResource(R.string.reconcile_label)) }, onClick = { vm.reconcile() expanded = false diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/FeedEpisodesScreen.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/FeedEpisodesScreen.kt index d2127141..64e49a4e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/FeedEpisodesScreen.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/FeedEpisodesScreen.kt @@ -16,6 +16,7 @@ import ac.mdiq.podcini.ui.actions.SwipeActions import ac.mdiq.podcini.ui.actions.SwipeActions.Companion.SwipeActionsSettingDialog import ac.mdiq.podcini.ui.actions.SwipeActions.NoActionSwipeAction import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.activity.MainActivity.Companion.isBSExpanded import ac.mdiq.podcini.ui.activity.MainActivity.Companion.mainNavController import ac.mdiq.podcini.ui.activity.MainActivity.Screens import ac.mdiq.podcini.ui.compose.* @@ -490,7 +491,7 @@ fun FeedEpisodesScreen() { actions = { IconButton(onClick = { mainNavController.navigate(Screens.Queues.name) - MainActivity.collapseBottomSheet() + isBSExpanded = false }) { Icon(imageVector = ImageVector.vectorResource(R.drawable.playlist_play), contentDescription = "queue") } if (vm.feed != null) IconButton(onClick = { setSearchTerms("", vm.feed) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/NavDrawerScreen.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/NavDrawerScreen.kt index 8b75441a..eb3bf56f 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/NavDrawerScreen.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/NavDrawerScreen.kt @@ -8,6 +8,7 @@ import ac.mdiq.podcini.storage.model.* import ac.mdiq.podcini.storage.model.EpisodeFilter.Companion.unfiltered import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.ui.activity.MainActivity.Companion.closeDrawer +import ac.mdiq.podcini.ui.activity.MainActivity.Companion.isBSExpanded import ac.mdiq.podcini.ui.activity.MainActivity.Companion.mainNavController import ac.mdiq.podcini.ui.activity.MainActivity.Screens import ac.mdiq.podcini.ui.activity.PreferenceActivity @@ -89,7 +90,7 @@ fun NavDrawerScreen() { Logd(TAG, "ON_START") } Lifecycle.Event.ON_RESUME -> { - Logd(TAG, "ON_START") + Logd(TAG, "ON_RESUME") vm.loadData() } Lifecycle.Event.ON_STOP -> { @@ -133,7 +134,7 @@ fun NavDrawerScreen() { feedOnDisplay = f mainNavController.navigate(Screens.FeedEpisodes.name) { popUpTo(Screens.FeedEpisodes.name) { inclusive = true } } closeDrawer() - MainActivity.collapseBottomSheet() + isBSExpanded = false }) { AsyncImage(model = f.imageUrl, contentDescription = "imgvCover", placeholder = painterResource(R.mipmap.ic_launcher), error = painterResource(R.mipmap.ic_launcher), modifier = Modifier.width(40.dp).height(40.dp)) diff --git a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/SubscriptionsScreen.kt b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/SubscriptionsScreen.kt index 30ef438e..fa9c0c4e 100644 --- a/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/SubscriptionsScreen.kt +++ b/app/src/main/kotlin/ac/mdiq/podcini/ui/screens/SubscriptionsScreen.kt @@ -130,7 +130,7 @@ class SubscriptionsVM(val context: Context, val lcScope: CoroutineScope) { internal var displayedFolder by mutableStateOf("") internal var txtvInformation by mutableStateOf("") - internal var feedCount by mutableStateOf("") + internal var feedCountState by mutableStateOf("") internal var feedSorted by mutableIntStateOf(0) internal var sortIndex by mutableStateOf(0) @@ -186,7 +186,7 @@ class SubscriptionsVM(val context: Context, val lcScope: CoroutineScope) { tags.addAll(getTags()) } - private var eventSink: Job? = null + private var eventSink: Job? = null private var eventStickySink: Job? = null internal fun cancelFlowEvents() { eventSink?.cancel() @@ -241,7 +241,7 @@ class SubscriptionsVM(val context: Context, val lcScope: CoroutineScope) { noSubscription = feedList.isEmpty() feedListFiltered.clear() feedListFiltered.addAll(feedList) - feedCount = feedListFiltered.size.toString() + " / " + feedCount.toString() + feedCountState = feedListFiltered.size.toString() + " / " + feedCount.toString() infoTextFiltered = " " if (feedsFilter.isNotEmpty()) infoTextFiltered = context.getString(R.string.filtered_label) txtvInformation = (infoTextFiltered + infoTextUpdate) @@ -455,7 +455,7 @@ fun SubscriptionsScreen() { val queues = realm.query(PlayQueue::class).find() vm.queueIds.addAll(queues.map { it.id }) vm.spinnerTexts.addAll(queues.map { it.name }) - vm.feedCount = vm.feedListFiltered.size.toString() + " / " + feedCount.toString() + vm.feedCountState = vm.feedListFiltered.size.toString() + " / " + feedCount.toString() vm.loadSubscriptions() } Lifecycle.Event.ON_START -> { @@ -464,10 +464,10 @@ fun SubscriptionsScreen() { } Lifecycle.Event.ON_RESUME -> { Logd(TAG, "ON_RESUME") - vm.cancelFlowEvents() } Lifecycle.Event.ON_STOP -> { Logd(TAG, "ON_STOP") + vm.cancelFlowEvents() } Lifecycle.Event.ON_DESTROY -> { Logd(TAG, "ON_DESTROY") @@ -535,7 +535,7 @@ fun SubscriptionsScreen() { if (vm.feedsFilter.isNotEmpty()) vm.showFilterDialog = true } ) Spacer(Modifier.weight(1f)) - Text(vm.feedCount, color = textColor) + Text(vm.feedCountState, color = textColor) } } @@ -1004,7 +1004,9 @@ fun SubscriptionsScreen() { val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> window.setGravity(Gravity.BOTTOM) - window.setDimAmount(0f) +// window.setDimAmount(0f) +// window.setBackgroundDrawableResource(android.R.color.transparent) +// WindowCompat.setDecorFitsSystemWindows(window, false) } Surface(modifier = Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 10.dp).height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) { @@ -1258,7 +1260,7 @@ fun SubscriptionsScreen() { val dialogWindowProvider = LocalView.current.parent as? DialogWindowProvider dialogWindowProvider?.window?.let { window -> window.setGravity(Gravity.BOTTOM) - window.setDimAmount(0f) +// window.setDimAmount(0f) } Surface(modifier = Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 10.dp).height(350.dp), color = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary)) { diff --git a/changelog.md b/changelog.md index bdb85dde..ac5108bb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,21 @@ +# 7.3.3 + +* major changes in 7.3 + * final major step migrating to Jetpack Compose, fully Compose based with minimal View components + * no more fragments and no more xml layouts, navigation, drawer and bottom sheet are in Compose + * all activities derive from ComponentActivity + * various view related androidx dependencies removed + * made monitoring of feeds less aggressive, possibly improving efficiency + * fixed episodes list out of bound crashes +* adjusted the DB migration procedure in 7.2.0 to prevent crash when updating from 6.16.4 or earlier +* fixed exception thrown in fileExist that causes crash +* Reconcile in menu appears regardless of episodes present in Downloaded +* Reconcile first updates DB based on downloaded media files then performs double checks +* fixed non-functioning back-press +* fixed PlayerUI expand/collapse +* fixed filter/sort dialogs blanking out system bar +* fixed Subscriptions infor bar texts + # 7.3.2 * added more checking in the DB migration procedure in 7.2.0, trying to prevent some reported crashes diff --git a/fastlane/metadata/android/en-US/changelogs/3020340.txt b/fastlane/metadata/android/en-US/changelogs/3020340.txt new file mode 100644 index 00000000..c6a0ba6c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020340.txt @@ -0,0 +1,17 @@ + Version 7.3.3 + +* major changes in 7.3 + * final major step migrating to Jetpack Compose, fully Compose based with minimal View components + * no more fragments and no more xml layouts, navigation, drawer and bottom sheet are in Compose + * all activities derive from ComponentActivity + * various view related androidx dependencies removed + * made monitoring of feeds less aggressive, possibly improving efficiency + * fixed episodes list out of bound crashes +* adjusted the DB migration procedure in 7.2.0 to prevent crash when updating from 6.16.4 or earlier +* fixed exception thrown in fileExist that causes crash +* Reconcile in menu appears regardless of episodes present in Downloaded +* Reconcile first updates DB based on downloaded media files then performs double checks +* fixed non-functioning back-press +* fixed PlayerUI expand/collapse +* fixed filter/sort dialogs blanking out system bar +* fixed Subscriptions infor bar texts