Dep Updates and Analytics & Crashlytics Changed

This commit is contained in:
shabinder 2021-08-20 02:14:56 +05:30
parent ad94224d5c
commit 9d1e29b7e7
18 changed files with 290 additions and 146 deletions

View File

@ -14,7 +14,6 @@
* * along with this program. If not, see <https://www.gnu.org/licenses/>. * * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import Extras.Android.Acra
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import org.jetbrains.compose.compose import org.jetbrains.compose.compose
@ -73,7 +72,6 @@ android {
} }
} }
kotlinOptions { kotlinOptions {
useIR = true
jvmTarget = "1.8" jvmTarget = "1.8"
} }
compileOptions { compileOptions {
@ -123,10 +121,8 @@ dependencies {
// Extras // Extras
with(Extras.Android) { with(Extras.Android) {
implementation(Acra.notification) implementation(countly)
implementation(Acra.http)
implementation(appUpdator) implementation(appUpdator)
implementation(matomo)
} }
with(Versions.androidxLifecycle) { with(Versions.androidxLifecycle) {
@ -138,7 +134,7 @@ dependencies {
// implementation("com.jakewharton.timber:timber:4.7.1") // implementation("com.jakewharton.timber:timber:4.7.1")
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}") implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation("com.github.shabinder:storage-chooser:2.0.4.45") implementation("com.github.shabinder:storage-chooser:2.0.4.45")
implementation("com.google.accompanist:accompanist-insets:0.12.0") implementation("com.google.accompanist:accompanist-insets:0.16.1")
// Test // Test
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")

View File

@ -40,6 +40,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application <application
android:name=".App" android:name=".App"
@ -73,5 +75,16 @@
</activity> </activity>
<service android:name=".service.ForegroundService"/> <service android:name=".service.ForegroundService"/>
<service android:name="org.openudid.OpenUDID_service">
<intent-filter>
<action android:name="org.openudid.GETUDID" />
</intent-filter>
</service>
<receiver android:name="ly.count.android.sdk.ReferrerReceiver" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View File

@ -17,22 +17,12 @@
package com.shabinder.spotiflyer package com.shabinder.spotiflyer
import android.app.Application import android.app.Application
import android.content.Context
import com.shabinder.common.di.initKoin import com.shabinder.common.di.initKoin
import com.shabinder.common.translations.Strings
import com.shabinder.spotiflyer.di.appModule import com.shabinder.spotiflyer.di.appModule
import org.acra.config.httpSender
import org.acra.config.notification
import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.HttpSender
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger import org.koin.android.ext.koin.androidLogger
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.logger.Level import org.koin.core.logger.Level
import org.matomo.sdk.Matomo
import org.matomo.sdk.Tracker
import org.matomo.sdk.TrackerBuilder
class App : Application(), KoinComponent { class App : Application(), KoinComponent {
@ -40,21 +30,6 @@ class App : Application(), KoinComponent {
const val SIGNATURE_HEX = "53304f6d75736a2f30484230334c454b714753525763724259444d3d0a" const val SIGNATURE_HEX = "53304f6d75736a2f30484230334c454b714753525763724259444d3d0a"
} }
val tracker: Tracker by lazy {
TrackerBuilder.createDefault(
"https://matomo.spotiflyer.ml/matomo.php", 1
)
.build(Matomo.getInstance(this)).apply {
if (BuildConfig.DEBUG) {
/*Timber.plant(DebugTree())
addTrackingCallback {
Timber.d(it.toMap().toString())
it
}*/
}
}
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -66,35 +41,4 @@ class App : Application(), KoinComponent {
modules(appModule(loggingEnabled)) modules(appModule(loggingEnabled))
} }
} }
@Suppress("SpellCheckingInspection")
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
// Crashlytics
initAcra {
buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.JSON
/*
* Prompt User Before Sending Any Crash Report
* Obeying `F-Droid Inclusion Privacy Rules`
* */
notification {
title = Strings.acraNotificationTitle()
text = Strings.acraNotificationText()
channelName = "SpotiFlyer_Crashlytics"
channelDescription = "Notification Channel to send Spotiflyer Crashes."
sendOnClick = true
}
// Send Crash Report to self hosted Acrarium (FOSS)
httpSender {
uri = "https://acrarium.spotiflyer.ml/acrarium/report"
basicAuthLogin = "sDj2xCKQIxw0dujf"
basicAuthPassword = "O83du0TsgsDJ69zN"
httpMethod = HttpSender.Method.POST
connectionTimeout = 15000
socketTimeout = 20000
compress = true
}
}
}
} }

View File

@ -17,12 +17,7 @@
package com.shabinder.spotiflyer package com.shabinder.spotiflyer
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ClipData import android.content.*
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.net.Uri import android.net.Uri
@ -40,13 +35,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface import androidx.compose.material.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -65,14 +54,11 @@ import com.google.accompanist.insets.statusBarsPadding
import com.shabinder.common.di.ConnectionLiveData import com.shabinder.common.di.ConnectionLiveData
import com.shabinder.common.di.Dir import com.shabinder.common.di.Dir
import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.FetchPlatformQueryResult
import com.shabinder.common.di.analytics.AnalyticsManager
import com.shabinder.common.di.observeAsState import com.shabinder.common.di.observeAsState
import com.shabinder.common.di.preference.PreferenceManager import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.models.Actions import com.shabinder.common.models.*
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.PlatformActions
import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey
import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Analytics import com.shabinder.common.root.SpotiFlyerRoot.Analytics
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
@ -84,17 +70,14 @@ import com.shabinder.spotiflyer.service.ForegroundService
import com.shabinder.spotiflyer.ui.AnalyticsDialog import com.shabinder.spotiflyer.ui.AnalyticsDialog
import com.shabinder.spotiflyer.ui.NetworkDialog import com.shabinder.spotiflyer.ui.NetworkDialog
import com.shabinder.spotiflyer.ui.PermissionDialog import com.shabinder.spotiflyer.ui.PermissionDialog
import com.shabinder.spotiflyer.utils.checkAppSignature import com.shabinder.spotiflyer.utils.*
import com.shabinder.spotiflyer.utils.checkIfLatestVersion
import com.shabinder.spotiflyer.utils.checkPermissions
import com.shabinder.spotiflyer.utils.disableDozeMode
import com.shabinder.spotiflyer.utils.requestStoragePermission
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koin.core.parameter.parametersOf
import org.matomo.sdk.extra.TrackHelper import org.matomo.sdk.extra.TrackHelper
import java.io.File import java.io.File
@ -104,12 +87,13 @@ class MainActivity : ComponentActivity() {
private val fetcher: FetchPlatformQueryResult by inject() private val fetcher: FetchPlatformQueryResult by inject()
private val dir: Dir by inject() private val dir: Dir by inject()
private val preferenceManager: PreferenceManager by inject() private val preferenceManager: PreferenceManager by inject()
private val analyticsManager: AnalyticsManager by inject { parametersOf(this) }
private lateinit var root: SpotiFlyerRoot private lateinit var root: SpotiFlyerRoot
private val callBacks: SpotiFlyerRootCallBacks get() = root.callBacks private val callBacks: SpotiFlyerRootCallBacks get() = root.callBacks
private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1) private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
private var permissionGranted = mutableStateOf(true) private var permissionGranted = mutableStateOf(true)
private val internetAvailability by lazy { ConnectionLiveData(applicationContext) } private val internetAvailability by lazy { ConnectionLiveData(applicationContext) }
private val tracker get() = (application as App).tracker
private val visibleChild get(): SpotiFlyerRoot.Child = root.routerState.value.activeChild.instance private val visibleChild get(): SpotiFlyerRoot.Child = root.routerState.value.activeChild.instance
// Variable for storing instance of our service class // Variable for storing instance of our service class
@ -186,11 +170,10 @@ class MainActivity : ComponentActivity() {
* and Track Downloads for all other releases like F-Droid, * and Track Downloads for all other releases like F-Droid,
* for `Github Downloads` we will track Downloads using : https://tooomm.github.io/github-release-stats/?username=Shabinder&repository=SpotiFlyer * for `Github Downloads` we will track Downloads using : https://tooomm.github.io/github-release-stats/?username=Shabinder&repository=SpotiFlyer
* */ * */
if (isGithubRelease) { checkIfLatestVersion() } if (isGithubRelease) {
if (preferenceManager.isAnalyticsEnabled && !isGithubRelease) { checkIfLatestVersion()
// Download/App Install Event for F-Droid builds
TrackHelper.track().download().with(tracker)
} }
// TODO Track Download Event
handleIntentFromExternalActivity() handleIntentFromExternalActivity()
initForegroundService() initForegroundService()
@ -297,14 +280,19 @@ class MainActivity : ComponentActivity() {
} }
} }
override fun showPopUpMessage(string: String, long: Boolean) = this@MainActivity.showPopUpMessage(string, long) override fun showPopUpMessage(string: String, long: Boolean) =
this@MainActivity.showPopUpMessage(string, long)
override fun setDownloadDirectoryAction(callBack: (String) -> Unit) = setUpOnPrefClickListener(callBack) override fun setDownloadDirectoryAction(callBack: (String) -> Unit) =
setUpOnPrefClickListener(callBack)
override fun queryActiveTracks() = this@MainActivity.queryActiveTracks() override fun queryActiveTracks() = this@MainActivity.queryActiveTracks()
override fun giveDonation() { override fun giveDonation() {
openPlatform("", platformLink = "https://razorpay.com/payment-button/pl_GnKuuDBdBu0ank/view/?utm_source=payment_button&utm_medium=button&utm_campaign=payment_button") openPlatform(
"",
platformLink = "https://razorpay.com/payment-button/pl_GnKuuDBdBu0ank/view/?utm_source=payment_button&utm_medium=button&utm_campaign=payment_button"
)
} }
override fun shareApp() { override fun shareApp() {
@ -341,7 +329,8 @@ class MainActivity : ComponentActivity() {
} }
} }
override fun writeMp3Tags(trackDetails: TrackDetails) { /*IMPLEMENTED*/ } override fun writeMp3Tags(trackDetails: TrackDetails) { /*IMPLEMENTED*/
}
override val isInternetAvailable get() = internetAvailability.value ?: true override val isInternetAvailable get() = internetAvailability.value ?: true
} }
@ -351,35 +340,19 @@ class MainActivity : ComponentActivity() {
* */ * */
override val analytics = object : Analytics { override val analytics = object : Analytics {
override fun appLaunchEvent() { override fun appLaunchEvent() {
if (preferenceManager.isAnalyticsEnabled) { analyticsManager.sendEvent("app_launch")
TrackHelper.track()
.event("events", "App_Launch")
.name("App Launch").with(tracker)
}
} }
override fun homeScreenVisit() { override fun homeScreenVisit() {
if (preferenceManager.isAnalyticsEnabled) { analyticsManager.sendView("home_screen")
// HomeScreen Visit Event
TrackHelper.track().screen("/main_activity/home_screen")
.title("HomeScreen").with(tracker)
}
} }
override fun listScreenVisit() { override fun listScreenVisit() {
if (preferenceManager.isAnalyticsEnabled) { analyticsManager.sendView("list_screen")
// ListScreen Visit Event
TrackHelper.track().screen("/main_activity/list_screen")
.title("ListScreen").with(tracker)
}
} }
override fun donationDialogVisit() { override fun donationDialogVisit() {
if (preferenceManager.isAnalyticsEnabled) { analyticsManager.sendEvent("open_donation_dialog")
// Donation Dialog Open Event
TrackHelper.track().screen("/main_activity/donation_dialog")
.title("DonationDialog").with(tracker)
}
} }
} }
} }
@ -481,6 +454,16 @@ class MainActivity : ComponentActivity() {
unbindService() unbindService()
} }
override fun onStart() {
super.onStart()
analyticsManager.onStart()
}
override fun onStop() {
super.onStop()
analyticsManager.onStop()
}
companion object { companion object {
const val disableDozeCode = 1223 const val disableDozeCode = 1223
} }

View File

@ -22,9 +22,9 @@ object Versions {
const val versionCode = 22 const val versionCode = 22
// Kotlin // Kotlin
const val kotlinVersion = "1.5.10" const val kotlinVersion = "1.5.21"
const val coroutinesVersion = "1.5.0" const val coroutinesVersion = "1.5.1"
// Code Formatting // Code Formatting
const val ktLint = "10.1.0" const val ktLint = "10.1.0"
@ -41,24 +41,24 @@ object Versions {
const val mokoParcelize = "0.7.1" const val mokoParcelize = "0.7.1"
// Internet // Internet
const val ktor = "1.6.0" const val ktor = "1.6.2"
const val kotlinxSerialization = "1.2.1" const val kotlinxSerialization = "1.2.2"
// Database // Database
const val sqlDelight = "1.5.0" const val sqlDelight = "1.5.1"
const val sqliteJdbcDriver = "3.34.0" const val sqliteJdbcDriver = "3.34.0"
const val slf4j = "1.7.31" const val slf4j = "1.7.31"
// Internationalisation // Internationalisation
const val i18n4k = "0.1.2" const val i18n4k = "0.1.3"
// Android // Android
const val minSdkVersion = 21 const val minSdkVersion = 21
const val compileSdkVersion = 30 const val compileSdkVersion = 30
const val targetSdkVersion = 29 const val targetSdkVersion = 29
const val androidxLifecycle = "2.3.1" const val androidxLifecycle = "2.4.0-alpha03"
} }
object HostOS { object HostOS {
@ -81,8 +81,8 @@ object Koin {
} }
object Androidx { object Androidx {
const val androidxActivity = "androidx.activity:activity-compose:1.3.0-beta02" const val androidxActivity = "androidx.activity:activity-compose:1.3.1"
const val core = "androidx.core:core-ktx:1.5.0" const val core = "androidx.core:core-ktx:1.6.0"
const val palette = "androidx.palette:palette-ktx:1.0.0" const val palette = "androidx.palette:palette-ktx:1.0.0"
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutinesVersion}" const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.coroutinesVersion}"
@ -98,6 +98,7 @@ object KTLint {
object JetBrains { object JetBrains {
object Kotlin { object Kotlin {
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt"
const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlinVersion}" const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlinVersion}"
const val serialization = "org.jetbrains.kotlin:kotlin-serialization:${Versions.kotlinVersion}" const val serialization = "org.jetbrains.kotlin:kotlin-serialization:${Versions.kotlinVersion}"
const val testCommon = "org.jetbrains.kotlin:kotlin-test-common:${Versions.kotlinVersion}" const val testCommon = "org.jetbrains.kotlin:kotlin-test-common:${Versions.kotlinVersion}"
@ -107,15 +108,16 @@ object JetBrains {
object Compose { object Compose {
// __LATEST_COMPOSE_RELEASE_VERSION__ // __LATEST_COMPOSE_RELEASE_VERSION__
const val VERSION = "0.4.0" private const val VERSION = "1.0.0-alpha3"
const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION" const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION"
} }
} }
object Mosaic { object Mosaic {
const val gradlePlugin = "com.jakewharton.mosaic:mosaic-gradle-plugin:${Versions.mosaic}" const val gradlePlugin = "com.jakewharton.mosaic:mosaic-gradle-plugin:${Versions.mosaic}"
} }
object Decompose { object Decompose {
private const val VERSION = "0.2.6" private const val VERSION = "0.3.1"
const val decompose = "com.arkivanov.decompose:decompose:$VERSION" const val decompose = "com.arkivanov.decompose:decompose:$VERSION"
const val decomposeIosX64 = "com.arkivanov.decompose:decompose-iosx64:$VERSION" const val decomposeIosX64 = "com.arkivanov.decompose:decompose-iosx64:$VERSION"
const val decomposeIosArm64 = "com.arkivanov.decompose:decompose-iosarm64:$VERSION" const val decomposeIosArm64 = "com.arkivanov.decompose:decompose-iosarm64:$VERSION"
@ -164,16 +166,13 @@ object Extras {
const val jaudioTagger = "com.github.Shabinder:JAudioTagger-Android:1.0" const val jaudioTagger = "com.github.Shabinder:JAudioTagger-Android:1.0"
const val kermit = "co.touchlab:kermit:${Versions.kermit}" const val kermit = "co.touchlab:kermit:${Versions.kermit}"
object Android { object Android {
object Acra { // Self Hosted Analytics & Crashlytics (FOSS)
// Self Hosted Crashlytics (FOSS) val countly = "ly.count.android:sdk:20.11.8"
private const val VERSION = "5.8.3"
val http = "ch.acra:acra-http:$VERSION"
val notification = "ch.acra:acra-notification:$VERSION"
}
// Self Hosted Analytics (FOSS)
val matomo = "org.matomo.sdk:tracker:4.1.2"
val appUpdator = "com.github.amitbd1508:AppUpdater:4.1.0" val appUpdator = "com.github.amitbd1508:AppUpdater:4.1.0"
} }
object Desktop {
val countly = "ly.count.sdk:java:20.11.0"
}
} }
object Serialization { object Serialization {

View File

@ -37,7 +37,6 @@ android {
sourceSets { sourceSets {
named("main") { named("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml") manifest.srcFile("src/androidMain/AndroidManifest.xml")
java.srcDirs("src/androidMain/kotlin")
res.srcDirs("src/androidMain/res") res.srcDirs("src/androidMain/res")
} }
} }

View File

@ -43,7 +43,7 @@ kotlin {
implementation(Extras.kermit) implementation(Extras.kermit)
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}") implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") { implementation(JetBrains.Kotlin.coroutines) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
isForce = true isForce = true
} }
@ -51,7 +51,7 @@ kotlin {
} }
named("androidMain") { named("androidMain") {
dependencies { dependencies {
implementation("androidx.appcompat:appcompat:1.3.0") implementation(Androidx.androidxActivity)
implementation(Androidx.core) implementation(Androidx.core)
} }
} }

View File

@ -66,7 +66,7 @@ kotlin {
implementation(Serialization.json) implementation(Serialization.json)
implementation("co.touchlab:stately-common:1.1.7") implementation("co.touchlab:stately-common:1.1.7")
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}") implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") { implementation(JetBrains.Kotlin.coroutines) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
isForce = true isForce = true
} }
@ -75,7 +75,7 @@ kotlin {
named("androidMain") { named("androidMain") {
dependencies { dependencies {
implementation("androidx.appcompat:appcompat:1.3.0") implementation(Androidx.androidxActivity)
implementation(Androidx.core) implementation(Androidx.core)
implementation(compose.runtime) implementation(compose.runtime)
implementation(compose.material) implementation(compose.material)

View File

@ -42,6 +42,7 @@ kotlin {
dependencies { dependencies {
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Extras.mp3agic) implementation(Extras.mp3agic)
implementation(Extras.Android.countly)
// implementation(files("$rootDir/libs/mobile-ffmpeg.aar")) // implementation(files("$rootDir/libs/mobile-ffmpeg.aar"))
} }
} }
@ -49,6 +50,7 @@ kotlin {
dependencies { dependencies {
implementation(compose.materialIconsExtended) implementation(compose.materialIconsExtended)
implementation(Extras.mp3agic) implementation(Extras.mp3agic)
implementation(Extras.Desktop.countly)
} }
} }
jsMain { jsMain {

View File

@ -0,0 +1,71 @@
package com.shabinder.common.di.analytics
import android.app.Activity
import android.app.Application
import ly.count.android.sdk.Countly
import ly.count.android.sdk.CountlyConfig
import ly.count.android.sdk.DeviceId
import org.koin.dsl.bind
import org.koin.dsl.module
internal class AndroidAnalyticsManager(private val mainActivity: Activity) : AnalyticsManager {
init {
init()
}
override fun init() {
Countly.sharedInstance().init(
CountlyConfig(
mainActivity.applicationContext as Application,
COUNTLY_CONFIG.APP_KEY,
COUNTLY_CONFIG.SERVER_URL
).apply {
setIdMode(DeviceId.Type.OPEN_UDID)
setViewTracking(true)
enableCrashReporting()
setLoggingEnabled(true)
setRecordAllThreadsWithCrash()
setRequiresConsent(true)
setShouldIgnoreAppCrawlers(true)
setEventQueueSizeToSend(5)
}
)
}
override fun onStart() {
Countly.sharedInstance().onStart(mainActivity)
}
override fun onStop() {
Countly.sharedInstance().onStop()
}
override fun giveConsent() {
Countly.sharedInstance().consent().giveConsentAll()
}
override fun isTracking(): Boolean = Countly.sharedInstance().consent().getConsent(Countly.CountlyFeatureNames.events)
override fun revokeConsent() {
Countly.sharedInstance().consent().removeConsentAll()
}
override fun sendView(name: String, extras: Map<String, Any>) {
Countly.sharedInstance().views().recordView(name, extras)
}
override fun sendEvent(eventName: String, extras: Map<String, Any>) {
Countly.sharedInstance().events().recordEvent(eventName, extras)
}
override fun sendCrashReport(error: Throwable, extras: Map<String, Any>) {
Countly.sharedInstance().crashes().recordUnhandledException(error, extras)
}
}
actual fun analyticsModule() = module {
factory { (mainActivity: Activity) ->
AndroidAnalyticsManager(mainActivity)
} bind AnalyticsManager::class
}

View File

@ -20,6 +20,7 @@ import co.touchlab.kermit.Kermit
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import com.shabinder.common.database.databaseModule import com.shabinder.common.database.databaseModule
import com.shabinder.common.database.getLogger import com.shabinder.common.database.getLogger
import com.shabinder.common.di.analytics.analyticsModule
import com.shabinder.common.di.preference.PreferenceManager import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.di.providers.providersModule import com.shabinder.common.di.providers.providersModule
import io.ktor.client.* import io.ktor.client.*
@ -39,6 +40,7 @@ fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclarat
appDeclaration() appDeclaration()
modules( modules(
commonModule(enableNetworkLogs = enableNetworkLogs), commonModule(enableNetworkLogs = enableNetworkLogs),
analyticsModule(),
providersModule(), providersModule(),
databaseModule() databaseModule()
) )

View File

@ -0,0 +1,23 @@
package com.shabinder.common.di.analytics
import org.koin.core.module.Module
interface AnalyticsManager {
fun init()
fun onStart()
fun onStop()
fun giveConsent()
fun isTracking(): Boolean
fun revokeConsent()
fun sendView(name: String, extras: Map<String, Any> = emptyMap())
fun sendEvent(eventName: String, extras: Map<String, Any> = emptyMap())
fun sendCrashReport(error: Throwable, extras: Map<String, Any> = emptyMap())
}
@Suppress("ClassName", "SpellCheckingInspection")
object COUNTLY_CONFIG {
const val APP_KEY = "27820f304468cc651ef47d787f0cb5fe11c577df"
const val SERVER_URL = "https://counlty.shabinder.in"
}
expect fun analyticsModule(): Module

View File

@ -14,8 +14,8 @@ class PreferenceManager(settings: Settings) : Settings by settings {
} }
/* ANALYTICS */ /* ANALYTICS */
val isAnalyticsEnabled get() = getBooleanOrNull(ANALYTICS_KEY) ?: false // val isAnalyticsEnabled get() = getBooleanOrNull(ANALYTICS_KEY) ?: false
fun toggleAnalytics(enabled: Boolean) = putBoolean(ANALYTICS_KEY, enabled) // fun toggleAnalytics(enabled: Boolean) = putBoolean(ANALYTICS_KEY, enabled)
/* DOWNLOAD DIRECTORY */ /* DOWNLOAD DIRECTORY */
val downloadDir get() = getStringOrNull(DIR_KEY) val downloadDir get() = getStringOrNull(DIR_KEY)

View File

@ -0,0 +1,82 @@
package com.shabinder.common.di.analytics
import com.shabinder.common.di.Dir
import ly.count.sdk.java.Config
import ly.count.sdk.java.Config.DeviceIdStrategy
import ly.count.sdk.java.Config.Feature
import ly.count.sdk.java.ConfigCore.LoggingLevel
import ly.count.sdk.java.Countly
import org.koin.dsl.bind
import org.koin.dsl.module
import java.io.File
internal class DesktopAnalyticsManager(
private val dir: Dir
) : AnalyticsManager {
init {
init()
}
override fun init() {
val config: Config = Config(COUNTLY_CONFIG.SERVER_URL, COUNTLY_CONFIG.APP_KEY).apply {
eventsBufferSize = 2
loggingLevel = LoggingLevel.DEBUG
setDeviceIdStrategy(DeviceIdStrategy.UUID)
enableFeatures(*featuresSet)
setRequiresConsent(true)
}
Countly.init(File(dir.defaultDir()), config)
Countly.session().begin();
}
override fun giveConsent() {
Countly.onConsent(*featuresSet)
}
override fun isTracking(): Boolean = Countly.isTracking(Feature.Events)
override fun revokeConsent() {
Countly.onConsentRemoval(*featuresSet)
}
override fun sendView(name: String, extras: Map<String, Any>) {
Countly.api().view(name)
}
override fun sendEvent(eventName: String, extras: Map<String, Any>) {
Countly.api().event(eventName)
.setSegmentation(extras.filterValues { it is String } as? Map<String, String> ?: emptyMap()).record()
}
override fun sendCrashReport(error: Throwable, extras: Map<String, Any>) {
Countly.api().addCrashReport(
error,
extras.getOrDefault("fatal", true) as Boolean,
error.javaClass.simpleName,
extras.filterValues { it is String } as? Map<String, String> ?: emptyMap()
)
}
companion object {
val featuresSet = arrayOf(
Feature.Events,
Feature.Sessions,
Feature.CrashReporting,
Feature.Views,
Feature.UserProfiles,
Feature.Location,
)
}
override fun onStart() {}
override fun onStop() {}
}
actual fun analyticsModule() = module {
single { DesktopAnalyticsManager(get()) } bind AnalyticsManager::class
}

View File

@ -0,0 +1,30 @@
package com.shabinder.common.di.analytics
import org.koin.dsl.bind
import org.koin.dsl.module
// TODO("Not yet implemented")
private val webAnalytics =
object : AnalyticsManager {
override fun init() {}
override fun onStart() {}
override fun onStop() {}
override fun giveConsent() {}
override fun isTracking(): Boolean = false
override fun revokeConsent() {}
override fun sendView(name: String, extras: Map<String, Any>) {}
override fun sendEvent(eventName: String, extras: Map<String, Any>) {}
override fun sendCrashReport(error: Throwable, extras: Map<String, Any>) {}
}
actual fun analyticsModule() = module {
single { webAnalytics } bind AnalyticsManager::class
}

View File

@ -50,7 +50,7 @@ dependencies {
implementation(Ktor.clientSerialization) implementation(Ktor.clientSerialization)
implementation(Serialization.json) implementation(Serialization.json)
// testDeps // testDeps
testImplementation(kotlin("test-junit")) testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
} }
tasks.test { tasks.test {

View File

@ -26,7 +26,7 @@ dependencies {
implementation(Ktor.clientSerialization) implementation(Ktor.clientSerialization)
implementation(Serialization.json) implementation(Serialization.json)
// testDeps // testDeps
testImplementation(kotlin("test-junit")) testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.5.21")
} }
tasks.test { tasks.test {

View File

@ -27,7 +27,7 @@ repositories {
} }
dependencies { dependencies {
implementation(kotlin("stdlib-js")) implementation("org.jetbrains.kotlin:kotlin-stdlib-js:1.5.21")
implementation(Koin.core) implementation(Koin.core)
implementation(Extras.kermit) implementation(Extras.kermit)
implementation(Decompose.decompose) implementation(Decompose.decompose)
@ -44,11 +44,11 @@ dependencies {
implementation("co.touchlab:stately-common:1.1.7") implementation("co.touchlab:stately-common:1.1.7")
implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}") implementation("dev.icerock.moko:parcelize:${Versions.mokoParcelize}")
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1") // implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1") { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2") {
// https://youtrack.jetbrains.com/issue/KTOR-2670 // https://youtrack.jetbrains.com/issue/KTOR-2670
isForce = true isForce = true
} }
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-native-mt") { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1-native-mt") {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
isForce = true isForce = true
} }