mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Improved the visibility of permissionDeniedLayout
when permission is denied.
* Improved the display of the ZIM file list when permission is granted. * Displaying the snackBar with Compose UI when there is not enough storage to copy the ZIM file. * Created the `SwipeRefreshLayout` according to our theme, along with custom logic to enable or disable it, since the default PullToRefresh does not support this feature. * Created `ContentLoadingProgressBar` to show circular and horizontal progress bars with progress, styled according to our theme.
This commit is contained in:
parent
de46b8f0dd
commit
5b507d8f3d
@ -30,7 +30,7 @@ import org.kiwix.kiwixmobile.intro.IntroModule
|
|||||||
import org.kiwix.kiwixmobile.language.LanguageFragment
|
import org.kiwix.kiwixmobile.language.LanguageFragment
|
||||||
import org.kiwix.kiwixmobile.localFileTransfer.LocalFileTransferFragment
|
import org.kiwix.kiwixmobile.localFileTransfer.LocalFileTransferFragment
|
||||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||||
import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragment
|
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragment
|
||||||
import org.kiwix.kiwixmobile.nav.destination.library.OnlineLibraryFragment
|
import org.kiwix.kiwixmobile.nav.destination.library.OnlineLibraryFragment
|
||||||
import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment
|
import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment
|
||||||
import org.kiwix.kiwixmobile.settings.KiwixSettingsFragment
|
import org.kiwix.kiwixmobile.settings.KiwixSettingsFragment
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Kiwix Android
|
* Kiwix Android
|
||||||
* Copyright (c) 2020 Kiwix <android.kiwix.org>
|
* Copyright (c) 2025 Kiwix <android.kiwix.org>
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -16,7 +16,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.nav.destination.library
|
package org.kiwix.kiwixmobile.nav.destination.library.local
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.app.Activity.RESULT_OK
|
import android.app.Activity.RESULT_OK
|
||||||
@ -30,8 +30,6 @@ import android.os.Environment
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.GONE
|
|
||||||
import android.view.View.VISIBLE
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
@ -67,6 +65,7 @@ import org.kiwix.kiwixmobile.cachedComponent
|
|||||||
import org.kiwix.kiwixmobile.core.R.string
|
import org.kiwix.kiwixmobile.core.R.string
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isManageExternalStoragePermissionGranted
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isManageExternalStoragePermissionGranted
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
|
||||||
@ -100,6 +99,7 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDis
|
|||||||
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||||
import org.kiwix.kiwixmobile.databinding.FragmentDestinationLibraryBinding
|
import org.kiwix.kiwixmobile.databinding.FragmentDestinationLibraryBinding
|
||||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||||
|
import org.kiwix.kiwixmobile.nav.destination.library.CopyMoveFileHandler
|
||||||
import org.kiwix.kiwixmobile.zimManager.MAX_PROGRESS
|
import org.kiwix.kiwixmobile.zimManager.MAX_PROGRESS
|
||||||
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
|
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
|
||||||
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions
|
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions
|
||||||
@ -151,6 +151,18 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
*/
|
*/
|
||||||
private var noFilesViewItem = mutableStateOf(Triple("", "", false))
|
private var noFilesViewItem = mutableStateOf(Triple("", "", false))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a Pair which is responsible for showing and hiding the "Pull to refresh"
|
||||||
|
* animation.
|
||||||
|
*
|
||||||
|
* A [Pair] containing:
|
||||||
|
* - [Boolean]: The first boolean triggers/hide the "pull to refresh" animation.
|
||||||
|
* - [Boolean]: The second boolean enable/disable the "pull to refresh".
|
||||||
|
*/
|
||||||
|
private var swipeRefreshItem = mutableStateOf(Pair(false, true))
|
||||||
|
|
||||||
|
private var scanningProgressItem = mutableStateOf(Pair(false, ZERO))
|
||||||
|
|
||||||
private val zimManageViewModel by lazy {
|
private val zimManageViewModel by lazy {
|
||||||
requireActivity().viewModel<ZimManageViewModel>(viewModelFactory)
|
requireActivity().viewModel<ZimManageViewModel>(viewModelFactory)
|
||||||
}
|
}
|
||||||
@ -159,20 +171,14 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
registerForActivityResult(
|
registerForActivityResult(
|
||||||
ActivityResultContracts.RequestMultiplePermissions()
|
ActivityResultContracts.RequestMultiplePermissions()
|
||||||
) { permissionResult ->
|
) { permissionResult ->
|
||||||
val isGranted =
|
val isGranted = permissionResult.values.all { it }
|
||||||
permissionResult.entries.all(
|
val isPermanentlyDenied = readStorageHasBeenPermanentlyDenied(isGranted)
|
||||||
Map.Entry<String, @kotlin.jvm.JvmSuppressWildcards Boolean>::value
|
permissionDeniedLayoutShowing = isPermanentlyDenied
|
||||||
)
|
noFilesViewItem.value = Triple(
|
||||||
if (readStorageHasBeenPermanentlyDenied(isGranted)) {
|
requireActivity().resources.getString(string.grant_read_storage_permission),
|
||||||
permissionDeniedLayoutShowing = true
|
requireActivity().resources.getString(string.go_to_settings_label),
|
||||||
noFilesViewItem.value = Triple(
|
isPermanentlyDenied
|
||||||
requireActivity().resources.getString(string.grant_read_storage_permission),
|
)
|
||||||
requireActivity().resources.getString(string.go_to_settings_label),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
} else if (isGranted) {
|
|
||||||
permissionDeniedLayoutShowing = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inject(baseActivity: BaseActivity) {
|
override fun inject(baseActivity: BaseActivity) {
|
||||||
@ -198,10 +204,11 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
onClick = { onBookItemClick(it) },
|
onClick = { onBookItemClick(it) },
|
||||||
onLongClick = { onBookItemLongClick(it) },
|
onLongClick = { onBookItemLongClick(it) },
|
||||||
onMultiSelect = { offerAction(RequestSelect(it)) },
|
onMultiSelect = { offerAction(RequestSelect(it)) },
|
||||||
onRefresh = {},
|
onRefresh = { onSwipeRefresh() },
|
||||||
swipeRefreshItem = true to true,
|
swipeRefreshItem = swipeRefreshItem.value,
|
||||||
noFilesViewItem = noFilesViewItem.value,
|
noFilesViewItem = noFilesViewItem.value,
|
||||||
onDownloadButtonClick = { downloadBookButtonClick() }
|
onDownloadButtonClick = { downloadBookButtonClick() },
|
||||||
|
scanningProgressItem = scanningProgressItem.value
|
||||||
) {
|
) {
|
||||||
NavigationIcon(
|
NavigationIcon(
|
||||||
iconItem = IconItem.Vector(Icons.Filled.Menu),
|
iconItem = IconItem.Vector(Icons.Filled.Menu),
|
||||||
@ -255,7 +262,6 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setUpSwipeRefreshLayout()
|
|
||||||
copyMoveFileHandler?.apply {
|
copyMoveFileHandler?.apply {
|
||||||
setFileCopyMoveCallback(this@LocalLibraryFragment)
|
setFileCopyMoveCallback(this@LocalLibraryFragment)
|
||||||
setLifeCycleScope(lifecycleScope)
|
setLifeCycleScope(lifecycleScope)
|
||||||
@ -272,13 +278,10 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
disposable.add(sideEffects())
|
disposable.add(sideEffects())
|
||||||
disposable.add(fileSelectActions())
|
disposable.add(fileSelectActions())
|
||||||
zimManageViewModel.deviceListScanningProgress.observe(viewLifecycleOwner) {
|
zimManageViewModel.deviceListScanningProgress.observe(viewLifecycleOwner) {
|
||||||
fragmentDestinationLibraryBinding?.scanningProgressView?.apply {
|
// hide this progress bar when scanning is complete.
|
||||||
progress = it
|
scanningProgressItem.value = Pair(it != MAX_PROGRESS, it)
|
||||||
// hide this progress bar when scanning is complete.
|
// enable if the previous scanning is completes.
|
||||||
visibility = if (it == MAX_PROGRESS) GONE else VISIBLE
|
swipeRefreshItem.value = Pair(false, it == MAX_PROGRESS)
|
||||||
// enable if the previous scanning is completes.
|
|
||||||
fragmentDestinationLibraryBinding?.zimSwiperefresh?.isEnabled = it == MAX_PROGRESS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) {
|
if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) {
|
||||||
zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode)
|
zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode)
|
||||||
@ -319,32 +322,26 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
requireArguments().clear()
|
requireArguments().clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpSwipeRefreshLayout() {
|
private fun onSwipeRefresh() {
|
||||||
fragmentDestinationLibraryBinding?.zimSwiperefresh?.setOnRefreshListener {
|
if (permissionDeniedLayoutShowing) {
|
||||||
if (permissionDeniedLayoutShowing) {
|
// When permission denied layout is showing hide the "Swipe refresh".
|
||||||
fragmentDestinationLibraryBinding?.zimSwiperefresh?.isRefreshing = false
|
swipeRefreshItem.value = false to true
|
||||||
|
} else {
|
||||||
|
if (!requireActivity().isManageExternalStoragePermissionGranted(sharedPreferenceUtil)) {
|
||||||
|
showManageExternalStoragePermissionDialog()
|
||||||
|
// Set loading to false since the dialog is currently being displayed.
|
||||||
|
// If the user clicks on "No" in the permission dialog,
|
||||||
|
// the loading icon remains visible infinitely.
|
||||||
|
swipeRefreshItem.value = false to true
|
||||||
} else {
|
} else {
|
||||||
if (!requireActivity().isManageExternalStoragePermissionGranted(sharedPreferenceUtil)) {
|
// hide the swipe refreshing because now we are showing the ContentLoadingProgressBar
|
||||||
showManageExternalStoragePermissionDialog()
|
// to show the progress of how many files are scanned.
|
||||||
// Set loading to false since the dialog is currently being displayed.
|
// disable the swipe refresh layout until the ongoing scanning will not complete
|
||||||
// If the user clicks on "No" in the permission dialog,
|
// to avoid multiple scanning.
|
||||||
// the loading icon remains visible infinitely.
|
swipeRefreshItem.value = false to false
|
||||||
fragmentDestinationLibraryBinding?.zimSwiperefresh?.isRefreshing = false
|
// Show the progress Bar.
|
||||||
} else {
|
scanningProgressItem.value = true to ZERO
|
||||||
fragmentDestinationLibraryBinding?.zimSwiperefresh?.apply {
|
requestFileSystemCheck()
|
||||||
// hide the swipe refreshing because now we are showing the ContentLoadingProgressBar
|
|
||||||
// to show the progress of how many files are scanned.
|
|
||||||
isRefreshing = false
|
|
||||||
// disable the swipe refresh layout until the ongoing scanning will not complete
|
|
||||||
// to avoid multiple scanning.
|
|
||||||
isEnabled = false
|
|
||||||
}
|
|
||||||
fragmentDestinationLibraryBinding?.scanningProgressView?.apply {
|
|
||||||
visibility = VISIBLE
|
|
||||||
progress = 0
|
|
||||||
}
|
|
||||||
requestFileSystemCheck()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,7 +391,7 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
fileSelectLauncher.launch(Intent.createChooser(intent, "Select a zim file"))
|
fileSelectLauncher.launch(Intent.createChooser(intent, "Select a zim file"))
|
||||||
} catch (ex: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
activity.toast(resources.getString(R.string.no_app_found_to_open), Toast.LENGTH_SHORT)
|
activity.toast(resources.getString(R.string.no_app_found_to_open), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -450,7 +447,7 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isValidZimFile(fileName: String): Boolean =
|
private fun isValidZimFile(fileName: String): Boolean =
|
||||||
FileUtils.isValidZimFile(fileName) || FileUtils.isSplittedZimFile(fileName)
|
FileUtils.isValidZimFile(fileName) || isSplittedZimFile(fileName)
|
||||||
|
|
||||||
private suspend fun getZimFileFromUri(
|
private suspend fun getZimFileFromUri(
|
||||||
uri: Uri
|
uri: Uri
|
||||||
@ -513,7 +510,11 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
) {
|
) {
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
} else if (!permissionDeniedLayoutShowing) {
|
} else if (!permissionDeniedLayoutShowing) {
|
||||||
fragmentDestinationLibraryBinding?.zimfilelist?.visibility = VISIBLE
|
noFilesViewItem.value = Triple(
|
||||||
|
requireActivity().resources.getString(string.no_files_here),
|
||||||
|
requireActivity().resources.getString(string.download_books),
|
||||||
|
false
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,8 +522,6 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
mainRepositoryActions.dispose()
|
mainRepositoryActions.dispose()
|
||||||
actionMode = null
|
actionMode = null
|
||||||
fragmentDestinationLibraryBinding?.zimfilelist?.adapter = null
|
|
||||||
fragmentDestinationLibraryBinding = null
|
|
||||||
disposable.clear()
|
disposable.clear()
|
||||||
storagePermissionLauncher?.unregister()
|
storagePermissionLauncher?.unregister()
|
||||||
storagePermissionLauncher = null
|
storagePermissionLauncher = null
|
||||||
@ -680,11 +679,11 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showStorageSelectionSnackBar(message: String) {
|
private fun showStorageSelectionSnackBar(message: String) {
|
||||||
fragmentDestinationLibraryBinding?.zimfilelist?.snack(
|
snackBarHostState.snack(
|
||||||
message,
|
message = message,
|
||||||
requireActivity().findViewById(R.id.bottom_nav_view),
|
actionLabel = getString(string.download_change_storage),
|
||||||
string.download_change_storage,
|
lifecycleScope = lifecycleScope,
|
||||||
{
|
actionClick = {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
showStorageSelectDialog((requireActivity() as KiwixMainActivity).getStorageDeviceList())
|
showStorageSelectDialog((requireActivity() as KiwixMainActivity).getStorageDeviceList())
|
||||||
}
|
}
|
||||||
@ -738,7 +737,7 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
|
|||||||
) { permissionResult ->
|
) { permissionResult ->
|
||||||
val isGranted =
|
val isGranted =
|
||||||
permissionResult.entries.all(
|
permissionResult.entries.all(
|
||||||
Map.Entry<String, @kotlin.jvm.JvmSuppressWildcards Boolean>::value
|
Map.Entry<String, @JvmSuppressWildcards Boolean>::value
|
||||||
)
|
)
|
||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
zimFileUri?.let {
|
zimFileUri?.let {
|
@ -16,10 +16,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.nav.destination.library
|
package org.kiwix.kiwixmobile.nav.destination.library.local
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -43,9 +42,12 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import org.kiwix.kiwixmobile.R.string
|
import org.kiwix.kiwixmobile.R.string
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
|
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
|
import org.kiwix.kiwixmobile.core.ui.components.KiwixButton
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.KiwixSnackbarHost
|
import org.kiwix.kiwixmobile.core.ui.components.KiwixSnackbarHost
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.ProgressBarStyle
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.SwipeRefreshLayout
|
||||||
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
|
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
|
||||||
import org.kiwix.kiwixmobile.core.ui.theme.Black
|
import org.kiwix.kiwixmobile.core.ui.theme.Black
|
||||||
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
|
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
|
||||||
@ -60,13 +62,14 @@ import org.kiwix.kiwixmobile.ui.ZimFilesLanguageHeader
|
|||||||
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
|
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Suppress("ComposableLambdaParameterNaming", "LongParameterList", "UnusedParameter")
|
@Suppress("ComposableLambdaParameterNaming", "LongParameterList")
|
||||||
@Composable
|
@Composable
|
||||||
fun LocalLibraryScreen(
|
fun LocalLibraryScreen(
|
||||||
state: FileSelectListState,
|
state: FileSelectListState,
|
||||||
snackBarHostState: SnackbarHostState,
|
snackBarHostState: SnackbarHostState,
|
||||||
swipeRefreshItem: Pair<Boolean, Boolean>,
|
swipeRefreshItem: Pair<Boolean, Boolean>,
|
||||||
onRefresh: () -> Unit,
|
onRefresh: () -> Unit,
|
||||||
|
scanningProgressItem: Pair<Boolean, Int>,
|
||||||
noFilesViewItem: Triple<String, String, Boolean>,
|
noFilesViewItem: Triple<String, String, Boolean>,
|
||||||
onDownloadButtonClick: () -> Unit,
|
onDownloadButtonClick: () -> Unit,
|
||||||
fabButtonClick: () -> Unit,
|
fabButtonClick: () -> Unit,
|
||||||
@ -76,28 +79,26 @@ fun LocalLibraryScreen(
|
|||||||
onMultiSelect: ((BookOnDisk) -> Unit)? = null,
|
onMultiSelect: ((BookOnDisk) -> Unit)? = null,
|
||||||
navigationIcon: @Composable () -> Unit
|
navigationIcon: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
// val swipeRefreshState = rememberPullToRefreshState()
|
|
||||||
KiwixTheme {
|
KiwixTheme {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
snackbarHost = { KiwixSnackbarHost(snackbarHostState = snackBarHostState) },
|
snackbarHost = { KiwixSnackbarHost(snackbarHostState = snackBarHostState) },
|
||||||
topBar = { KiwixAppBar(R.string.library, navigationIcon, actionMenuItems) },
|
topBar = { KiwixAppBar(R.string.library, navigationIcon, actionMenuItems) },
|
||||||
modifier = Modifier.systemBarsPadding()
|
modifier = Modifier.systemBarsPadding()
|
||||||
) { contentPadding ->
|
) { contentPadding ->
|
||||||
Box(
|
SwipeRefreshLayout(
|
||||||
|
isRefreshing = swipeRefreshItem.first,
|
||||||
|
isEnabled = swipeRefreshItem.second,
|
||||||
|
onRefresh = onRefresh,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(contentPadding)
|
.padding(contentPadding)
|
||||||
// .pullToRefresh(
|
|
||||||
// isRefreshing = swipeRefreshItem.first,
|
|
||||||
// state = swipeRefreshState,
|
|
||||||
// enabled = swipeRefreshItem.second,
|
|
||||||
// onRefresh = { onRefresh }
|
|
||||||
// )
|
|
||||||
// .pullToRefreshIndicator(
|
|
||||||
// state = swipeRefreshState,
|
|
||||||
// isRefreshing = swipeRefreshItem.first
|
|
||||||
// )
|
|
||||||
) {
|
) {
|
||||||
|
if (scanningProgressItem.first) {
|
||||||
|
ContentLoadingProgressBar(
|
||||||
|
progressBarStyle = ProgressBarStyle.HORIZONTAL,
|
||||||
|
progress = scanningProgressItem.second
|
||||||
|
)
|
||||||
|
}
|
||||||
if (noFilesViewItem.third) {
|
if (noFilesViewItem.third) {
|
||||||
NoFilesView(noFilesViewItem, onDownloadButtonClick)
|
NoFilesView(noFilesViewItem, onDownloadButtonClick)
|
||||||
} else {
|
} else {
|
@ -21,7 +21,7 @@ package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
|
||||||
import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections
|
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
|
||||||
|
|
||||||
object NavigateToDownloads : SideEffect<Unit> {
|
object NavigateToDownloads : SideEffect<Unit> {
|
||||||
override fun invokeWith(activity: AppCompatActivity) {
|
override fun invokeWith(activity: AppCompatActivity) {
|
||||||
|
@ -25,11 +25,10 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
|
|
||||||
import org.kiwix.kiwixmobile.core.extensions.toast
|
import org.kiwix.kiwixmobile.core.extensions.toast
|
||||||
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
|
||||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||||
import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader
|
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader
|
||||||
|
|
||||||
@Suppress("InjectDispatcher")
|
@Suppress("InjectDispatcher")
|
||||||
data class OpenFileWithNavigation(private val bookOnDisk: BooksOnDiskListItem.BookOnDisk) :
|
data class OpenFileWithNavigation(private val bookOnDisk: BooksOnDiskListItem.BookOnDisk) :
|
||||||
|
@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/libraryFragment"
|
android:id="@+id/libraryFragment"
|
||||||
android:name="org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragment"
|
android:name="org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragment"
|
||||||
android:label="Library">
|
android:label="Library">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_library_to_navigation_reader"
|
android:id="@+id/action_navigation_library_to_navigation_reader"
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.ProgressIndicatorDefaults.drawStopIndicator
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.HUNDERED
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue400
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray350
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ContentLoadingProgressBar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
progressBarStyle: ProgressBarStyle = ProgressBarStyle.CIRCLE,
|
||||||
|
progress: Int = ZERO,
|
||||||
|
progressBarColor: Color = DenimBlue400,
|
||||||
|
progressBarTrackColor: Color = MineShaftGray350
|
||||||
|
) {
|
||||||
|
when (progressBarStyle) {
|
||||||
|
ProgressBarStyle.CIRCLE -> {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = modifier,
|
||||||
|
color = progressBarColor,
|
||||||
|
trackColor = progressBarTrackColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressBarStyle.HORIZONTAL -> {
|
||||||
|
LinearProgressIndicator(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
progress = { progress.toFloat() / HUNDERED },
|
||||||
|
color = progressBarColor,
|
||||||
|
trackColor = progressBarTrackColor,
|
||||||
|
gapSize = ZERO.dp,
|
||||||
|
strokeCap = StrokeCap.Butt,
|
||||||
|
drawStopIndicator = {
|
||||||
|
drawStopIndicator(
|
||||||
|
drawScope = this,
|
||||||
|
stopSize = ZERO.dp,
|
||||||
|
color = progressBarTrackColor,
|
||||||
|
strokeCap = StrokeCap.Butt
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ProgressBarStyle {
|
||||||
|
HORIZONTAL,
|
||||||
|
CIRCLE
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshState
|
||||||
|
import androidx.compose.material3.pulltorefresh.pullToRefresh
|
||||||
|
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.Black
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.White
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SwipeRefreshLayout(
|
||||||
|
isRefreshing: Boolean,
|
||||||
|
isEnabled: Boolean,
|
||||||
|
onRefresh: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
state: PullToRefreshState = rememberPullToRefreshState(),
|
||||||
|
contentAlignment: Alignment = Alignment.TopStart,
|
||||||
|
indicator: @Composable BoxScope.() -> Unit = {
|
||||||
|
Indicator(
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
|
isRefreshing = isRefreshing,
|
||||||
|
state = state,
|
||||||
|
containerColor = White,
|
||||||
|
color = Black
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content: @Composable BoxScope.() -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier.pullToRefresh(
|
||||||
|
state = state,
|
||||||
|
isRefreshing = isRefreshing,
|
||||||
|
onRefresh = onRefresh,
|
||||||
|
enabled = isEnabled
|
||||||
|
),
|
||||||
|
contentAlignment = contentAlignment
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
indicator()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user