From 3509059d1f724f84c58a738466ebf45c77ac5b1c Mon Sep 17 00:00:00 2001 From: Aryan Date: Tue, 17 Mar 2026 11:53:52 +0530 Subject: [PATCH] refactor: remove legacy folder sync migration and enhance and add cache maintenance tools to release builds (#83) --- .../main/java/com/aryan/reader/HomeScreen.kt | 104 ++++++++++++------ .../java/com/aryan/reader/MainViewModel.kt | 50 +++------ 2 files changed, 88 insertions(+), 66 deletions(-) diff --git a/app/src/main/java/com/aryan/reader/HomeScreen.kt b/app/src/main/java/com/aryan/reader/HomeScreen.kt index 5ea2791..e83f60c 100644 --- a/app/src/main/java/com/aryan/reader/HomeScreen.kt +++ b/app/src/main/java/com/aryan/reader/HomeScreen.kt @@ -158,6 +158,9 @@ fun HomeScreen( var showSignOutConfirmDialog by remember { mutableStateOf(false) } var showAboutDialog by remember { mutableStateOf(false) } + var showClearBookCacheDialog by remember { mutableStateOf(false) } + var showClearReflowCacheDialog by remember { mutableStateOf(false) } + val feedbackResult = navController.currentBackStackEntry?.savedStateHandle?.getLiveData("banner_message") ?.observeAsState() @@ -291,7 +294,7 @@ fun HomeScreen( DefaultTopAppBar( uiState = uiState, onRenderModeChange = viewModel::setRenderMode, - onClearCache = viewModel::clearBookCache, + onClearCache = { showClearBookCacheDialog = true }, onClearCloudData = { showClearAllDataDialog = true }, onAboutClick = { showAboutDialog = true }, onDrawerClick = { @@ -301,7 +304,7 @@ fun HomeScreen( }, onShowDeviceManagement = viewModel::showDeviceManagementForDebug, onFolderSyncToggle = viewModel::setFolderSyncEnabled, - onClearReflowCache = viewModel::clearReflowCache + onClearReflowCache = { showClearReflowCacheDialog = true } ) } else { ContextualTopAppBar( @@ -391,6 +394,30 @@ fun HomeScreen( navController.navigate(AppDestinations.PRO_SCREEN_ROUTE) }) } + + if (showClearBookCacheDialog) { + DangerousFolderActionDialog( + title = "Clear Book Cache", + message = "This will clear all processed page in pagination mode. This helps fix layout issues but will require books to be re-processed next time you open them.", + onConfirm = { + viewModel.clearBookCache() + showClearBookCacheDialog = false + }, + onDismiss = { showClearBookCacheDialog = false } + ) + } + + if (showClearReflowCacheDialog) { + DangerousFolderActionDialog( + title = "Clear Reflow Cache", + message = "This will delete all generated 'Text View' versions of your PDFs and clear their associated images/HTML cache. Your original PDFs will remain untouched.", + onConfirm = { + viewModel.clearReflowCache() + showClearReflowCacheDialog = false + }, + onDismiss = { showClearReflowCacheDialog = false } + ) + } } } if (showAboutDialog) { @@ -427,11 +454,6 @@ fun HomeScreen( ) } } - if (uiState.showFolderMigrationDialog) { - FolderMigrationDialog( - onConfirm = { viewModel.completeFolderMigration() } - ) - } } } @@ -693,7 +715,7 @@ fun DefaultTopAppBar( onRenderModeChange: (RenderMode) -> Unit, onClearCache: () -> Unit, onClearCloudData: () -> Unit, - onClearReflowCache: () -> Unit, // Add this parameter + onClearReflowCache: () -> Unit, onDrawerClick: () -> Unit, onAboutClick: () -> Unit, onShowDeviceManagement: () -> Unit, @@ -725,20 +747,22 @@ fun DefaultTopAppBar( showOptionsMenu = false }) + HorizontalDivider() + DropdownMenuItem(text = { Text("Clear Book Cache") }, onClick = { + onClearCache() + showOptionsMenu = false + }) + DropdownMenuItem(text = { Text("Clear Reflow Cache") }, onClick = { + onClearReflowCache() + showOptionsMenu = false + }) + if (BuildConfig.DEBUG && BuildConfig.FLAVOR != "oss") { HorizontalDivider() DropdownMenuItem(text = { Text("[Debug] Show Device Management") }, onClick = { onShowDeviceManagement() showOptionsMenu = false }) - DropdownMenuItem(text = { Text("[Debug] Clear Book Cache") }, onClick = { - onClearCache() - showOptionsMenu = false - }) - DropdownMenuItem(text = { Text("[Debug] Clear Reflow Cache") }, onClick = { - onClearReflowCache() - showOptionsMenu = false - }) DropdownMenuItem( text = { Text("[Debug] Clear Cloud & Local Data") }, onClick = { @@ -1193,27 +1217,41 @@ fun FpsMonitor(modifier: Modifier = Modifier) { } @Composable -private fun FolderMigrationDialog(onConfirm: () -> Unit) { +fun DangerousFolderActionDialog( + title: String, + message: String, + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { AlertDialog( - onDismissRequest = { }, - icon = { Icon(Icons.Default.FolderSpecial, contentDescription = null) }, - title = { Text("Folder Sync Update") }, - text = { - Column { - Text( - "We've improved Folder Sync! Books are now read directly from your folder without duplicating files." - ) - Spacer(modifier = Modifier.height(12.dp)) - Text( - "To keep your reading progress safe, your previously synced books have been converted to standard local books. You may see duplicates once the folder resyncs; you can safely delete the old copies at your convenience.", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant + onDismissRequest = onDismiss, + icon = { + Icon( + imageVector = Icons.Default.Info, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) + }, + title = { + Text( + text = title, + color = MaterialTheme.colorScheme.error + ) + }, + text = { Text(message) }, + confirmButton = { + TextButton( + onClick = onConfirm, + colors = ButtonDefaults.textButtonColors( + contentColor = MaterialTheme.colorScheme.error ) + ) { + Text("Confirm & Clear") } }, - confirmButton = { - TextButton(onClick = onConfirm) { - Text("Got it") + dismissButton = { + TextButton(onClick = onDismiss) { + Text("Cancel") } } ) diff --git a/app/src/main/java/com/aryan/reader/MainViewModel.kt b/app/src/main/java/com/aryan/reader/MainViewModel.kt index 0a0c008..324df2d 100644 --- a/app/src/main/java/com/aryan/reader/MainViewModel.kt +++ b/app/src/main/java/com/aryan/reader/MainViewModel.kt @@ -114,7 +114,6 @@ import java.util.concurrent.TimeUnit private const val KEY_RENDER_MODE = "render_mode" private const val KEY_FOLDER_SYNC_ENABLED = "folder_sync_enabled" -private const val KEY_FOLDER_MIGRATION_COMPLETED = "folder_migration_completed_v2" private const val KEY_FILTER_FILE_TYPES = "filter_file_types" private const val KEY_FILTER_FOLDERS = "filter_folders" @@ -221,7 +220,6 @@ data class ReaderScreenState( val hasUnreadFeedback: Boolean = false, val searchQuery: String = "", val isSearchActive: Boolean = false, - val showFolderMigrationDialog: Boolean = false, val isRefreshing: Boolean = false, val reflowProgress: Float? = null, val recentFiles: List = emptyList(), @@ -597,14 +595,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio remoteConfigRepository.init() - val isMigrationCompleted = prefs.getBoolean(KEY_FOLDER_MIGRATION_COMPLETED, false) - if (_internalState.value.syncedFolders.isNotEmpty()) { - if (isMigrationCompleted) { - syncFolderMetadata() - } else { - Timber.d("App Start: Skipping sync. Waiting for migration/detachment logic.") - } + syncFolderMetadata() } viewModelScope.launch { billingClientWrapper.initializeConnection() } @@ -656,27 +648,6 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio } } } - val migrationCompleted = prefs.getBoolean(KEY_FOLDER_MIGRATION_COMPLETED, false) - - val hasFolders = _internalState.value.syncedFolders.isNotEmpty() - if (hasFolders && !migrationCompleted) { - Timber.tag("FolderSync").d("First time after refactor: Showing migration dialog.") - _internalState.update { it.copy(showFolderMigrationDialog = true) } - } - } - - fun completeFolderMigration() { - Timber.tag("FolderSync") - .d("User acknowledged update. Detaching old books and starting fresh scan.") - - viewModelScope.launch { - recentFilesRepository.detachAllFolderBooks() - - prefs.edit { putBoolean(KEY_FOLDER_MIGRATION_COMPLETED, true) } - _internalState.update { it.copy(showFolderMigrationDialog = false) } - - scanSyncedFolder() - } } private fun getDisplayPathFromUri(context: Context, uriString: String): String { @@ -1482,7 +1453,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio _internalState.update { it.copy( - syncedFolders = newStats, showFolderMigrationDialog = false + syncedFolders = newStats ) } @@ -2623,7 +2594,6 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio viewModelScope.launch { val existing = recentFilesRepository.getFileByBookId(reflowBookId) if (existing != null) { - showBanner("Opening existing text view...") if (autoOpenPage != null) { switchToFileSeamlessly(existing, autoOpenPage) } else { @@ -3809,8 +3779,22 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio imagesDir.deleteRecursively() } + val allFiles = recentFilesRepository.getAllFilesForSync() + val reflowBooks = allFiles.filter { it.bookId.endsWith("_reflow") } + + if (reflowBooks.isNotEmpty()) { + val reflowBookIds = reflowBooks.map { it.bookId } + + reflowBookIds.forEach { bookId -> + clearImportedFileCache(bookId) + pdfTextRepository.clearBookText(bookId) + } + + recentFilesRepository.deleteFilePermanently(reflowBookIds) + } + withContext(Dispatchers.Main) { - showBanner("Reflow cache & images cleared.") + showBanner("Reflow cache & generated text views cleared.") } } }