mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-08 14:52:13 -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_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 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 TURBINE_FLOW_TEST = "1.2.0"
|
||||||
|
|
||||||
const val COIL_COMPOSE = "3.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.COMPOSE_LIVE_DATA)
|
||||||
implementation(Libs.COIL3_COMPOSE)
|
implementation(Libs.COIL3_COMPOSE)
|
||||||
implementation(Libs.COIL3_OKHTTP_COMPOSE)
|
implementation(Libs.COIL3_OKHTTP_COMPOSE)
|
||||||
|
implementation(Libs.COMPOSE_NAVIGATION)
|
||||||
|
|
||||||
// Compose UI test implementation
|
// Compose UI test implementation
|
||||||
androidTestImplementation(Libs.COMPOSE_UI_TEST_JUNIT)
|
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
|
const val READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA = 0.38f
|
||||||
val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp
|
val SEARCH_PLACEHOLDER_TEXT_SIZE = 12.sp
|
||||||
val DONATION_LAYOUT_MAXIMUM_WIDTH = 400.dp
|
val DONATION_LAYOUT_MAXIMUM_WIDTH = 400.dp
|
||||||
|
|
||||||
|
// MainActivity dimens
|
||||||
|
val NAVIGATION_DRAWER_WIDTH = 280.dp
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user