diff --git a/app/build.gradle b/app/build.gradle
index 90b4cdb94..421774062 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -29,7 +29,10 @@ android {
resValue "string", "app_name", "Wulkanowy"
- manifestPlaceholders = [firebase_enabled: project.hasProperty("enableFirebase")]
+ manifestPlaceholders = [
+ firebase_enabled: project.hasProperty("enableFirebase"),
+ admob_project_id: ""
+ ]
javaCompileOptions {
annotationProcessorOptions {
arguments += [
@@ -39,6 +42,8 @@ android {
}
}
+ buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
+
if (System.env.SET_BUILD_TIMESTAMP) {
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
} else {
@@ -82,23 +87,21 @@ android {
productFlavors {
hms {
dimension "platform"
- manifestPlaceholders = [
- install_channel: "AppGallery"
- ]
+ manifestPlaceholders = [install_channel: "AppGallery"]
}
play {
dimension "platform"
manifestPlaceholders = [
- install_channel: "Google Play"
+ install_channel : "Google Play",
+ admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
]
+ buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
}
fdroid {
dimension "platform"
- manifestPlaceholders = [
- install_channel: "F-Droid"
- ]
+ manifestPlaceholders = [install_channel: "F-Droid"]
}
}
@@ -233,6 +236,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.2'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
+ playImplementation 'com.google.android.gms:play-services-ads:20.4.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.3.0.301'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.1.200'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8c7eb8527..810d469f4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -165,33 +165,32 @@
-
-
-
-
-
-
-
+
+
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
index 6bcf5f77b..552749349 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
@@ -82,18 +82,20 @@ class AboutPresenter @Inject constructor(
private fun loadData() {
view?.run {
- updateData(listOfNotNull(
- versionRes,
- creatorsRes,
- feedbackRes,
- faqRes,
- discordRes,
- facebookRes,
- twitterRes,
- homepageRes,
- licensesRes,
- privacyRes
- ))
+ updateData(
+ listOfNotNull(
+ versionRes,
+ creatorsRes,
+ feedbackRes,
+ faqRes,
+ discordRes,
+ facebookRes,
+ twitterRes,
+ homepageRes,
+ licensesRes,
+ privacyRes
+ )
+ )
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
index 405bfbc5a..440bbd5d4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
@@ -299,7 +299,8 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter
+
+
+
+
+
+
diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml
index c512a5f21..1aba7d856 100644
--- a/app/src/main/res/values/preferences_keys.xml
+++ b/app/src/main/res/values/preferences_keys.xml
@@ -34,4 +34,5 @@
message_send_recipients
last_sync_date
notifications_piggyback
+ single_ad_support
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 48c7712dc..13030366a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -658,10 +658,19 @@
Reply with message history
Show arithmetic average when no weights provided
+ Support
+ Watch single ad to support project
+ Consent to data processing
+ To view an advertisement you must agree to the data processing terms of our Privacy Policy
+ Agree
+ Privacy policy
+ Ad is loading
+
Advanced
Appearance & Behavior
Notifications
Synchronization
+ Advertisements
Grades
Dashboard
@@ -681,6 +690,7 @@
Plus and minus values, average calculation
Advanced
App version, contributors, social portals, licenses
+ Displaying advertisements, project support
diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml
index 086214923..5bf7ad8aa 100644
--- a/app/src/main/res/xml/scheme_preferences.xml
+++ b/app/src/main/res/xml/scheme_preferences.xml
@@ -1,33 +1,33 @@
+ app:title="@string/pref_appearance_category" />
+ app:title="@string/pref_notifications_category" />
+ app:title="@string/pref_sync_category" />
+ app:title="@string/pref_advanced_category" />
+ app:title="@string/about_title" />
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
new file mode 100644
index 000000000..960a54b8c
--- /dev/null
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
@@ -0,0 +1,94 @@
+package io.github.wulkanowy.ui.modules.settings.ads
+
+import android.os.Bundle
+import android.view.View
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.base.ErrorDialog
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.utils.openInternetBrowser
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
+
+ @Inject
+ lateinit var presenter: AdsPresenter
+
+ override val titleStringId = R.string.pref_settings_ads_title
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ setPreferencesFromResource(R.xml.scheme_preferences_ads, rootKey)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ findPreference(getString(R.string.pref_key_ads_single_support))?.setOnPreferenceClickListener {
+ presenter.onWatchSingleAdSelected()
+ true
+ }
+ }
+
+ override fun showAd(ad: RewardedInterstitialAd) {
+ if (isVisible) {
+ ad.show(requireActivity()) {}
+ }
+ }
+
+ override fun showPrivacyPolicyDialog() {
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(getString(R.string.pref_ads_privacy_title))
+ .setMessage(getString(R.string.pref_ads_privacy_description))
+ .setPositiveButton(getString(R.string.pref_ads_privacy_agree)) { _, _ -> presenter.onAgreedPrivacy() }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .setNeutralButton(getString(R.string.pref_ads_privacy_link)) { _, _ -> presenter.onPrivacySelected() }
+ .show()
+ }
+
+ override fun openPrivacyPolicy() {
+ requireContext().openInternetBrowser(
+ "https://wulkanowy.github.io/polityka-prywatnosci.html",
+ ::showMessage
+ )
+ }
+
+ override fun showLoadingSupportAd(show: Boolean) {
+ findPreference(getString(R.string.pref_key_ads_single_support))?.run {
+ isEnabled = !show
+ summary = if (show) getString(R.string.pref_ads_loading) else null
+ }
+ }
+
+ override fun showError(text: String, error: Throwable) {
+ (activity as? BaseActivity<*, *>)?.showError(text, error)
+ }
+
+ override fun showMessage(text: String) {
+ (activity as? BaseActivity<*, *>)?.showMessage(text)
+ }
+
+ override fun showExpiredDialog() {
+ (activity as? BaseActivity<*, *>)?.showExpiredDialog()
+ }
+
+ override fun showChangePasswordSnackbar(redirectUrl: String) {
+ (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl)
+ }
+
+ override fun openClearLoginView() {
+ (activity as? BaseActivity<*, *>)?.openClearLoginView()
+ }
+
+ override fun showErrorDetailsDialog(error: Throwable) {
+ ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ }
+}
\ No newline at end of file
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
new file mode 100644
index 000000000..fd5cc9b6f
--- /dev/null
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
@@ -0,0 +1,41 @@
+package io.github.wulkanowy.ui.modules.settings.ads
+
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.AdsHelper
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+class AdsPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val adsHelper: AdsHelper
+) : BasePresenter(errorHandler, studentRepository) {
+
+ override fun onAttachView(view: AdsView) {
+ super.onAttachView(view)
+ view.initView()
+ Timber.i("Settings ads view was initialized")
+ }
+
+ fun onWatchSingleAdSelected() {
+ view?.showPrivacyPolicyDialog()
+ }
+
+ fun onPrivacySelected() {
+ view?.openPrivacyPolicy()
+ }
+
+ fun onAgreedPrivacy() {
+ view?.showLoadingSupportAd(true)
+ presenterScope.launch {
+ runCatching { adsHelper.getSupportAd() }
+ .onFailure(errorHandler::dispatch)
+ .onSuccess { it?.let { view?.showAd(it) } }
+
+ view?.showLoadingSupportAd(false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt
new file mode 100644
index 000000000..25eeaaeca
--- /dev/null
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt
@@ -0,0 +1,17 @@
+package io.github.wulkanowy.ui.modules.settings.ads
+
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
+import io.github.wulkanowy.ui.base.BaseView
+
+interface AdsView : BaseView {
+
+ fun initView()
+
+ fun showAd(ad: RewardedInterstitialAd)
+
+ fun showPrivacyPolicyDialog()
+
+ fun openPrivacyPolicy()
+
+ fun showLoadingSupportAd(show: Boolean)
+}
\ No newline at end of file
diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
new file mode 100644
index 000000000..f363c13f9
--- /dev/null
+++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -0,0 +1,39 @@
+package io.github.wulkanowy.utils
+
+import android.content.Context
+import com.google.android.gms.ads.AdRequest
+import com.google.android.gms.ads.LoadAdError
+import com.google.android.gms.ads.MobileAds
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
+import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback
+import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.wulkanowy.BuildConfig
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+class AdsHelper @Inject constructor(@ApplicationContext private val context: Context) {
+
+ suspend fun getSupportAd(): RewardedInterstitialAd? {
+ MobileAds.initialize(context)
+
+ val adRequest = AdRequest.Builder().build()
+
+ return suspendCoroutine {
+ RewardedInterstitialAd.load(
+ context,
+ BuildConfig.SINGLE_SUPPORT_AD_ID,
+ adRequest,
+ object : RewardedInterstitialAdLoadCallback() {
+ override fun onAdLoaded(rewardedInterstitialAd: RewardedInterstitialAd) {
+ it.resume(rewardedInterstitialAd)
+ }
+
+ override fun onAdFailedToLoad(loadAdError: LoadAdError) {
+ it.resumeWithException(IllegalArgumentException(loadAdError.message))
+ }
+ })
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/play/res/xml/scheme_preferences.xml b/app/src/play/res/xml/scheme_preferences.xml
new file mode 100644
index 000000000..05b0bf644
--- /dev/null
+++ b/app/src/play/res/xml/scheme_preferences.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/play/res/xml/scheme_preferences_ads.xml b/app/src/play/res/xml/scheme_preferences_ads.xml
new file mode 100644
index 000000000..6b3625cae
--- /dev/null
+++ b/app/src/play/res/xml/scheme_preferences_ads.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file