mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Migrated KiwixMainActivity and CustomMainActivity to Jetpack Compose.
This commit is contained in:
parent
9853e0abd7
commit
095317e102
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.main
|
||||
|
||||
import androidx.annotation.IdRes
|
||||
|
||||
data class BottomNavItem(
|
||||
@IdRes val id: Int,
|
||||
val title: String,
|
||||
val iconRes: Int
|
||||
)
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.main
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.BottomAppBarScrollBehavior
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import org.kiwix.kiwixmobile.R.drawable
|
||||
import org.kiwix.kiwixmobile.R.id
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun BottomNavigationBar(
|
||||
navController: NavController,
|
||||
scrollBehavior: BottomAppBarScrollBehavior,
|
||||
topLevelDestinations: List<Int>
|
||||
) {
|
||||
val bottomNavItems = listOf(
|
||||
BottomNavItem(
|
||||
id = id.readerFragment,
|
||||
title = stringResource(id = R.string.reader),
|
||||
iconRes = drawable.ic_reader_navigation_white_24px
|
||||
),
|
||||
BottomNavItem(
|
||||
id = id.libraryFragment,
|
||||
title = stringResource(id = R.string.library),
|
||||
iconRes = drawable.ic_library_navigation_white_24dp
|
||||
),
|
||||
BottomNavItem(
|
||||
id = id.downloadsFragment,
|
||||
title = stringResource(id = R.string.download),
|
||||
iconRes = drawable.ic_download_navigation_white_24dp
|
||||
)
|
||||
)
|
||||
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestinationId = navBackStackEntry?.destination?.id
|
||||
|
||||
if (currentDestinationId in topLevelDestinations) {
|
||||
BottomAppBar(scrollBehavior = scrollBehavior) {
|
||||
bottomNavItems.forEach { item ->
|
||||
NavigationBarItem(
|
||||
selected = currentDestinationId == item.id,
|
||||
onClick = { navController.navigate(item.id) },
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(id = item.iconRes),
|
||||
contentDescription = item.title
|
||||
)
|
||||
},
|
||||
label = { Text(item.title) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainNavGraph(
|
||||
fragmentManager: FragmentManager,
|
||||
navGraphId: Int
|
||||
) {
|
||||
val navController = remember {
|
||||
fragmentManager.findNavController(R.id.nav_host_fragment)
|
||||
}
|
||||
|
||||
// Drawer states
|
||||
val leftDrawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
|
||||
val rightDrawerVisible = remember { mutableStateOf(false) }
|
||||
|
||||
// Bottom nav destinations
|
||||
val bottomNavDestinations = listOf(
|
||||
id.readerFragment,
|
||||
id.libraryFragment,
|
||||
id.downloadsFragment
|
||||
)
|
||||
|
||||
// Observe current destination
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestinationId = navBackStackEntry?.destination?.id
|
||||
|
||||
// Coroutine scope for drawer
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = leftDrawerState,
|
||||
drawerContent = { DrawerContentLeft() }
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
// Fragment content
|
||||
FragmentContainer(
|
||||
fragmentManager = fragmentManager,
|
||||
containerId = R.id.nav_host_fragment,
|
||||
navGraphId = navGraphId
|
||||
)
|
||||
|
||||
// Right drawer (slide in)
|
||||
AnimatedVisibility(
|
||||
visible = rightDrawerVisible.value,
|
||||
enter = slideInHorizontally(initialOffsetX = { it }),
|
||||
exit = slideOutHorizontally(targetOffsetX = { it }),
|
||||
modifier = Modifier.align(Alignment.CenterEnd)
|
||||
) {
|
||||
DrawerContentRight(
|
||||
onClose = { rightDrawerVisible.value = false }
|
||||
)
|
||||
}
|
||||
|
||||
// Bottom nav only on selected destinations
|
||||
if (currentDestinationId in bottomNavDestinations) {
|
||||
BottomNavigationBar(
|
||||
navController = navController,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -372,4 +372,6 @@ object Libs {
|
||||
|
||||
const val COIL3_COMPOSE = "io.coil-kt.coil3:coil-compose:${Versions.COIL_COMPOSE}"
|
||||
const val COIL3_OKHTTP_COMPOSE = "io.coil-kt.coil3:coil-network-okhttp:${Versions.COIL_COMPOSE}"
|
||||
const val COMPOSE_NAVIGATION =
|
||||
"androidx.navigation:navigation-compose:${Versions.COMPOSE_NAVIGATION}"
|
||||
}
|
||||
|
@ -117,6 +117,8 @@ object Versions {
|
||||
const val TURBINE_FLOW_TEST = "1.2.0"
|
||||
|
||||
const val COIL_COMPOSE = "3.2.0"
|
||||
|
||||
const val COMPOSE_NAVIGATION = "2.7.7"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -245,6 +245,7 @@ class AllProjectConfigurer {
|
||||
implementation(Libs.COMPOSE_LIVE_DATA)
|
||||
implementation(Libs.COIL3_COMPOSE)
|
||||
implementation(Libs.COIL3_OKHTTP_COMPOSE)
|
||||
implementation(Libs.COMPOSE_NAVIGATION)
|
||||
|
||||
// Compose UI test implementation
|
||||
androidTestImplementation(Libs.COMPOSE_UI_TEST_JUNIT)
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.main
|
||||
|
||||
data class DrawerMenuItem(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val iconRes: Int,
|
||||
val visible: Boolean = true,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
|
||||
data class DrawerMenuGroup(val drawerMenuItemList: List<DrawerMenuItem>)
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2025 Kiwix <android.kiwix.org>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.kiwix.kiwixmobile.core.main
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
|
||||
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.NAVIGATION_DRAWER_WIDTH
|
||||
|
||||
@Composable
|
||||
fun MainDrawerMenu(drawerMenuGroupList: List<DrawerMenuGroup>) {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.width(NAVIGATION_DRAWER_WIDTH)
|
||||
.fillMaxHeight(),
|
||||
shadowElevation = EIGHT_DP
|
||||
) {
|
||||
Column {
|
||||
// Banner image at the top
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_home_kiwix_banner),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
drawerMenuGroupList.forEach {
|
||||
DrawerGroup(it.drawerMenuItemList)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DrawerGroup(items: List<DrawerMenuItem>) {
|
||||
Column {
|
||||
items.filter { it.visible }.forEach { item ->
|
||||
DrawerMenuItemView(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DrawerMenuItemView(item: DrawerMenuItem) {
|
||||
ListItem(
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(id = item.iconRes),
|
||||
contentDescription = item.title
|
||||
)
|
||||
},
|
||||
headlineContent = {
|
||||
Text(text = item.title)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { item.onClick }
|
||||
)
|
||||
}
|
@ -195,4 +195,7 @@ object ComposeDimens {
|
||||
const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f
|
||||
val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp
|
||||
val DONATION_LAYOUT_MAXIMUM_WIDTH = 400.dp
|
||||
|
||||
// MainActivity dimens
|
||||
val NAVIGATION_DRAWER_WIDTH = 280.dp
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user