android: add core common module logging crashlytics and feature flags

This commit is contained in:
Codex
2026-03-09 16:04:53 +03:00
parent ef5f866bd0
commit 65e74cffdb
14 changed files with 153 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ plugins {
id("org.jetbrains.kotlin.plugin.serialization")
id("com.google.dagger.hilt.android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
android {
@@ -19,6 +20,10 @@ android {
versionCode = 1
versionName = "0.1.0"
buildConfigField("String", "API_BASE_URL", "\"https://chat.daemonlord.ru/\"")
buildConfigField("String", "API_VERSION_HEADER", "\"2026-03\"")
buildConfigField("boolean", "FEATURE_ACCOUNT_MANAGEMENT", "true")
buildConfigField("boolean", "FEATURE_TWO_FACTOR", "true")
buildConfigField("boolean", "FEATURE_MEDIA_GALLERY", "true")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@@ -62,6 +67,7 @@ android {
}
dependencies {
implementation(project(":core:common"))
implementation(platform("com.google.firebase:firebase-bom:34.10.0"))
implementation("androidx.core:core-ktx:1.15.0")
@@ -98,6 +104,8 @@ dependencies {
kapt("com.google.dagger:hilt-compiler:2.52")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
implementation("com.google.firebase:firebase-messaging")
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.jakewharton.timber:timber:5.0.1")
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")

View File

@@ -5,14 +5,20 @@ import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.disk.DiskCache
import coil.memory.MemoryCache
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.HiltAndroidApp
import ru.daemonlord.messenger.core.notifications.NotificationChannels
import java.io.File
import timber.log.Timber
@HiltAndroidApp
class MessengerApplication : Application(), ImageLoaderFactory {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
NotificationChannels.ensureCreated(this)
}

View File

@@ -0,0 +1,38 @@
package ru.daemonlord.messenger.core.logging
import com.google.firebase.crashlytics.FirebaseCrashlytics
import javax.inject.Inject
import javax.inject.Singleton
import timber.log.Timber
@Singleton
class TimberAppLogger @Inject constructor(
private val crashlytics: FirebaseCrashlytics,
) : AppLogger {
override fun d(tag: String, message: String) {
Timber.tag(tag).d(message)
}
override fun i(tag: String, message: String) {
Timber.tag(tag).i(message)
}
override fun w(tag: String, message: String, throwable: Throwable?) {
if (throwable != null) {
Timber.tag(tag).w(throwable, message)
crashlytics.recordException(throwable)
} else {
Timber.tag(tag).w(message)
}
}
override fun e(tag: String, message: String, throwable: Throwable?) {
if (throwable != null) {
Timber.tag(tag).e(throwable, message)
crashlytics.recordException(throwable)
} else {
Timber.tag(tag).e(message)
}
}
}

View File

@@ -0,0 +1,17 @@
package ru.daemonlord.messenger.core.network
import okhttp3.Interceptor
import okhttp3.Response
import ru.daemonlord.messenger.BuildConfig
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ApiVersionInterceptor @Inject constructor() : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.header("X-Api-Version", BuildConfig.API_VERSION_HEADER)
.build()
return chain.proceed(request)
}
}

View File

@@ -0,0 +1,23 @@
package ru.daemonlord.messenger.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.daemonlord.messenger.BuildConfig
import ru.daemonlord.messenger.core.feature.FeatureFlags
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object FeatureFlagsModule {
@Provides
@Singleton
fun provideFeatureFlags(): FeatureFlags {
return FeatureFlags(
accountManagementEnabled = BuildConfig.FEATURE_ACCOUNT_MANAGEMENT,
twoFactorEnabled = BuildConfig.FEATURE_TWO_FACTOR,
mediaGalleryEnabled = BuildConfig.FEATURE_MEDIA_GALLERY,
)
}
}

View File

@@ -0,0 +1,25 @@
package ru.daemonlord.messenger.di
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.daemonlord.messenger.core.logging.AppLogger
import ru.daemonlord.messenger.core.logging.TimberAppLogger
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
abstract class LoggingModule {
@Binds
@Singleton
abstract fun bindAppLogger(logger: TimberAppLogger): AppLogger
companion object {
@Provides
@Singleton
fun provideCrashlytics(): FirebaseCrashlytics = FirebaseCrashlytics.getInstance()
}
}

View File

@@ -13,11 +13,13 @@ import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import ru.daemonlord.messenger.BuildConfig
import ru.daemonlord.messenger.core.network.AuthHeaderInterceptor
import ru.daemonlord.messenger.core.network.ApiVersionInterceptor
import ru.daemonlord.messenger.core.network.TokenRefreshAuthenticator
import ru.daemonlord.messenger.data.auth.api.AuthApiService
import ru.daemonlord.messenger.data.chat.api.ChatApiService
import ru.daemonlord.messenger.data.media.api.MediaApiService
import ru.daemonlord.messenger.data.message.api.MessageApiService
import ru.daemonlord.messenger.data.user.api.UserApiService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@@ -87,11 +89,13 @@ object NetworkModule {
@Singleton
fun provideApiClient(
loggingInterceptor: HttpLoggingInterceptor,
apiVersionInterceptor: ApiVersionInterceptor,
authHeaderInterceptor: AuthHeaderInterceptor,
tokenRefreshAuthenticator: TokenRefreshAuthenticator,
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(apiVersionInterceptor)
.addInterceptor(authHeaderInterceptor)
.authenticator(tokenRefreshAuthenticator)
.connectTimeout(20, TimeUnit.SECONDS)
@@ -137,4 +141,10 @@ object NetworkModule {
fun provideMediaApiService(retrofit: Retrofit): MediaApiService {
return retrofit.create(MediaApiService::class.java)
}
@Provides
@Singleton
fun provideUserApiService(retrofit: Retrofit): UserApiService {
return retrofit.create(UserApiService::class.java)
}
}

View File

@@ -1,9 +0,0 @@
package ru.daemonlord.messenger.domain.common
sealed interface AppError {
data object InvalidCredentials : AppError
data object Unauthorized : AppError
data object Network : AppError
data class Server(val message: String?) : AppError
data class Unknown(val cause: Throwable?) : AppError
}

View File

@@ -1,6 +0,0 @@
package ru.daemonlord.messenger.domain.common
sealed interface AppResult<out T> {
data class Success<T>(val data: T) : AppResult<T>
data class Error(val reason: AppError) : AppResult<Nothing>
}