Refactored the ZimHostFragment to use the ZimHostScreen instead of XML layout.

This commit is contained in:
MohitMaliFtechiz 2025-03-13 15:41:15 +05:30
parent b608b838a3
commit 29d0215984
2 changed files with 155 additions and 126 deletions

View File

@ -31,18 +31,20 @@ import android.os.Build
import android.os.Bundle
import android.os.IBinder
import android.provider.Settings
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.Toolbar
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.ComposeView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import org.kiwix.kiwixmobile.R.layout
import org.kiwix.kiwixmobile.R.drawable
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
@ -55,6 +57,10 @@ import org.kiwix.kiwixmobile.core.navigateToSettings
import org.kiwix.kiwixmobile.core.qr.GenerateQR
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.theme.StartServerGreen
import org.kiwix.kiwixmobile.core.ui.theme.StopServerRed
import org.kiwix.kiwixmobile.core.utils.ConnectivityReporter
import org.kiwix.kiwixmobile.core.utils.ServerUtils
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
@ -63,8 +69,6 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.StartServer
import org.kiwix.kiwixmobile.core.utils.files.Log
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.databinding.ActivityZimHostBinding
@ -97,23 +101,29 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
@Inject
lateinit var generateQr: GenerateQR
private lateinit var booksAdapter: BooksOnDiskAdapter
private lateinit var bookDelegate: BookOnDiskDelegate.BookDelegate
private var hotspotService: HotspotService? = null
private var ip: String? = null
private lateinit var serviceConnection: ServiceConnection
private var dialog: Dialog? = null
private var activityZimHostBinding: ActivityZimHostBinding? = null
private var isHotspotServiceRunning = false
override val fragmentTitle: String? by lazy {
getString(R.string.menu_wifi_hotspot)
}
override val fragmentToolbar: Toolbar? by lazy {
activityZimHostBinding?.root?.findViewById(R.id.toolbar)
}
private var serverIpText = mutableStateOf("")
private var shareIconItem = mutableStateOf(false to {})
private var qrImageItem: MutableState<Pair<Boolean, IconItem>> =
mutableStateOf(false to IconItem.Drawable(drawable.ic_storage))
private var startServerButtonItem =
mutableStateOf(
Triple(
"",
StartServerGreen,
{ startServerButtonClick() }
)
)
private var booksList: MutableState<List<BooksOnDiskListItem>> = mutableStateOf(arrayListOf())
private val selectedBooksPath: ArrayList<String>
get() {
return booksAdapter.items
return booksList.value
.filter(BooksOnDiskListItem::isSelected)
.filterIsInstance<BookOnDisk>()
.map {
@ -173,9 +183,22 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
activityZimHostBinding = ActivityZimHostBinding.inflate(inflater, container, false)
return activityZimHostBinding?.root
): View? = ComposeView(requireContext()).apply {
setContent {
ZimHostScreen(
serverIpText = serverIpText.value,
shareIconItem = shareIconItem.value,
qrImageItem = qrImageItem.value,
startServerButtonItem = startServerButtonItem.value,
selectionMode = SelectionMode.MULTI,
onMultiSelect = { select(it) },
booksList = booksList.value,
) {
NavigationIcon(
onClick = { activity?.onBackPressedDispatcher?.onBackPressed() }
)
}
}
}
override fun inject(baseActivity: BaseActivity) {
@ -184,17 +207,17 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bookDelegate =
BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil, multiSelectAction = ::select)
bookDelegate.selectionMode = SelectionMode.MULTI
booksAdapter =
BooksOnDiskAdapter(
bookDelegate,
BookOnDiskDelegate.LanguageDelegate
)
activityZimHostBinding?.recyclerViewZimHost?.adapter = booksAdapter
//
// bookDelegate =
// BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil, multiSelectAction = ::select)
// bookDelegate.selectionMode = SelectionMode.MULTI
// booksAdapter =
// BooksOnDiskAdapter(
// bookDelegate,
// BookOnDiskDelegate.LanguageDelegate
// )
//
// activityZimHostBinding?.recyclerViewZimHost?.adapter = booksAdapter
presenter.attachView(this)
serviceConnection =
@ -208,18 +231,18 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
hotspotService?.registerCallBack(this@ZimHostFragment)
}
}
}
activityZimHostBinding?.startServerButton?.setOnClickListener {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
) {
if (requireActivity().hasNotificationPermission(sharedPreferenceUtil)) {
handleStoragePermissionAndServer()
} else {
notificationPermissionListener.launch(POST_NOTIFICATIONS)
}
} else {
private fun startServerButtonClick() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
) {
if (requireActivity().hasNotificationPermission(sharedPreferenceUtil)) {
handleStoragePermissionAndServer()
} else {
notificationPermissionListener.launch(POST_NOTIFICATIONS)
}
} else {
handleStoragePermissionAndServer()
}
}
@ -295,14 +318,14 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
}
private fun select(bookOnDisk: BooksOnDiskListItem.BookOnDisk) {
val booksList: List<BooksOnDiskListItem> = booksAdapter.items.onEach {
val tempBooksList: List<BooksOnDiskListItem> = booksList.value.onEach {
if (it == bookOnDisk) {
it.isSelected = !it.isSelected
}
it
}
booksAdapter.items = booksList
saveHostedBooks(booksList)
booksList.value = tempBooksList
saveHostedBooks(tempBooksList)
if (ServerUtils.isServerStarted) {
startWifiHotspot(true)
}
@ -356,55 +379,42 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
}
private fun layoutServerStarted() {
activityZimHostBinding?.serverTextView?.apply {
text = getString(R.string.server_started_message, ip)
movementMethod = LinkMovementMethod.getInstance()
}
configureUrlSharingIcon()
configureQrIcon()
activityZimHostBinding?.startServerButton?.text = getString(R.string.stop_server_label)
activityZimHostBinding?.startServerButton?.setBackgroundColor(
ContextCompat.getColor(requireActivity(), R.color.stopServerRed)
)
bookDelegate.selectionMode = SelectionMode.MULTI
booksAdapter.notifyDataSetChanged()
serverIpText.value = getString(R.string.server_started_message, ip)
// activityZimHostBinding?.serverTextView?.apply {
// text = getString(R.string.server_started_message, ip)
// movementMethod = LinkMovementMethod.getInstance()
// }
configureUrlSharingIcon(true)
configureQrIcon(true)
startServerButtonItem.value =
Triple(getString(R.string.stop_server_label), StopServerRed) { startServerButtonClick() }
}
private fun configureUrlSharingIcon() {
activityZimHostBinding?.shareServerUrlIcon?.apply {
visibility = View.VISIBLE
setOnClickListener {
val urlSharingIntent = Intent(Intent.ACTION_SEND)
urlSharingIntent.apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, ip)
}
startActivity(urlSharingIntent)
private fun configureUrlSharingIcon(shouldShow: Boolean) {
shareIconItem.value = shouldShow to {
val urlSharingIntent = Intent(Intent.ACTION_SEND)
urlSharingIntent.apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, ip)
}
startActivity(urlSharingIntent)
}
}
private fun configureQrIcon() {
activityZimHostBinding?.serverQrCode?.apply {
ip?.let {
val qr = generateQr.createQR(it)
setImageBitmap(qr)
visibility = View.VISIBLE
}
}
private fun configureQrIcon(shouldShow: Boolean) {
val qrImage = ip?.let {
val qr = generateQr.createQR(it)
IconItem.ImageBitmap(qr.asImageBitmap())
} ?: IconItem.Drawable(org.kiwix.kiwixmobile.R.drawable.ic_storage)
qrImageItem.value = shouldShow to qrImage
}
private fun layoutServerStopped() {
activityZimHostBinding?.serverTextView?.text =
getString(R.string.server_textview_default_message)
activityZimHostBinding?.shareServerUrlIcon?.visibility = View.GONE
activityZimHostBinding?.serverQrCode?.visibility = View.GONE
activityZimHostBinding?.startServerButton?.text = getString(R.string.start_server_label)
activityZimHostBinding?.startServerButton?.setBackgroundColor(
ContextCompat.getColor(requireActivity(), R.color.startServerGreen)
)
bookDelegate.selectionMode = SelectionMode.MULTI
booksAdapter.notifyDataSetChanged()
serverIpText.value = getString(R.string.server_textview_default_message)
configureUrlSharingIcon(false)
configureQrIcon(false)
startServerButtonItem.value =
Triple(getString(R.string.start_server_label), StartServerGreen) { startServerButtonClick() }
}
override fun onDestroyView() {
@ -477,7 +487,7 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
override fun addBooks(books: List<BooksOnDiskListItem>) {
// Check if this is the app module, as custom apps may have multiple package names
if (!requireActivity().isCustomApp()) {
booksAdapter.items = books
booksList.value = books
} else {
val updatedBooksList: MutableList<BooksOnDiskListItem> = arrayListOf()
books.forEach {
@ -494,7 +504,7 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
updatedBooksList.add(it)
}
}
booksAdapter.items = updatedBooksList
booksList.value = updatedBooksList
}
}

View File

@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.webserver
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
@ -28,13 +29,11 @@ import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@ -42,28 +41,36 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.models.toPainter
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.MATERIAL_MINIMUM_HEIGHT_AND_WIDTH
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.MAXIMUM_HEIGHT_OF_QR_CODE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.MINIMUM_HEIGHT_OF_BOOKS_LIST
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.MINIMUM_HEIGHT_OF_QR_CODE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.ui.BookItem
import org.kiwix.kiwixmobile.ui.ZimFilesLanguageHeader
@Suppress("ComposableLambdaParameterNaming")
@Suppress("ComposableLambdaParameterNaming", "LongParameterList")
@Composable
fun ZimHostScreen(
serverIpText: String,
shareIconItem: Pair<MutableState<Boolean>, () -> Unit>,
qrImageItem: Pair<MutableState<Boolean>, IconItem>,
shareIconItem: Pair<Boolean, () -> Unit>,
qrImageItem: Pair<Boolean, IconItem>,
booksList: List<BooksOnDiskListItem>,
startServerButtonItem: Pair<String, Color>,
startServerButtonItem: Triple<String, Color, () -> Unit>,
selectionMode: SelectionMode,
onClick: ((BookOnDisk) -> Unit)? = null,
onLongClick: ((BookOnDisk) -> Unit)? = null,
onMultiSelect: ((BookOnDisk) -> Unit)? = null,
navigationIcon: @Composable () -> Unit
) {
KiwixTheme {
@ -78,14 +85,13 @@ fun ZimHostScreen(
ServerIpText(serverIpText, Modifier.weight(1f))
ShareIcon(shareIconItem)
}
Column(modifier = Modifier.weight(1f).verticalScroll(rememberScrollState())) {
QRImage(qrImageItem)
BookItemList(booksList)
Box(modifier = Modifier.weight(1f)) {
BookItemList(booksList, selectionMode, qrImageItem, onClick, onLongClick, onMultiSelect)
}
KiwixButton(
startServerButtonItem.first,
{},
modifier = Modifier.fillMaxWidth(),
startServerButtonItem.third,
modifier = Modifier.fillMaxWidth().padding(FOUR_DP),
buttonBackgroundColor = startServerButtonItem.second
)
}
@ -106,14 +112,14 @@ fun ServerIpText(
}
@Composable
fun ShareIcon(shareIconItem: Pair<MutableState<Boolean>, () -> Unit>) {
if (shareIconItem.first.value) {
fun ShareIcon(shareIconItem: Pair<Boolean, () -> Unit>) {
if (shareIconItem.first) {
Image(
painter = painterResource(id = R.drawable.ic_share_35dp),
contentDescription = stringResource(id = R.string.share_host_address),
modifier = Modifier
.clickable { shareIconItem.second.invoke() }
.padding(4.dp)
.padding(FOUR_DP)
.heightIn(min = MATERIAL_MINIMUM_HEIGHT_AND_WIDTH)
.widthIn(min = MATERIAL_MINIMUM_HEIGHT_AND_WIDTH),
contentScale = ContentScale.Inside
@ -122,43 +128,56 @@ fun ShareIcon(shareIconItem: Pair<MutableState<Boolean>, () -> Unit>) {
}
@Composable
fun QRImage(qrImageItem: Pair<MutableState<Boolean>, IconItem>) {
Image(
painter = qrImageItem.second.toPainter(),
contentDescription = stringResource(id = R.string.qr_code),
modifier = Modifier
.fillMaxWidth()
.heightIn(min = MINIMUM_HEIGHT_OF_QR_CODE, max = MAXIMUM_HEIGHT_OF_QR_CODE)
.padding(horizontal = SIXTEEN_DP),
contentScale = ContentScale.Fit
)
fun QRImage(qrImageItem: Pair<Boolean, IconItem>) {
if (qrImageItem.first) {
Image(
painter = qrImageItem.second.toPainter(),
contentDescription = stringResource(id = R.string.qr_code),
modifier = Modifier
.fillMaxWidth()
.heightIn(min = MINIMUM_HEIGHT_OF_QR_CODE, max = MAXIMUM_HEIGHT_OF_QR_CODE)
.padding(horizontal = SIXTEEN_DP),
contentScale = ContentScale.Fit
)
}
}
@Suppress("UnusedParameter")
@Composable
fun BookItemList(booksList: List<BooksOnDiskListItem>) {
fun BookItemList(
booksList: List<BooksOnDiskListItem>,
selectionMode: SelectionMode,
qrImageItem: Pair<Boolean, IconItem>,
onClick: ((BookOnDisk) -> Unit)?,
onLongClick: ((BookOnDisk) -> Unit)?,
onMultiSelect: ((BookOnDisk) -> Unit)?
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.heightIn(min = MINIMUM_HEIGHT_OF_BOOKS_LIST),
content = {
// Adding QR image here because of when we scrolls then QR image will go up.
item {
QRImage(qrImageItem)
}
itemsIndexed(booksList) { _, bookItem ->
when (bookItem) {
is BooksOnDiskListItem.LanguageItem -> {
ZimFilesLanguageHeader(bookItem)
}
is BooksOnDiskListItem.BookOnDisk -> {
BookItem(
bookOnDisk = bookItem,
selectionMode = selectionMode,
onClick = onClick,
onLongClick = onLongClick,
onMultiSelect = onMultiSelect
)
}
}
}
}
)
}
// @Preview
// @Preview(name = "DarkMode", uiMode = Configuration.UI_MODE_NIGHT_YES)
// @Composable
// fun PreviewScreen() {
// val shareIconVisibility = remember { mutableStateOf(true) }
// ZimHostScreen(
// stringResource(R.string.server_textview_default_message),
// shareIconVisibility to {},
// shareIconVisibility to IconItem.Drawable(org.kiwix.kiwixmobile.R.drawable.launch_screen),
// listOf(),
// stringResource(R.string.start_server_label) to StartServerGreen,
// {
// NavigationIcon(onClick = {})
// }
// )
// }