diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt index 55990093a4..84becb2992 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt @@ -66,6 +66,7 @@ class DeepLinksTest : BaseActivityTest() { prefIsTest = true playStoreRestrictionPermissionDialog = false putPrefLanguage("en") + lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() } } } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt index 4a2ffd1740..a05f85fb8a 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/download/DownloadTest.kt @@ -94,6 +94,10 @@ class DownloadTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt index 6d74501cd8..f313bb8bea 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt @@ -57,7 +57,9 @@ class HelpFragmentTest : BaseActivityTest() { handleLocaleChange( it, "en", - SharedPreferenceUtil(context) + SharedPreferenceUtil(context).apply { + lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() + } ) } } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadTest.kt index 63ba1fae3b..fd6d0e0f09 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/initial/download/InitialDownloadTest.kt @@ -85,6 +85,10 @@ class InitialDownloadTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/intro/IntroFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/intro/IntroFragmentTest.kt index c9ad296522..fc6e9c026d 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/intro/IntroFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/intro/IntroFragmentTest.kt @@ -67,6 +67,10 @@ class IntroFragmentTest : BaseActivityTest() { PreferenceManager.getDefaultSharedPreferences(context).edit { putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, true) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/language/LanguageFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/language/LanguageFragmentTest.kt index 4c380bb160..51e11b5294 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/language/LanguageFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/language/LanguageFragmentTest.kt @@ -82,6 +82,10 @@ class LanguageFragmentTest { putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/localLibrary/CopyMoveFileHandlerTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/localLibrary/CopyMoveFileHandlerTest.kt index a7f7b14a79..26927c92e9 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/localLibrary/CopyMoveFileHandlerTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/localLibrary/CopyMoveFileHandlerTest.kt @@ -77,6 +77,10 @@ class CopyMoveFileHandlerTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt index 71ecb8f009..420c932e59 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt @@ -64,6 +64,10 @@ class TopLevelDestinationTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_SHOW_SHOWCASE, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt index 47cf9c6e0b..e9578891e4 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/mimetype/MimeTypeTest.kt @@ -56,6 +56,10 @@ class MimeTypeTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LocalLibraryTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LocalLibraryTest.kt index 80caf41101..1d650bf063 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LocalLibraryTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LocalLibraryTest.kt @@ -90,6 +90,10 @@ class LocalLibraryTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_SHOW_MANAGE_PERMISSION_DIALOG_ON_REFRESH, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/note/NoteFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/note/NoteFragmentTest.kt index b8279c26eb..2c0dcd2d23 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/note/NoteFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/note/NoteFragmentTest.kt @@ -74,6 +74,10 @@ class NoteFragmentTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt index ced90fedbf..61d0fe51d0 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/ImportBookmarkTest.kt @@ -113,6 +113,10 @@ class ImportBookmarkTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index f86a28b931..febf8a06b1 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -70,6 +70,10 @@ class LibkiwixBookmarkTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt index 73dbf64762..e433902452 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt @@ -68,6 +68,10 @@ class NavigationHistoryTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/EncodedUrlTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/EncodedUrlTest.kt index e92c9abf22..a387f40460 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/EncodedUrlTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/EncodedUrlTest.kt @@ -56,6 +56,10 @@ class EncodedUrlTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchFragmentTest.kt index 5f830e7980..d325c6a1a0 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchFragmentTest.kt @@ -87,6 +87,10 @@ class SearchFragmentTest : BaseActivityTest() { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt index 0dfb9cb75a..fd04bb49ee 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt @@ -82,6 +82,7 @@ class KiwixSettingsFragmentTest { SharedPreferenceUtil(it).apply { setIsPlayStoreBuildType(true) playStoreRestrictionPermissionDialog = false + lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() } ) it.navigate(R.id.introFragment) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt index 1e24d4093a..d8ca561647 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt @@ -108,6 +108,7 @@ class ZimHostFragmentTest { prefIsTest = true playStoreRestrictionPermissionDialog = false putPrefLanguage("en") + lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() } } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index 3577aa62c2..5d9e709c7f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -287,7 +287,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { drawerContainerLayout.closeDrawer(drawerNavView) } - private fun openSupportKiwixExternalLink() { + fun openSupportKiwixExternalLink() { externalLinkOpener.openExternalUrl(KIWIX_SUPPORT_URL.toUri().browserIntent()) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index e95d01394d..00a4015ab1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -46,6 +46,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.MotionEvent import android.view.View +import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import android.view.animation.AnimationUtils @@ -133,6 +134,8 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource import org.kiwix.kiwixmobile.core.search.viewmodel.effects.SearchItemToOpen import org.kiwix.kiwixmobile.core.utils.AnimationUtils.rotate import org.kiwix.kiwixmobile.core.utils.DimenUtils.getToolbarHeight +import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler +import org.kiwix.kiwixmobile.core.utils.DonationDialogHandler.ShowDonationDialogCallback import org.kiwix.kiwixmobile.core.utils.ExternalLinkOpener import org.kiwix.kiwixmobile.core.utils.LanguageUtils import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.getCurrentLocale @@ -173,7 +176,8 @@ abstract class CoreReaderFragment : FragmentActivityExtensions, WebViewProvider, ReadAloudCallbacks, - NavigationHistoryClickListener { + NavigationHistoryClickListener, + ShowDonationDialogCallback { protected val webViewList: MutableList = ArrayList() private val webUrlsProcessor = BehaviorProcessor.create() private var fragmentReaderBinding: FragmentReaderBinding? = null @@ -227,6 +231,10 @@ abstract class CoreReaderFragment : @Inject var alertDialogShower: DialogShower? = null + @JvmField + @Inject + var donationDialogHandler: DonationDialogHandler? = null + @JvmField @Inject var painter: DarkModeViewPainter? = null @@ -297,6 +305,7 @@ abstract class CoreReaderFragment : private var tableDrawerAdapter: TableDrawerAdapter? = null private var tableDrawerRight: RecyclerView? = null private var tabCallback: ItemTouchHelper.Callback? = null + private var donationLayout: FrameLayout? = null private var bookmarkingDisposable: Disposable? = null private var isBookmarked = false private lateinit var serviceConnection: ServiceConnection @@ -389,6 +398,7 @@ abstract class CoreReaderFragment : ) { super.onViewCreated(view, savedInstanceState) setupMenu() + donationDialogHandler?.setDonationDialogCallBack(this) val activity = requireActivity() as AppCompatActivity? activity?.let { WebView(it).destroy() // Workaround for buggy webViews see #710 @@ -511,6 +521,7 @@ abstract class CoreReaderFragment : tabRecyclerView = findViewById(R.id.tab_switcher_recycler_view) snackBarRoot = findViewById(R.id.snackbar_root) bottomToolbarToc = findViewById(R.id.bottom_toolbar_toc) + donationLayout = findViewById(R.id.donation_layout) } } } @@ -1213,6 +1224,8 @@ abstract class CoreReaderFragment : unRegisterReadAloudService() storagePermissionForNotesLauncher?.unregister() storagePermissionForNotesLauncher = null + donationDialogHandler?.setDonationDialogCallBack(null) + donationDialogHandler = null } private fun unBindViewsAndBinding() { @@ -1243,6 +1256,8 @@ abstract class CoreReaderFragment : closeAllTabsButton = null tableDrawerRightContainer = null fragmentReaderBinding = null + donationLayout?.removeAllViews() + donationLayout = null } private fun updateTableOfContents() { @@ -1846,6 +1861,76 @@ abstract class CoreReaderFragment : if (tts == null) { setUpTTS() } + donationDialogHandler?.attemptToShowDonationPopup() + } + + @Suppress("InflateParams", "MagicNumber") + protected open fun showDonationLayout() { + val donationCardView = layoutInflater.inflate(R.layout.layout_donation_bottom_sheet, null) + val layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT + ).apply { + val rightAndLeftMargin = requireActivity().resources.getDimensionPixelSize( + org.kiwix.kiwixmobile.core.R.dimen.activity_horizontal_margin + ) + setMargins( + rightAndLeftMargin, + 0, + rightAndLeftMargin, + getBottomMarginForDonationPopup() + ) + } + + donationCardView.layoutParams = layoutParams + donationLayout?.apply { + removeAllViews() + addView(donationCardView) + setDonationLayoutVisibility(VISIBLE) + } + donationCardView.findViewById(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 getBottomMarginForDonationPopup(): Int { + var bottomMargin = requireActivity().resources.getDimensionPixelSize( + R.dimen.donation_popup_bottom_margin + ) + val bottomAppBar = requireActivity() + .findViewById(R.id.bottom_toolbar) + if (bottomAppBar.visibility == VISIBLE) { + // 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 + } + + protected open fun openKiwixSupportUrl() { + (requireActivity() as CoreMainActivity).openSupportKiwixExternalLink() + } + + private fun setDonationLayoutVisibility(visibility: Int) { + donationLayout?.visibility = visibility } private fun openFullScreenIfEnabled() { @@ -2388,6 +2473,10 @@ abstract class CoreReaderFragment : unbindService() } + override fun showDonationDialog() { + showDonationLayout() + } + private fun bindService() { requireActivity().bindService( Intent(requireActivity(), ReadAloudService::class.java), serviceConnection, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt new file mode 100644 index 0000000000..c924a8d8e7 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt @@ -0,0 +1,82 @@ +/* + * Kiwix Android + * Copyright (c) 2024 Kiwix + * 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 . + * + */ + +package org.kiwix.kiwixmobile.core.utils + +import android.app.Activity +import org.kiwix.kiwixmobile.core.dao.NewBookDao +import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp +import javax.inject.Inject + +const val THREE_DAYS_IN_MILLISECONDS = 3 * 24 * 60 * 60 * 1000L +const val THREE_MONTHS_IN_MILLISECONDS = 90 * 24 * 60 * 60 * 1000L + +class DonationDialogHandler @Inject constructor( + private val activity: Activity, + private val sharedPreferenceUtil: SharedPreferenceUtil, + private val newBookDao: NewBookDao +) { + + private var showDonationDialogCallback: ShowDonationDialogCallback? = null + + fun setDonationDialogCallBack(showDonationDialogCallback: ShowDonationDialogCallback?) { + this.showDonationDialogCallback = showDonationDialogCallback + } + + fun attemptToShowDonationPopup() { + val currentMilliSeconds = System.currentTimeMillis() + val lastPopupMillis = sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds + val timeDifference = currentMilliSeconds - lastPopupMillis + if (shouldShowInitialPopup(lastPopupMillis) || timeDifference >= THREE_MONTHS_IN_MILLISECONDS) { + if (isZimFilesAvailableInLibrary() && isTimeToShowDonation(currentMilliSeconds)) { + showDonationDialogCallback?.showDonationDialog() + resetDonateLater() + } + } + } + + private fun shouldShowInitialPopup(lastPopupMillis: Long): Boolean = lastPopupMillis == 0L + private fun isTimeToShowDonation(currentMillis: Long): Boolean = + isLaterNotClicked() || isLaterPeriodOver(currentMillis) + + private fun isLaterNotClicked(): Boolean = sharedPreferenceUtil.laterClickedMilliSeconds == 0L + + private fun isLaterPeriodOver(currentMillis: Long): Boolean { + val timeDifference = currentMillis - sharedPreferenceUtil.laterClickedMilliSeconds + return timeDifference >= THREE_DAYS_IN_MILLISECONDS + } + + fun isZimFilesAvailableInLibrary(): Boolean = + if (activity.isCustomApp()) true else newBookDao.getBooks().isNotEmpty() + + fun updateLastDonationPopupShownTime() { + sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() + } + + fun donateLater(currentMillis: Long = System.currentTimeMillis()) { + sharedPreferenceUtil.laterClickedMilliSeconds = currentMillis + } + + fun resetDonateLater() { + sharedPreferenceUtil.laterClickedMilliSeconds = 0L + } + + interface ShowDonationDialogCallback { + fun showDonationDialog() + } +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 323facf7a5..ad321b4958 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -271,6 +271,22 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { } } + var lastDonationPopupShownInMilliSeconds: Long + get() = sharedPreferences.getLong(PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, 0L) + set(value) { + sharedPreferences.edit { + putLong(PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, value) + } + } + + var laterClickedMilliSeconds: Long + get() = sharedPreferences.getLong(PREF_LATER_CLICKED_MILLIS, 0L) + set(value) { + sharedPreferences.edit { + putLong(PREF_LATER_CLICKED_MILLIS, value) + } + } + fun getPublicDirectoryPath(path: String): String = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { path @@ -321,5 +337,8 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { const val PREF_NOTES_MIGRATED = "pref_notes_migrated" const val PREF_APP_DIRECTORY_TO_PUBLIC_MIGRATED = "pref_app_directory_to_public_migrated" const val PREF_COPY_MOVE_PERMISSION = "pref_copy_move_permission" + private const val PREF_LATER_CLICKED_MILLIS = "pref_later_clicked_millis" + const val PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS = + "pref_last_donation_shown_in_milliseconds" } } diff --git a/core/src/main/res/drawable/ic_donation_icon.xml b/core/src/main/res/drawable/ic_donation_icon.xml new file mode 100644 index 0000000000..a3f8a1c61b --- /dev/null +++ b/core/src/main/res/drawable/ic_donation_icon.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/core/src/main/res/layout/fragment_reader.xml b/core/src/main/res/layout/fragment_reader.xml index 7032482099..aa3d60ce59 100644 --- a/core/src/main/res/layout/fragment_reader.xml +++ b/core/src/main/res/layout/fragment_reader.xml @@ -57,6 +57,15 @@ android:layout_height="match_parent" android:background="@android:color/black" android:visibility="invisible" /> + + diff --git a/core/src/main/res/layout/layout_donation_bottom_sheet.xml b/core/src/main/res/layout/layout_donation_bottom_sheet.xml new file mode 100644 index 0000000000..529a12e763 --- /dev/null +++ b/core/src/main/res/layout/layout_donation_bottom_sheet.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml index 40923458fe..aea27dfc0b 100644 --- a/core/src/main/res/values/dimens.xml +++ b/core/src/main/res/values/dimens.xml @@ -24,4 +24,5 @@ 48dp 24dp 13dp + 20dp diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index ea6803dc6b..16283d3033 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -389,4 +389,7 @@ Go to previous screen Save or Open this file? Choosing Open will open this file in external reader application. + Donate Today + %s needs your help. + Make a donation diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt new file mode 100644 index 0000000000..6cfa469eeb --- /dev/null +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt @@ -0,0 +1,127 @@ +/* + * Kiwix Android + * Copyright (c) 2024 Kiwix + * 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 . + * + */ + +package org.kiwix.kiwixmobile.core.utils + +import android.app.Activity +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.kiwix.kiwixmobile.core.dao.NewBookDao +import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions + +class DonationDialogHandlerTest { + private lateinit var activity: Activity + private lateinit var sharedPreferenceUtil: SharedPreferenceUtil + private lateinit var newBookDao: NewBookDao + private lateinit var donationDialogHandler: DonationDialogHandler + private lateinit var showDonationDialogCallback: DonationDialogHandler.ShowDonationDialogCallback + + @BeforeEach + fun setup() { + activity = mockk(relaxed = true) + sharedPreferenceUtil = mockk(relaxed = true) + newBookDao = mockk(relaxed = true) + showDonationDialogCallback = mockk(relaxed = true) + donationDialogHandler = DonationDialogHandler(activity, sharedPreferenceUtil, newBookDao) + donationDialogHandler.setDonationDialogCallBack(showDonationDialogCallback) + } + + @Test + fun `test should show initial popup`() { + every { sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds } returns 0L + every { newBookDao.getBooks() } returns listOf(mockk()) + donationDialogHandler.attemptToShowDonationPopup() + verify { showDonationDialogCallback.showDonationDialog() } + } + + @Test + fun `test should not show popup when time difference is less than 3 months`() { + val currentMilliSeconds = System.currentTimeMillis() + every { + sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds + } returns currentMilliSeconds - (THREE_MONTHS_IN_MILLISECONDS / 2) + every { newBookDao.getBooks() } returns listOf(mockk()) + donationDialogHandler.attemptToShowDonationPopup() + verify(exactly = 0) { showDonationDialogCallback.showDonationDialog() } + } + + @Test + fun `test should show popup when time difference is more than 3 months`() { + val currentMilliSeconds = System.currentTimeMillis() + every { + sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds + } returns currentMilliSeconds - (THREE_MONTHS_IN_MILLISECONDS + 1000) + every { newBookDao.getBooks() } returns listOf(mockk()) + donationDialogHandler.attemptToShowDonationPopup() + verify { showDonationDialogCallback.showDonationDialog() } + } + + @Test + fun `test donate later saves the correct time`() { + val currentMilliSeconds = System.currentTimeMillis() + every { sharedPreferenceUtil.laterClickedMilliSeconds = any() } just Runs + donationDialogHandler.donateLater(currentMilliSeconds) + verify { sharedPreferenceUtil.laterClickedMilliSeconds = currentMilliSeconds } + } + + @Test + fun `test reset donate later sets value to zero`() { + every { sharedPreferenceUtil.laterClickedMilliSeconds = 0L } just Runs + donationDialogHandler.resetDonateLater() + verify { sharedPreferenceUtil.laterClickedMilliSeconds = 0L } + } + + @Test + fun `test isZimFilesAvailableInLibrary returns true when isCustomApp is true`() { + with(mockk()) { + every { activity.packageName } returns "org.kiwix.kiwixcustom" + every { activity.isCustomApp() } returns true + val result = donationDialogHandler.isZimFilesAvailableInLibrary() + assertTrue(result) + } + } + + @Test + fun `test isZimFilesAvailableInLibrary returns false when no books and isCustomApp is false`() { + with(mockk()) { + every { activity.packageName } returns "org.kiwix.kiwixmobile" + every { activity.isCustomApp() } returns false + every { newBookDao.getBooks() } returns emptyList() + val result = donationDialogHandler.isZimFilesAvailableInLibrary() + assertFalse(result) + } + } + + @Test + fun `isZimFilesAvailableInLibrary returns true when books available and isCustomApp is false`() { + with(mockk()) { + every { activity.packageName } returns "org.kiwix.kiwixmobile" + every { activity.isCustomApp() } returns false + every { newBookDao.getBooks() } returns listOf(mockk()) + val result = donationDialogHandler.isZimFilesAvailableInLibrary() + assertTrue(result) + } + } +} diff --git a/custom/src/androidTest/java/org/kiwix/kiwixmobile/custom/search/SearchFragmentTestForCustomApp.kt b/custom/src/androidTest/java/org/kiwix/kiwixmobile/custom/search/SearchFragmentTestForCustomApp.kt index 80a00d2736..f0f91af830 100644 --- a/custom/src/androidTest/java/org/kiwix/kiwixmobile/custom/search/SearchFragmentTestForCustomApp.kt +++ b/custom/src/androidTest/java/org/kiwix/kiwixmobile/custom/search/SearchFragmentTestForCustomApp.kt @@ -98,6 +98,10 @@ class SearchFragmentTestForCustomApp { putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) putString(SharedPreferenceUtil.PREF_LANG, "en") + putLong( + SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS, + System.currentTimeMillis() + ) } activityScenario = ActivityScenario.launch(CustomMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt index 1926899bfd..2ccc70b1f8 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/main/CustomReaderFragment.kt @@ -28,10 +28,12 @@ import android.view.View.VISIBLE import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.net.toUri import androidx.drawerlayout.widget.DrawerLayout import androidx.navigation.fragment.findNavController import org.kiwix.kiwixmobile.core.R.dimen import org.kiwix.kiwixmobile.core.base.BaseActivity +import org.kiwix.kiwixmobile.core.extensions.browserIntent import org.kiwix.kiwixmobile.core.extensions.getResizedDrawable import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.main.CoreReaderFragment @@ -328,6 +330,22 @@ class CustomReaderFragment : CoreReaderFragment() { newMainPageTab() } + /** + * Overrides the method to show the donation popup. When the "Support url" is disabled + * in a custom app, this function stop to show the donationPopup. + */ + override fun showDonationLayout() { + if (BuildConfig.SUPPORT_URL.isNotEmpty()) { + super.showDonationLayout() + } + } + + override fun openKiwixSupportUrl() { + if (BuildConfig.SUPPORT_URL.isNotEmpty()) { + openExternalUrl(BuildConfig.SUPPORT_URL.toUri().browserIntent()) + } + } + override fun onDestroyView() { super.onDestroyView() permissionRequiredDialog = null