Skip to content

Commit a951c94

Browse files
committed
feat: enable interstitial ads, add gdpr dialog
1 parent 4c79d7f commit a951c94

File tree

7 files changed

+121
-57
lines changed

7 files changed

+121
-57
lines changed

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ dependencies {
179179
// implementation 'com.google.guava:listenablefuture:1.0'
180180
implementation 'androidx.paging:paging-runtime:3.2.0'
181181
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
182+
implementation 'com.google.android.ump:user-messaging-platform:2.1.0'
182183

183184
implementation 'androidx.activity:activity-ktx:1.7.2'
184185
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'

app/src/main/java/com/pr0gramm/app/ApplicationClass.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import com.pr0gramm.app.services.Track
1616
import com.pr0gramm.app.sync.SyncStatsWorker
1717
import com.pr0gramm.app.sync.SyncWorker
1818
import com.pr0gramm.app.ui.ActivityErrorHandler
19-
import com.pr0gramm.app.ui.AdService
2019
import com.pr0gramm.app.ui.dialogs.ErrorDialogFragment.Companion.GlobalErrorDialogHandler
2120
import com.pr0gramm.app.util.AndroidUtility
2221
import com.pr0gramm.app.util.AndroidUtility.buildVersionCode
@@ -25,7 +24,7 @@ import com.pr0gramm.app.util.debugOnly
2524
import com.pr0gramm.app.util.di.InjectorAware
2625
import com.pr0gramm.app.util.doInBackground
2726
import kotlinx.coroutines.runBlocking
28-
import java.util.*
27+
import java.util.WeakHashMap
2928
import java.util.concurrent.TimeUnit
3029
import java.util.logging.Level
3130
import java.util.logging.LogManager
@@ -106,10 +105,6 @@ open class ApplicationClass : Application(), InjectorAware {
106105
log?.handlers?.forEach { it.level = Level.INFO }
107106
}
108107

109-
logger.time("Initializing MobileAds") {
110-
AdService.initializeMobileAds(this)
111-
}
112-
113108
// wait for firebase setup to finish
114109
runBlocking {
115110
firebaseJob.join()

app/src/main/java/com/pr0gramm/app/services/Track.kt

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ object Track : InjectorAware {
4444

4545
fun loginSuccessful() {
4646
send("login") {
47-
putBoolean("success", true)
47+
putBoolean(FirebaseAnalytics.Param.SUCCESS, true)
4848
}
4949

5050
Stats().increment("login.succeeded")
5151
}
5252

5353
fun loginFailed(type: String) {
5454
send("login") {
55-
putBoolean("success", false)
56-
putString("type", type)
55+
putBoolean(FirebaseAnalytics.Param.SUCCESS, false)
56+
putString(FirebaseAnalytics.Param.VALUE, type)
5757
}
5858

5959
Stats().increment("login.failed", "reason:$type")
@@ -65,7 +65,7 @@ object Track : InjectorAware {
6565

6666
fun writeComment(root: Boolean) {
6767
send("write_comment") {
68-
putBoolean("root", root)
68+
putBoolean(FirebaseAnalytics.Param.VALUE, root)
6969
}
7070
}
7171

@@ -79,22 +79,22 @@ object Track : InjectorAware {
7979

8080
fun votePost(vote: Vote) {
8181
send("vote") {
82-
putString("vote_type", vote.name)
83-
putString("content_type", "post")
82+
putString(FirebaseAnalytics.Param.VALUE, vote.name)
83+
putString(FirebaseAnalytics.Param.CONTENT_TYPE, "post")
8484
}
8585
}
8686

8787
fun voteTag(vote: Vote) {
8888
send("vote") {
89-
putString("vote_type", vote.name)
90-
putString("content_type", "tag")
89+
putString(FirebaseAnalytics.Param.VALUE, vote.name)
90+
putString(FirebaseAnalytics.Param.CONTENT_TYPE, "tag")
9191
}
9292
}
9393

9494
fun voteComment(vote: Vote) {
9595
send("vote") {
96-
putString("vote_type", vote.name)
97-
putString("content_type", "comment")
96+
putString(FirebaseAnalytics.Param.VALUE, vote.name)
97+
putString(FirebaseAnalytics.Param.CONTENT_TYPE, "comment")
9898
}
9999
}
100100

@@ -104,7 +104,7 @@ object Track : InjectorAware {
104104

105105
fun openBrowser(type: String) {
106106
send("open_browser") {
107-
putString("type", type)
107+
putString(FirebaseAnalytics.Param.VALUE, type)
108108
}
109109
}
110110

@@ -115,8 +115,8 @@ object Track : InjectorAware {
115115
val sizeCategory = "%d-%d kb".format(categoryStart, categoryStart + 512)
116116

117117
send("upload") {
118-
putLong("size", size)
119-
putString("size_category", sizeCategory)
118+
putLong(FirebaseAnalytics.Param.VALUE, size)
119+
putString(FirebaseAnalytics.Param.ITEM_CATEGORY, sizeCategory)
120120
}
121121
}
122122

@@ -126,13 +126,13 @@ object Track : InjectorAware {
126126

127127
fun inboxNotificationClosed(method: String) {
128128
send("inbox_notification_close") {
129-
putString("method", method)
129+
putString(FirebaseAnalytics.Param.METHOD, method)
130130
}
131131
}
132132

133133
fun preloadCurrentFeed(size: Int) {
134134
send("preload_feed") {
135-
putInt("item_count", size)
135+
putInt(FirebaseAnalytics.Param.VALUE, size)
136136
}
137137
}
138138

@@ -162,7 +162,7 @@ object Track : InjectorAware {
162162

163163
fun specialMenuActionClicked(uri: Uri) {
164164
send("special_menu_item") {
165-
putString("uri", uri.toString())
165+
putString(FirebaseAnalytics.Param.VALUE, uri.toString())
166166
}
167167
}
168168

@@ -178,15 +178,15 @@ object Track : InjectorAware {
178178

179179
fun openFeed(filter: FeedFilter) {
180180
send("view_feed") {
181-
filter.tags?.let { putBoolean("tags", true) }
182-
filter.collection?.let { putBoolean("collection", true) }
183-
filter.username?.let { putBoolean("username", true) }
181+
filter.tags?.let { putBoolean(FirebaseAnalytics.Param.TERM, true) }
182+
filter.collection?.let { putBoolean(FirebaseAnalytics.Param.GROUP_ID, true) }
183+
filter.username?.let { putBoolean(FirebaseAnalytics.Param.AFFILIATION, true) }
184184
}
185185
}
186186

187187
fun viewItem(itemId: Long) {
188188
send("view_item") {
189-
putLong("id", itemId)
189+
putLong(FirebaseAnalytics.Param.ITEM_ID, itemId)
190190
}
191191
}
192192

@@ -202,7 +202,7 @@ object Track : InjectorAware {
202202

203203
fun openZoomView(itemId: Long) {
204204
send("zoom_view") {
205-
putLong("id", itemId)
205+
putLong(FirebaseAnalytics.Param.ITEM_ID, itemId)
206206
}
207207
}
208208

@@ -215,7 +215,7 @@ object Track : InjectorAware {
215215
installerTracked = true
216216

217217
send("installer") {
218-
putString("package", name.toString())
218+
putString(FirebaseAnalytics.Param.VALUE, name.toString())
219219
}
220220
}
221221
}

app/src/main/java/com/pr0gramm/app/services/config/ConfigService.kt

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ import android.content.Context
66
import android.content.SharedPreferences
77
import android.provider.Settings
88
import androidx.core.content.edit
9-
import com.pr0gramm.app.*
9+
import com.pr0gramm.app.BuildConfig
1010
import com.pr0gramm.app.Duration.Companion.minutes
11+
import com.pr0gramm.app.Instant
12+
import com.pr0gramm.app.Logger
13+
import com.pr0gramm.app.MoshiInstance
1114
import com.pr0gramm.app.api.pr0gramm.Api
1215
import com.pr0gramm.app.model.config.Config
13-
import com.pr0gramm.app.util.*
16+
import com.pr0gramm.app.util.AndroidUtility
17+
import com.pr0gramm.app.util.debugOnly
1418
import com.pr0gramm.app.util.di.injector
19+
import com.pr0gramm.app.util.doInBackground
20+
import com.pr0gramm.app.util.getStringOrNull
21+
import com.pr0gramm.app.util.runEvery
1522
import com.squareup.moshi.JsonDataException
1623
import com.squareup.moshi.adapter
1724
import kotlinx.coroutines.flow.Flow
@@ -104,14 +111,17 @@ class ConfigService(context: Application,
104111
debugOnly {
105112
// update config for development.
106113
return configState.copy(
107-
adTypesLoggedIn = listOf(Config.AdType.FEED),
108-
adTypesLoggedOut = listOf(Config.AdType.FEED, Config.AdType.FEED_TO_POST_INTERSTITIAL),
109-
interstitialAdIntervalInSeconds = 10,
110-
specialMenuItems = configState.specialMenuItems.takeIf { it.isNotEmpty() }
111-
?: listOf(Config.MenuItem(
112-
name = "Wichteln",
113-
icon = "https://materialdesignicons.com/api/download/22D0C782-CD05-4FEB-845F-BBA7126C7326/000000/1/FFFFFF/0/48",
114-
link = "https://pr0gramm.com/new/wichteln")))
114+
adTypesLoggedIn = listOf(Config.AdType.FEED),
115+
adTypesLoggedOut = listOf(Config.AdType.FEED, Config.AdType.FEED_TO_POST_INTERSTITIAL),
116+
interstitialAdIntervalInSeconds = 600,
117+
specialMenuItems = configState.specialMenuItems.takeIf { it.isNotEmpty() }
118+
?: listOf(
119+
Config.MenuItem(
120+
name = "Wichteln",
121+
icon = "https://materialdesignicons.com/api/download/22D0C782-CD05-4FEB-845F-BBA7126C7326/000000/1/FFFFFF/0/48",
122+
link = "https://pr0gramm.com/new/wichteln"
123+
)
124+
))
115125
}
116126

117127
return configState

app/src/main/java/com/pr0gramm/app/ui/AdService.kt

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.pr0gramm.app.model.config.Config
2020
import com.pr0gramm.app.services.Track
2121
import com.pr0gramm.app.services.UserService
2222
import com.pr0gramm.app.services.config.ConfigService
23+
import com.pr0gramm.app.time
2324
import com.pr0gramm.app.util.AndroidUtility
2425
import com.pr0gramm.app.util.Holder
2526
import com.pr0gramm.app.util.ignoreAllExceptions
@@ -43,7 +44,6 @@ class AdService(
4344
private val userService: UserService,
4445
) {
4546

46-
private val logger = Logger("AdService")
4747
private var lastInterstitialAdShown: Instant? = null
4848

4949
/**
@@ -128,9 +128,6 @@ class AdService(
128128
}
129129

130130
fun buildInterstitialAd(context: Context): Holder<InterstitialAd?> {
131-
return Holder { null }
132-
// currently not available
133-
134131
return if (enabledForTypeNow(Config.AdType.FEED_TO_POST_INTERSTITIAL)) {
135132
val value = CompletableDeferred<InterstitialAd?>()
136133

@@ -178,6 +175,8 @@ class AdService(
178175
}
179176

180177
companion object {
178+
private val logger = Logger("AdService")
179+
181180
private val interstitialUnitId: String = if (BuildConfig.DEBUG) {
182181
"ca-app-pub-3940256099942544/1033173712"
183182
} else {
@@ -190,21 +189,32 @@ class AdService(
190189
"/61585078/pr0gramm.com_a_sticky-top"
191190
}
192191

192+
private var initialized: Boolean = false
193+
193194
fun initializeMobileAds(context: Context) {
194-
// for some reason an internal getVersionString returns null,
195-
// and the result is not checked. We ignore the error in that case
196-
ignoreAllExceptions {
197-
val listener = OnInitializationCompleteListener { }
198-
MobileAds.initialize(context, listener)
199-
200-
MobileAds.setAppVolume(0f)
201-
MobileAds.setAppMuted(true)
202-
203-
MobileAds.setRequestConfiguration(
204-
RequestConfiguration.Builder()
205-
.setTestDeviceIds(listOf("D5DDF82D7F630F71AB2E7699408B1429"))
206-
.build()
207-
)
195+
if (initialized) {
196+
return
197+
}
198+
199+
// run initialization code only once
200+
initialized = true
201+
202+
logger.time("Initializing MobileAds") {
203+
// for some reason an internal getVersionString returns null,
204+
// and the result is not checked. We ignore the error in that case
205+
ignoreAllExceptions {
206+
val listener = OnInitializationCompleteListener { }
207+
MobileAds.initialize(context, listener)
208+
209+
MobileAds.setAppVolume(0f)
210+
MobileAds.setAppMuted(true)
211+
212+
MobileAds.setRequestConfiguration(
213+
RequestConfiguration.Builder()
214+
.setTestDeviceIds(listOf("D5DDF82D7F630F71AB2E7699408B1429"))
215+
.build()
216+
)
217+
}
208218
}
209219
}
210220
}

app/src/main/java/com/pr0gramm/app/ui/MainActivity.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import androidx.fragment.app.DialogFragment
2222
import androidx.fragment.app.Fragment
2323
import androidx.fragment.app.FragmentManager
2424
import com.google.android.material.snackbar.Snackbar
25+
import com.google.android.ump.ConsentInformation
26+
import com.google.android.ump.ConsentRequestParameters
27+
import com.google.android.ump.FormError
28+
import com.google.android.ump.UserMessagingPlatform
2529
import com.pr0gramm.app.*
2630
import com.pr0gramm.app.Duration.Companion.seconds
2731
import com.pr0gramm.app.api.pr0gramm.MessageType
@@ -62,6 +66,7 @@ class MainActivity : BaseAppCompatActivity("MainActivity"),
6266
PermissionHelperActivity,
6367
RecyclerViewPoolProvider by RecyclerViewPoolMap() {
6468

69+
private var consentInfo: ConsentInformation? = null
6570
private val handler = Handler(Looper.getMainLooper())
6671
private var permissionHelper = PermissionHelperDelegate(this)
6772

@@ -170,6 +175,49 @@ class MainActivity : BaseAppCompatActivity("MainActivity"),
170175
invalidateRecyclerViewPool()
171176
}
172177
}
178+
179+
askConsent()
180+
}
181+
182+
private fun askConsent() {
183+
val params = ConsentRequestParameters.Builder()
184+
.setTagForUnderAgeOfConsent(false)
185+
.build()
186+
187+
consentInfo = UserMessagingPlatform.getConsentInformation(this).also { consentInfo ->
188+
consentInfo.requestConsentInfoUpdate(
189+
this,
190+
params,
191+
this::onConsentInfoUpdateSuccess,
192+
this::onConsentInfoUpdateFailure
193+
)
194+
}
195+
196+
// try to initialize mobile adds in parallel, we might already have
197+
// consent from a previous form
198+
initializeMobileAdsSdk()
199+
}
200+
201+
private fun onConsentInfoUpdateSuccess() {
202+
UserMessagingPlatform.loadAndShowConsentFormIfRequired(this) { err ->
203+
if (err != null) {
204+
logger.warn { "Failed to get consent: $err" }
205+
return@loadAndShowConsentFormIfRequired
206+
}
207+
208+
// we have consent, try to initialize mobile sdk
209+
initializeMobileAdsSdk()
210+
}
211+
}
212+
213+
private fun onConsentInfoUpdateFailure(err: FormError) {
214+
logger.warn { "Failed to update consent form: $err" }
215+
}
216+
217+
private fun initializeMobileAdsSdk() {
218+
launchWhenCreated {
219+
AdService.initializeMobileAds(this@MainActivity)
220+
}
173221
}
174222

175223
private fun buildDrawerArrowDrawable(): DrawerArrowDrawable {

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
}
77

88
dependencies {
9-
classpath 'com.android.tools.build:gradle:8.1.0'
9+
classpath 'com.android.tools.build:gradle:8.1.1'
1010
}
1111
}
1212

0 commit comments

Comments
 (0)