Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 36504fe4b9 | |||
| fc3b3cfcb3 |
@@ -92,6 +92,7 @@ dependencies {
|
|||||||
implementation(projects.feature.photoviewer)
|
implementation(projects.feature.photoviewer)
|
||||||
implementation(projects.feature.createchat)
|
implementation(projects.feature.createchat)
|
||||||
|
|
||||||
|
implementation(projects.core.logger)
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
implementation(projects.core.ui)
|
implementation(projects.core.ui)
|
||||||
implementation(projects.core.data)
|
implementation(projects.core.data)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package dev.meloda.fast
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.core.os.LocaleListCompat
|
||||||
@@ -23,6 +22,7 @@ import dev.meloda.fast.datastore.AppSettings
|
|||||||
import dev.meloda.fast.datastore.UserSettings
|
import dev.meloda.fast.datastore.UserSettings
|
||||||
import dev.meloda.fast.domain.GetCurrentAccountUseCase
|
import dev.meloda.fast.domain.GetCurrentAccountUseCase
|
||||||
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.model.BaseError
|
import dev.meloda.fast.model.BaseError
|
||||||
import dev.meloda.fast.model.api.domain.VkUser
|
import dev.meloda.fast.model.api.domain.VkUser
|
||||||
import dev.meloda.fast.navigation.Main
|
import dev.meloda.fast.navigation.Main
|
||||||
@@ -67,7 +67,8 @@ class MainViewModelImpl(
|
|||||||
private val getCurrentAccountUseCase: GetCurrentAccountUseCase,
|
private val getCurrentAccountUseCase: GetCurrentAccountUseCase,
|
||||||
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
private val loadUserByIdUseCase: LoadUserByIdUseCase,
|
||||||
private val userSettings: UserSettings,
|
private val userSettings: UserSettings,
|
||||||
private val longPollController: LongPollController
|
private val longPollController: LongPollController,
|
||||||
|
private val logger: FastLogger
|
||||||
) : MainViewModel, ViewModel() {
|
) : MainViewModel, ViewModel() {
|
||||||
|
|
||||||
override val startDestination = MutableStateFlow<Any?>(null)
|
override val startDestination = MutableStateFlow<Any?>(null)
|
||||||
@@ -203,7 +204,10 @@ class MainViewModelImpl(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val currentAccount = getCurrentAccountUseCase()
|
val currentAccount = getCurrentAccountUseCase()
|
||||||
|
|
||||||
Log.d("MainViewModel", "currentAccount: $currentAccount")
|
logger.debug(
|
||||||
|
this@MainViewModelImpl::class,
|
||||||
|
"loadAccounts(): currentAccount: $currentAccount"
|
||||||
|
)
|
||||||
|
|
||||||
listenLongPollState()
|
listenLongPollState()
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import com.skydoves.compose.stability.runtime.ComposeStabilityAnalyzer
|
|||||||
import dev.meloda.fast.auth.BuildConfig
|
import dev.meloda.fast.auth.BuildConfig
|
||||||
import dev.meloda.fast.common.di.applicationModule
|
import dev.meloda.fast.common.di.applicationModule
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
|
import dev.meloda.fast.logger.FastLogLevel
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.presentation.CrashActivity
|
import dev.meloda.fast.presentation.CrashActivity
|
||||||
import org.koin.android.ext.android.get
|
import org.koin.android.ext.android.get
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
@@ -30,6 +32,14 @@ class AppGlobal : Application(), ImageLoaderFactory {
|
|||||||
|
|
||||||
initKoin()
|
initKoin()
|
||||||
initCrashHandler()
|
initCrashHandler()
|
||||||
|
|
||||||
|
val logLevel =
|
||||||
|
if (BuildConfig.DEBUG) FastLogLevel.DEBUG
|
||||||
|
else FastLogLevel.ERROR
|
||||||
|
|
||||||
|
get<FastLogger>()
|
||||||
|
.apply { setLogLevel(logLevel) }
|
||||||
|
.also { FastLogger.setInstance(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newImageLoader(): ImageLoader = get()
|
override fun newImageLoader(): ImageLoader = get()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import dev.meloda.fast.convos.di.createChatModule
|
|||||||
import dev.meloda.fast.domain.di.domainModule
|
import dev.meloda.fast.domain.di.domainModule
|
||||||
import dev.meloda.fast.friends.di.friendsModule
|
import dev.meloda.fast.friends.di.friendsModule
|
||||||
import dev.meloda.fast.languagepicker.di.languagePickerModule
|
import dev.meloda.fast.languagepicker.di.languagePickerModule
|
||||||
|
import dev.meloda.fast.logger.loggerModule
|
||||||
import dev.meloda.fast.messageshistory.di.messagesHistoryModule
|
import dev.meloda.fast.messageshistory.di.messagesHistoryModule
|
||||||
import dev.meloda.fast.photoviewer.di.photoViewModule
|
import dev.meloda.fast.photoviewer.di.photoViewModule
|
||||||
import dev.meloda.fast.profile.di.profileModule
|
import dev.meloda.fast.profile.di.profileModule
|
||||||
@@ -49,6 +50,8 @@ val applicationModule = module {
|
|||||||
createChatModule
|
createChatModule
|
||||||
)
|
)
|
||||||
|
|
||||||
|
includes(loggerModule)
|
||||||
|
|
||||||
// TODO: 14/05/2024, Danil Nikolaev: extract all operations with preferences to standalone class
|
// TODO: 14/05/2024, Danil Nikolaev: extract all operations with preferences to standalone class
|
||||||
singleOf(PreferenceManager::getDefaultSharedPreferences)
|
singleOf(PreferenceManager::getDefaultSharedPreferences)
|
||||||
single<Resources> { androidContext().resources }
|
single<Resources> { androidContext().resources }
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import android.content.res.Configuration
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.SystemBarStyle
|
import androidx.activity.SystemBarStyle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
@@ -24,10 +23,13 @@ import dev.meloda.fast.MainViewModel
|
|||||||
import dev.meloda.fast.MainViewModelImpl
|
import dev.meloda.fast.MainViewModelImpl
|
||||||
import dev.meloda.fast.common.AppConstants
|
import dev.meloda.fast.common.AppConstants
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.service.OnlineService
|
import dev.meloda.fast.service.OnlineService
|
||||||
import dev.meloda.fast.service.longpolling.LongPollingService
|
import dev.meloda.fast.service.longpolling.LongPollingService
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
|
import dev.meloda.fast.ui.common.LocalLogger
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@@ -64,27 +66,28 @@ class MainActivity : AppCompatActivity() {
|
|||||||
requestNotificationPermissions()
|
requestNotificationPermissions()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val logger: FastLogger = koinInject()
|
||||||
LaunchedEffect(viewModel) {
|
|
||||||
Log.d("VM_CREATE", "onCreate: viewModel: $viewModel")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
LifecycleResumeEffect(true) {
|
LifecycleResumeEffect(true) {
|
||||||
viewModel.onAppResumed(intent)
|
viewModel.onAppResumed(intent)
|
||||||
onPauseOrDispose {}
|
onPauseOrDispose {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalLogger provides logger) {
|
||||||
RootScreen(
|
RootScreen(
|
||||||
toggleLongPollService = { enable, inBackground ->
|
toggleLongPollService = { enable, inBackground ->
|
||||||
toggleLongPollService(
|
toggleLongPollService(
|
||||||
enable = enable,
|
enable = enable,
|
||||||
inBackground = inBackground ?: AppSettings.Experimental.longPollInBackground
|
inBackground = inBackground
|
||||||
|
?: AppSettings.Experimental.longPollInBackground
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
toggleOnlineService = ::toggleOnlineService
|
toggleOnlineService = ::toggleOnlineService
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createNotificationChannels() {
|
private fun createNotificationChannels() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.Manifest
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -63,6 +62,7 @@ import dev.meloda.fast.photoviewer.presentation.PhotoViewDialog
|
|||||||
import dev.meloda.fast.settings.navigation.navigateToSettings
|
import dev.meloda.fast.settings.navigation.navigateToSettings
|
||||||
import dev.meloda.fast.settings.navigation.settingsScreen
|
import dev.meloda.fast.settings.navigation.settingsScreen
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
|
import dev.meloda.fast.ui.common.LocalLogger
|
||||||
import dev.meloda.fast.ui.common.LocalSizeConfig
|
import dev.meloda.fast.ui.common.LocalSizeConfig
|
||||||
import dev.meloda.fast.ui.model.DeviceSize
|
import dev.meloda.fast.ui.model.DeviceSize
|
||||||
import dev.meloda.fast.ui.model.SizeConfig
|
import dev.meloda.fast.ui.model.SizeConfig
|
||||||
@@ -83,6 +83,7 @@ fun RootScreen(
|
|||||||
toggleLongPollService: (enable: Boolean, inBackground: Boolean?) -> Unit,
|
toggleLongPollService: (enable: Boolean, inBackground: Boolean?) -> Unit,
|
||||||
toggleOnlineService: (enable: Boolean) -> Unit
|
toggleOnlineService: (enable: Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val logger = LocalLogger.current
|
||||||
val resources = LocalResources.current
|
val resources = LocalResources.current
|
||||||
|
|
||||||
val userSettings: UserSettings = koinInject()
|
val userSettings: UserSettings = koinInject()
|
||||||
@@ -92,10 +93,6 @@ fun RootScreen(
|
|||||||
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
val longPollStateToApply by longPollController.stateToApply.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
val viewModel: MainViewModel = koinViewModel<MainViewModelImpl>()
|
||||||
LaunchedEffect(viewModel) {
|
|
||||||
Log.d("VM_CREATE", "RootScreen(): viewModel: $viewModel")
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
val currentUser: VkUser? by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val permissionState =
|
val permissionState =
|
||||||
@@ -126,13 +123,12 @@ fun RootScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LifecycleResumeEffect(longPollStateToApply) {
|
LifecycleResumeEffect(longPollStateToApply) {
|
||||||
Log.d("LongPollMainActivity", "longPollStateToApply: $longPollStateToApply")
|
logger.debug("RootScreen", "longPollStateToApply: $longPollStateToApply")
|
||||||
if (longPollStateToApply != LongPollState.Background) {
|
if (longPollStateToApply != LongPollState.Background) {
|
||||||
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
if (longPollStateToApply.isLaunched() && longPollCurrentState.isLaunched()
|
||||||
&& longPollCurrentState != longPollStateToApply
|
&& longPollCurrentState != longPollStateToApply
|
||||||
) {
|
) {
|
||||||
toggleLongPollService(false, null)
|
toggleLongPollService(false, null)
|
||||||
Log.d("LongPoll", "recreate()")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLongPollService(
|
toggleLongPollService(
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import android.os.IBinder
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import dev.meloda.fast.common.extensions.createTimerFlow
|
import dev.meloda.fast.common.extensions.createTimerFlow
|
||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.domain.AccountUseCase
|
|
||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
|
import dev.meloda.fast.domain.AccountUseCase
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -24,11 +25,12 @@ import kotlin.time.Duration.Companion.minutes
|
|||||||
|
|
||||||
class OnlineService : Service() {
|
class OnlineService : Service() {
|
||||||
|
|
||||||
|
private val logger: FastLogger by inject()
|
||||||
|
|
||||||
private val job = SupervisorJob()
|
private val job = SupervisorJob()
|
||||||
|
|
||||||
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||||
Log.d(TAG, "error: $throwable")
|
logger.error(this::class.java, "CoroutineException", throwable)
|
||||||
throwable.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val coroutineContext: CoroutineContext
|
private val coroutineContext: CoroutineContext
|
||||||
@@ -42,17 +44,20 @@ class OnlineService : Service() {
|
|||||||
private var onlineJob: Job? = null
|
private var onlineJob: Job? = null
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
Log.d(STATE_TAG, "onBind: intent: $intent")
|
logger.debug(this::class, "STATE: onBind(): intent: $intent")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (startId > 1) return START_STICKY
|
if (startId > 1) return START_STICKY
|
||||||
|
|
||||||
Log.d(STATE_TAG, "onStartCommand: flags: $flags; startId: $startId\ninstance: $this")
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"STATE: onStartCommand(): flags: %s; startId: %s;\ninstance: %s"
|
||||||
|
.format("$flags", "$startId", "$this")
|
||||||
|
)
|
||||||
|
|
||||||
createTimer()
|
createTimer()
|
||||||
|
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,13 +73,13 @@ class OnlineService : Service() {
|
|||||||
private fun setOnline() {
|
private fun setOnline() {
|
||||||
if (onlineJob != null) return
|
if (onlineJob != null) return
|
||||||
|
|
||||||
Log.d(TAG, "setOnline()")
|
logger.debug(this::class, "setOnline()")
|
||||||
|
|
||||||
onlineJob = coroutineScope.launch {
|
onlineJob = coroutineScope.launch {
|
||||||
val token = UserConfig.fastToken ?: UserConfig.accessToken
|
val token = UserConfig.fastToken ?: UserConfig.accessToken
|
||||||
|
|
||||||
if (token.isBlank()) {
|
if (token.isBlank()) {
|
||||||
Log.d(TAG, "setOnline: token is empty")
|
logger.debug(this::class, "setOnline(): token is empty")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +89,10 @@ class OnlineService : Service() {
|
|||||||
).onEach { state ->
|
).onEach { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.w(TAG, "setOnline(): error: $error")
|
logger.error(this@OnlineService::class, "setOnline(): ERROR: $error")
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
Log.d(TAG, "setOnline(): success: $response")
|
logger.debug(this@OnlineService::class, "setOnline(): response: $response")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}.collect()
|
}.collect()
|
||||||
@@ -96,7 +101,7 @@ class OnlineService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(STATE_TAG, "onDestroy")
|
logger.debug(this::class, "onDestroy()")
|
||||||
|
|
||||||
timerJob?.cancel("OnlineService destroyed")
|
timerJob?.cancel("OnlineService destroyed")
|
||||||
onlineJob?.cancel("OnlineService destroyed")
|
onlineJob?.cancel("OnlineService destroyed")
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
import com.conena.nanokt.android.app.stopForegroundCompat
|
import com.conena.nanokt.android.app.stopForegroundCompat
|
||||||
@@ -19,8 +18,10 @@ import dev.meloda.fast.common.model.LongPollState
|
|||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
|
import dev.meloda.fast.domain.LongPollEventsHandler
|
||||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||||
import dev.meloda.fast.domain.LongPollUseCase
|
import dev.meloda.fast.domain.LongPollUseCase
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.model.api.data.LongPollUpdates
|
import dev.meloda.fast.model.api.data.LongPollUpdates
|
||||||
import dev.meloda.fast.model.api.data.VkLongPollData
|
import dev.meloda.fast.model.api.data.VkLongPollData
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
@@ -40,6 +41,8 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
|
|
||||||
class LongPollingService : Service() {
|
class LongPollingService : Service() {
|
||||||
|
|
||||||
|
private val logger: FastLogger by inject()
|
||||||
|
|
||||||
private val longPollController: LongPollController by inject()
|
private val longPollController: LongPollController by inject()
|
||||||
|
|
||||||
private val job = SupervisorJob()
|
private val job = SupervisorJob()
|
||||||
@@ -56,6 +59,7 @@ class LongPollingService : Service() {
|
|||||||
|
|
||||||
private val longPollUseCase: LongPollUseCase by inject()
|
private val longPollUseCase: LongPollUseCase by inject()
|
||||||
private val updatesParser: LongPollUpdatesParser by inject()
|
private val updatesParser: LongPollUpdatesParser by inject()
|
||||||
|
private val eventsHandler: LongPollEventsHandler by inject()
|
||||||
|
|
||||||
private var currentJob: Job? = null
|
private var currentJob: Job? = null
|
||||||
|
|
||||||
@@ -63,20 +67,21 @@ class LongPollingService : Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
Log.d(STATE_TAG, "onCreate()")
|
logger.debug(this::class, "STATE: onCreate()")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
Log.d(STATE_TAG, "onBind: intent: $intent")
|
logger.debug(this::class, "STATE: onBind(): intent: $intent")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (startId > 1) return START_STICKY
|
if (startId > 1) return START_STICKY
|
||||||
|
|
||||||
Log.d(
|
logger.debug(
|
||||||
STATE_TAG,
|
this::class,
|
||||||
"onStartCommand: asForeground: $inBackground; flags: $flags; startId: $startId;\ninstance: $this"
|
"STATE: onStartCommand(): asForeground: %s; flags: %s; startId: %s;\ninstance: %s"
|
||||||
|
.format("$inBackground", "$flags", "$startId", "$this")
|
||||||
)
|
)
|
||||||
|
|
||||||
startJob()
|
startJob()
|
||||||
@@ -131,11 +136,15 @@ class LongPollingService : Service() {
|
|||||||
|
|
||||||
private fun startPolling(): Job {
|
private fun startPolling(): Job {
|
||||||
if (job.isCompleted || job.isCancelled) {
|
if (job.isCompleted || job.isCancelled) {
|
||||||
Log.d(STATE_TAG, "Job is completed or cancelled")
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"startPolling(): Job is already done. isCompleted: %s; isCancelled: %s"
|
||||||
|
.format("${job.isCompleted}", "${job.isCancelled}")
|
||||||
|
)
|
||||||
throw Exception("Job is over")
|
throw Exception("Job is over")
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(STATE_TAG, "Starting job...")
|
logger.debug(this::class, "startPolling(): Starting job.")
|
||||||
|
|
||||||
return coroutineScope.launch(coroutineContext) {
|
return coroutineScope.launch(coroutineContext) {
|
||||||
longPollController.updateCurrentState(
|
longPollController.updateCurrentState(
|
||||||
@@ -193,7 +202,7 @@ class LongPollingService : Service() {
|
|||||||
if (updates == null) {
|
if (updates == null) {
|
||||||
failCount++
|
failCount++
|
||||||
} else {
|
} else {
|
||||||
updates.forEach(updatesParser::parseNextUpdate)
|
parseUpdates(updates)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUpdatesResponse = getUpdatesResponse(serverInfo.copy(ts = newTs))
|
lastUpdatesResponse = getUpdatesResponse(serverInfo.copy(ts = newTs))
|
||||||
@@ -211,11 +220,11 @@ class LongPollingService : Service() {
|
|||||||
).listenValue(coroutineScope) { state ->
|
).listenValue(coroutineScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
success = { response ->
|
success = { response ->
|
||||||
Log.d(TAG, "getServerInfo: serverInfoResponse: $response")
|
logger.debug(this::class, "getServerInfo(): response: $response")
|
||||||
it.resume(response)
|
it.resume(response)
|
||||||
},
|
},
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.e(TAG, "getServerInfo: $error")
|
logger.error(this::class, "getServerInfo(): ERROR: $error")
|
||||||
it.resume(null)
|
it.resume(null)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -235,19 +244,24 @@ class LongPollingService : Service() {
|
|||||||
).listenValue(coroutineScope) { state ->
|
).listenValue(coroutineScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
success = { response ->
|
success = { response ->
|
||||||
Log.d(TAG, "lastUpdateResponse: $response")
|
logger.debug(this::class, "getUpdatesResponse(): response: $response")
|
||||||
it.resume(response)
|
it.resume(response)
|
||||||
},
|
},
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.d(TAG, "getUpdatesResponse: error: $error")
|
logger.debug(this::class, "getUpdatesResponse(): error: $error")
|
||||||
it.resume(null)
|
it.resume(null)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun parseUpdates(updates: List<List<Any>>) {
|
||||||
|
val parsedUpdates = updates.flatMap { updatesParser.parseNextUpdate(it) }
|
||||||
|
eventsHandler.handleEvents(parsedUpdates)
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleError(throwable: Throwable) {
|
private fun handleError(throwable: Throwable) {
|
||||||
Log.e(TAG, "error: $throwable")
|
logger.error(this::class, "CoroutineException", throwable)
|
||||||
|
|
||||||
if (throwable !is NoAccessTokenException) {
|
if (throwable !is NoAccessTokenException) {
|
||||||
throwable.printStackTrace()
|
throwable.printStackTrace()
|
||||||
@@ -262,7 +276,7 @@ class LongPollingService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(STATE_TAG, "onDestroy")
|
logger.debug(this::class, "STATE: onDestroy()")
|
||||||
longPollController.updateCurrentState(LongPollState.Stopped)
|
longPollController.updateCurrentState(LongPollState.Stopped)
|
||||||
try {
|
try {
|
||||||
AppSettings.edit { putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true) }
|
AppSettings.edit { putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true) }
|
||||||
@@ -274,7 +288,7 @@ class LongPollingService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onTrimMemory(level: Int) {
|
override fun onTrimMemory(level: Int) {
|
||||||
Log.d(STATE_TAG, "onTrimMemory. Level: $level")
|
logger.debug(this::class, "STATE: onTrimMemory(): Level: $level")
|
||||||
super.onTrimMemory(level)
|
super.onTrimMemory(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.meloda.fast.service.longpolling.di
|
package dev.meloda.fast.service.longpolling.di
|
||||||
|
|
||||||
|
import dev.meloda.fast.domain.LongPollEventsHandler
|
||||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||||
import dev.meloda.fast.domain.LongPollUseCase
|
import dev.meloda.fast.domain.LongPollUseCase
|
||||||
import dev.meloda.fast.domain.LongPollUseCaseImpl
|
import dev.meloda.fast.domain.LongPollUseCaseImpl
|
||||||
@@ -10,4 +11,5 @@ import org.koin.dsl.module
|
|||||||
val longPollModule = module {
|
val longPollModule = module {
|
||||||
singleOf(::LongPollUseCaseImpl) bind LongPollUseCase::class
|
singleOf(::LongPollUseCaseImpl) bind LongPollUseCase::class
|
||||||
singleOf(::LongPollUpdatesParser)
|
singleOf(::LongPollUpdatesParser)
|
||||||
|
singleOf(::LongPollEventsHandler)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,13 +1,13 @@
|
|||||||
package dev.meloda.fast.common.model
|
package dev.meloda.fast.common.model
|
||||||
|
|
||||||
enum class LogLevel(val value: Int) {
|
enum class NetworkLogLevel(val value: Int) {
|
||||||
NONE(0),
|
NONE(0),
|
||||||
BASIC(1),
|
BASIC(1),
|
||||||
HEADERS(2),
|
HEADERS(2),
|
||||||
BODY(3);
|
BODY(3);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(value: Int): LogLevel = entries.firstOrNull { it.value == value }
|
fun parse(value: Int): NetworkLogLevel = entries.firstOrNull { it.value == value }
|
||||||
?: throw IllegalArgumentException("Unknown log level with value: $value")
|
?: throw IllegalArgumentException("Unknown log level with value: $value")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,425 @@
|
|||||||
|
{
|
||||||
|
"formatVersion": 1,
|
||||||
|
"database": {
|
||||||
|
"version": 12,
|
||||||
|
"identityHash": "5eca3b3da167aaf7e772977a1f4e56e2",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"tableName": "users",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `isOnline` INTEGER NOT NULL, `isOnlineMobile` INTEGER NOT NULL, `onlineAppId` INTEGER, `lastSeen` INTEGER, `lastSeenStatus` TEXT, `birthday` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `photo400Orig` TEXT, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "firstName",
|
||||||
|
"columnName": "firstName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastName",
|
||||||
|
"columnName": "lastName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isOnline",
|
||||||
|
"columnName": "isOnline",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isOnlineMobile",
|
||||||
|
"columnName": "isOnlineMobile",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "onlineAppId",
|
||||||
|
"columnName": "onlineAppId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastSeen",
|
||||||
|
"columnName": "lastSeen",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastSeenStatus",
|
||||||
|
"columnName": "lastSeenStatus",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "birthday",
|
||||||
|
"columnName": "birthday",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo50",
|
||||||
|
"columnName": "photo50",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo100",
|
||||||
|
"columnName": "photo100",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo200",
|
||||||
|
"columnName": "photo200",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo400Orig",
|
||||||
|
"columnName": "photo400Orig",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "groups",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `screenName` TEXT NOT NULL, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `membersCount` INTEGER, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "name",
|
||||||
|
"columnName": "name",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "screenName",
|
||||||
|
"columnName": "screenName",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo50",
|
||||||
|
"columnName": "photo50",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo100",
|
||||||
|
"columnName": "photo100",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo200",
|
||||||
|
"columnName": "photo200",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "membersCount",
|
||||||
|
"columnName": "membersCount",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "messages",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `cmId` INTEGER NOT NULL, `text` TEXT, `isOut` INTEGER NOT NULL, `peerId` INTEGER NOT NULL, `fromId` INTEGER NOT NULL, `date` INTEGER NOT NULL, `randomId` INTEGER NOT NULL, `action` TEXT, `actionMemberId` INTEGER, `actionText` TEXT, `actionCmId` INTEGER, `actionMessage` TEXT, `updateTime` INTEGER, `isImportant` INTEGER NOT NULL, `forwardIds` TEXT, `attachments` TEXT, `replyMessageId` INTEGER, `geoType` TEXT, `pinnedAt` INTEGER, `isPinned` INTEGER NOT NULL, `isDeleted` INTEGER NOT NULL, `isSpam` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "cmId",
|
||||||
|
"columnName": "cmId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "text",
|
||||||
|
"columnName": "text",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isOut",
|
||||||
|
"columnName": "isOut",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "peerId",
|
||||||
|
"columnName": "peerId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "fromId",
|
||||||
|
"columnName": "fromId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "date",
|
||||||
|
"columnName": "date",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "randomId",
|
||||||
|
"columnName": "randomId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "action",
|
||||||
|
"columnName": "action",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actionMemberId",
|
||||||
|
"columnName": "actionMemberId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actionText",
|
||||||
|
"columnName": "actionText",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actionCmId",
|
||||||
|
"columnName": "actionCmId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "actionMessage",
|
||||||
|
"columnName": "actionMessage",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "updateTime",
|
||||||
|
"columnName": "updateTime",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isImportant",
|
||||||
|
"columnName": "isImportant",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "forwardIds",
|
||||||
|
"columnName": "forwardIds",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "attachments",
|
||||||
|
"columnName": "attachments",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "replyMessageId",
|
||||||
|
"columnName": "replyMessageId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "geoType",
|
||||||
|
"columnName": "geoType",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pinnedAt",
|
||||||
|
"columnName": "pinnedAt",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isPinned",
|
||||||
|
"columnName": "isPinned",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isDeleted",
|
||||||
|
"columnName": "isDeleted",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isSpam",
|
||||||
|
"columnName": "isSpam",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tableName": "convos",
|
||||||
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localId` INTEGER NOT NULL, `ownerId` INTEGER, `title` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `isPhantom` INTEGER NOT NULL, `lastCmId` INTEGER NOT NULL, `inReadCmId` INTEGER NOT NULL, `outReadCmId` INTEGER NOT NULL, `inRead` INTEGER NOT NULL, `outRead` INTEGER NOT NULL, `lastMessageId` INTEGER, `unreadCount` INTEGER NOT NULL, `membersCount` INTEGER, `canChangePin` INTEGER NOT NULL, `canChangeInfo` INTEGER NOT NULL, `majorId` INTEGER NOT NULL, `minorId` INTEGER NOT NULL, `pinnedMessageId` INTEGER, `peerType` TEXT NOT NULL, `isArchived` INTEGER NOT NULL, PRIMARY KEY(`id`))",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldPath": "id",
|
||||||
|
"columnName": "id",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "localId",
|
||||||
|
"columnName": "localId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "ownerId",
|
||||||
|
"columnName": "ownerId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "title",
|
||||||
|
"columnName": "title",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo50",
|
||||||
|
"columnName": "photo50",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo100",
|
||||||
|
"columnName": "photo100",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "photo200",
|
||||||
|
"columnName": "photo200",
|
||||||
|
"affinity": "TEXT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isPhantom",
|
||||||
|
"columnName": "isPhantom",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastCmId",
|
||||||
|
"columnName": "lastCmId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inReadCmId",
|
||||||
|
"columnName": "inReadCmId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "outReadCmId",
|
||||||
|
"columnName": "outReadCmId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "inRead",
|
||||||
|
"columnName": "inRead",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "outRead",
|
||||||
|
"columnName": "outRead",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "lastMessageId",
|
||||||
|
"columnName": "lastMessageId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "unreadCount",
|
||||||
|
"columnName": "unreadCount",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "membersCount",
|
||||||
|
"columnName": "membersCount",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "canChangePin",
|
||||||
|
"columnName": "canChangePin",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "canChangeInfo",
|
||||||
|
"columnName": "canChangeInfo",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "majorId",
|
||||||
|
"columnName": "majorId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "minorId",
|
||||||
|
"columnName": "minorId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "pinnedMessageId",
|
||||||
|
"columnName": "pinnedMessageId",
|
||||||
|
"affinity": "INTEGER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "peerType",
|
||||||
|
"columnName": "peerType",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "isArchived",
|
||||||
|
"columnName": "isArchived",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primaryKey": {
|
||||||
|
"autoGenerate": false,
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"setupQueries": [
|
||||||
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5eca3b3da167aaf7e772977a1f4e56e2')"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ import dev.meloda.fast.model.database.VkUserEntity
|
|||||||
VkConvoEntity::class
|
VkConvoEntity::class
|
||||||
],
|
],
|
||||||
|
|
||||||
version = 11
|
version = 12
|
||||||
)
|
)
|
||||||
@TypeConverters(Converters::class)
|
@TypeConverters(Converters::class)
|
||||||
abstract class CacheDatabase : RoomDatabase() {
|
abstract class CacheDatabase : RoomDatabase() {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ abstract class ConvoDao : EntityDao<VkConvoEntity> {
|
|||||||
abstract suspend fun getAll(): List<VkConvoEntity>
|
abstract suspend fun getAll(): List<VkConvoEntity>
|
||||||
|
|
||||||
@Query("SELECT * FROM convos WHERE id IN (:ids)")
|
@Query("SELECT * FROM convos WHERE id IN (:ids)")
|
||||||
abstract suspend fun getAllByIds(ids: List<Int>): List<VkConvoEntity>
|
abstract suspend fun getAllByIds(ids: List<Long>): List<VkConvoEntity>
|
||||||
|
|
||||||
@Query("SELECT * FROM convos WHERE id IS (:id)")
|
@Query("SELECT * FROM convos WHERE id IS (:id)")
|
||||||
abstract suspend fun getById(id: Long): VkConvoEntity?
|
abstract suspend fun getById(id: Long): VkConvoEntity?
|
||||||
@@ -23,8 +23,23 @@ abstract class ConvoDao : EntityDao<VkConvoEntity> {
|
|||||||
abstract suspend fun getByIdWithMessage(id: Long): ConvoWithMessage?
|
abstract suspend fun getByIdWithMessage(id: Long): ConvoWithMessage?
|
||||||
|
|
||||||
@Query("DELETE FROM convos WHERE rowid IN (:ids)")
|
@Query("DELETE FROM convos WHERE rowid IN (:ids)")
|
||||||
abstract suspend fun deleteByIds(ids: List<Int>): Int
|
abstract suspend fun deleteByIds(ids: List<Long>): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET inReadCmId = :cmId, unreadCount = :unreadCount WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateReadIncoming(convoId: Long, cmId: Long, unreadCount: Int): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET outReadCmId = :cmId, unreadCount = :unreadCount WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateReadOutgoing(convoId: Long, cmId: Long, unreadCount: Int): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET isArchived = :isArchived WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateIsArchived(convoId: Long, isArchived: Boolean): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET majorId = :majorId WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateMajorId(convoId: Long, majorId: Int): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET minorId = :minorId WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateMinorId(convoId: Long, minorId: Int): Int
|
||||||
|
|
||||||
|
@Query("UPDATE convos SET lastCmId = :cmId WHERE id = :convoId")
|
||||||
|
abstract suspend fun updateLastCmId(convoId: Long, cmId: Long): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import dev.meloda.fast.model.database.VkMessageEntity
|
|||||||
@Dao
|
@Dao
|
||||||
abstract class MessageDao : EntityDao<VkMessageEntity> {
|
abstract class MessageDao : EntityDao<VkMessageEntity> {
|
||||||
|
|
||||||
@Query("SELECT * FROM messages")
|
@Query("SELECT * FROM messages WHERE isDeleted = 0 AND isSpam = 0")
|
||||||
abstract suspend fun getAll(): List<VkMessageEntity>
|
abstract suspend fun getAll(): List<VkMessageEntity>
|
||||||
|
|
||||||
@Query("SELECT * FROM messages WHERE peerId IS (:convoId)")
|
@Query("SELECT * FROM messages WHERE peerId IS (:convoId)")
|
||||||
@@ -21,4 +21,13 @@ abstract class MessageDao : EntityDao<VkMessageEntity> {
|
|||||||
|
|
||||||
@Query("DELETE FROM messages WHERE id IN (:ids)")
|
@Query("DELETE FROM messages WHERE id IN (:ids)")
|
||||||
abstract suspend fun deleteByIds(ids: List<Int>): Int
|
abstract suspend fun deleteByIds(ids: List<Int>): Int
|
||||||
|
|
||||||
|
@Query("UPDATE messages SET isDeleted = :isDeleted WHERE peerId = :convoId AND cmId = :cmId")
|
||||||
|
abstract suspend fun markAsDeleted(convoId: Long, cmId: Long, isDeleted: Boolean): Int
|
||||||
|
|
||||||
|
@Query("UPDATE messages SET isImportant = :isImportant WHERE peerId = :convoId AND cmId = :cmId")
|
||||||
|
abstract suspend fun markAsImportant(convoId: Long, cmId: Long, isImportant: Boolean): Int
|
||||||
|
|
||||||
|
@Query("UPDATE messages SET isSpam = :isSpam WHERE peerId = :convoId AND cmId = :cmId")
|
||||||
|
abstract suspend fun markAsSpam(convoId: Long, cmId: Long, isSpam: Boolean): Int
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package dev.meloda.fast.datastore
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import dev.meloda.fast.common.model.DarkMode
|
import dev.meloda.fast.common.model.DarkMode
|
||||||
import dev.meloda.fast.common.model.LogLevel
|
import dev.meloda.fast.common.model.NetworkLogLevel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -238,11 +238,11 @@ object AppSettings {
|
|||||||
)
|
)
|
||||||
set(value) = put(SettingsKeys.KEY_DEBUG_SHOW_CRASH_ALERT, value)
|
set(value) = put(SettingsKeys.KEY_DEBUG_SHOW_CRASH_ALERT, value)
|
||||||
|
|
||||||
var networkLogLevel: LogLevel
|
var networkLogLevel: NetworkLogLevel
|
||||||
get() = get(
|
get() = get(
|
||||||
SettingsKeys.KEY_DEBUG_NETWORK_LOG_LEVEL,
|
SettingsKeys.KEY_DEBUG_NETWORK_LOG_LEVEL,
|
||||||
SettingsKeys.DEFAULT_NETWORK_LOG_LEVEL
|
SettingsKeys.DEFAULT_NETWORK_LOG_LEVEL
|
||||||
).let(LogLevel::parse)
|
).let(NetworkLogLevel::parse)
|
||||||
set(level) = put(SettingsKeys.KEY_DEBUG_NETWORK_LOG_LEVEL, level.value)
|
set(level) = put(SettingsKeys.KEY_DEBUG_NETWORK_LOG_LEVEL, level.value)
|
||||||
|
|
||||||
var showDebugCategory: Boolean
|
var showDebugCategory: Boolean
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package dev.meloda.fast.domain
|
||||||
|
|
||||||
|
import dev.meloda.fast.database.dao.ConvoDao
|
||||||
|
import dev.meloda.fast.database.dao.MessageDao
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
|
import dev.meloda.fast.model.LongPollParsedEvent
|
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class LongPollEventsHandler(
|
||||||
|
private val logger: FastLogger,
|
||||||
|
private val convoUseCase: ConvoUseCase,
|
||||||
|
private val messagesUseCase: MessagesUseCase,
|
||||||
|
private val convoDao: ConvoDao,
|
||||||
|
private val messageDao: MessageDao,
|
||||||
|
) {
|
||||||
|
private val job = SupervisorJob()
|
||||||
|
|
||||||
|
private val exceptionHandler =
|
||||||
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
|
logger.error(this::class, "CoroutineException", throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val coroutineContext: CoroutineContext
|
||||||
|
get() = Dispatchers.Default + job + exceptionHandler
|
||||||
|
|
||||||
|
private val coroutineScope = CoroutineScope(coroutineContext)
|
||||||
|
|
||||||
|
suspend fun handleEvents(events: List<LongPollParsedEvent>) {
|
||||||
|
events.forEach { handleNextEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleNextEvent(event: LongPollParsedEvent) {
|
||||||
|
when (event) {
|
||||||
|
is LongPollParsedEvent.AudioMessageListened -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.ChatArchived -> {
|
||||||
|
val affectedRows = convoDao.updateIsArchived(
|
||||||
|
convoId = event.convo.id,
|
||||||
|
isArchived = event.convo.isArchived
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"isArchived ${event.convo.isArchived}: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.ChatCleared -> {
|
||||||
|
val affectedRows = convoDao.updateLastCmId(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.toCmId
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"updateLastCmId: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.ChatMajorChanged -> {
|
||||||
|
val affectedRows = convoDao.updateMajorId(
|
||||||
|
convoId = event.peerId,
|
||||||
|
majorId = event.majorId
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"updateMajorId: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.ChatMinorChanged -> {
|
||||||
|
val affectedRows = convoDao.updateMinorId(
|
||||||
|
convoId = event.peerId,
|
||||||
|
minorId = event.minorId
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"updateMinorId: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.Interaction -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageCacheClear -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageDeleted -> {
|
||||||
|
val affectedRows = messageDao.markAsDeleted(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.cmId,
|
||||||
|
isDeleted = true
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"markDeleted: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageEdited -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageMarkedAsImportant -> {
|
||||||
|
val affectedRows = messageDao.markAsImportant(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.cmId,
|
||||||
|
isImportant = event.marked
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"markImportant: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageMarkedAsNotSpam -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageMarkedAsSpam -> {
|
||||||
|
val affectedRows = messageDao.markAsSpam(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.cmId,
|
||||||
|
isSpam = true
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"markSpam: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageRestored -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.MessageUpdated -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.NewMessage -> {
|
||||||
|
messagesUseCase.storeMessage(event.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.IncomingMessageRead -> {
|
||||||
|
val affectedRows = convoDao.updateReadIncoming(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.cmId,
|
||||||
|
unreadCount = event.unreadCount
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"inMessageRead: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.OutgoingMessageRead -> {
|
||||||
|
val affectedRows = convoDao.updateReadOutgoing(
|
||||||
|
convoId = event.peerId,
|
||||||
|
cmId = event.cmId,
|
||||||
|
unreadCount = event.unreadCount
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
this::class,
|
||||||
|
"outMessageRead: updated $affectedRows rows."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is LongPollParsedEvent.UnreadCounter -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package dev.meloda.fast.domain
|
package dev.meloda.fast.domain
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import dev.meloda.fast.common.VkConstants
|
import dev.meloda.fast.common.VkConstants
|
||||||
import dev.meloda.fast.common.extensions.asInt
|
import dev.meloda.fast.common.extensions.asInt
|
||||||
import dev.meloda.fast.common.extensions.asLong
|
import dev.meloda.fast.common.extensions.asLong
|
||||||
@@ -8,6 +7,7 @@ import dev.meloda.fast.common.extensions.listenValue
|
|||||||
import dev.meloda.fast.common.extensions.toList
|
import dev.meloda.fast.common.extensions.toList
|
||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.model.ApiEvent
|
import dev.meloda.fast.model.ApiEvent
|
||||||
import dev.meloda.fast.model.ConvoFlags
|
import dev.meloda.fast.model.ConvoFlags
|
||||||
import dev.meloda.fast.model.InteractionType
|
import dev.meloda.fast.model.InteractionType
|
||||||
@@ -22,12 +22,12 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
class LongPollUpdatesParser(
|
class LongPollUpdatesParser(
|
||||||
|
private val logger: FastLogger,
|
||||||
private val convoUseCase: ConvoUseCase,
|
private val convoUseCase: ConvoUseCase,
|
||||||
private val messagesUseCase: MessagesUseCase
|
private val messagesUseCase: MessagesUseCase
|
||||||
) {
|
) {
|
||||||
@@ -35,8 +35,7 @@ class LongPollUpdatesParser(
|
|||||||
|
|
||||||
private val exceptionHandler =
|
private val exceptionHandler =
|
||||||
CoroutineExceptionHandler { _, throwable ->
|
CoroutineExceptionHandler { _, throwable ->
|
||||||
Log.e("LongPollUpdatesParser", "error: $throwable")
|
logger.error(this::class, "CoroutineException", throwable)
|
||||||
throwable.printStackTrace()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val coroutineContext: CoroutineContext
|
private val coroutineContext: CoroutineContext
|
||||||
@@ -47,11 +46,14 @@ class LongPollUpdatesParser(
|
|||||||
private val listenersMap: MutableMap<LongPollEvent, MutableList<VkEventCallback<LongPollParsedEvent>>> =
|
private val listenersMap: MutableMap<LongPollEvent, MutableList<VkEventCallback<LongPollParsedEvent>>> =
|
||||||
mutableMapOf()
|
mutableMapOf()
|
||||||
|
|
||||||
fun parseNextUpdate(event: List<Any>) {
|
suspend fun parseNextUpdate(event: List<Any>): List<LongPollParsedEvent> {
|
||||||
val eventId = event.first().asInt()
|
val eventId = event.first().asInt()
|
||||||
|
|
||||||
when (val eventType = ApiEvent.parseOrNull(eventId)) {
|
return when (val eventType = ApiEvent.parseOrNull(eventId)) {
|
||||||
null -> Log.d("LongPollUpdatesParser", "parseNextUpdate: unknownEvent: $event")
|
null -> {
|
||||||
|
logger.debug(this::class, "parseNextUpdate(): unknownEvent: $event")
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
ApiEvent.MESSAGE_SET_FLAGS -> parseMessageSetFlags(eventType, event)
|
ApiEvent.MESSAGE_SET_FLAGS -> parseMessageSetFlags(eventType, event)
|
||||||
ApiEvent.MESSAGE_CLEAR_FLAGS -> parseMessageClearFlags(eventType, event)
|
ApiEvent.MESSAGE_CLEAR_FLAGS -> parseMessageClearFlags(eventType, event)
|
||||||
@@ -77,8 +79,11 @@ class LongPollUpdatesParser(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageSetFlags(eventType: ApiEvent, event: List<Any>) {
|
private fun parseMessageSetFlags(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseMessageSetFlags(): $eventType: $event")
|
||||||
|
|
||||||
val cmId = event[1].asLong()
|
val cmId = event[1].asLong()
|
||||||
val flags = event[2].asInt()
|
val flags = event[2].asInt()
|
||||||
@@ -97,12 +102,8 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]?.let { listeners ->
|
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]
|
||||||
listeners.map { vkEventCallback ->
|
?.forEach { it.onEvent(eventToSend) }
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsImportant>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFlags.SPAM -> {
|
MessageFlags.SPAM -> {
|
||||||
@@ -111,13 +112,7 @@ class LongPollUpdatesParser(
|
|||||||
cmId = cmId
|
cmId = cmId
|
||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
listenersMap[LongPollEvent.MARKED_AS_SPAM]?.forEach { it.onEvent(eventToSend) }
|
||||||
listenersMap[LongPollEvent.MARKED_AS_SPAM]?.let { listeners ->
|
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsSpam>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFlags.DELETED -> {
|
MessageFlags.DELETED -> {
|
||||||
@@ -136,13 +131,7 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
listenersMap[LongPollEvent.MESSAGE_DELETED]?.forEach { it.onEvent(eventToSend) }
|
||||||
listenersMap[LongPollEvent.MESSAGE_DELETED]?.let { listeners ->
|
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageDeleted>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFlags.AUDIO_LISTENED -> {
|
MessageFlags.AUDIO_LISTENED -> {
|
||||||
@@ -152,29 +141,37 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.AUDIO_MESSAGE_LISTENED]?.let { listeners ->
|
listenersMap[LongPollEvent.AUDIO_MESSAGE_LISTENED]
|
||||||
listeners.map { vkEventCallback ->
|
?.forEach { it.onEvent(eventToSend) }
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.AudioMessageListened>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Unit
|
MessageFlags.UNREAD -> Unit
|
||||||
|
MessageFlags.OUTGOING -> Unit
|
||||||
|
MessageFlags.FROM_GROUP_CHAT -> Unit
|
||||||
|
MessageFlags.CANCEL_SPAM -> Unit
|
||||||
|
MessageFlags.DELETED_FOR_ALL -> Unit
|
||||||
|
MessageFlags.DO_NOT_SHOW_NOTIFICATION -> Unit
|
||||||
|
MessageFlags.MESSAGE_WITH_REPLY -> Unit
|
||||||
|
MessageFlags.REACTION -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToSend.forEach { eventToSend ->
|
eventsToSend.forEach { eventToSend ->
|
||||||
listenersMap[LongPollEvent.MESSAGE_SET_FLAGS]?.let { listeners ->
|
listenersMap[LongPollEvent.MESSAGE_SET_FLAGS]?.let { listeners ->
|
||||||
listeners.map { vkEventCallback ->
|
listeners.forEach { vkEventCallback ->
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent>)?.onEvent(eventToSend)
|
vkEventCallback.onEvent(eventToSend)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageClearFlags(eventType: ApiEvent, event: List<Any>) {
|
return eventsToSend
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
}
|
||||||
|
|
||||||
|
private suspend fun parseMessageClearFlags(
|
||||||
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseMessageClearFlags(): $eventType: $event")
|
||||||
|
|
||||||
val cmId = event[1].asLong()
|
val cmId = event[1].asLong()
|
||||||
val flags = event[2].asInt()
|
val flags = event[2].asInt()
|
||||||
@@ -184,7 +181,9 @@ class LongPollUpdatesParser(
|
|||||||
|
|
||||||
val parsedFlags = MessageFlags.parse(flags)
|
val parsedFlags = MessageFlags.parse(flags)
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
val message = loadMessage(peerId = peerId, cmId = cmId)
|
||||||
|
|
||||||
parsedFlags.forEach { flag ->
|
parsedFlags.forEach { flag ->
|
||||||
when (flag) {
|
when (flag) {
|
||||||
MessageFlags.IMPORTANT -> {
|
MessageFlags.IMPORTANT -> {
|
||||||
@@ -195,81 +194,65 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]?.let { listeners ->
|
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]
|
||||||
listeners.map { vkEventCallback ->
|
?.forEach { it.onEvent(eventToSend) }
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsImportant>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFlags.SPAM -> {
|
MessageFlags.SPAM -> {
|
||||||
if (parsedFlags.contains(MessageFlags.CANCEL_SPAM)) {
|
if (parsedFlags.contains(MessageFlags.CANCEL_SPAM)) {
|
||||||
withContext(Dispatchers.IO) {
|
if (message != null) {
|
||||||
val message = loadMessage(
|
|
||||||
peerId = peerId,
|
|
||||||
cmId = cmId
|
|
||||||
)
|
|
||||||
message?.let {
|
|
||||||
val eventToSend =
|
val eventToSend =
|
||||||
LongPollParsedEvent.MessageMarkedAsNotSpam(message = message)
|
LongPollParsedEvent.MessageMarkedAsNotSpam(message = message)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.MARKED_AS_NOT_SPAM]?.let { listeners ->
|
listenersMap[LongPollEvent.MARKED_AS_NOT_SPAM]
|
||||||
listeners.map { vkEventCallback ->
|
?.forEach { it.onEvent(eventToSend) }
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsNotSpam>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageFlags.DELETED -> {
|
MessageFlags.DELETED -> {
|
||||||
withContext(Dispatchers.IO) {
|
if (message != null) {
|
||||||
val message = loadMessage(
|
|
||||||
peerId = peerId,
|
|
||||||
cmId = cmId
|
|
||||||
)
|
|
||||||
message?.let {
|
|
||||||
val eventToSend =
|
val eventToSend =
|
||||||
LongPollParsedEvent.MessageRestored(message = message)
|
LongPollParsedEvent.MessageRestored(message = message)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.MESSAGE_RESTORED]?.let { listeners ->
|
listenersMap[LongPollEvent.MESSAGE_RESTORED]
|
||||||
listeners.map { vkEventCallback ->
|
?.forEach { it.onEvent(eventToSend) }
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageRestored>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> Unit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToSend.forEach { eventToSend ->
|
MessageFlags.UNREAD -> Unit
|
||||||
listenersMap[LongPollEvent.MESSAGE_CLEAR_FLAGS]?.let { listeners ->
|
MessageFlags.OUTGOING -> Unit
|
||||||
listeners.map { vkEventCallback ->
|
MessageFlags.AUDIO_LISTENED -> Unit
|
||||||
vkEventCallback.onEvent(eventToSend)
|
MessageFlags.FROM_GROUP_CHAT -> Unit
|
||||||
}
|
MessageFlags.CANCEL_SPAM -> Unit
|
||||||
}
|
MessageFlags.DELETED_FOR_ALL -> Unit
|
||||||
}
|
MessageFlags.DO_NOT_SHOW_NOTIFICATION -> Unit
|
||||||
|
MessageFlags.MESSAGE_WITH_REPLY -> Unit
|
||||||
|
MessageFlags.REACTION -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageNew(eventType: ApiEvent, event: List<Any>) {
|
listenersMap[LongPollEvent.MESSAGE_CLEAR_FLAGS]?.forEach { listener ->
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventsToSend.forEach { listener.onEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation.resume(eventsToSend)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun parseMessageNew(
|
||||||
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseMessageNew(): $eventType: $event")
|
||||||
|
|
||||||
val cmId = event[1].asLong()
|
val cmId = event[1].asLong()
|
||||||
val peerId = event[4].asLong()
|
val peerId = event[4].asLong()
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
val message =
|
val message = async { loadMessage(peerId = peerId, cmId = cmId) }.await()
|
||||||
async { loadMessage(peerId = peerId, cmId = cmId) }.await()
|
|
||||||
|
|
||||||
val convo =
|
val convo =
|
||||||
async {
|
async {
|
||||||
@@ -280,88 +263,88 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
}.await()
|
}.await()
|
||||||
|
|
||||||
message?.let {
|
if (message != null) {
|
||||||
listenersMap[LongPollEvent.MESSAGE_NEW]?.let {
|
val event = LongPollParsedEvent.NewMessage(
|
||||||
it.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.NewMessage>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.NewMessage(
|
|
||||||
message = message,
|
message = message,
|
||||||
inArchive = convo?.isArchived == true
|
inArchive = convo?.isArchived == true
|
||||||
// TODO: 03-Apr-25, Danil Nikolaev:
|
// TODO: 03-Apr-25, Danil Nikolaev:
|
||||||
// load user settings about restoring chats with
|
// load user settings about restoring chats with
|
||||||
// enabled notifications from archive
|
// enabled notifications from archive
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
listenersMap[LongPollEvent.MESSAGE_NEW]?.forEach { it.onEvent(event) }
|
||||||
}
|
continuation.resume(listOf(event))
|
||||||
|
} else {
|
||||||
|
continuation.resume(emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageEdit(eventType: ApiEvent, event: List<Any>) {
|
private suspend fun parseMessageEdit(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseMessageEdit(): $eventType: $event")
|
||||||
|
|
||||||
val cmId = event[1].asLong()
|
val cmId = event[1].asLong()
|
||||||
val peerId = event[3].asLong()
|
val peerId = event[3].asLong()
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
loadMessage(
|
val message = loadMessage(peerId = peerId, cmId = cmId)
|
||||||
peerId = peerId,
|
if (message != null) {
|
||||||
cmId = cmId
|
val event = LongPollParsedEvent.MessageEdited(message)
|
||||||
)?.let { message ->
|
listenersMap[LongPollEvent.MESSAGE_EDITED]?.forEach { it.onEvent(event) }
|
||||||
listenersMap[LongPollEvent.MESSAGE_EDITED]?.let {
|
continuation.resume(listOf(event))
|
||||||
it.map { vkEventCallback ->
|
} else {
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.MessageEdited>)
|
continuation.resume(emptyList())
|
||||||
.onEvent(LongPollParsedEvent.MessageEdited(message))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageReadIncoming(eventType: ApiEvent, event: List<Any>) {
|
private fun parseMessageReadIncoming(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseMessageReadIncoming(): $eventType: $event")
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val cmId = event[2].asLong()
|
val cmId = event[2].asLong()
|
||||||
val unreadCount = event[3].asInt()
|
val unreadCount = event[3].asInt()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.INCOMING_MESSAGE_READ]?.let { listeners ->
|
val event = LongPollParsedEvent.IncomingMessageRead(
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.IncomingMessageRead>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.IncomingMessageRead(
|
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
cmId = cmId,
|
cmId = cmId,
|
||||||
unreadCount = unreadCount
|
unreadCount = unreadCount
|
||||||
)
|
)
|
||||||
)
|
listenersMap[LongPollEvent.INCOMING_MESSAGE_READ]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageReadOutgoing(eventType: ApiEvent, event: List<Any>) {
|
private fun parseMessageReadOutgoing(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseMessageReadOutgoing(): $eventType: $event")
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val cmId = event[2].asLong()
|
val cmId = event[2].asLong()
|
||||||
val unreadCount = event[3].asInt()
|
val unreadCount = event[3].asInt()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.OUTGOING_MESSAGE_READ]?.let { listeners ->
|
val event = LongPollParsedEvent.OutgoingMessageRead(
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.OutgoingMessageRead>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.OutgoingMessageRead(
|
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
cmId = cmId,
|
cmId = cmId,
|
||||||
unreadCount = unreadCount
|
unreadCount = unreadCount
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
listenersMap[LongPollEvent.OUTGOING_MESSAGE_READ]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseChatClearFlags(eventType: ApiEvent, event: List<Any>) {
|
private suspend fun parseChatClearFlags(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseChatClearFlags(): $eventType: $event")
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val flags = event[2].asInt()
|
val flags = event[2].asInt()
|
||||||
@@ -391,32 +374,37 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.CHAT_ARCHIVED]?.let { listeners ->
|
listenersMap[LongPollEvent.CHAT_ARCHIVED]?.forEach { it.onEvent(eventToSend) }
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.ChatArchived>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConvoFlags.DISABLE_PUSH -> Unit
|
||||||
|
ConvoFlags.DISABLE_SOUND -> Unit
|
||||||
|
ConvoFlags.INCOMING_CHAT_REQUEST -> Unit
|
||||||
|
ConvoFlags.DECLINED_CHAT_REQUEST -> Unit
|
||||||
|
ConvoFlags.MENTION -> Unit
|
||||||
|
ConvoFlags.HIDE_CHAT_FROM_SEARCH -> Unit
|
||||||
|
ConvoFlags.BUSINESS_CHAT -> Unit
|
||||||
|
ConvoFlags.MARKED_MESSAGE -> Unit
|
||||||
|
ConvoFlags.DO_NOT_NOTIFY_MENTIONS_ALL_ONLINE -> Unit
|
||||||
|
ConvoFlags.DO_NOT_NOTIFY_ALL_MENTIONS -> Unit
|
||||||
|
ConvoFlags.MARKED_AS_UNREAD -> Unit
|
||||||
|
ConvoFlags.CALL_IN_PROGRESS -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Unit
|
listenersMap[LongPollEvent.CHAT_CLEAR_FLAGS]?.forEach { listener ->
|
||||||
|
eventsToSend.forEach { listener.onEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation.resume(eventsToSend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToSend.forEach { eventToSend ->
|
private suspend fun parseChatSetFlags(
|
||||||
listenersMap[LongPollEvent.CHAT_CLEAR_FLAGS]?.let { listeners ->
|
eventType: ApiEvent,
|
||||||
listeners.map { vkEventCallback ->
|
event: List<Any>
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent>)?.onEvent(
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
eventToSend
|
logger.debug(this::class, "parseChatSetFlags(): $eventType: $event")
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseChatSetFlags(eventType: ApiEvent, event: List<Any>) {
|
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val flags = event[2].asInt()
|
val flags = event[2].asInt()
|
||||||
@@ -446,89 +434,88 @@ class LongPollUpdatesParser(
|
|||||||
)
|
)
|
||||||
eventsToSend += eventToSend
|
eventsToSend += eventToSend
|
||||||
|
|
||||||
listenersMap[LongPollEvent.CHAT_ARCHIVED]?.let { listeners ->
|
listenersMap[LongPollEvent.CHAT_ARCHIVED]?.forEach { it.onEvent(eventToSend) }
|
||||||
listeners.map { vkEventCallback ->
|
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.ChatArchived>)
|
|
||||||
?.onEvent(eventToSend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConvoFlags.DISABLE_PUSH -> Unit
|
||||||
|
ConvoFlags.DISABLE_SOUND -> Unit
|
||||||
|
ConvoFlags.INCOMING_CHAT_REQUEST -> Unit
|
||||||
|
ConvoFlags.DECLINED_CHAT_REQUEST -> Unit
|
||||||
|
ConvoFlags.MENTION -> Unit
|
||||||
|
ConvoFlags.HIDE_CHAT_FROM_SEARCH -> Unit
|
||||||
|
ConvoFlags.BUSINESS_CHAT -> Unit
|
||||||
|
ConvoFlags.MARKED_MESSAGE -> Unit
|
||||||
|
ConvoFlags.DO_NOT_NOTIFY_MENTIONS_ALL_ONLINE -> Unit
|
||||||
|
ConvoFlags.DO_NOT_NOTIFY_ALL_MENTIONS -> Unit
|
||||||
|
ConvoFlags.MARKED_AS_UNREAD -> Unit
|
||||||
|
ConvoFlags.CALL_IN_PROGRESS -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Unit
|
listenersMap[LongPollEvent.CHAT_SET_FLAGS]?.forEach { listener ->
|
||||||
|
eventsToSend.forEach { listener.onEvent(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
continuation.resume(eventsToSend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eventsToSend.forEach { eventToSend ->
|
private fun parseMessagesDeleted(
|
||||||
listenersMap[LongPollEvent.CHAT_SET_FLAGS]?.let { listeners ->
|
eventType: ApiEvent,
|
||||||
listeners.map { vkEventCallback ->
|
event: List<Any>
|
||||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent>)?.onEvent(
|
): List<LongPollParsedEvent> {
|
||||||
eventToSend
|
logger.debug(this::class, "parseMessagesDeleted(): $eventType: $event")
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseMessagesDeleted(eventType: ApiEvent, event: List<Any>) {
|
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val cmId = event[2].asLong()
|
val cmId = event[2].asLong()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.CHAT_CLEARED]?.let { listeners ->
|
val event = LongPollParsedEvent.ChatCleared(
|
||||||
listeners.forEach { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatCleared>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.ChatCleared(
|
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
toCmId = cmId
|
toCmId = cmId
|
||||||
)
|
)
|
||||||
)
|
listenersMap[LongPollEvent.CHAT_CLEARED]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseChatMajorChanged(eventType: ApiEvent, event: List<Any>) {
|
private fun parseChatMajorChanged(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseChatMajorChanged(): $eventType: $event")
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val majorId = event[2].asInt()
|
val majorId = event[2].asInt()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.CHAT_MAJOR_CHANGED]?.let { listeners ->
|
val event = LongPollParsedEvent.ChatMajorChanged(
|
||||||
listeners.forEach { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatMajorChanged>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.ChatMajorChanged(
|
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
majorId = majorId,
|
majorId = majorId,
|
||||||
)
|
)
|
||||||
)
|
listenersMap[LongPollEvent.CHAT_MAJOR_CHANGED]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseChatMinorChanged(eventType: ApiEvent, event: List<Any>) {
|
private fun parseChatMinorChanged(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseChatMinorChanged(): $eventType: $event")
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
val minorId = event[2].asInt()
|
val minorId = event[2].asInt()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.CHAT_MINOR_CHANGED]?.let { listeners ->
|
val event = LongPollParsedEvent.ChatMinorChanged(
|
||||||
listeners.forEach { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatMinorChanged>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.ChatMinorChanged(
|
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
minorId = minorId,
|
minorId = minorId,
|
||||||
)
|
)
|
||||||
)
|
listenersMap[LongPollEvent.CHAT_MINOR_CHANGED]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseInteraction(eventType: ApiEvent, event: List<Any>) {
|
private fun parseInteraction(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseInteraction(): $eventType: $event")
|
||||||
|
|
||||||
val interactionType = when (eventType) {
|
val interactionType = when (eventType) {
|
||||||
ApiEvent.TYPING -> InteractionType.Typing
|
ApiEvent.TYPING -> InteractionType.Typing
|
||||||
@@ -536,7 +523,7 @@ class LongPollUpdatesParser(
|
|||||||
ApiEvent.PHOTO_UPLOADING -> InteractionType.Photo
|
ApiEvent.PHOTO_UPLOADING -> InteractionType.Photo
|
||||||
ApiEvent.VIDEO_UPLOADING -> InteractionType.Video
|
ApiEvent.VIDEO_UPLOADING -> InteractionType.Video
|
||||||
ApiEvent.FILE_UPLOADING -> InteractionType.File
|
ApiEvent.FILE_UPLOADING -> InteractionType.File
|
||||||
else -> return
|
else -> return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val longPollEvent: LongPollEvent = when (eventType) {
|
val longPollEvent: LongPollEvent = when (eventType) {
|
||||||
@@ -545,7 +532,6 @@ class LongPollUpdatesParser(
|
|||||||
ApiEvent.PHOTO_UPLOADING -> LongPollEvent.PHOTO_UPLOADING
|
ApiEvent.PHOTO_UPLOADING -> LongPollEvent.PHOTO_UPLOADING
|
||||||
ApiEvent.VIDEO_UPLOADING -> LongPollEvent.VIDEO_UPLOADING
|
ApiEvent.VIDEO_UPLOADING -> LongPollEvent.VIDEO_UPLOADING
|
||||||
ApiEvent.FILE_UPLOADING -> LongPollEvent.FILE_UPLOADING
|
ApiEvent.FILE_UPLOADING -> LongPollEvent.FILE_UPLOADING
|
||||||
else -> return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val peerId = event[1].asLong()
|
val peerId = event[1].asLong()
|
||||||
@@ -554,26 +540,25 @@ class LongPollUpdatesParser(
|
|||||||
val timestamp = event[4].asInt()
|
val timestamp = event[4].asInt()
|
||||||
|
|
||||||
// if userIds contains only account's id, then we don't need to show our status
|
// if userIds contains only account's id, then we don't need to show our status
|
||||||
if (userIds.isEmpty()) return
|
if (userIds.isEmpty()) return emptyList()
|
||||||
|
|
||||||
listenersMap[longPollEvent]?.let { listeners ->
|
val event = LongPollParsedEvent.Interaction(
|
||||||
listeners.forEach { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.Interaction>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.Interaction(
|
|
||||||
interactionType = interactionType,
|
interactionType = interactionType,
|
||||||
peerId = peerId,
|
peerId = peerId,
|
||||||
userIds = userIds,
|
userIds = userIds,
|
||||||
totalCount = totalCount,
|
totalCount = totalCount,
|
||||||
timestamp = timestamp
|
timestamp = timestamp
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
listenersMap[longPollEvent]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseUnreadCounterUpdate(eventType: ApiEvent, event: List<Any>) {
|
private fun parseUnreadCounterUpdate(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> {
|
||||||
|
logger.debug(this::class, "parseUnreadCounterUpdate(): $eventType: $event")
|
||||||
|
|
||||||
val unreadCount = event[1].asInt()
|
val unreadCount = event[1].asInt()
|
||||||
val unreadUnmutedCount = event[2].asInt()
|
val unreadUnmutedCount = event[2].asInt()
|
||||||
@@ -583,11 +568,7 @@ class LongPollUpdatesParser(
|
|||||||
val archiveUnreadUnmutedCount = event[8].asInt()
|
val archiveUnreadUnmutedCount = event[8].asInt()
|
||||||
val archiveMentionsCount = event[9].asInt()
|
val archiveMentionsCount = event[9].asInt()
|
||||||
|
|
||||||
listenersMap[LongPollEvent.UNREAD_COUNTER_UPDATE]?.let { listeners ->
|
val event = LongPollParsedEvent.UnreadCounter(
|
||||||
listeners.forEach { vkEventCallback ->
|
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.UnreadCounter>)
|
|
||||||
.onEvent(
|
|
||||||
LongPollParsedEvent.UnreadCounter(
|
|
||||||
unread = unreadCount,
|
unread = unreadCount,
|
||||||
unreadUnmuted = unreadUnmutedCount,
|
unreadUnmuted = unreadUnmutedCount,
|
||||||
showOnlyMuted = showOnlyMuted,
|
showOnlyMuted = showOnlyMuted,
|
||||||
@@ -596,45 +577,48 @@ class LongPollUpdatesParser(
|
|||||||
archiveUnmuted = archiveUnreadUnmutedCount,
|
archiveUnmuted = archiveUnreadUnmutedCount,
|
||||||
archiveMentions = archiveMentionsCount
|
archiveMentions = archiveMentionsCount
|
||||||
)
|
)
|
||||||
)
|
listenersMap[LongPollEvent.UNREAD_COUNTER_UPDATE]?.forEach { it.onEvent(event) }
|
||||||
}
|
return listOf(event)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageUpdated(eventType: ApiEvent, event: List<Any>) {
|
private suspend fun parseMessageUpdated(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseMessageUpdated(): $eventType: $event")
|
||||||
|
|
||||||
val cmId = event[1].asLong()
|
val cmId = event[1].asLong()
|
||||||
val peerId = event[4].asLong()
|
val peerId = event[4].asLong()
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
loadMessage(
|
val message = loadMessage(peerId = peerId, cmId = cmId)
|
||||||
peerId = peerId,
|
|
||||||
cmId = cmId
|
if (message != null) {
|
||||||
)?.let { message ->
|
val event = LongPollParsedEvent.MessageUpdated(message)
|
||||||
listenersMap[LongPollEvent.MESSAGE_UPDATED]?.let {
|
listenersMap[LongPollEvent.MESSAGE_UPDATED]?.forEach { it.onEvent(event) }
|
||||||
it.map { vkEventCallback ->
|
continuation.resume(listOf(event))
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.MessageUpdated>)
|
} else {
|
||||||
.onEvent(LongPollParsedEvent.MessageUpdated(message))
|
continuation.resume(emptyList())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseMessageCacheClear(eventType: ApiEvent, event: List<Any>) {
|
private suspend fun parseMessageCacheClear(
|
||||||
Log.d("LongPollUpdatesParser", "$eventType $event")
|
eventType: ApiEvent,
|
||||||
|
event: List<Any>
|
||||||
|
): List<LongPollParsedEvent> = suspendCancellableCoroutine { continuation ->
|
||||||
|
logger.debug(this::class, "parseMessageCacheClear(): $eventType: $event")
|
||||||
|
|
||||||
val messageId = event[1].asLong()
|
val messageId = event[1].asLong()
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
loadMessage(messageId = messageId)?.let { message ->
|
val message = loadMessage(messageId = messageId)
|
||||||
listenersMap[LongPollEvent.MESSAGE_CACHE_CLEAR]?.let {
|
if (message != null) {
|
||||||
it.map { vkEventCallback ->
|
val event = LongPollParsedEvent.MessageCacheClear(message)
|
||||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.MessageCacheClear>)
|
listenersMap[LongPollEvent.MESSAGE_CACHE_CLEAR]?.forEach { it.onEvent(event) }
|
||||||
.onEvent(LongPollParsedEvent.MessageCacheClear(message))
|
continuation.resume(listOf(event))
|
||||||
}
|
} else {
|
||||||
}
|
continuation.resume(emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -643,7 +627,7 @@ class LongPollUpdatesParser(
|
|||||||
peerId: Long? = null,
|
peerId: Long? = null,
|
||||||
cmId: Long? = null,
|
cmId: Long? = null,
|
||||||
messageId: Long? = null
|
messageId: Long? = null
|
||||||
): VkMessage? = suspendCoroutine { continuation ->
|
): VkMessage? = suspendCancellableCoroutine { continuation ->
|
||||||
require((peerId != null && cmId != null) || messageId != null)
|
require((peerId != null && cmId != null) || messageId != null)
|
||||||
|
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
@@ -657,7 +641,7 @@ class LongPollUpdatesParser(
|
|||||||
).listenValue(this) { state ->
|
).listenValue(this) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.e("LongPollUpdatesParser", "loadMessage: error: $error")
|
logger.error(this::class, "loadMessage(): ERROR: $error")
|
||||||
continuation.resume(null)
|
continuation.resume(null)
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
@@ -677,7 +661,7 @@ class LongPollUpdatesParser(
|
|||||||
peerId: Long,
|
peerId: Long,
|
||||||
extended: Boolean = false,
|
extended: Boolean = false,
|
||||||
fields: String? = null
|
fields: String? = null
|
||||||
): VkConvo? = suspendCoroutine { continuation ->
|
): VkConvo? = suspendCancellableCoroutine { continuation ->
|
||||||
coroutineScope.launch(Dispatchers.IO) {
|
coroutineScope.launch(Dispatchers.IO) {
|
||||||
convoUseCase.getById(
|
convoUseCase.getById(
|
||||||
peerIds = listOf(peerId),
|
peerIds = listOf(peerId),
|
||||||
@@ -686,7 +670,7 @@ class LongPollUpdatesParser(
|
|||||||
).listenValue(coroutineScope) { state ->
|
).listenValue(coroutineScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.e("LongPollUpdatesParser", "loadConvo: error: $error")
|
logger.error(this::class, "loadConvo(): ERROR: $error")
|
||||||
continuation.resume(null)
|
continuation.resume(null)
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.fast.android.library)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "dev.meloda.fast.logger"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.koin.android)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.meloda.fast.logger;
|
||||||
|
|
||||||
|
enum class FastLogLevel {
|
||||||
|
VERBOSE,
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARNING,
|
||||||
|
ERROR,
|
||||||
|
ASSERT;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun parse(value: Int): FastLogLevel {
|
||||||
|
if (value !in 0..5) throw IllegalArgumentException("Unknown LogLevel value $value")
|
||||||
|
return entries.first { it.ordinal == value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package dev.meloda.fast.logger
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class FastLogger {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private lateinit var instance: FastLogger
|
||||||
|
|
||||||
|
fun setInstance(logger: FastLogger) {
|
||||||
|
if (::instance.isInitialized) {
|
||||||
|
throw IllegalStateException("FastLogger has already been initialized.")
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getInstance(): FastLogger {
|
||||||
|
if (!::instance.isInitialized) {
|
||||||
|
throw UninitializedPropertyAccessException("FastLogger is not initialized.")
|
||||||
|
}
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var logLevel: FastLogLevel = FastLogLevel.ERROR
|
||||||
|
|
||||||
|
fun setLogLevel(logLevel: FastLogLevel) {
|
||||||
|
Log.v(this::class.java.simpleName, "Set LogLevel from ${this.logLevel} to $logLevel")
|
||||||
|
this.logLevel = logLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verbose(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
verbose(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verbose(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.VERBOSE)) {
|
||||||
|
Log.v(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun debug(clazz: KClass<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
debug(clazz.java, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun debug(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
debug(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun debug(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.DEBUG)) {
|
||||||
|
Log.d(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun info(clazz: KClass<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
info(clazz.java, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun info(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
info(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun info(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.INFO)) {
|
||||||
|
Log.i(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun warning(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
warning(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun warning(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.WARNING)) {
|
||||||
|
Log.w(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(clazz: KClass<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
error(clazz.java, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
error(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun error(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.ERROR)) {
|
||||||
|
Log.e(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assert(clazz: Class<*>, message: String, throwable: Throwable? = null) {
|
||||||
|
assert(clazz.simpleName, message, throwable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun assert(tag: String, message: String, throwable: Throwable? = null) {
|
||||||
|
if (shouldLog(FastLogLevel.ASSERT)) {
|
||||||
|
Log.wtf(tag, message, throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldLog(level: FastLogLevel): Boolean = level.ordinal >= logLevel.ordinal
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package dev.meloda.fast.logger
|
||||||
|
|
||||||
|
import org.koin.core.module.dsl.singleOf
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val loggerModule = module {
|
||||||
|
singleOf(::FastLogger)
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
package dev.meloda.fast.model.api.data
|
package dev.meloda.fast.model.api.data
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
enum class AttachmentType(var value: String) {
|
enum class AttachmentType(var value: String) {
|
||||||
UNKNOWN("unknown"),
|
UNKNOWN("unknown"),
|
||||||
PHOTO("photo"),
|
PHOTO("photo"),
|
||||||
@@ -42,10 +40,6 @@ enum class AttachmentType(var value: String) {
|
|||||||
it.value == value
|
it.value == value
|
||||||
} ?: UNKNOWN
|
} ?: UNKNOWN
|
||||||
|
|
||||||
if (parsedValue == UNKNOWN) {
|
|
||||||
Log.e("AttachmentType", "Unknown attachment type: $value")
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedValue
|
return parsedValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,5 +117,6 @@ fun VkMessageData.asDomain(): VkMessage = VkMessage(
|
|||||||
pinnedAt = pinnedAt,
|
pinnedAt = pinnedAt,
|
||||||
isPinned = isPinned == true,
|
isPinned = isPinned == true,
|
||||||
formatData = formatData?.asDomain(),
|
formatData = formatData?.asDomain(),
|
||||||
isSpam = false
|
isSpam = false,
|
||||||
|
isDeleted = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -56,5 +56,6 @@ data class VkPinnedMessageData(
|
|||||||
isPinned = true,
|
isPinned = true,
|
||||||
isSpam = false,
|
isSpam = false,
|
||||||
formatData = null,
|
formatData = null,
|
||||||
|
isDeleted = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ data class VkMessage(
|
|||||||
val group: VkGroupDomain?,
|
val group: VkGroupDomain?,
|
||||||
val actionUser: VkUser?,
|
val actionUser: VkUser?,
|
||||||
val actionGroup: VkGroupDomain?,
|
val actionGroup: VkGroupDomain?,
|
||||||
|
|
||||||
|
val isDeleted: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun isPeerChat() = peerId > 2_000_000_000
|
fun isPeerChat() = peerId > 2_000_000_000
|
||||||
@@ -111,7 +113,7 @@ fun VkMessage.asEntity(): VkMessageEntity = VkMessageEntity(
|
|||||||
actionCmId = actionCmId,
|
actionCmId = actionCmId,
|
||||||
actionMessage = actionMessage,
|
actionMessage = actionMessage,
|
||||||
updateTime = updateTime,
|
updateTime = updateTime,
|
||||||
important = isImportant,
|
isImportant = isImportant,
|
||||||
forwardIds = forwards.orEmpty().map(VkMessage::id),
|
forwardIds = forwards.orEmpty().map(VkMessage::id),
|
||||||
// TODO: 05/05/2024, Danil Nikolaev: save attachments
|
// TODO: 05/05/2024, Danil Nikolaev: save attachments
|
||||||
attachments = emptyList(),
|
attachments = emptyList(),
|
||||||
@@ -119,4 +121,6 @@ fun VkMessage.asEntity(): VkMessageEntity = VkMessageEntity(
|
|||||||
geoType = geoType,
|
geoType = geoType,
|
||||||
pinnedAt = pinnedAt,
|
pinnedAt = pinnedAt,
|
||||||
isPinned = isPinned,
|
isPinned = isPinned,
|
||||||
|
isDeleted = isDeleted,
|
||||||
|
isSpam = isSpam
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,13 +21,15 @@ data class VkMessageEntity(
|
|||||||
val actionCmId: Long?,
|
val actionCmId: Long?,
|
||||||
val actionMessage: String?,
|
val actionMessage: String?,
|
||||||
val updateTime: Int?,
|
val updateTime: Int?,
|
||||||
val important: Boolean,
|
val isImportant: Boolean,
|
||||||
val forwardIds: List<Long>?,
|
val forwardIds: List<Long>?,
|
||||||
val attachments: List<String>?, // TODO: 01/05/2024, Danil Nikolaev: how to store???
|
val attachments: List<String>?, // TODO: 01/05/2024, Danil Nikolaev: how to store???
|
||||||
val replyMessageId: Long?,
|
val replyMessageId: Long?,
|
||||||
val geoType: String?,
|
val geoType: String?,
|
||||||
val pinnedAt: Int?,
|
val pinnedAt: Int?,
|
||||||
val isPinned: Boolean
|
val isPinned: Boolean,
|
||||||
|
val isDeleted: Boolean,
|
||||||
|
val isSpam: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
fun VkMessageEntity.asExternalModel(): VkMessage = VkMessage(
|
fun VkMessageEntity.asExternalModel(): VkMessage = VkMessage(
|
||||||
@@ -45,7 +47,7 @@ fun VkMessageEntity.asExternalModel(): VkMessage = VkMessage(
|
|||||||
actionCmId = actionCmId,
|
actionCmId = actionCmId,
|
||||||
actionMessage = actionMessage,
|
actionMessage = actionMessage,
|
||||||
updateTime = updateTime,
|
updateTime = updateTime,
|
||||||
isImportant = important,
|
isImportant = isImportant,
|
||||||
forwards = emptyList(),//forwards.orEmpty().map(VkMessageEntity::asExternalModel),
|
forwards = emptyList(),//forwards.orEmpty().map(VkMessageEntity::asExternalModel),
|
||||||
// TODO: 05/05/2024, Danil Nikolaev: restore attachments
|
// TODO: 05/05/2024, Danil Nikolaev: restore attachments
|
||||||
attachments = attachments.orEmpty().map { VkUnknownAttachment },
|
attachments = attachments.orEmpty().map { VkUnknownAttachment },
|
||||||
@@ -59,4 +61,5 @@ fun VkMessageEntity.asExternalModel(): VkMessage = VkMessage(
|
|||||||
isPinned = isPinned,
|
isPinned = isPinned,
|
||||||
isSpam = false,
|
isSpam = false,
|
||||||
formatData = null,
|
formatData = null,
|
||||||
|
isDeleted = isDeleted
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ dependencies {
|
|||||||
api(projects.core.common)
|
api(projects.core.common)
|
||||||
api(projects.core.model)
|
api(projects.core.model)
|
||||||
api(projects.core.datastore)
|
api(projects.core.datastore)
|
||||||
|
api(projects.core.logger)
|
||||||
|
|
||||||
implementation(libs.moshi.kotlin)
|
implementation(libs.moshi.kotlin)
|
||||||
implementation(libs.koin.android)
|
implementation(libs.koin.android)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package dev.meloda.fast.network
|
package dev.meloda.fast.network
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.slack.eithernet.ApiException
|
import com.slack.eithernet.ApiException
|
||||||
import com.slack.eithernet.errorType
|
import com.slack.eithernet.errorType
|
||||||
import com.slack.eithernet.toType
|
import com.slack.eithernet.toType
|
||||||
import com.squareup.moshi.JsonDataException
|
import com.squareup.moshi.JsonDataException
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import retrofit2.Converter
|
import retrofit2.Converter
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
@@ -16,7 +16,10 @@ import java.lang.reflect.Type
|
|||||||
*
|
*
|
||||||
* допускает Unit как SuccessType в случае невозможности каста ответа в ErrorType
|
* допускает Unit как SuccessType в случае невозможности каста ответа в ErrorType
|
||||||
*/
|
*/
|
||||||
class ResponseConverterFactory(private val converter: JsonConverter) : Converter.Factory() {
|
class ResponseConverterFactory(
|
||||||
|
private val converter: JsonConverter,
|
||||||
|
private val logger: FastLogger
|
||||||
|
) : Converter.Factory() {
|
||||||
|
|
||||||
override fun responseBodyConverter(
|
override fun responseBodyConverter(
|
||||||
type: Type,
|
type: Type,
|
||||||
@@ -29,6 +32,7 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter
|
|||||||
successType = type,
|
successType = type,
|
||||||
errorRaw = errorRaw,
|
errorRaw = errorRaw,
|
||||||
converter = converter,
|
converter = converter,
|
||||||
|
logger = logger
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +40,7 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter
|
|||||||
private val successType: Type,
|
private val successType: Type,
|
||||||
private val errorRaw: Class<*>,
|
private val errorRaw: Class<*>,
|
||||||
private val converter: JsonConverter,
|
private val converter: JsonConverter,
|
||||||
|
private val logger: FastLogger
|
||||||
) : Converter<ResponseBody, Any?> {
|
) : Converter<ResponseBody, Any?> {
|
||||||
override fun convert(value: ResponseBody): Any? {
|
override fun convert(value: ResponseBody): Any? {
|
||||||
val string = value.string()
|
val string = value.string()
|
||||||
@@ -53,7 +58,7 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter
|
|||||||
},
|
},
|
||||||
onFailure = { failure ->
|
onFailure = { failure ->
|
||||||
if (failure is JsonDataException) {
|
if (failure is JsonDataException) {
|
||||||
Log.d("ResponseBodyConverter", "convertJsonDataException: $failure")
|
logger.error(this::class, "convert(): ERROR", failure)
|
||||||
throw ApiException(
|
throw ApiException(
|
||||||
RestApiError(
|
RestApiError(
|
||||||
errorCode = -1,
|
errorCode = -1,
|
||||||
@@ -68,10 +73,11 @@ class ResponseConverterFactory(private val converter: JsonConverter) : Converter
|
|||||||
converter.fromJson(errorRaw, string)
|
converter.fromJson(errorRaw, string)
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = { errorModel ->
|
onSuccess = { errorModel ->
|
||||||
Log.d("ResponseBodyConverter", "convert: $errorModel")
|
logger.debug(this::class, "convert(): errorModel: $errorModel")
|
||||||
throw ApiException(errorModel)
|
throw ApiException(errorModel)
|
||||||
},
|
},
|
||||||
onFailure = { exception ->
|
onFailure = { exception ->
|
||||||
|
logger.error(this::class, "convert(): INNER: ERROR", exception)
|
||||||
if (!isUnit) {
|
if (!isUnit) {
|
||||||
throw exception
|
throw exception
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+11
-13
@@ -1,9 +1,9 @@
|
|||||||
package dev.meloda.fast.network.interceptor
|
package dev.meloda.fast.network.interceptor
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import dev.meloda.fast.common.extensions.listenValue
|
import dev.meloda.fast.common.extensions.listenValue
|
||||||
import dev.meloda.fast.datastore.AppSettings
|
import dev.meloda.fast.datastore.AppSettings
|
||||||
import dev.meloda.fast.datastore.CaptchaTokenResult
|
import dev.meloda.fast.datastore.CaptchaTokenResult
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -14,11 +14,7 @@ import org.json.JSONObject
|
|||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
class Error14HandlingInterceptor(
|
class Error14HandlingInterceptor(private val logger: FastLogger) : Interceptor {
|
||||||
// private val domains: Set<String> = emptySet(),
|
|
||||||
) : Interceptor {
|
|
||||||
|
|
||||||
private val cookie = AtomicReference<String?>(null)
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private const val CAPTCHA_ERROR_CODE = 14
|
private const val CAPTCHA_ERROR_CODE = 14
|
||||||
@@ -26,6 +22,8 @@ class Error14HandlingInterceptor(
|
|||||||
private val executor = Executors.newSingleThreadExecutor()
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val cookie = AtomicReference<String?>(null)
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
val request = chain.request().withCookie()
|
val request = chain.request().withCookie()
|
||||||
val response = chain.proceed(request)
|
val response = chain.proceed(request)
|
||||||
@@ -41,23 +39,23 @@ class Error14HandlingInterceptor(
|
|||||||
|
|
||||||
executor.submit {
|
executor.submit {
|
||||||
AppSettings.setCaptchaRedirectUri(redirectUri)
|
AppSettings.setCaptchaRedirectUri(redirectUri)
|
||||||
Log.d("Error14Interceptor", "passCaptchaAndGetToken: $redirectUri")
|
logger.debug(this::class, "passCaptchaAndGetToken: $redirectUri")
|
||||||
|
|
||||||
var job: Job? = null
|
var job: Job? = null
|
||||||
job = AppSettings.getCaptchaResultFlow()
|
job = AppSettings.getCaptchaResultFlow()
|
||||||
.listenValue(CoroutineScope(Dispatchers.IO)) {
|
.listenValue(CoroutineScope(Dispatchers.IO)) {
|
||||||
Log.d("Error14Interceptor", "passCaptchaAndGetToken: $it")
|
logger.debug(this::class, "passCaptchaAndGetToken: $it")
|
||||||
if (it != CaptchaTokenResult.Initial) {
|
if (it != CaptchaTokenResult.Initial) {
|
||||||
synchronized(tokenResult) {
|
synchronized(tokenResult) {
|
||||||
Log.d(
|
logger.debug(
|
||||||
"Error14Interceptor",
|
this::class,
|
||||||
"passCaptchaAndGetToken: SYNCHRONIZED: $it"
|
"passCaptchaAndGetToken: SYNCHRONIZED: $it"
|
||||||
)
|
)
|
||||||
tokenResult.set(wrapResult(it))
|
tokenResult.set(wrapResult(it))
|
||||||
tokenResult.notifyAll()
|
tokenResult.notifyAll()
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
Log.d(
|
logger.debug(
|
||||||
"Error14Interceptor",
|
this::class,
|
||||||
"passCaptchaAndGetToken: NULL RESULT"
|
"passCaptchaAndGetToken: NULL RESULT"
|
||||||
)
|
)
|
||||||
AppSettings.setCaptchaResult(CaptchaTokenResult.Initial)
|
AppSettings.setCaptchaResult(CaptchaTokenResult.Initial)
|
||||||
@@ -71,7 +69,7 @@ class Error14HandlingInterceptor(
|
|||||||
tokenResult.wait()
|
tokenResult.wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d("Error14Interceptor", "passCaptchaAndGetToken: GET VALUE")
|
logger.debug(this::class, "passCaptchaAndGetToken: GET VALUE")
|
||||||
tokenResult.get().getOrThrow()
|
tokenResult.get().getOrThrow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core.common)
|
api(projects.core.common)
|
||||||
api(projects.core.model)
|
api(projects.core.model)
|
||||||
|
api(projects.core.logger)
|
||||||
implementation(projects.core.presentation)
|
implementation(projects.core.presentation)
|
||||||
|
|
||||||
implementation(libs.haze)
|
implementation(libs.haze)
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package dev.meloda.fast.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
|
|
||||||
|
val LocalLogger = compositionLocalOf { FastLogger.getInstance() }
|
||||||
+12
-7
@@ -1,7 +1,6 @@
|
|||||||
package dev.meloda.fast.auth.captcha.presentation
|
package dev.meloda.fast.auth.captcha.presentation
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.util.Log
|
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
@@ -32,7 +31,9 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.ui.R
|
import dev.meloda.fast.ui.R
|
||||||
|
import dev.meloda.fast.ui.common.LocalLogger
|
||||||
import dev.meloda.fast.ui.components.ActionInvokeDismiss
|
import dev.meloda.fast.ui.components.ActionInvokeDismiss
|
||||||
import dev.meloda.fast.ui.components.FullScreenDialog
|
import dev.meloda.fast.ui.components.FullScreenDialog
|
||||||
import dev.meloda.fast.ui.components.MaterialDialog
|
import dev.meloda.fast.ui.components.MaterialDialog
|
||||||
@@ -46,6 +47,8 @@ fun CaptchaScreen(
|
|||||||
onBack: () -> Unit = {},
|
onBack: () -> Unit = {},
|
||||||
onResult: (String) -> Unit = {}
|
onResult: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
val logger = LocalLogger.current
|
||||||
|
|
||||||
if (captchaRedirectUri != null) {
|
if (captchaRedirectUri != null) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
@@ -114,7 +117,10 @@ fun CaptchaScreen(
|
|||||||
view: WebView?,
|
view: WebView?,
|
||||||
request: WebResourceRequest?
|
request: WebResourceRequest?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
Log.i(TAG, "shouldOverrideUrlLoading: $request")
|
logger.info(
|
||||||
|
"CaptchaScreen",
|
||||||
|
"WebViewClient(): shouldOverrideUrlLoading(): request: $request"
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,19 +182,18 @@ fun CaptchaScreen(
|
|||||||
|
|
||||||
class WebCaptchaListener(
|
class WebCaptchaListener(
|
||||||
private val onSuccessTokenReceived: (String) -> Unit,
|
private val onSuccessTokenReceived: (String) -> Unit,
|
||||||
private val onCloseRequested: (String) -> Unit
|
private val onCloseRequested: (String) -> Unit,
|
||||||
|
private val logger: FastLogger
|
||||||
) {
|
) {
|
||||||
private val tag = "WebCaptchaListener"
|
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun VKCaptchaGetResult(arg: String) {
|
fun VKCaptchaGetResult(arg: String) {
|
||||||
onSuccessTokenReceived(arg)
|
onSuccessTokenReceived(arg)
|
||||||
Log.i(tag, "VKCaptchaGetResult($arg)")
|
logger.info(this::class, "VKCaptchaGetResult(): arg: $arg")
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun VKCaptchaCloseCaptcha(arg: String) {
|
fun VKCaptchaCloseCaptcha(arg: String) {
|
||||||
onCloseRequested(arg)
|
onCloseRequested(arg)
|
||||||
Log.i(tag, "VKCaptchaCloseCaptcha($arg)")
|
logger.info(this::class, "VKCaptchaCloseCaptcha(): arg: $arg")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package dev.meloda.fast.auth.login
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dev.meloda.fast.auth.login.model.CaptchaArguments
|
import dev.meloda.fast.auth.login.model.CaptchaArguments
|
||||||
@@ -28,6 +27,7 @@ import dev.meloda.fast.datastore.AppSettings
|
|||||||
import dev.meloda.fast.datastore.UserSettings
|
import dev.meloda.fast.datastore.UserSettings
|
||||||
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
import dev.meloda.fast.domain.LoadUserByIdUseCase
|
||||||
import dev.meloda.fast.domain.OAuthUseCase
|
import dev.meloda.fast.domain.OAuthUseCase
|
||||||
|
import dev.meloda.fast.logger.FastLogger
|
||||||
import dev.meloda.fast.model.database.AccountEntity
|
import dev.meloda.fast.model.database.AccountEntity
|
||||||
import dev.meloda.fast.network.OAuthErrorDomain
|
import dev.meloda.fast.network.OAuthErrorDomain
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
@@ -48,7 +48,8 @@ class LoginViewModel(
|
|||||||
private val accountsRepository: AccountsRepository,
|
private val accountsRepository: AccountsRepository,
|
||||||
private val loginValidator: LoginValidator,
|
private val loginValidator: LoginValidator,
|
||||||
private val longPollController: LongPollController,
|
private val longPollController: LongPollController,
|
||||||
private val userSettings: UserSettings
|
private val userSettings: UserSettings,
|
||||||
|
private val logger: FastLogger
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _screenState = MutableStateFlow(LoginScreenState.EMPTY)
|
private val _screenState = MutableStateFlow(LoginScreenState.EMPTY)
|
||||||
val screenState = _screenState.asStateFlow()
|
val screenState = _screenState.asStateFlow()
|
||||||
@@ -189,7 +190,7 @@ class LoginViewModel(
|
|||||||
).listenValue(viewModelScope) { state ->
|
).listenValue(viewModelScope) { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.d("LoginViewModelImpl", "login: error: $error")
|
logger.error(this::class, "getSilentToken(): ERROR: $error")
|
||||||
|
|
||||||
_screenState.updateValue { copy(isLoading = false) }
|
_screenState.updateValue { copy(isLoading = false) }
|
||||||
|
|
||||||
|
|||||||
+1
-5
@@ -1,6 +1,5 @@
|
|||||||
package dev.meloda.fast.chatmaterials.util
|
package dev.meloda.fast.chatmaterials.util
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import dev.meloda.fast.chatmaterials.model.UiChatMaterial
|
import dev.meloda.fast.chatmaterials.model.UiChatMaterial
|
||||||
import dev.meloda.fast.common.util.AndroidUtils
|
import dev.meloda.fast.common.util.AndroidUtils
|
||||||
import dev.meloda.fast.model.api.data.AttachmentType
|
import dev.meloda.fast.model.api.data.AttachmentType
|
||||||
@@ -135,8 +134,5 @@ fun VkAttachmentHistoryMessage.asPresentation(): UiChatMaterial? =
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> null
|
||||||
Log.w("ChatMaterialMapper", "Unsupported type: $type")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,6 +613,7 @@ class ConvosViewModel(
|
|||||||
if (convoIndex == null) { // диалога нет в списке
|
if (convoIndex == null) { // диалога нет в списке
|
||||||
// pizdets
|
// pizdets
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: 30.05.2026, Danil Nikolaev: reimplement
|
||||||
newConvos.removeAt(convoIndex)
|
newConvos.removeAt(convoIndex)
|
||||||
|
|
||||||
replaceConvos(newConvos.sorted())
|
replaceConvos(newConvos.sorted())
|
||||||
|
|||||||
+1
-11
@@ -6,7 +6,6 @@ import android.content.Context
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
@@ -685,8 +684,6 @@ class MessagesHistoryViewModelImpl(
|
|||||||
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
|
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
|
||||||
val message = event.message
|
val message = event.message
|
||||||
|
|
||||||
Log.d("MessagesHistoryViewModel", "handleNewMessage: $message")
|
|
||||||
|
|
||||||
if (message.peerId != screenState.value.convoId) return
|
if (message.peerId != screenState.value.convoId) return
|
||||||
if (messages.value.indexOfFirstOrNull { it.id == message.id } != null) return
|
if (messages.value.indexOfFirstOrNull { it.id == message.id } != null) return
|
||||||
|
|
||||||
@@ -835,8 +832,6 @@ class MessagesHistoryViewModelImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadConvo() {
|
private fun loadConvo() {
|
||||||
Log.d("MessagesHistoryViewModelImpl", "loadConvo()")
|
|
||||||
|
|
||||||
loadConvosByIdUseCase(
|
loadConvosByIdUseCase(
|
||||||
peerIds = listOf(screenState.value.convoId),
|
peerIds = listOf(screenState.value.convoId),
|
||||||
extended = true,
|
extended = true,
|
||||||
@@ -904,8 +899,6 @@ class MessagesHistoryViewModelImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadMessagesHistory(offset: Int = currentOffset.value) {
|
private fun loadMessagesHistory(offset: Int = currentOffset.value) {
|
||||||
Log.d("MessagesHistoryViewModel", "loadMessagesHistory: $offset")
|
|
||||||
|
|
||||||
messagesUseCase.getMessagesHistory(
|
messagesUseCase.getMessagesHistory(
|
||||||
convoId = screenState.value.convoId,
|
convoId = screenState.value.convoId,
|
||||||
count = MESSAGES_LOAD_COUNT,
|
count = MESSAGES_LOAD_COUNT,
|
||||||
@@ -1037,6 +1030,7 @@ class MessagesHistoryViewModelImpl(
|
|||||||
isSpam = false,
|
isSpam = false,
|
||||||
pinnedAt = null,
|
pinnedAt = null,
|
||||||
formatData = formatData,
|
formatData = formatData,
|
||||||
|
isDeleted = false
|
||||||
)
|
)
|
||||||
formatData = formatData.copy(items = emptyList())
|
formatData = formatData.copy(items = emptyList())
|
||||||
sendingMessages += newMessage
|
sendingMessages += newMessage
|
||||||
@@ -1078,8 +1072,6 @@ class MessagesHistoryViewModelImpl(
|
|||||||
state.processState(
|
state.processState(
|
||||||
any = { sendingMessages.remove(newMessage) },
|
any = { sendingMessages.remove(newMessage) },
|
||||||
error = { error ->
|
error = { error ->
|
||||||
Log.d("MessagesHistoryViewModelImpl", "sendMessage: ERROR: $error")
|
|
||||||
|
|
||||||
val failedId = -500_000L - failedMessages.size
|
val failedId = -500_000L - failedMessages.size
|
||||||
val newFailedMessage = newMessage.copy(id = failedId)
|
val newFailedMessage = newMessage.copy(id = failedId)
|
||||||
failedMessages += newFailedMessage
|
failedMessages += newFailedMessage
|
||||||
@@ -1143,8 +1135,6 @@ class MessagesHistoryViewModelImpl(
|
|||||||
) ?: return
|
) ?: return
|
||||||
|
|
||||||
// TODO: 13/03/2026, Danil Nikolaev: check if message is exact same, then do not edit
|
// TODO: 13/03/2026, Danil Nikolaev: check if message is exact same, then do not edit
|
||||||
|
|
||||||
Log.d("MessagesHistoryViewModelImpl", "editMessage: $newMessage")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun markAsImportant(
|
private fun markAsImportant(
|
||||||
|
|||||||
-2
@@ -1,7 +1,6 @@
|
|||||||
package dev.meloda.fast.messageshistory.presentation
|
package dev.meloda.fast.messageshistory.presentation
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
@@ -129,7 +128,6 @@ fun MessagesList(
|
|||||||
when (attachment) {
|
when (attachment) {
|
||||||
is VkPhotoDomain -> {
|
is VkPhotoDomain -> {
|
||||||
val maxSize = attachment.getMaxSize()
|
val maxSize = attachment.getMaxSize()
|
||||||
Log.d("MessagesList", "onPhotoLongClicked. Max size: ${maxSize?.url}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -64,7 +64,6 @@ fun DynamicPreviewGrid(
|
|||||||
val spacingPx = with(LocalDensity.current) { spacing.toPx() }
|
val spacingPx = with(LocalDensity.current) { spacing.toPx() }
|
||||||
|
|
||||||
val rows = previews.chunked(3)
|
val rows = previews.chunked(3)
|
||||||
Log.d("ROWS", "DynamicPreviewGrid: ${rows.size}")
|
|
||||||
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(spacing)) {
|
Column(verticalArrangement = Arrangement.spacedBy(spacing)) {
|
||||||
rows.forEachIndexed { outerIndex, row ->
|
rows.forEachIndexed { outerIndex, row ->
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import dev.meloda.fast.common.extensions.findWithIndex
|
|||||||
import dev.meloda.fast.common.extensions.listenValue
|
import dev.meloda.fast.common.extensions.listenValue
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
import dev.meloda.fast.common.extensions.setValue
|
||||||
import dev.meloda.fast.common.model.DarkMode
|
import dev.meloda.fast.common.model.DarkMode
|
||||||
import dev.meloda.fast.common.model.LogLevel
|
import dev.meloda.fast.common.model.NetworkLogLevel
|
||||||
import dev.meloda.fast.common.model.LongPollState
|
import dev.meloda.fast.common.model.LongPollState
|
||||||
import dev.meloda.fast.common.model.UiText
|
import dev.meloda.fast.common.model.UiText
|
||||||
import dev.meloda.fast.common.model.parseString
|
import dev.meloda.fast.common.model.parseString
|
||||||
@@ -497,10 +497,10 @@ class SettingsViewModel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val logLevelValues = listOf(
|
val logLevelValues = listOf(
|
||||||
LogLevel.NONE to UiText.Simple("None"),
|
NetworkLogLevel.NONE to UiText.Simple("None"),
|
||||||
LogLevel.BASIC to UiText.Simple("Basic"),
|
NetworkLogLevel.BASIC to UiText.Simple("Basic"),
|
||||||
LogLevel.HEADERS to UiText.Simple("Headers"),
|
NetworkLogLevel.HEADERS to UiText.Simple("Headers"),
|
||||||
LogLevel.BODY to UiText.Simple("Body")
|
NetworkLogLevel.BODY to UiText.Simple("Body")
|
||||||
).toMap()
|
).toMap()
|
||||||
|
|
||||||
val debugNetworkLogLevel = SettingsItem.ListItem(
|
val debugNetworkLogLevel = SettingsItem.ListItem(
|
||||||
@@ -509,10 +509,10 @@ class SettingsViewModel(
|
|||||||
valueClass = Int::class,
|
valueClass = Int::class,
|
||||||
defaultValue = SettingsKeys.DEFAULT_NETWORK_LOG_LEVEL,
|
defaultValue = SettingsKeys.DEFAULT_NETWORK_LOG_LEVEL,
|
||||||
titles = logLevelValues.values.toList(),
|
titles = logLevelValues.values.toList(),
|
||||||
values = logLevelValues.keys.toList().map(LogLevel::value)
|
values = logLevelValues.keys.toList().map(NetworkLogLevel::value)
|
||||||
).apply {
|
).apply {
|
||||||
textProvider = TextProvider { item ->
|
textProvider = TextProvider { item ->
|
||||||
val textValue = logLevelValues[LogLevel.parse(item.value)].parseString(resources)
|
val textValue = logLevelValues[NetworkLogLevel.parse(item.value)].parseString(resources)
|
||||||
|
|
||||||
UiText.Simple("Current value: $textValue")
|
UiText.Simple("Current value: $textValue")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,3 +55,4 @@ include(":feature:friends")
|
|||||||
include(":feature:profile")
|
include(":feature:profile")
|
||||||
include(":feature:createchat")
|
include(":feature:createchat")
|
||||||
include(":core:presentation")
|
include(":core:presentation")
|
||||||
|
include(":core:logger")
|
||||||
|
|||||||
Reference in New Issue
Block a user