InstallationPing.kt 3.53 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.components.metrics

import android.content.Context
import android.content.SharedPreferences
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.support.base.log.logger.Logger
import org.mozilla.fenix.GleanMetrics.Installation
import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.ext.settings

class InstallationPing(private val context: Context) {

    private val prefs: SharedPreferences by lazy {
        context.getSharedPreferences(
            "${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE
        )
    }

    /**
     * Checks whether or not the installation ping was already
     * triggered by the application.
     *
     * Note that this only tells us that Fenix triggered the
     * ping and then delegated the transmission to Glean. We
     * have no way to tell if it was actually sent or not.
     *
     * @return true if it was already triggered, false otherwise.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun wasAlreadyTriggered(): Boolean {
        return prefs.getBoolean("ping_sent", false)
    }

    /**
     * Marks the "installation" ping as triggered by the application.
     * This ensures the ping is not triggered again at the next app
     * start.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun markAsTriggered() {
        prefs.edit().putBoolean("ping_sent", true).apply()
    }

    /**
     * Fills the metrics and triggers the 'installation' ping.
     * This is a separate function to simplify unit-testing.
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun triggerPing() {
57
58
59
60
61
62
63
64
65
        if (checkMetricsNotEmpty()) {
            context.settings().also {
                Installation.campaign.set(it.adjustCampaignId)
                Installation.adgroup.set(it.adjustAdGroup)
                Installation.creative.set(it.adjustCreative)
                Installation.network.set(it.adjustNetwork)
                Installation.timestamp.set()
            }

66
            CoroutineScope(Dispatchers.IO).launch {
67
                MetricsUtils.getHashedIdentifier(context)?.let {
68
                    Installation.identifier.set(it)
69
70
                }

71
72
73
74
75
76
77
                Pings.installation.submit()
                markAsTriggered()
            }
        }
    }

    /**
78
79
80
     * Check that at least one of the metrics values is set before sending the ping.
     * Note: it is normal for many of these values to not be set as campaigns do not always
     * utilize every attribute!
81
82
83
     * */
    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    internal fun checkMetricsNotEmpty(): Boolean = listOf(
84
        context.settings().adjustCampaignId,
85
86
87
        context.settings().adjustAdGroup,
        context.settings().adjustCreative,
        context.settings().adjustNetwork
88
    ).any { it.isNotEmpty() }
89
90
91
92
93
94
95
96
97
98
99
100
101
102

    /**
     * Trigger sending the `installation` ping if it wasn't sent already.
     * Then, mark it so that it doesn't get triggered next time Fenix
     * starts.
     */
    fun checkAndSend() {
        if (wasAlreadyTriggered()) {
            Logger.debug("InstallationPing - already generated")
            return
        }
        triggerPing()
    }
}