diff --git a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt index 748eff9f1..bb02c12f8 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt @@ -128,7 +128,6 @@ class KiwixMainActivity : CoreMainActivity() { private val storageDeviceList = arrayListOf() private val pendingIntentFlow = MutableStateFlow(null) - @Suppress("InjectDispatcher") @OptIn(ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { cachedComponent.inject(this) @@ -146,6 +145,8 @@ class KiwixMainActivity : CoreMainActivity() { KiwixDestination.Reader.route } } + RestoreDrawerStateOnOrientationChange() + PersistDrawerStateOnChange() KiwixMainActivityScreen( navController = navController, leftDrawerContent = leftDrawerMenu, @@ -160,8 +161,7 @@ class KiwixMainActivity : CoreMainActivity() { LaunchedEffect(navController) { navController.addOnDestinationChangedListener(finishActionModeOnDestinationChange) } - val lifecycleOwner = LocalLifecycleOwner.current - val lifecycle = lifecycleOwner.lifecycle + val lifecycle = LocalLifecycleOwner.current.lifecycle LaunchedEffect(navController, pendingIntent) { snapshotFlow { pendingIntent } .filterNotNull() @@ -176,12 +176,17 @@ class KiwixMainActivity : CoreMainActivity() { } DialogHost(alertDialogShower) } - lifecycleScope.launch { - migrateInternalToPublicAppDirectory() - } + runMigrations() intent?.let { pendingIntentFlow.value = it } + } + + @Suppress("InjectDispatcher") + private fun runMigrations() { + lifecycleScope.launch { + migrateInternalToPublicAppDirectory() + } // run the migration on background thread to avoid any UI related issues. CoroutineScope(Dispatchers.IO).launch { if (!sharedPreferenceUtil.prefIsTest) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index 9e17a9ac7..f9c213baa 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -28,9 +28,14 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AppCompatActivity import androidx.compose.material3.BottomAppBarScrollBehavior import androidx.compose.material3.DrawerState +import androidx.compose.material3.DrawerValue import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.platform.LocalConfiguration import androidx.core.net.toUri import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope @@ -166,6 +171,16 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { @Inject lateinit var downloadMonitor: DownloadMonitor + /** + * Manages the visibility of the left drawer by tracking its state. + * In Compose, when the screen rotates and the screen width is above 600dp, + * the drawerState is automatically set to open. This causes unexpected behavior. + * To ensure a smooth user experience, we save the drawer state in a boolean so + * that it survives configuration changes and is not affected by Compose’s + * default implementation. + */ + private var wasLeftDrawerOpen = false + @Suppress("InjectDispatcher") override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.KiwixTheme) @@ -199,6 +214,38 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { leftDrawerMenu.addAll(leftNavigationDrawerMenuItems) } + /** + * Restores the drawer state after an orientation change. + * + * In Compose, rotating the device (especially on large screens) can cause the drawer + * to be automatically opened by default. To provide a consistent user experience, + * this function syncs the drawer's state (open/closed) with the last known value + * stored in [wasLeftDrawerOpen]. + */ + @Composable + fun RestoreDrawerStateOnOrientationChange() { + LaunchedEffect(LocalConfiguration.current.orientation) { + if (wasLeftDrawerOpen) { + openNavigationDrawer() + } else { + closeNavigationDrawer() + } + } + } + + /** + * Tracks the current drawer state and updates [wasLeftDrawerOpen] whenever the + * drawer is opened or closed. This ensures the drawer state is persisted across + * configuration changes (e.g., screen rotations) and can be restored later. + */ + @Composable + fun PersistDrawerStateOnChange() { + LaunchedEffect(leftDrawerState) { + snapshotFlow { leftDrawerState.currentValue } + .collect { wasLeftDrawerOpen = it == DrawerValue.Open } + } + } + @Suppress("DEPRECATION") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt index 868d39a66..4749b5ece 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomMainActivity.kt @@ -77,6 +77,8 @@ class CustomMainActivity : CoreMainActivity() { navController = rememberNavController() leftDrawerState = rememberDrawerState(DrawerValue.Closed) uiCoroutineScope = rememberCoroutineScope() + RestoreDrawerStateOnOrientationChange() + PersistDrawerStateOnChange() CustomMainActivityScreen( navController = navController, leftDrawerContent = leftDrawerMenu, @@ -96,9 +98,6 @@ class CustomMainActivity : CoreMainActivity() { .migrate() } } - if (savedInstanceState != null) { - return - } } override fun getIconResId() = R.mipmap.ic_launcher