mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-09 15:27:55 -04:00
Added a donation popup to our application.
* The donation popup will be shown to the user every three months. * If the user clicks the "Later" button, the popup will appear again after 3 days. * The donation popup will only be shown when there is at least one book available in the library. If no ZIM file is present, it’s not ideal to ask for a donation, as the user has not yet used the application. * The donation popup will only be shown for custom apps when the support_url is configured. If the support menu item is hidden in the sidebar (a feature we offer), the donation popup will not be displayed, as there is no support_url available for that custom app.
This commit is contained in:
parent
681e3a04db
commit
e55deb851f
@ -287,7 +287,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
||||
drawerContainerLayout.closeDrawer(drawerNavView)
|
||||
}
|
||||
|
||||
private fun openSupportKiwixExternalLink() {
|
||||
fun openSupportKiwixExternalLink() {
|
||||
externalLinkOpener.openExternalUrl(KIWIX_SUPPORT_URL.toUri().browserIntent())
|
||||
}
|
||||
|
||||
|
@ -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<KiwixWebView> = ArrayList()
|
||||
private val webUrlsProcessor = BehaviorProcessor.create<String>()
|
||||
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?.showDonationDialogCallBack(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?.showDonationDialogCallBack(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,53 @@ 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
|
||||
)
|
||||
val bottomToolBarHeight =
|
||||
requireActivity()
|
||||
.findViewById<BottomAppBar>(org.kiwix.kiwixmobile.core.R.id.bottom_toolbar).measuredHeight
|
||||
layoutParams.setMargins(16, 0, 16, bottomToolBarHeight + 10)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun openKiwixSupportUrl() {
|
||||
(requireActivity() as CoreMainActivity).openSupportKiwixExternalLink()
|
||||
}
|
||||
|
||||
private fun setDonationLayoutVisibility(visibility: Int) {
|
||||
donationLayout?.visibility = visibility
|
||||
}
|
||||
|
||||
private fun openFullScreenIfEnabled() {
|
||||
@ -2388,6 +2450,10 @@ abstract class CoreReaderFragment :
|
||||
unbindService()
|
||||
}
|
||||
|
||||
override fun showDonationDialog() {
|
||||
showDonationLayout()
|
||||
}
|
||||
|
||||
private fun bindService() {
|
||||
requireActivity().bindService(
|
||||
Intent(requireActivity(), ReadAloudService::class.java), serviceConnection,
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
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 showDonationDialogCallBack(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
|
||||
}
|
||||
|
||||
private fun isZimFilesAvailableInLibrary(): Boolean =
|
||||
if (activity.isCustomApp()) true else newBookDao.getBooks().isNotEmpty()
|
||||
|
||||
fun updateLastDonationPopupShownTime() {
|
||||
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
fun donateLater() {
|
||||
sharedPreferenceUtil.laterClickedMilliSeconds = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private fun resetDonateLater() {
|
||||
sharedPreferenceUtil.laterClickedMilliSeconds = 0L
|
||||
}
|
||||
|
||||
interface ShowDonationDialogCallback {
|
||||
fun showDonationDialog()
|
||||
}
|
||||
}
|
@ -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"
|
||||
private const val PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS =
|
||||
"pref_last_donation_shown_in_milliseconds"
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,14 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/donation_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_margin="@dimen/activity_horizontal_margin" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
87
core/src/main/res/layout/layout_donation_bottom_sheet.xml
Normal file
87
core/src/main/res/layout/layout_donation_bottom_sheet.xml
Normal file
@ -0,0 +1,87 @@
|
||||
<?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"
|
||||
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="40dp"
|
||||
android:layout_height="40dp"
|
||||
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_support_24px"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleText"
|
||||
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"
|
||||
android:textAppearance="@style/TextAppearance.M3.Sys.Typescale.TitleMedium"
|
||||
android:textColor="@color/mine_shaft_gray900"
|
||||
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="@color/denim_blue800"
|
||||
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="@color/denim_blue800"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/descriptionText" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
@ -389,4 +389,7 @@
|
||||
<string name="toolbar_back_button_content_description">Go to previous screen</string>
|
||||
<string name="save_or_open_unsupported_files_dialog_title">Save or Open this file?</string>
|
||||
<string name="save_or_open_unsupported_files_dialog_message">Choosing Open will open this file in external reader application.</string>
|
||||
<string name="donation_dialog_title">Donate Today</string>
|
||||
<string name="donation_dialog_description">%s needs your help.</string>
|
||||
<string name="make_donation">Make a donation</string>
|
||||
</resources>
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user