Updated on 2026-08-14
This commit is contained in:
parent
a774347c70
commit
a71e17561f
4 changed files with 56 additions and 10 deletions
|
|
@ -4,18 +4,15 @@ import android.content.Context
|
||||||
import com.tangem.datasource.BuildConfig
|
import com.tangem.datasource.BuildConfig
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import org.joda.time.DateTime
|
import org.joda.time.DateTime
|
||||||
import org.joda.time.format.DateTimeFormatterBuilder
|
import org.joda.time.format.DateTimeFormatterBuilder
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.BufferedWriter
|
import java.io.*
|
||||||
import java.io.File
|
import java.util.zip.ZipEntry
|
||||||
import java.io.FileWriter
|
import java.util.zip.ZipOutputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
@ -30,7 +27,7 @@ import javax.inject.Singleton
|
||||||
@Singleton
|
@Singleton
|
||||||
class AppLogsStore @Inject constructor(
|
class AppLogsStore @Inject constructor(
|
||||||
@ApplicationContext private val applicationContext: Context,
|
@ApplicationContext private val applicationContext: Context,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val scope = CoroutineScope(
|
private val scope = CoroutineScope(
|
||||||
|
|
@ -38,8 +35,10 @@ class AppLogsStore @Inject constructor(
|
||||||
CoroutineExceptionHandler { _, error -> Timber.e("AppLogsStore.scope is failed $error") },
|
CoroutineExceptionHandler { _, error -> Timber.e("AppLogsStore.scope is failed $error") },
|
||||||
)
|
)
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
private val zipMutex = Mutex()
|
||||||
|
|
||||||
private val file = File(applicationContext.filesDir, NEW_LOG_FILE_NAME)
|
private val file = File(applicationContext.filesDir, PERMITTED_FILE_NAME)
|
||||||
|
private val fileZip = File(applicationContext.filesDir, PERMITTED_FILE_NAME_ZIP)
|
||||||
|
|
||||||
private val formatter = DateTimeFormatterBuilder()
|
private val formatter = DateTimeFormatterBuilder()
|
||||||
.appendDayOfMonth(2)
|
.appendDayOfMonth(2)
|
||||||
|
|
@ -58,6 +57,16 @@ class AppLogsStore @Inject constructor(
|
||||||
/** Get log file */
|
/** Get log file */
|
||||||
fun getFile(): File? = if (file.exists()) file else null
|
fun getFile(): File? = if (file.exists()) file else null
|
||||||
|
|
||||||
|
suspend fun getZipFile(): File? {
|
||||||
|
return zipMutex.withLock {
|
||||||
|
if (file.exists()) {
|
||||||
|
zip(listOf(file), fileZip)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Save log [message] */
|
/** Save log [message] */
|
||||||
fun saveLogMessage(tag: String, message: String) {
|
fun saveLogMessage(tag: String, message: String) {
|
||||||
// Temporally logs are not saved in prod environment
|
// Temporally logs are not saved in prod environment
|
||||||
|
|
@ -133,8 +142,41 @@ class AppLogsStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("NestedBlockDepth")
|
||||||
|
private suspend fun zip(filesToCompress: List<File>, outputZipFile: File): File? {
|
||||||
|
return withContext(dispatchers.io) {
|
||||||
|
if (outputZipFile.exists() && !outputZipFile.delete()) {
|
||||||
|
return@withContext null
|
||||||
|
}
|
||||||
|
|
||||||
|
val buffer = ByteArray(BUFFER_SIZE)
|
||||||
|
|
||||||
|
FileOutputStream(outputZipFile).use { fos ->
|
||||||
|
ZipOutputStream(fos).use { zos ->
|
||||||
|
filesToCompress.forEach { file ->
|
||||||
|
FileInputStream(file).use { inStream ->
|
||||||
|
val ze = ZipEntry(file.name)
|
||||||
|
zos.putNextEntry(ze)
|
||||||
|
var len: Int
|
||||||
|
while (inStream.read(buffer).also { len = it } > 0) {
|
||||||
|
zos.write(buffer, 0, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zos.finish() // Ensures the zip output is finalized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputZipFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
const val BUFFER_SIZE = 1024
|
||||||
|
|
||||||
const val LOG_FILE_NAME = "logs.txt"
|
const val LOG_FILE_NAME = "logs.txt"
|
||||||
const val NEW_LOG_FILE_NAME = "app_logs.txt"
|
const val NEW_LOG_FILE_NAME = "app_logs.txt"
|
||||||
|
// the only name that we allow to send as email to company addresses
|
||||||
|
const val PERMITTED_FILE_NAME = "log.txt"
|
||||||
|
const val PERMITTED_FILE_NAME_ZIP = "log.zip"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +94,8 @@ internal class DefaultFeedbackRepository(
|
||||||
|
|
||||||
override fun getLogFile(): File? = appLogsStore.getFile()
|
override fun getLogFile(): File? = appLogsStore.getFile()
|
||||||
|
|
||||||
|
override suspend fun getZipLogFile(): File? = appLogsStore.getZipFile()
|
||||||
|
|
||||||
override fun sendEmail(feedbackEmail: FeedbackEmail) {
|
override fun sendEmail(feedbackEmail: FeedbackEmail) {
|
||||||
emailSender.send(
|
emailSender.send(
|
||||||
EmailSender.Email(
|
EmailSender.Email(
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class SendFeedbackEmailUseCase(
|
||||||
subject = emailSubjectResolver.resolve(type),
|
subject = emailSubjectResolver.resolve(type),
|
||||||
message = createMessage(type),
|
message = createMessage(type),
|
||||||
// Temporally user data is not sent
|
// Temporally user data is not sent
|
||||||
file = null, // feedbackRepository.getLogFile(),
|
file = null, // feedbackRepository.getZipLogFile(),
|
||||||
)
|
)
|
||||||
|
|
||||||
feedbackRepository.sendEmail(email)
|
feedbackRepository.sendEmail(email)
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,7 @@ interface FeedbackRepository {
|
||||||
|
|
||||||
fun getLogFile(): File?
|
fun getLogFile(): File?
|
||||||
|
|
||||||
|
suspend fun getZipLogFile(): File?
|
||||||
|
|
||||||
fun sendEmail(feedbackEmail: FeedbackEmail)
|
fun sendEmail(feedbackEmail: FeedbackEmail)
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue