Merge pull request #4337 from jackq97/#4332_migrate_donation_layout_into_compose

#4332 migrate donation layout into compose
This commit is contained in:
Kelson 2025-06-29 11:18:21 +02:00 committed by GitHub
commit 11a2f90b6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 515 additions and 199 deletions

View File

@ -0,0 +1,229 @@
/*
* 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.reader
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.core.content.edit
import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.accessibility.AccessibilityChecks
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesCheck
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.anyOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.THREE_MONTHS_IN_MILLISECONDS
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.nav.destination.library.library
import org.kiwix.kiwixmobile.testutils.RetryRule
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.closeSystemDialogs
import org.kiwix.kiwixmobile.testutils.TestUtils.isSystemUINotRespondingDialogVisible
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
class DonationDialogTest : BaseActivityTest() {
@Rule(order = RETRY_RULE_ORDER)
@JvmField
val retryRule = RetryRule()
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
val composeTestRule = createComposeRule()
private lateinit var kiwixMainActivity: KiwixMainActivity
private lateinit var sharedPreferenceUtil: SharedPreferenceUtil
@Before
override fun waitForIdle() {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
if (isSystemUINotRespondingDialogVisible(this)) {
closeSystemDialogs(context, this)
}
waitForIdle()
}
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false)
putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false)
putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true)
putString(SharedPreferenceUtil.PREF_LANG, "en")
}
sharedPreferenceUtil = SharedPreferenceUtil(context)
activityScenario =
ActivityScenario.launch(KiwixMainActivity::class.java).apply {
moveToState(Lifecycle.State.RESUMED)
onActivity {
handleLocaleChange(
it,
"en",
sharedPreferenceUtil
)
}
}
}
init {
AccessibilityChecks.enable().apply {
setRunChecksFromRootView(true)
setSuppressingResultMatcher(
anyOf(
allOf(
matchesCheck(TouchTargetSizeCheck::class.java),
matchesViews(withContentDescription("More options"))
),
matchesCheck(SpeakableTextPresentCheck::class.java)
)
)
}
}
@Test
fun showDonationPopupWhenApplicationIsThreeMonthOldAndHaveAtleastOneZIMFile() {
loadZIMFileInApplication()
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds = 0L
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun shouldNotShowDonationPopupWhenApplicationIsThreeMonthOldAndDoNotHaveAnyZIMFile() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
activityScenario.onActivity {
kiwixMainActivity = it
kiwixMainActivity.navigate(R.id.libraryFragment)
}
deleteAllZIMFilesFromApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
@Test
fun shouldNotShowPopupIfTimeSinceLastPopupIsLessThanThreeMonth() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS / 2)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
@Test
fun shouldShowDonationPopupIfTimeSinceLastPopupExceedsThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS + 1000)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun testShouldShowDonationPopupWhenLaterClickedTimeExceedsThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS + 1000)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun testShouldNotShowPopupIfLaterClickedTimeIsLessThanThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds =
System.currentTimeMillis() - 10000L
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
private fun openReaderFragment() {
UiThreadStatement.runOnUiThread {
kiwixMainActivity.navigate(kiwixMainActivity.readerFragmentResId)
}
}
private fun loadZIMFileInApplication() {
openLocalLibraryScreen()
deleteAllZIMFilesFromApplication()
val loadFileStream =
DonationDialogTest::class.java.classLoader.getResourceAsStream("testzim.zim")
val zimFile =
File(
context.getExternalFilesDirs(null)[0],
"testzim.zim"
)
if (zimFile.exists()) zimFile.delete()
zimFile.createNewFile()
loadFileStream.use { inputStream ->
val outputStream: OutputStream = FileOutputStream(zimFile)
outputStream.use { it ->
val buffer = ByteArray(inputStream.available())
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
it.write(buffer, 0, length)
}
}
}
refreshZIMFilesList()
}
private fun openLocalLibraryScreen() {
activityScenario.onActivity {
kiwixMainActivity = it
kiwixMainActivity.navigate(R.id.libraryFragment)
}
}
private fun refreshZIMFilesList() {
library {
refreshList(composeTestRule)
waitUntilZimFilesRefreshing(composeTestRule)
}
}
private fun deleteAllZIMFilesFromApplication() {
refreshZIMFilesList()
library {
// delete all the ZIM files showing in the LocalLibrary
// screen to properly test the scenario.
deleteZimIfExists(composeTestRule)
}
}
@After
fun finish() {
TestUtils.deleteTemporaryFilesOfTestCases(context)
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.reader
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.onNodeWithTag
import applyWithViewHierarchyPrinting
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.core.main.reader.DONATION_LAYOUT_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
fun donation(func: DonationRobot.() -> Unit) = DonationRobot().applyWithViewHierarchyPrinting(func)
class DonationRobot : BaseRobot() {
fun assertDonationDialogDisplayed(composeTestRule: ComposeContentTestRule) {
composeTestRule.apply {
waitUntilTimeout()
onNodeWithTag(DONATION_LAYOUT_TESTING_TAG).isDisplayed()
}
}
fun assertDonationDialogIsNotDisplayed(composeTestRule: ComposeContentTestRule) {
composeTestRule.apply {
waitUntilTimeout()
onNodeWithTag(DONATION_LAYOUT_TESTING_TAG).isNotDisplayed()
}
}
}

View File

@ -25,6 +25,5 @@
<dimen name="favicon_margin_right">8dp</dimen>
<dimen name="item_library_margin_top">8dp</dimen>
<dimen name="stop_horizontal_margin">2dp</dimen>
<dimen name="material_design_appbar_size">48dp</dimen>
</resources>

View File

@ -39,18 +39,13 @@ import android.os.Looper
import android.provider.Settings
import android.util.AttributeSet
import android.view.ActionMode
import android.view.Gravity.BOTTOM
import android.view.Gravity.CENTER_HORIZONTAL
import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.webkit.WebBackForwardList
import android.webkit.WebView
import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
@ -106,7 +101,6 @@ import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isLandScapeMode
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.closeFullScreenMode
@ -155,7 +149,6 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
import org.kiwix.kiwixmobile.core.ui.models.IconItem
import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler
import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler.ShowDonationDialogCallback
import org.kiwix.kiwixmobile.core.utils.ExternalLinkOpener
@ -277,7 +270,6 @@ abstract class CoreReaderFragment :
private var isFirstRun = false
private var tableDrawerAdapter: TableDrawerAdapter? = null
private var tableDrawerRight: RecyclerView? = null
private var donationLayout: FrameLayout? = null
private var bookmarkingJob: Job? = null
private var isBookmarked = false
private lateinit var serviceConnection: ServiceConnection
@ -339,7 +331,10 @@ abstract class CoreReaderFragment :
shouldShowFullScreenMode = false,
searchPlaceHolderItemForCustomApps = false to {
openSearch(searchString = "", isOpenedFromTabView = false, false)
}
},
appName = "",
donateButtonClick = {},
laterButtonClick = {}
)
)
private var readerLifeCycleScope: CoroutineScope? = null
@ -453,7 +448,17 @@ abstract class CoreReaderFragment :
readerScreenTitle = context.getString(R.string.reader),
darkModeViewPainter = darkModeViewPainter,
fullScreenItem = fullScreenItem.first to getVideoView(),
tocButtonItem = getTocButtonStateAndAction()
tocButtonItem = getTocButtonStateAndAction(),
appName = (requireActivity() as CoreMainActivity).appName,
donateButtonClick = {
donationDialogHandler?.updateLastDonationPopupShownTime()
openKiwixSupportUrl()
readerScreenState.update { copy(shouldShowDonationPopup = false) }
},
laterButtonClick = {
donationDialogHandler?.donateLater()
readerScreenState.update { copy(shouldShowDonationPopup = false) }
}
)
}
}
@ -1176,8 +1181,6 @@ abstract class CoreReaderFragment :
compatCallback = null
drawerLayout = null
tableDrawerRightContainer = null
donationLayout?.removeAllViews()
donationLayout = null
}
private fun updateTableOfContents() {
@ -1908,88 +1911,14 @@ abstract class CoreReaderFragment :
lifecycleScope.launch { donationDialogHandler?.attemptToShowDonationPopup() }
}
@Suppress("InflateParams", "MagicNumber")
protected open fun showDonationLayout() {
val donationCardView = layoutInflater.inflate(R.layout.layout_donation_bottom_sheet, null)
val layoutParams = FrameLayout.LayoutParams(
getDonationPopupWidth(),
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
val rightAndLeftMargin = requireActivity().resources.getDimensionPixelSize(
R.dimen.activity_horizontal_margin
)
setMargins(
rightAndLeftMargin,
0,
rightAndLeftMargin,
getBottomMarginForDonationPopup()
)
gravity = BOTTOM or CENTER_HORIZONTAL
}
donationCardView.layoutParams = layoutParams
donationLayout?.apply {
removeAllViews()
addView(donationCardView)
setDonationLayoutVisibility(VISIBLE)
}
donationCardView.findViewById<TextView>(R.id.descriptionText).apply {
text = getString(
R.string.donation_dialog_description,
(requireActivity() as CoreMainActivity).appName
)
}
val donateButton: TextView = donationCardView.findViewById(R.id.donateButton)
donateButton.setOnClickListener {
donationDialogHandler?.updateLastDonationPopupShownTime()
setDonationLayoutVisibility(GONE)
openKiwixSupportUrl()
}
val laterButton: TextView = donationCardView.findViewById(R.id.laterButton)
laterButton.setOnClickListener {
donationDialogHandler?.donateLater()
setDonationLayoutVisibility(GONE)
}
}
private fun getDonationPopupWidth(): Int {
val deviceWidth = requireActivity().getWindowWidth()
val maximumDonationLayoutWidth =
requireActivity().resources.getDimensionPixelSize(R.dimen.maximum_donation_popup_width)
return when {
deviceWidth > maximumDonationLayoutWidth || requireActivity().isLandScapeMode() -> {
maximumDonationLayoutWidth
}
else -> FrameLayout.LayoutParams.MATCH_PARENT
}
}
private fun getBottomMarginForDonationPopup(): Int {
var bottomMargin = requireActivity().resources.getDimensionPixelSize(
R.dimen.donation_popup_bottom_margin
)
if (readerScreenState.value.shouldShowBottomAppBar) {
// if bottomAppBar is visible then add the height of the bottomAppBar.
bottomMargin +=
requireActivity().resources.getDimensionPixelSize(
R.dimen.material_minimum_height_and_width
)
bottomMargin += requireActivity().resources.getDimensionPixelSize(R.dimen.card_margin)
}
return bottomMargin
readerScreenState.update { copy(shouldShowDonationPopup = true) }
}
protected open fun openKiwixSupportUrl() {
(requireActivity() as CoreMainActivity).openSupportKiwixExternalLink()
}
private fun setDonationLayoutVisibility(visibility: Int) {
donationLayout?.visibility = visibility
}
private fun openFullScreenIfEnabled() {
if (isInFullScreenMode()) {
openFullScreen()

View File

@ -0,0 +1,199 @@
/*
* 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.reader
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.utils.ComposeDimens
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.DONATION_LAYOUT_MAXIMUM_WIDTH
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
const val DONATION_LAYOUT_TESTING_TAG = "donationLayoutTestingTag"
@Composable
fun DonationLayout(
appName: String,
onDonateButtonClick: () -> Unit,
onLaterButtonClick: () -> Unit
) {
val donationLayoutWidth = getDonationLayoutWidth()
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.then(
if (donationLayoutWidth != Dp.Unspecified) {
Modifier.width(donationLayoutWidth)
} else {
Modifier.fillMaxWidth()
}
)
.padding(horizontal = SIXTEEN_DP),
) {
DonationLayoutCard(
appName,
onDonateButtonClick,
onLaterButtonClick
)
}
}
@Composable
fun DonationLayoutCard(
appName: String,
onDonateButtonClick: () -> Unit,
onLaterButtonClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(ComposeDimens.SIXTEEN_DP)
.testTag(DONATION_LAYOUT_TESTING_TAG),
shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(defaultElevation = ComposeDimens.SIX_DP),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer)
) {
Column(
modifier = Modifier
.padding(horizontal = ComposeDimens.SIXTEEN_DP)
.padding(top = ComposeDimens.SIXTEEN_DP)
) {
DonationDialogContent(appName)
DonationDialogButtons(onDonateButtonClick, onLaterButtonClick)
}
}
}
@Composable
private fun DonationDialogContent(appName: String) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Top
) {
Image(
painter = painterResource(id = R.drawable.ic_donation_icon),
contentDescription = stringResource(id = R.string.donation_dialog_title),
modifier = Modifier
.size(ComposeDimens.FIFTY_DP)
)
Spacer(modifier = Modifier.width(ComposeDimens.TWELVE_DP))
Column {
DonationDialogHeadingText()
DonationDialogSubHeadingText(appName = appName)
}
}
}
@Composable
private fun DonationDialogButtons(
onDonateButtonClick: () -> Unit,
onLaterButtonClick: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.weight(1f))
DonationDialogButton(
onButtonClick = onLaterButtonClick,
buttonText = R.string.rate_dialog_neutral
)
DonationDialogButton(
onButtonClick = onDonateButtonClick,
buttonText = R.string.make_donation
)
}
}
@Composable
fun DonationDialogButton(
onButtonClick: () -> Unit,
@StringRes buttonText: Int
) {
TextButton(onClick = onButtonClick) {
Text(
text = stringResource(buttonText),
color = MaterialTheme.colorScheme.primary
)
}
}
@Composable
fun DonationDialogHeadingText() {
Text(
text = stringResource(id = R.string.donation_dialog_title),
style = MaterialTheme.typography.titleMedium,
fontSize = ComposeDimens.SMALL_TITLE_TEXT_SIZE
)
}
@Composable
fun DonationDialogSubHeadingText(appName: String) {
Text(
text = stringResource(
R.string.donation_dialog_description,
appName
),
fontSize = ComposeDimens.FOURTEEN_SP,
color = MaterialTheme.colorScheme.onTertiary,
modifier = Modifier.padding(top = ComposeDimens.FOUR_DP)
)
}
@Composable
private fun getDonationLayoutWidth(): Dp {
val configuration = LocalWindowInfo.current
val screenWidth = configuration.containerSize.width.dp
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
return if (screenWidth > DONATION_LAYOUT_MAXIMUM_WIDTH || isLandscape) {
DONATION_LAYOUT_MAXIMUM_WIDTH
} else {
Dp.Unspecified
}
}

View File

@ -133,6 +133,7 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.CLOSE_TAB_ICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FIVE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.FOUR_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_TOOLBAR_HEIGHT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.ONE_DP
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_BUTTON_ICON_SIZE
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.READER_BOTTOM_APP_BAR_DISABLE_BUTTON_ALPHA
@ -235,6 +236,7 @@ private fun ReaderContentLayout(
ShowProgressBarIfZIMFilePageIsLoading(state)
Column(Modifier.align(Alignment.BottomCenter)) {
TtsControls(state)
ShowDonationLayout(state)
BottomAppBarOfReaderScreen(
state.bookmarkButtonItem,
state.previousPageButtonItem,
@ -251,7 +253,6 @@ private fun ReaderContentLayout(
)
}
}
ShowDonationLayout(state)
}
}
}
@ -585,14 +586,14 @@ private fun BottomAppBarButtonIcon(
}
@Composable
private fun BoxScope.ShowDonationLayout(state: ReaderScreenState) {
private fun ShowDonationLayout(state: ReaderScreenState) {
if (state.shouldShowDonationPopup) {
Box(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
) {
// TODO create donation popup layout.
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
DonationLayout(
state.appName,
state.donateButtonClick,
state.laterButtonClick
)
}
}
}
@ -703,7 +704,6 @@ private fun BoxScope.CloseAllTabButton(onCloseAllTabs: () -> Unit) {
}
}
@Suppress("MagicNumber")
@Composable
fun TabItemView(
index: Int,
@ -715,7 +715,7 @@ fun TabItemView(
) {
val cardElevation = if (isSelected) EIGHT_DP else TWO_DP
val borderColor = if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent
val (cardWidth, cardHeight) = getTabCardSize(toolbarHeightDp = 56.dp)
val (cardWidth, cardHeight) = getTabCardSize(toolbarHeightDp = KIWIX_TOOLBAR_HEIGHT)
Box(modifier = modifier) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,

View File

@ -156,5 +156,17 @@ data class ReaderScreenState(
/**
* Manages the showing/hiding of search placeholder in toolbar for custom apps.
*/
val searchPlaceHolderItemForCustomApps: Pair<Boolean, () -> Unit>
val searchPlaceHolderItemForCustomApps: Pair<Boolean, () -> Unit>,
/**
* Manages the showing of application name in donation layout.
*/
val appName: String,
/**
* Handles the click when user clicks on "Make a donation" button.
*/
val donateButtonClick: () -> Unit,
/**
* Handles the click when user clicks on "Later" button in donation layout.
*/
val laterButtonClick: () -> Unit
)

View File

@ -37,6 +37,7 @@ object ComposeDimens {
// KiwixAppBar(Toolbar) dimens
val ACTION_MENU_TEXTVIEW_BUTTON_PADDING = 13.dp
val KIWIX_TOOLBAR_HEIGHT = 56.dp
// Padding & Margins
val SIXTY_DP = 60.dp
@ -193,4 +194,5 @@ object ComposeDimens {
val BACK_TO_TOP_BUTTON_BOTTOM_MARGIN = 80.dp
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
}

View File

@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Kiwix Android
~ Copyright (c) 2024 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/>.
~
-->
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/ThemeOverlay.MaterialComponents.Dialog.Alert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:padding="16dp"
app:cardCornerRadius="12dp"
app:cardElevation="6dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/heart_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentStart="true"
android:layout_margin="@dimen/activity_horizontal_margin"
android:layout_marginEnd="12dp"
android:contentDescription="@string/donation_dialog_title"
android:src="@drawable/ic_donation_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/titleText"
style="@style/TextAppearance.M3.Sys.Typescale.TitleMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/fullscreen_control_button_margin"
android:text="@string/donation_dialog_title"
android:textSize="16sp"
app:layout_constraintStart_toEndOf="@id/heart_icon"
app:layout_constraintTop_toTopOf="@+id/heart_icon" />
<TextView
android:id="@+id/descriptionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/donation_dialog_description"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="@id/titleText"
app:layout_constraintTop_toBottomOf="@+id/titleText" />
<TextView
android:id="@+id/laterButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/activity_horizontal_margin"
android:text="@string/rate_dialog_neutral"
android:textColor="?attr/colorPrimary"
app:layout_constraintEnd_toStartOf="@+id/donateButton"
app:layout_constraintTop_toBottomOf="@id/descriptionText" />
<TextView
android:id="@+id/donateButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/activity_horizontal_margin"
android:text="@string/make_donation"
android:textColor="?attr/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/descriptionText" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -8,24 +8,12 @@
<dimen name="switch_padding">10dp</dimen>
<dimen name="kiwix_search_widget_layout_id_height">50dp</dimen>
<dimen name="webview_search_edit_text_margin_right">10dip</dimen>
<dimen name="fullscreen_control_button_margin">7dp</dimen>
<dimen name="kiwix_search_widget_margin">1dp</dimen>
<dimen name="progress_view_height">2dp</dimen>
<dimen name="title_text_margin">16dp</dimen>
<dimen name="title_text_padding">16dp</dimen>
<dimen name="kiwix_search_widget_padding">0dp</dimen>
<dimen name="section_list_height">56dp</dimen>
<dimen name="card_margin">5dp</dimen>
<dimen name="fab_vertical_offset">25dp</dimen>
<dimen name="material_design_appbar_size">48dp</dimen>
<dimen name="hamburger_icon_size">36dp</dimen>
<dimen name="material_minimum_height_and_width">48dp</dimen>
<dimen name="close_tab_button_size">24dp</dimen>
<dimen name="find_in_page_button_padding">13dp</dimen>
<dimen name="donation_popup_bottom_margin">20dp</dimen>
<dimen name="maximum_donation_popup_width">400dp</dimen>
<dimen name="showcase_view_maximum_width_in_landscape_mode">150dp</dimen>
<dimen name="showcase_view_maximum_height">10dp</dimen>
</resources>

View File

@ -154,7 +154,7 @@ class DonationDialogHandlerTest {
}
@Test
fun `test should show popup if later clicked time is less than three months`() =
fun `test should not show popup if later clicked time is less than three months`() =
runTest {
donationDialogHandler = spyk(donationDialogHandler)
val currentMilliSeconds = System.currentTimeMillis()