diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ccca534..02baf45 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,6 +65,7 @@ android { dependencies { + implementation(project(":architecture")) implementation(project(":database")) implementation(project(":datastore")) implementation(project(":datasource")) diff --git a/app/src/main/java/gq/kirmanak/mealient/App.kt b/app/src/main/java/gq/kirmanak/mealient/App.kt index 7854133..24332c9 100644 --- a/app/src/main/java/gq/kirmanak/mealient/App.kt +++ b/app/src/main/java/gq/kirmanak/mealient/App.kt @@ -2,6 +2,8 @@ package gq.kirmanak.mealient import android.app.Application import dagger.hilt.android.HiltAndroidApp +import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration +import gq.kirmanak.mealient.data.analytics.Analytics import gq.kirmanak.mealient.logging.Logger import javax.inject.Inject @@ -11,8 +13,15 @@ class App : Application() { @Inject lateinit var logger: Logger + @Inject + lateinit var buildConfiguration: BuildConfiguration + + @Inject + lateinit var analytics: Analytics + override fun onCreate() { super.onCreate() logger.v { "onCreate() called" } + analytics.setIsEnabled(!buildConfiguration.isDebug()) } } diff --git a/app/src/main/java/gq/kirmanak/mealient/data/analytics/Analytics.kt b/app/src/main/java/gq/kirmanak/mealient/data/analytics/Analytics.kt new file mode 100644 index 0000000..351ae24 --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealient/data/analytics/Analytics.kt @@ -0,0 +1,6 @@ +package gq.kirmanak.mealient.data.analytics + +interface Analytics { + + fun setIsEnabled(enabled: Boolean) +} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/analytics/AnalyticsImpl.kt b/app/src/main/java/gq/kirmanak/mealient/data/analytics/AnalyticsImpl.kt new file mode 100644 index 0000000..40daa5e --- /dev/null +++ b/app/src/main/java/gq/kirmanak/mealient/data/analytics/AnalyticsImpl.kt @@ -0,0 +1,21 @@ +package gq.kirmanak.mealient.data.analytics + +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.crashlytics.FirebaseCrashlytics +import gq.kirmanak.mealient.logging.Logger +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AnalyticsImpl @Inject constructor( + private val firebaseAnalytics: FirebaseAnalytics, + private val firebaseCrashlytics: FirebaseCrashlytics, + private val logger: Logger, +) : Analytics { + + override fun setIsEnabled(enabled: Boolean) { + logger.v { "setIsEnabled() called with: enabled = $enabled" } + firebaseAnalytics.setAnalyticsCollectionEnabled(enabled) + firebaseCrashlytics.setCrashlyticsCollectionEnabled(enabled) + } +} \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/di/AppModule.kt b/app/src/main/java/gq/kirmanak/mealient/di/AppModule.kt index 8b3a7f7..aef7ee1 100644 --- a/app/src/main/java/gq/kirmanak/mealient/di/AppModule.kt +++ b/app/src/main/java/gq/kirmanak/mealient/di/AppModule.kt @@ -5,12 +5,16 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStoreFile +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.crashlytics.FirebaseCrashlytics import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import gq.kirmanak.mealient.data.analytics.Analytics +import gq.kirmanak.mealient.data.analytics.AnalyticsImpl import gq.kirmanak.mealient.data.storage.PreferencesStorage import gq.kirmanak.mealient.data.storage.PreferencesStorageImpl import javax.inject.Singleton @@ -24,9 +28,23 @@ interface AppModule { @Singleton fun provideDataStore(@ApplicationContext context: Context): DataStore = PreferenceDataStoreFactory.create { context.preferencesDataStoreFile("settings") } + + @Provides + @Singleton + fun provideFirebaseAnalytics(@ApplicationContext context: Context): FirebaseAnalytics = + FirebaseAnalytics.getInstance(context) + + @Provides + @Singleton + fun provideFirebaseCrashlytics(): FirebaseCrashlytics = + FirebaseCrashlytics.getInstance() } @Binds @Singleton fun bindPreferencesStorage(preferencesStorage: PreferencesStorageImpl): PreferencesStorage + + @Binds + @Singleton + fun bindAnalytics(analyticsImpl: AnalyticsImpl): Analytics } \ No newline at end of file diff --git a/app/src/test/java/gq/kirmanak/mealient/data/analytics/AnalyticsImplTest.kt b/app/src/test/java/gq/kirmanak/mealient/data/analytics/AnalyticsImplTest.kt new file mode 100644 index 0000000..e37e5b0 --- /dev/null +++ b/app/src/test/java/gq/kirmanak/mealient/data/analytics/AnalyticsImplTest.kt @@ -0,0 +1,42 @@ +package gq.kirmanak.mealient.data.analytics + +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.crashlytics.FirebaseCrashlytics +import gq.kirmanak.mealient.logging.Logger +import gq.kirmanak.mealient.test.FakeLogger +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import io.mockk.verify +import org.junit.Before +import org.junit.Test + +class AnalyticsImplTest { + + @MockK(relaxUnitFun = true) + lateinit var firebaseAnalytics: FirebaseAnalytics + + @MockK(relaxUnitFun = true) + lateinit var firebaseCrashlytics: FirebaseCrashlytics + + lateinit var subject: Analytics + + private val logger: Logger = FakeLogger() + + @Before + fun setUp() { + MockKAnnotations.init(this) + subject = AnalyticsImpl(firebaseAnalytics, firebaseCrashlytics, logger) + } + + @Test + fun `when setIsEnabled expect call to analytics`() { + subject.setIsEnabled(true) + verify { firebaseAnalytics.setAnalyticsCollectionEnabled(eq(true)) } + } + + @Test + fun `when setIsEnabled expect call to crashlytics`() { + subject.setIsEnabled(true) + verify { firebaseCrashlytics.setCrashlyticsCollectionEnabled(eq(true)) } + } +} \ No newline at end of file diff --git a/architecture/.gitignore b/architecture/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/architecture/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/architecture/build.gradle.kts b/architecture/build.gradle.kts new file mode 100644 index 0000000..011e9ed --- /dev/null +++ b/architecture/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("gq.kirmanak.mealient.library") + id("dagger.hilt.android.plugin") + id("kotlin-kapt") +} + +android { + namespace = "gq.kirmanak.mealient.architecture" +} + +dependencies { + implementation(libs.google.dagger.hiltAndroid) + kapt(libs.google.dagger.hiltCompiler) +} \ No newline at end of file diff --git a/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/ArchitectureModule.kt b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/ArchitectureModule.kt new file mode 100644 index 0000000..59a979e --- /dev/null +++ b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/ArchitectureModule.kt @@ -0,0 +1,18 @@ +package gq.kirmanak.mealient.architecture + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration +import gq.kirmanak.mealient.architecture.configuration.BuildConfigurationImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface ArchitectureModule { + + @Binds + @Singleton + fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration +} \ No newline at end of file diff --git a/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfiguration.kt b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfiguration.kt new file mode 100644 index 0000000..e076468 --- /dev/null +++ b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfiguration.kt @@ -0,0 +1,6 @@ +package gq.kirmanak.mealient.architecture.configuration + +interface BuildConfiguration { + + fun isDebug(): Boolean +} \ No newline at end of file diff --git a/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfigurationImpl.kt b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfigurationImpl.kt new file mode 100644 index 0000000..378b1a8 --- /dev/null +++ b/architecture/src/main/kotlin/gq/kirmanak/mealient/architecture/configuration/BuildConfigurationImpl.kt @@ -0,0 +1,14 @@ +package gq.kirmanak.mealient.architecture.configuration + +import androidx.viewbinding.BuildConfig +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class BuildConfigurationImpl @Inject constructor() : BuildConfiguration { + + @get:JvmName("_isDebug") + private val isDebug by lazy { BuildConfig.DEBUG } + + override fun isDebug(): Boolean = isDebug +} \ No newline at end of file diff --git a/logging/build.gradle.kts b/logging/build.gradle.kts index 518b450..efd2aee 100644 --- a/logging/build.gradle.kts +++ b/logging/build.gradle.kts @@ -9,6 +9,8 @@ android { } dependencies { + implementation(project(":architecture")) + implementation(libs.google.dagger.hiltAndroid) kapt(libs.google.dagger.hiltCompiler) } \ No newline at end of file diff --git a/logging/src/main/kotlin/gq/kirmanak/mealient/logging/LogcatAppender.kt b/logging/src/main/kotlin/gq/kirmanak/mealient/logging/LogcatAppender.kt index 638e1d3..e916fcf 100644 --- a/logging/src/main/kotlin/gq/kirmanak/mealient/logging/LogcatAppender.kt +++ b/logging/src/main/kotlin/gq/kirmanak/mealient/logging/LogcatAppender.kt @@ -1,29 +1,25 @@ package gq.kirmanak.mealient.logging -import android.os.Build import android.util.Log +import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration import javax.inject.Inject import javax.inject.Singleton @Singleton -class LogcatAppender @Inject constructor() : Appender { +class LogcatAppender @Inject constructor( + private val buildConfiguration: BuildConfiguration, +) : Appender { - private val isLoggable: Boolean by lazy { BuildConfig.DEBUG } + private val isLoggable: Boolean + get() = buildConfiguration.isDebug() override fun isLoggable(logLevel: LogLevel): Boolean = isLoggable override fun isLoggable(logLevel: LogLevel, tag: String): Boolean = isLoggable override fun log(logLevel: LogLevel, tag: String, message: String) { - // Tag length limit was removed in API 26. - val logTag = if (tag.length <= MAX_TAG_LENGTH || Build.VERSION.SDK_INT >= 26) { - tag - } else { - tag.substring(0, MAX_TAG_LENGTH) - } - if (message.length < MAX_LOG_LENGTH) { - Log.println(logLevel.priority, logTag, message) + Log.println(logLevel.priority, tag, message) return } @@ -36,7 +32,7 @@ class LogcatAppender @Inject constructor() : Appender { do { val end = newline.coerceAtMost(i + MAX_LOG_LENGTH) val part = message.substring(i, end) - Log.println(logLevel.priority, logTag, part) + Log.println(logLevel.priority, tag, part) i = end } while (i < newline) i++ @@ -45,7 +41,6 @@ class LogcatAppender @Inject constructor() : Appender { companion object { private const val MAX_LOG_LENGTH = 4000 - private const val MAX_TAG_LENGTH = 23 } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3c9d35e..a5dfdaf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,6 +20,7 @@ dependencyResolutionManagement { rootProject.name = "Mealient" include(":app") +include(":architecture") include(":database") include(":datastore") include(":logging")