TP-66620 | Alfred resource manager (#179)
Co-authored-by: Varun Jain <varun.jain@navi.com>
This commit is contained in:
@@ -46,7 +46,7 @@ import com.navi.alfred.utils.buildNegativeCaseEvent
|
||||
import com.navi.alfred.utils.captureBottomSheet
|
||||
import com.navi.alfred.utils.captureScreen
|
||||
import com.navi.alfred.utils.captureScreenshotOfCustomView
|
||||
import com.navi.alfred.utils.checkDbBeforeStartRecording
|
||||
import com.navi.alfred.utils.checkDbAndDeleteCorruptScreenshots
|
||||
import com.navi.alfred.utils.createBitmapForView
|
||||
import com.navi.alfred.utils.getCruiseConfig
|
||||
import com.navi.alfred.utils.getTouchEvent
|
||||
@@ -65,6 +65,7 @@ import java.lang.ref.WeakReference
|
||||
import java.util.Timer
|
||||
import java.util.TimerTask
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import okhttp3.Request
|
||||
@@ -94,7 +95,6 @@ object AlfredManager {
|
||||
internal var sessionStartRecordingTimeForCrash: Long? = null
|
||||
internal var eventStartRecordingTimeForCrash: Long? = null
|
||||
internal var timer: Timer? = null
|
||||
internal var zipUploadRetryCount: Int = 0
|
||||
internal var currentActivity: WeakReference<Activity>? = null
|
||||
internal lateinit var alfredDataBase: AlfredDatabase
|
||||
internal lateinit var screenShotDao: ScreenShotDao
|
||||
@@ -103,16 +103,27 @@ object AlfredManager {
|
||||
internal lateinit var analyticsDao: AnalyticsDAO
|
||||
internal lateinit var negativeCaseDao: NegativeCaseDao
|
||||
internal lateinit var failureEventDao: FailureEventDao
|
||||
internal lateinit var zipFileDetails: List<ZipDetailsHelper>
|
||||
internal lateinit var zipFileDetailsList: List<ZipDetailsHelper>
|
||||
internal lateinit var applicationContext: Context
|
||||
internal const val imageThreshHoldValue: Int = 60
|
||||
internal const val imageSecondThreshHoldValue: Int = 100
|
||||
var dialog: Dialog? = null
|
||||
lateinit var sensitiveComposeRepository: ComposeMaskingRepoImpl
|
||||
internal var isCriticalUserJourneyActive: AtomicBoolean = AtomicBoolean(false)
|
||||
internal var hasCheckedDb: Boolean = true
|
||||
internal var criticalJourneyResponseCode: Int = 0
|
||||
internal var criticalJourneyResponseMessage: String = ""
|
||||
|
||||
fun init(config: AlfredConfig, context: Context) {
|
||||
fun init(
|
||||
config: AlfredConfig,
|
||||
context: Context,
|
||||
criticalJourneyResponseCode: Int,
|
||||
criticalJourneyResponseMessage: String
|
||||
) {
|
||||
this.config = config
|
||||
this.applicationContext = context
|
||||
this.criticalJourneyResponseCode = criticalJourneyResponseCode
|
||||
this.criticalJourneyResponseMessage = criticalJourneyResponseMessage
|
||||
AlfredRetrofitProvider.init(applicationContext)
|
||||
AlfredFailureRetrofitProvider.init(applicationContext)
|
||||
config.setCruiseAttributes()
|
||||
@@ -135,7 +146,7 @@ object AlfredManager {
|
||||
screenShotTimer?.cancel()
|
||||
screenShotTimer = Timer()
|
||||
if (!hasRecordingStarted) {
|
||||
checkDbBeforeStartRecording()
|
||||
checkDbAndDeleteCorruptScreenshots()
|
||||
config.setAlfredSessionId()
|
||||
config.setAlfredEventId()
|
||||
config.setSessionStartRecordingTime()
|
||||
@@ -148,6 +159,7 @@ object AlfredManager {
|
||||
moduleName = moduleName
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(AddEventTask(startRecordingEvent, context))
|
||||
hasCheckedDb = false
|
||||
}
|
||||
currentActivity = WeakReference(activity)
|
||||
setCurrentScreenName(screenName)
|
||||
@@ -249,6 +261,7 @@ object AlfredManager {
|
||||
isAppInBackground = true
|
||||
hasRecordingStarted = false
|
||||
screenShotTimer?.cancel()
|
||||
isCriticalUserJourneyActive.set(false)
|
||||
val appBackgroundView =
|
||||
LayoutInflater.from(applicationContext)
|
||||
.inflate(R.layout.app_background_screen, null)
|
||||
@@ -488,4 +501,8 @@ object AlfredManager {
|
||||
fun isSensitiveComposeRepositoryInitialized(): Boolean {
|
||||
return this::sensitiveComposeRepository.isInitialized
|
||||
}
|
||||
|
||||
fun updateCriticalUserJourneyStatus(status: Boolean) {
|
||||
isCriticalUserJourneyActive.set(status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ package com.navi.alfred.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.navi.alfred.db.dao.AnalyticsDAO
|
||||
import com.navi.alfred.db.dao.ApiMetricDao
|
||||
import com.navi.alfred.db.dao.FailureEventDao
|
||||
@@ -21,6 +23,8 @@ import com.navi.alfred.db.model.FailureEvent
|
||||
import com.navi.alfred.db.model.NegativeCase
|
||||
import com.navi.alfred.db.model.ScreenShotPathHelper
|
||||
import com.navi.alfred.db.model.ZipDetailsHelper
|
||||
import com.navi.alfred.utils.AlfredConstants.DB_VERSION_V5
|
||||
import com.navi.alfred.utils.AlfredConstants.DB_VERSION_V6
|
||||
|
||||
@Database(
|
||||
entities =
|
||||
@@ -32,7 +36,7 @@ import com.navi.alfred.db.model.ZipDetailsHelper
|
||||
NegativeCase::class,
|
||||
FailureEvent::class
|
||||
],
|
||||
version = 5,
|
||||
version = 6,
|
||||
exportSchema = true
|
||||
)
|
||||
abstract class AlfredDatabase : RoomDatabase() {
|
||||
@@ -48,3 +52,15 @@ abstract class AlfredDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun failureEventDao(): FailureEventDao
|
||||
}
|
||||
|
||||
val ALFRED_DATABASE_MIGRATION_5_6 =
|
||||
object : Migration(DB_VERSION_V5, DB_VERSION_V6) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// this migration changes schema of ZipDetailsHelper table by adding a new column
|
||||
// zipUploadStatus and removing a redundant column zipFileName
|
||||
database.execSQL("ALTER TABLE ZipDetailsHelper DROP COLUMN zipFileName")
|
||||
database.execSQL(
|
||||
"ALTER TABLE ZipDetailsHelper ADD COLUMN zipUploadStatus INTEGER NOT NULL DEFAULT 0"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ object AlfredDatabaseHelper {
|
||||
}
|
||||
val instance =
|
||||
Room.databaseBuilder(context, AlfredDatabase::class.java, EVENT_DB_NAME)
|
||||
.addMigrations(ALFRED_DATABASE_MIGRATION_5_6)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
INSTANCE = instance
|
||||
|
||||
@@ -43,6 +43,11 @@ interface ScreenShotDao {
|
||||
@Query("SELECT * FROM ScreenShotPathHelper")
|
||||
fun fetchAllScreenShotsPath(): List<ScreenShotPathHelper>
|
||||
|
||||
@Query("SELECT id FROM ScreenShotPathHelper") fun fetchAllScreenShotIds(): List<Int>
|
||||
|
||||
@Query("SELECT * FROM ScreenShotPathHelper WHERE id IN (:idList)")
|
||||
fun fetchAllScreenShotsPathFromIds(idList: List<Int>): List<ScreenShotPathHelper>
|
||||
|
||||
@Query("DELETE FROM ScreenShotPathHelper WHERE id IN (:idList)")
|
||||
fun deleteScreenShot(idList: List<Int>)
|
||||
|
||||
@@ -62,6 +67,20 @@ interface ZipDetailsDao {
|
||||
fun fetchAllZipFilesDetails(): List<ZipDetailsHelper>
|
||||
|
||||
@Query("DELETE FROM ZipDetailsHelper WHERE id = :id") fun deleteZipFileDetail(id: Int)
|
||||
|
||||
@Query("SELECT count(*) FROM ZipDetailsHelper WHERE alfredEventId = :zipFileName")
|
||||
fun zipDetailExists(zipFileName: String): Boolean
|
||||
|
||||
@Query("SELECT ZipDetailsHelper.* FROM ZipDetailsHelper WHERE alfredEventId = :zipFileName")
|
||||
fun getZipFileDetail(zipFileName: String): ZipDetailsHelper?
|
||||
|
||||
@Query("DELETE FROM ZipDetailsHelper WHERE alfredEventId = :zipFileName")
|
||||
fun deleteZipFileDetail(zipFileName: String)
|
||||
|
||||
@Query(
|
||||
"UPDATE ZipDetailsHelper SET zipUploadStatus = :status WHERE alfredEventId = :zipFileName"
|
||||
)
|
||||
fun updateZipUploadStatus(zipFileName: String, status: Boolean)
|
||||
}
|
||||
|
||||
@Dao
|
||||
|
||||
@@ -33,9 +33,9 @@ data class ZipDetailsHelper(
|
||||
@ColumnInfo(name = "alfredSessionId") val alfredSessionId: String,
|
||||
@ColumnInfo(name = "sessionStartRecordingTime") val sessionStartRecordingTime: Long,
|
||||
@ColumnInfo(name = "eventStartRecordingTime") val eventStartRecordingTime: Long,
|
||||
@ColumnInfo(name = "zipFileName") val zipFileName: String,
|
||||
@ColumnInfo(name = "alfredEventId") val alfredEventId: String,
|
||||
@ColumnInfo(name = "latestScreenshotTimestamp") val latestScreenshotTimestamp: Long
|
||||
@ColumnInfo(name = "latestScreenshotTimestamp") val latestScreenshotTimestamp: Long,
|
||||
@ColumnInfo(name = "zipUploadStatus") val zipUploadStatus: Boolean
|
||||
) {
|
||||
@PrimaryKey(autoGenerate = true) var id: Int = 0
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
package com.navi.alfred.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.navi.alfred.db.model.ScreenShotPathHelper
|
||||
import java.util.*
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -19,9 +18,8 @@ data class WorkManagerFailureInputData(
|
||||
var alfredSessionId: String? = "",
|
||||
var sessionStartRecordingTime: Long = 0L,
|
||||
var eventStartRecordingTime: Long = 0L,
|
||||
var screenShots: List<ScreenShotPathHelper> = emptyList(),
|
||||
val id: UUID,
|
||||
val zipFileName: String,
|
||||
val alfredEventId: String,
|
||||
val latestScreenshotTimestamp: Long = 0L
|
||||
val latestScreenshotTimestamp: Long = 0L,
|
||||
var retryCount: Int = 0
|
||||
)
|
||||
|
||||
@@ -57,15 +57,12 @@ object AlfredFailureRetrofitProvider {
|
||||
.build()
|
||||
}
|
||||
} else {
|
||||
val errorMessage = ErrorMessage()
|
||||
errorMessage.statusCode = ApiConstants.NO_INTERNET
|
||||
errorMessage.message = AlfredConstants.NO_INTERNET_MESSAGE
|
||||
Response.Builder()
|
||||
.request(request)
|
||||
.protocol(Protocol.HTTP_2)
|
||||
.code(errorMessage.statusCode.orZero())
|
||||
.code(ApiConstants.NO_INTERNET.orZero())
|
||||
.body(ResponseBody.create("application/json".toMediaTypeOrNull(), "{}"))
|
||||
.message(errorMessage.message.orEmpty())
|
||||
.message(AlfredConstants.NO_INTERNET_MESSAGE.orEmpty())
|
||||
.build()
|
||||
}
|
||||
AlfredApiLogsManager.getAlfredApiLogsProvider()
|
||||
@@ -74,6 +71,7 @@ object AlfredFailureRetrofitProvider {
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
val alfredResourceManagerInterceptor = AlfredResourceManagerInterceptor()
|
||||
okHttpClient =
|
||||
OkHttpClient.Builder()
|
||||
.apply {
|
||||
@@ -90,6 +88,9 @@ object AlfredFailureRetrofitProvider {
|
||||
)
|
||||
}
|
||||
addInterceptor(networkInterceptor)
|
||||
addInterceptor(
|
||||
alfredResourceManagerInterceptor.resourceManagerNetworkInterceptor
|
||||
)
|
||||
connectionPool(ConnectionPool(0, 5, TimeUnit.MINUTES))
|
||||
.protocols(listOf(Protocol.HTTP_1_1))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.alfred.network
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.navi.alfred.AlfredManager
|
||||
import com.navi.alfred.model.ErrorMessage
|
||||
import com.navi.alfred.network.model.RequestInfo
|
||||
import com.navi.alfred.utils.AlfredConstants.BROADCAST_ACTION_TYPE
|
||||
import com.navi.alfred.utils.AlfredConstants.BROADCAST_ERROR_MESSAGE
|
||||
import com.navi.alfred.utils.AlfredConstants.BROADCAST_EXCEPTION
|
||||
import com.navi.alfred.utils.AlfredConstants.BROADCAST_REQUEST
|
||||
import com.navi.alfred.utils.AlfredConstants.HEADER_APP_REQUEST_ID
|
||||
import com.navi.alfred.utils.AlfredConstants.HEADER_X_TARGET
|
||||
import com.navi.alfred.utils.handleException
|
||||
import com.navi.alfred.utils.orZero
|
||||
import okhttp3.*
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
|
||||
internal class AlfredResourceManagerInterceptor {
|
||||
|
||||
val resourceManagerNetworkInterceptor: Interceptor
|
||||
get() = Interceptor { chain ->
|
||||
val request = chain.request()
|
||||
val response =
|
||||
if (!AlfredManager.isCriticalUserJourneyActive.get()) {
|
||||
try {
|
||||
chain.proceed(request).newBuilder().build()
|
||||
} catch (e: Exception) {
|
||||
val errorMessage = handleException(e)
|
||||
// send failure broadcast if the request is critical
|
||||
setNetworkFailureBroadcast(e, errorMessage, request)
|
||||
// A mocked response in case of n/w exception
|
||||
Response.Builder()
|
||||
.request(request)
|
||||
.protocol(Protocol.HTTP_2)
|
||||
.code(errorMessage.statusCode.orZero())
|
||||
.body(ResponseBody.create("application/json".toMediaTypeOrNull(), "{}"))
|
||||
.message(errorMessage.message.orEmpty())
|
||||
.build()
|
||||
}
|
||||
} else {
|
||||
Response.Builder()
|
||||
.request(request)
|
||||
.protocol(Protocol.HTTP_2)
|
||||
.code(AlfredManager.criticalJourneyResponseCode.orZero())
|
||||
.body(ResponseBody.create("application/json".toMediaTypeOrNull(), "{}"))
|
||||
.message(AlfredManager.criticalJourneyResponseMessage.orEmpty())
|
||||
.build()
|
||||
}
|
||||
AlfredApiLogsManager.getAlfredApiLogsProvider()
|
||||
?.sendApiLog(request = request, response = response)
|
||||
response
|
||||
}
|
||||
|
||||
private fun setNetworkFailureBroadcast(
|
||||
e: Exception,
|
||||
errorMessage: ErrorMessage,
|
||||
request: Request
|
||||
) {
|
||||
Log.d("Alfred", "AlfredResourceManagerInterceptor: setNetworkFailureBroadcast")
|
||||
val intent = Intent(BROADCAST_ACTION_TYPE)
|
||||
val requestInfo =
|
||||
RequestInfo(
|
||||
request.url.toString(),
|
||||
request.headers[HEADER_X_TARGET],
|
||||
request.headers[HEADER_APP_REQUEST_ID]
|
||||
)
|
||||
intent.putExtra(BROADCAST_EXCEPTION, e)
|
||||
intent.putExtra(BROADCAST_ERROR_MESSAGE, errorMessage)
|
||||
intent.putExtra(BROADCAST_REQUEST, requestInfo)
|
||||
AlfredManager.applicationContext.sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
@@ -67,15 +67,12 @@ object AlfredRetrofitProvider {
|
||||
.build()
|
||||
}
|
||||
} else {
|
||||
val errorMessage = ErrorMessage()
|
||||
errorMessage.statusCode = ApiConstants.NO_INTERNET
|
||||
errorMessage.message = AlfredConstants.NO_INTERNET_MESSAGE
|
||||
Response.Builder()
|
||||
.request(request)
|
||||
.protocol(Protocol.HTTP_2)
|
||||
.code(errorMessage.statusCode.orZero())
|
||||
.code(ApiConstants.NO_INTERNET.orZero())
|
||||
.body(ResponseBody.create("application/json".toMediaTypeOrNull(), "{}"))
|
||||
.message(errorMessage.message.orEmpty())
|
||||
.message(AlfredConstants.NO_INTERNET_MESSAGE.orEmpty())
|
||||
.build()
|
||||
}
|
||||
AlfredApiLogsManager.getAlfredApiLogsProvider()
|
||||
@@ -84,6 +81,7 @@ object AlfredRetrofitProvider {
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
val alfredResourceManagerInterceptor = AlfredResourceManagerInterceptor()
|
||||
okHttpClient =
|
||||
OkHttpClient.Builder()
|
||||
.apply {
|
||||
@@ -100,6 +98,9 @@ object AlfredRetrofitProvider {
|
||||
)
|
||||
}
|
||||
addInterceptor(networkInterceptor)
|
||||
addInterceptor(
|
||||
alfredResourceManagerInterceptor.resourceManagerNetworkInterceptor
|
||||
)
|
||||
connectionPool(ConnectionPool(0, 5, TimeUnit.MINUTES))
|
||||
.protocols(listOf(Protocol.HTTP_1_1))
|
||||
}
|
||||
|
||||
@@ -111,4 +111,13 @@ object AlfredConstants {
|
||||
const val LATEST_SCREENSHOT_TIMESTAMP = "LATEST_SCREENSHOT_TIMESTAMP"
|
||||
const val SNAPSHOT_PER_SECOND = "SNAPSHOT_PER_SECOND"
|
||||
const val NO_INTERNET_MESSAGE = "No internet"
|
||||
const val IS_ZIP_FILE_PREPARED = "IS_ZIP_FILE_PREPARED"
|
||||
const val ZIP_UPLOAD_WORK = "ZIP_UPLOAD_WORK"
|
||||
const val EVENTS_UPLOAD_WORK = "EVENTS_UPLOAD_WORK"
|
||||
const val ZIP_UPLOAD_RETRY_WORK = "ZIP_UPLOAD_RETRY_WORK"
|
||||
const val ZIP_FILE_DETAIL_LIST = "zipFileDetailList"
|
||||
const val CRITICAL_SECTION_MESSAGE = "CRITICAL_SECTION_ENTERED"
|
||||
const val SCREENSHOT_ID_LIST = "SCREENSHOT_ID_LIST"
|
||||
const val DB_VERSION_V5 = 5
|
||||
const val DB_VERSION_V6 = 6
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ object ApiConstants {
|
||||
const val API_CODE_UNKNOWN_HOST = 21
|
||||
const val API_CODE_CONNECT_EXCEPTION = 22
|
||||
const val API_CODE_SOCKET = 26
|
||||
const val CRITICAL_SECTION = 27
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.navi.alfred.db.model.AnalyticsEvent
|
||||
import com.navi.alfred.db.model.ApiMetricHelper
|
||||
import com.navi.alfred.db.model.FailureEvent
|
||||
import com.navi.alfred.db.model.NegativeCase
|
||||
import com.navi.alfred.db.model.ZipDetailsHelper
|
||||
import com.navi.alfred.dispatcher.AlfredDispatcher
|
||||
import com.navi.alfred.model.AnalyticsRequest
|
||||
import com.navi.alfred.model.BaseAttribute
|
||||
@@ -94,7 +95,10 @@ internal suspend fun sendEventsToServer(
|
||||
)
|
||||
true
|
||||
} else {
|
||||
if (!isNoInternetResponse(response.code())) {
|
||||
if (
|
||||
!isNoInternetResponse(response.code()) &&
|
||||
!isCriticalJourneyResponse(response.code())
|
||||
) {
|
||||
val zipNamesList: MutableList<String> = mutableListOf()
|
||||
val eventIdList: MutableList<String> = mutableListOf()
|
||||
events.forEach { event ->
|
||||
@@ -197,7 +201,10 @@ internal suspend fun sendNegativeCaseToServer(
|
||||
)
|
||||
true
|
||||
} else {
|
||||
if (!isNoInternetResponse(response.code())) {
|
||||
if (
|
||||
!isNoInternetResponse(response.code()) &&
|
||||
!isCriticalJourneyResponse(response.code())
|
||||
) {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = API_ERROR,
|
||||
@@ -280,9 +287,8 @@ internal suspend fun sendFailureEventsToServer(workManagerFlow: Boolean? = false
|
||||
)
|
||||
return if (
|
||||
(response.isSuccessful &&
|
||||
response.code() == AlfredConstants.CODE_API_SUCCESS) or
|
||||
(response.code() == AlfredConstants.CODE_API_BAD_REQUEST) or
|
||||
(isNoInternetResponse(response.code()))
|
||||
response.code() == AlfredConstants.CODE_API_SUCCESS) ||
|
||||
(response.code() == AlfredConstants.CODE_API_BAD_REQUEST)
|
||||
) {
|
||||
AlfredManager.failureEventDao.deleteFailureEvents(
|
||||
failureEvents.map { it.eventId }
|
||||
@@ -310,76 +316,37 @@ internal suspend fun sendFailureEventsToServer(workManagerFlow: Boolean? = false
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun sendAlfredSessionEvent(
|
||||
dumpFlow: Boolean = false,
|
||||
index: Int? = null,
|
||||
currentZipName: String? = null,
|
||||
latestScreenshotTimestamp: Long? = null
|
||||
) {
|
||||
var request: SessionRequest? = null
|
||||
if (dumpFlow) {
|
||||
var clientTs: Long? = null
|
||||
var sessionTimeStamp: Long? = null
|
||||
var sessionId: String? = null
|
||||
if (index != null) {
|
||||
val zipFileDetail = AlfredManager.zipFileDetails[index]
|
||||
clientTs = zipFileDetail.eventStartRecordingTime
|
||||
sessionTimeStamp = zipFileDetail.sessionStartRecordingTime
|
||||
sessionId = zipFileDetail.alfredSessionId
|
||||
} else {
|
||||
clientTs = AlfredManager.eventStartRecordingTimeForCrash
|
||||
sessionTimeStamp = AlfredManager.sessionStartRecordingTimeForCrash
|
||||
sessionId = AlfredManager.sessionIdForCrash
|
||||
}
|
||||
internal fun sendAlfredSessionEvent(zipFileDetails: ZipDetailsHelper, dumpFlow: Boolean = false) {
|
||||
val request =
|
||||
SessionRequest(
|
||||
base_attribute =
|
||||
BaseAttribute(
|
||||
sessionId = zipFileDetails.alfredSessionId,
|
||||
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
|
||||
clientTs = zipFileDetails.eventStartRecordingTime,
|
||||
latestScreenshotTimestamp = zipFileDetails.latestScreenshotTimestamp,
|
||||
sessionTimeStamp = zipFileDetails.sessionStartRecordingTime
|
||||
),
|
||||
session_upload_event_attributes =
|
||||
SessionEventAttribute(
|
||||
eventId = zipFileDetails.alfredEventId,
|
||||
beginningDeviceAttributes =
|
||||
DeviceAttributes(
|
||||
battery = AlfredManager.config.batteryPercentageBeforeEventStart,
|
||||
cpu = AlfredManager.config.cpuUsageBeforeEventStart,
|
||||
memory = AlfredManager.config.memoryUsageBeforeEventStart,
|
||||
storage = AlfredManager.config.storageUsageBeforeEventStart
|
||||
),
|
||||
endDeviceAttributes =
|
||||
DeviceAttributes(
|
||||
battery = AlfredManager.config.getBatteryPercentage(),
|
||||
cpu = AlfredManager.config.getCpuUsage(),
|
||||
memory = AlfredManager.config.getMemoryUsagePercentage(),
|
||||
storage = AlfredManager.config.getStorageUsage()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
request =
|
||||
SessionRequest(
|
||||
base_attribute =
|
||||
BaseAttribute(
|
||||
sessionId = sessionId,
|
||||
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
|
||||
clientTs = clientTs,
|
||||
sessionTimeStamp = sessionTimeStamp,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp
|
||||
),
|
||||
session_upload_event_attributes =
|
||||
SessionEventAttribute(
|
||||
eventId = currentZipName,
|
||||
beginningDeviceAttributes = DeviceAttributes(),
|
||||
endDeviceAttributes = DeviceAttributes()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
request =
|
||||
SessionRequest(
|
||||
base_attribute =
|
||||
BaseAttribute(
|
||||
sessionId = AlfredManager.config.getAlfredSessionId(),
|
||||
eventTimeStamp = AlfredManager.config.getEventTimeStamp(),
|
||||
clientTs = AlfredManager.config.getEventStartRecordingTime(),
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp,
|
||||
sessionTimeStamp = AlfredManager.config.getSessionStartRecordingTime()
|
||||
),
|
||||
session_upload_event_attributes =
|
||||
SessionEventAttribute(
|
||||
eventId = currentZipName,
|
||||
beginningDeviceAttributes =
|
||||
DeviceAttributes(
|
||||
battery = AlfredManager.config.batteryPercentageBeforeEventStart,
|
||||
cpu = AlfredManager.config.cpuUsageBeforeEventStart,
|
||||
memory = AlfredManager.config.memoryUsageBeforeEventStart,
|
||||
storage = AlfredManager.config.storageUsageBeforeEventStart
|
||||
),
|
||||
endDeviceAttributes =
|
||||
DeviceAttributes(
|
||||
battery = AlfredManager.config.getBatteryPercentage(),
|
||||
cpu = AlfredManager.config.getCpuUsage(),
|
||||
memory = AlfredManager.config.getMemoryUsagePercentage(),
|
||||
storage = AlfredManager.config.getStorageUsage()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
AlfredManager.coroutineScope.launch {
|
||||
val response =
|
||||
AlfredManager.networkRepository.sendSession(
|
||||
@@ -388,21 +355,24 @@ internal fun sendAlfredSessionEvent(
|
||||
request
|
||||
)
|
||||
if (response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
|
||||
AlfredManager.zipDetailsDao.deleteZipFileDetail(zipFileDetails.alfredEventId)
|
||||
if (!dumpFlow) {
|
||||
AlfredManager.config.setEventStartRecordingTime(true)
|
||||
handleDeviceAttributes()
|
||||
} else {
|
||||
index?.let { index ->
|
||||
AlfredManager.zipDetailsDao.deleteZipFileDetail(
|
||||
AlfredManager.zipFileDetails[index].id
|
||||
)
|
||||
}
|
||||
if (AlfredManager.workFailureData.size > 0) {
|
||||
AlfredManager.workFailureData.removeAt(0)
|
||||
}
|
||||
}
|
||||
if (!AlfredManager.hasCheckedDb) {
|
||||
checkDbToUploadFailedZips()
|
||||
AlfredManager.hasCheckedDb = true
|
||||
}
|
||||
} else {
|
||||
if (!isNoInternetResponse(response.code())) {
|
||||
if (
|
||||
!isNoInternetResponse(response.code()) &&
|
||||
!isCriticalJourneyResponse(response.code())
|
||||
) {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = API_ERROR,
|
||||
@@ -474,7 +444,10 @@ internal suspend fun sendIngestMetric(
|
||||
)
|
||||
true
|
||||
} else {
|
||||
if (!isNoInternetResponse(response.code())) {
|
||||
if (
|
||||
!isNoInternetResponse(response.code()) &&
|
||||
!isCriticalJourneyResponse(response.code())
|
||||
) {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = API_ERROR,
|
||||
|
||||
@@ -55,3 +55,7 @@ internal fun isNetworkAvailable(): Boolean {
|
||||
internal fun isNoInternetResponse(responseCode: Int): Boolean {
|
||||
return responseCode == ApiConstants.NO_INTERNET
|
||||
}
|
||||
|
||||
internal fun isCriticalJourneyResponse(responseCode: Int): Boolean {
|
||||
return responseCode == AlfredManager.criticalJourneyResponseCode
|
||||
}
|
||||
|
||||
@@ -243,40 +243,38 @@ internal suspend fun captureBottomSheet(
|
||||
if (AlfredManager.config.getDisableDialogScreenShot()) {
|
||||
return
|
||||
}
|
||||
if (bottomSheetView != null) {
|
||||
val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView)
|
||||
val bottomSheetScreenShot =
|
||||
captureScreen(
|
||||
bottomSheetView,
|
||||
context,
|
||||
true,
|
||||
screenName,
|
||||
AlfredManager.coroutineScope,
|
||||
bottomSheetCanvasForBitmap?.first,
|
||||
bottomSheetCanvasForBitmap?.second,
|
||||
moduleName = moduleName
|
||||
)
|
||||
val backgroundScreenShot =
|
||||
captureScreen(
|
||||
view,
|
||||
context,
|
||||
true,
|
||||
screenName,
|
||||
AlfredManager.coroutineScope,
|
||||
rootCanvas,
|
||||
rootBmp,
|
||||
moduleName = moduleName
|
||||
)
|
||||
if (bottomSheetScreenShot != null && backgroundScreenShot != null) {
|
||||
combineScreenshots(
|
||||
backgroundScreenShot,
|
||||
bottomSheetScreenShot,
|
||||
context,
|
||||
screenName,
|
||||
moduleName = moduleName,
|
||||
scope = AlfredManager.coroutineScope
|
||||
)
|
||||
}
|
||||
val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView)
|
||||
val bottomSheetScreenShot =
|
||||
captureScreen(
|
||||
bottomSheetView,
|
||||
context,
|
||||
true,
|
||||
screenName,
|
||||
AlfredManager.coroutineScope,
|
||||
bottomSheetCanvasForBitmap?.first,
|
||||
bottomSheetCanvasForBitmap?.second,
|
||||
moduleName = moduleName
|
||||
)
|
||||
val backgroundScreenShot =
|
||||
captureScreen(
|
||||
view,
|
||||
context,
|
||||
true,
|
||||
screenName,
|
||||
AlfredManager.coroutineScope,
|
||||
rootCanvas,
|
||||
rootBmp,
|
||||
moduleName = moduleName
|
||||
)
|
||||
if (bottomSheetScreenShot != null && backgroundScreenShot != null) {
|
||||
combineScreenshots(
|
||||
backgroundScreenShot,
|
||||
bottomSheetScreenShot,
|
||||
context,
|
||||
screenName,
|
||||
moduleName = moduleName,
|
||||
scope = AlfredManager.coroutineScope
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import com.google.gson.Gson
|
||||
import com.navi.alfred.AlfredManager
|
||||
import com.navi.alfred.db.model.ZipDetailsHelper
|
||||
import com.navi.alfred.dispatcher.AlfredDispatcher
|
||||
import com.navi.alfred.utils.AlfredConstants.API_ERROR
|
||||
import com.navi.alfred.utils.AlfredConstants.DEFAULT_GET_PRE_SIGNED_URL_URL
|
||||
@@ -26,89 +26,73 @@ import com.navi.alfred.utils.AlfredConstants.ZIP_ERROR
|
||||
import com.navi.alfred.worker.AddFailureTask
|
||||
import com.navi.alfred.worker.UploadEventsWorker
|
||||
import com.navi.alfred.worker.UploadFileWorker
|
||||
import com.navi.alfred.worker.ZipUploadRetryWorker
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import retrofit2.Response
|
||||
|
||||
internal suspend fun getPreSignedUrl(
|
||||
zipFileName: String,
|
||||
zipFileDetail: ZipDetailsHelper,
|
||||
dumpFlow: Boolean = false,
|
||||
index: Int? = null,
|
||||
workManagerFlow: Boolean? = false,
|
||||
alfredEventIdForDumpFlow: String? = null,
|
||||
latestScreenshotTimestamp: Long? = null
|
||||
workManagerFlow: Boolean? = false
|
||||
) {
|
||||
val currentZipName: String
|
||||
val latestScreenshotName: Long
|
||||
|
||||
if (dumpFlow) {
|
||||
currentZipName =
|
||||
alfredEventIdForDumpFlow
|
||||
?: UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
|
||||
latestScreenshotName = latestScreenshotTimestamp ?: 0L
|
||||
} else {
|
||||
currentZipName = AlfredManager.config.getAlfredEventId()
|
||||
latestScreenshotName = AlfredManager.config.getLatestScreenshotTimestamp()
|
||||
AlfredManager.config.setAlfredEventId()
|
||||
}
|
||||
val currentZipName = zipFileDetail.alfredEventId
|
||||
val bucketKey = currentZipName.plus(AlfredConstants.ZIP_FILE_EXTENSION)
|
||||
val zipUploadStatus = zipFileDetail.zipUploadStatus
|
||||
|
||||
val response =
|
||||
AlfredManager.networkRepository.getPreSignedUrl(bucketKey, AlfredManager.config.getApiKey())
|
||||
if (!zipUploadStatus) {
|
||||
AlfredManager.networkRepository.getPreSignedUrl(
|
||||
bucketKey,
|
||||
AlfredManager.config.getApiKey()
|
||||
)
|
||||
} else {
|
||||
Response.error(AlfredConstants.CODE_API_BAD_REQUEST, null)
|
||||
}
|
||||
|
||||
if (response.isSuccessful && response.code() == AlfredConstants.CODE_API_SUCCESS) {
|
||||
checkFileExists(zipFileName, AlfredManager.applicationContext)?.let { file ->
|
||||
checkFileExists(currentZipName, AlfredManager.applicationContext)?.let { file ->
|
||||
response.body()?.data?.let {
|
||||
uploadFile(
|
||||
file,
|
||||
it,
|
||||
dumpFlow,
|
||||
index,
|
||||
zipFileName,
|
||||
workManagerFlow = workManagerFlow,
|
||||
currentZipName = currentZipName,
|
||||
latestScreenshotTimestamp = latestScreenshotName
|
||||
zipFileDetails = zipFileDetail,
|
||||
uploadFile = file,
|
||||
url = it,
|
||||
dumpFlow = dumpFlow,
|
||||
workManagerFlow = workManagerFlow
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = API_ERROR,
|
||||
requestUrl = DEFAULT_GET_PRE_SIGNED_URL_URL.plus(bucketKey),
|
||||
requestMethod = GET_METHOD,
|
||||
zipName = listOf(currentZipName),
|
||||
errorStatusCode = response.code().toLong(),
|
||||
errorMessage = response.message(),
|
||||
errorName = GET_PRE_SIGNED_URL_FAILURE
|
||||
if (!isCriticalJourneyResponse(response.code())) {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = API_ERROR,
|
||||
requestUrl = DEFAULT_GET_PRE_SIGNED_URL_URL.plus(bucketKey),
|
||||
requestMethod = GET_METHOD,
|
||||
zipName = listOf(currentZipName),
|
||||
errorStatusCode = response.code().toLong(),
|
||||
errorMessage = response.message(),
|
||||
errorName = GET_PRE_SIGNED_URL_FAILURE
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(
|
||||
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(
|
||||
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
|
||||
)
|
||||
}
|
||||
|
||||
if (!dumpFlow) {
|
||||
AlfredManager.hasUploadFlowStarted = false
|
||||
AlfredManager.config.getEventStartRecordingTime()?.let { eventStartRecordingTime ->
|
||||
insertZipDetailsToDbForDumpingLater(
|
||||
AlfredManager.config.getAlfredSessionId(),
|
||||
AlfredManager.config.getSessionStartRecordingTime(),
|
||||
eventStartRecordingTime,
|
||||
zipFileName,
|
||||
currentZipName,
|
||||
latestScreenshotName
|
||||
)
|
||||
}
|
||||
AlfredManager.config.setEventStartRecordingTime(true)
|
||||
}
|
||||
if (workManagerFlow == true) {
|
||||
val latestScreenshotName = zipFileDetail.latestScreenshotTimestamp
|
||||
uploadWorkManagerFailedZip(currentZipName, latestScreenshotName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkAndInitiateFileUploadWorkManager() {
|
||||
val screenShotList = AlfredManager.screenShotDao.fetchAllScreenShotsPath()
|
||||
AlfredManager.screenShotDao.deleteAllScreenShot()
|
||||
ScreenShotStorageHelper.clearAll()
|
||||
val screenShotIds = AlfredManager.screenShotDao.fetchAllScreenShotIds()
|
||||
AlfredManager.isAppInBackground = false
|
||||
AlfredManager.hasRecordingStarted = false
|
||||
AlfredManager.config.clearZipFileName()
|
||||
@@ -125,7 +109,7 @@ internal fun checkAndInitiateFileUploadWorkManager() {
|
||||
.putString(AlfredConstants.ALFRED_SESSION_ID, alfredSessionId)
|
||||
.putLong(AlfredConstants.SESSION_START_RECORDING_TIME, sessionTimestamp)
|
||||
.putLong(AlfredConstants.EVENT_START_RECORDING_TIME, clientTs ?: 0L)
|
||||
.putString(AlfredConstants.SCREENSHOT_LIST, Gson().toJson(screenShotList))
|
||||
.putIntArray(AlfredConstants.SCREENSHOT_ID_LIST, screenShotIds.toIntArray())
|
||||
.putString(AlfredConstants.ALFRED_EVENT_ID, alfredEventId)
|
||||
.putLong(AlfredConstants.LATEST_SCREENSHOT_TIMESTAMP, latestScreenshotTimestamp)
|
||||
.build()
|
||||
@@ -145,7 +129,7 @@ internal fun buildWorkManagerForZip(requestData: Data) {
|
||||
.setRequiresBatteryNotLow(false)
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val uniqueWorkName = "SESSION_UPLOAD_WORK"
|
||||
val uniqueWorkName = AlfredConstants.ZIP_UPLOAD_WORK
|
||||
val uniqueWorkStatus =
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.getWorkInfosForUniqueWork(uniqueWorkName)
|
||||
@@ -175,7 +159,7 @@ internal fun buildWorkManagerForEvents(requestData: Data) {
|
||||
.setRequiresBatteryNotLow(false)
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val uniqueWorkName = "EVENTS_UPLOAD_WORK"
|
||||
val uniqueWorkName = AlfredConstants.EVENTS_UPLOAD_WORK
|
||||
val uniqueWorkStatus =
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.getWorkInfosForUniqueWork(uniqueWorkName)
|
||||
@@ -200,53 +184,74 @@ internal fun buildWorkManagerForEvents(requestData: Data) {
|
||||
}
|
||||
|
||||
internal suspend fun uploadFile(
|
||||
zipFileDetails: ZipDetailsHelper,
|
||||
uploadFile: File,
|
||||
url: String,
|
||||
dumpFlow: Boolean = false,
|
||||
index: Int? = null,
|
||||
zipFileName: String,
|
||||
workManagerFlow: Boolean? = false,
|
||||
currentZipName: String? = "",
|
||||
latestScreenshotTimestamp: Long? = 0L
|
||||
) {
|
||||
val currentZipName = zipFileDetails.alfredEventId
|
||||
val requestBody = uploadFile.asRequestBody("application/zip".toMediaTypeOrNull())
|
||||
val uploadResponse = AlfredManager.networkRepository.uploadZipToS3(url, requestBody)
|
||||
|
||||
if (uploadResponse.isSuccessful && uploadResponse.code() == AlfredConstants.CODE_API_SUCCESS) {
|
||||
uploadFile.delete()
|
||||
sendAlfredSessionEvent(dumpFlow, index, currentZipName, latestScreenshotTimestamp)
|
||||
AlfredManager.zipDetailsDao.updateZipUploadStatus(currentZipName, true)
|
||||
sendAlfredSessionEvent(zipFileDetails, dumpFlow)
|
||||
} else {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = ZIP_ERROR,
|
||||
requestUrl = url,
|
||||
requestMethod = POST_METHOD,
|
||||
zipName = listOf(currentZipName ?: ""),
|
||||
errorStatusCode = uploadResponse.code().toLong(),
|
||||
errorMessage = uploadResponse.message(),
|
||||
errorName = UPLOAD_TO_S3_FAILURE
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(
|
||||
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
|
||||
)
|
||||
|
||||
if (!dumpFlow) {
|
||||
AlfredManager.config.getEventStartRecordingTime()?.let { eventStartRecordingTime ->
|
||||
insertZipDetailsToDbForDumpingLater(
|
||||
AlfredManager.config.getAlfredSessionId(),
|
||||
AlfredManager.config.getSessionStartRecordingTime(),
|
||||
eventStartRecordingTime,
|
||||
zipFileName,
|
||||
currentZipName ?: "",
|
||||
latestScreenshotTimestamp ?: 0L
|
||||
if (!isCriticalJourneyResponse(uploadResponse.code())) {
|
||||
val failureEvent =
|
||||
buildFailureEvent(
|
||||
errorType = ZIP_ERROR,
|
||||
requestUrl = url,
|
||||
requestMethod = POST_METHOD,
|
||||
zipName = listOf(currentZipName),
|
||||
errorStatusCode = uploadResponse.code().toLong(),
|
||||
errorMessage = uploadResponse.message(),
|
||||
errorName = UPLOAD_TO_S3_FAILURE
|
||||
)
|
||||
}
|
||||
AlfredManager.config.setEventStartRecordingTime(true)
|
||||
AlfredDispatcher.addTaskToQueue(
|
||||
AddFailureTask(failureEvent, context = AlfredManager.applicationContext)
|
||||
)
|
||||
}
|
||||
|
||||
if (workManagerFlow == true) {
|
||||
val latestScreenshotTimestamp = zipFileDetails.latestScreenshotTimestamp
|
||||
uploadWorkManagerFailedZip(currentZipName, latestScreenshotTimestamp)
|
||||
}
|
||||
}
|
||||
if (!dumpFlow) {
|
||||
AlfredManager.config.setEventStartRecordingTime(true)
|
||||
AlfredManager.hasUploadFlowStarted = false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun buildWorkManagerForZipUploadRetry(requestData: Data) {
|
||||
val constraints =
|
||||
Constraints.Builder()
|
||||
.setRequiresBatteryNotLow(false)
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
val uniqueWorkName = AlfredConstants.ZIP_UPLOAD_RETRY_WORK
|
||||
val uniqueWorkStatus =
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.getWorkInfosForUniqueWork(uniqueWorkName)
|
||||
.get()
|
||||
if (uniqueWorkStatus.isNullOrEmpty()) {
|
||||
val uniqueWorkRequest =
|
||||
OneTimeWorkRequestBuilder<ZipUploadRetryWorker>()
|
||||
.setConstraints(constraints)
|
||||
.setInputData(requestData)
|
||||
.build()
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.KEEP, uniqueWorkRequest)
|
||||
.enqueue()
|
||||
} else {
|
||||
val workRequest =
|
||||
OneTimeWorkRequestBuilder<ZipUploadRetryWorker>()
|
||||
.setConstraints(constraints)
|
||||
.setInputData(requestData)
|
||||
.build()
|
||||
WorkManager.getInstance(AlfredManager.applicationContext).enqueue(workRequest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.UUID
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -29,7 +28,8 @@ import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal fun toZip(imagePathList: List<ScreenShotPathHelper>) {
|
||||
val zipFileName = AlfredManager.config.getAlfredSessionId() + UUID.randomUUID().toString()
|
||||
val zipFileName = AlfredManager.config.getAlfredEventId()
|
||||
val latestScreenshotTimestamp = AlfredManager.config.getLatestScreenshotTimestamp()
|
||||
AlfredManager.config.setZipFileName(zipFileName)
|
||||
val zipFilePath: String = AlfredManager.applicationContext.filesDir.path + "/" + zipFileName
|
||||
val fileList = ArrayList<String>()
|
||||
@@ -42,7 +42,15 @@ internal fun toZip(imagePathList: List<ScreenShotPathHelper>) {
|
||||
}
|
||||
if (zip(fileList, zipFilePath) == true) {
|
||||
deleteScreenShot()
|
||||
insertZipDetailsToDbForUpload(
|
||||
alfredSessionId = AlfredManager.config.getAlfredSessionId(),
|
||||
sessionStartRecordingTime = AlfredManager.config.getSessionStartRecordingTime(),
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp,
|
||||
eventStartRecordingTime = AlfredManager.config.getEventStartRecordingTime() ?: 0L,
|
||||
alfredEventId = AlfredManager.config.getAlfredEventId()
|
||||
)
|
||||
AlfredManager.config.setNextEventStartRecordingTime()
|
||||
AlfredManager.config.setAlfredEventId()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,30 +94,32 @@ internal fun zip(_files: java.util.ArrayList<String>, zipFilePath: String?): Boo
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun checkDbBeforeStartRecording() {
|
||||
internal fun checkDbAndDeleteCorruptScreenshots() {
|
||||
AlfredManager.coroutineScope.launch(Dispatchers.IO) {
|
||||
if (AlfredManager.screenShotDao.getScreenShotCount() > 0) {
|
||||
AlfredManager.screenShotDao.deleteAllScreenShot()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkDbToUploadFailedZips() {
|
||||
AlfredManager.coroutineScope.launch(Dispatchers.IO) {
|
||||
if (AlfredManager.zipDetailsDao.getZipFilesDetailsCount() > 0) {
|
||||
AlfredManager.zipFileDetails = AlfredManager.zipDetailsDao.fetchAllZipFilesDetails()
|
||||
AlfredManager.zipFileDetails.forEachIndexed { index, DumpZipDetailsHelper ->
|
||||
if (
|
||||
checkFileExists(
|
||||
fileName = DumpZipDetailsHelper.zipFileName,
|
||||
AlfredManager.applicationContext
|
||||
) != null
|
||||
) {
|
||||
getPreSignedUrl(
|
||||
zipFileName = DumpZipDetailsHelper.zipFileName,
|
||||
dumpFlow = true,
|
||||
index = index,
|
||||
alfredEventIdForDumpFlow = DumpZipDetailsHelper.alfredEventId,
|
||||
latestScreenshotTimestamp = DumpZipDetailsHelper.latestScreenshotTimestamp
|
||||
)
|
||||
AlfredManager.zipFileDetailsList = AlfredManager.zipDetailsDao.fetchAllZipFilesDetails()
|
||||
AlfredManager.zipFileDetailsList.forEachIndexed { _, dumpZipDetailsHelper ->
|
||||
if (dumpZipDetailsHelper.zipUploadStatus) {
|
||||
sendAlfredSessionEvent(dumpZipDetailsHelper, dumpFlow = true)
|
||||
} else {
|
||||
AlfredManager.zipDetailsDao.deleteZipFileDetail(DumpZipDetailsHelper.id)
|
||||
if (
|
||||
checkFileExists(
|
||||
fileName = dumpZipDetailsHelper.alfredEventId,
|
||||
AlfredManager.applicationContext
|
||||
) != null
|
||||
) {
|
||||
getPreSignedUrl(zipFileDetail = dumpZipDetailsHelper, dumpFlow = true)
|
||||
} else {
|
||||
AlfredManager.zipDetailsDao.deleteZipFileDetail(dumpZipDetailsHelper.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,23 +128,25 @@ internal fun checkDbBeforeStartRecording() {
|
||||
|
||||
internal suspend fun checkToStartZipUpload() {
|
||||
val zipFileName = AlfredManager.config.getZipFileName().toString()
|
||||
if (zipFileName.isNotEmpty()) {
|
||||
if (checkFileExists(zipFileName, AlfredManager.applicationContext) != null) {
|
||||
if (!AlfredManager.hasUploadFlowStarted) {
|
||||
AlfredManager.hasUploadFlowStarted = true
|
||||
getPreSignedUrl(zipFileName)
|
||||
}
|
||||
if (
|
||||
zipFileName.isNotEmpty() &&
|
||||
checkFileExists(zipFileName, AlfredManager.applicationContext) != null &&
|
||||
!AlfredManager.hasUploadFlowStarted &&
|
||||
AlfredManager.zipDetailsDao.zipDetailExists(zipFileName)
|
||||
) {
|
||||
AlfredManager.hasUploadFlowStarted = true
|
||||
val zipFileDetail = AlfredManager.zipDetailsDao.getZipFileDetail(zipFileName)
|
||||
if (zipFileDetail != null && !zipFileDetail.zipUploadStatus) {
|
||||
getPreSignedUrl(zipFileDetail = zipFileDetail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun toZipForWorkManager(
|
||||
imagePathList: List<ScreenShotPathHelper>,
|
||||
zipFileName: String,
|
||||
sessionStartRecordingTime: Long? = null,
|
||||
alfredSessionId: String? = null,
|
||||
eventStartRecordingTime: Long? = null,
|
||||
index: Int? = null,
|
||||
sessionStartRecordingTime: Long,
|
||||
alfredSessionId: String,
|
||||
eventStartRecordingTime: Long,
|
||||
alfredEventId: String,
|
||||
latestScreenshotTimestamp: Long
|
||||
) {
|
||||
@@ -149,39 +161,46 @@ internal suspend fun toZipForWorkManager(
|
||||
}
|
||||
}
|
||||
}
|
||||
val zipFilePath = AlfredManager.applicationContext.filesDir.path + "/" + zipFileName
|
||||
val zipFilePath = AlfredManager.applicationContext.filesDir.path + "/" + alfredEventId
|
||||
if (zip(fileList, zipFilePath) == true) {
|
||||
clearScreenShot(imagePathList)
|
||||
getPreSignedUrl(
|
||||
zipFileName,
|
||||
index = index,
|
||||
workManagerFlow = true,
|
||||
dumpFlow = true,
|
||||
alfredEventIdForDumpFlow = alfredEventId,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp
|
||||
insertZipDetailsToDbForUpload(
|
||||
alfredSessionId = alfredSessionId,
|
||||
sessionStartRecordingTime = sessionStartRecordingTime,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp,
|
||||
eventStartRecordingTime = eventStartRecordingTime,
|
||||
alfredEventId = alfredEventId
|
||||
)
|
||||
AlfredManager.screenShotDao.deleteAllScreenShot()
|
||||
ScreenShotStorageHelper.clearAll()
|
||||
clearScreenShot(imagePathList)
|
||||
val zipFileDetail = AlfredManager.zipDetailsDao.getZipFileDetail(alfredEventId)
|
||||
if (zipFileDetail != null && !zipFileDetail.zipUploadStatus) {
|
||||
getPreSignedUrl(zipFileDetail = zipFileDetail, workManagerFlow = true, dumpFlow = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun insertZipDetailsToDbForDumpingLater(
|
||||
internal fun insertZipDetailsToDbForUpload(
|
||||
alfredSessionId: String,
|
||||
sessionStartRecordingTime: Long,
|
||||
eventStartRecordingTime: Long,
|
||||
zipFileName: String,
|
||||
alfredEventId: String,
|
||||
latestScreenshotTimestamp: Long
|
||||
latestScreenshotTimestamp: Long,
|
||||
zipUploadStatus: Boolean = false
|
||||
) {
|
||||
AlfredManager.zipDetailsDao.insert(
|
||||
data =
|
||||
ZipDetailsHelper(
|
||||
alfredSessionId = alfredSessionId,
|
||||
sessionStartRecordingTime = sessionStartRecordingTime,
|
||||
eventStartRecordingTime = eventStartRecordingTime,
|
||||
zipFileName = zipFileName,
|
||||
alfredEventId = alfredEventId,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp
|
||||
)
|
||||
)
|
||||
if (!AlfredManager.zipDetailsDao.zipDetailExists(alfredEventId)) {
|
||||
AlfredManager.zipDetailsDao.insert(
|
||||
data =
|
||||
ZipDetailsHelper(
|
||||
alfredSessionId = alfredSessionId,
|
||||
sessionStartRecordingTime = sessionStartRecordingTime,
|
||||
eventStartRecordingTime = eventStartRecordingTime,
|
||||
alfredEventId = alfredEventId,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp,
|
||||
zipUploadStatus = zipUploadStatus
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun startAnrCrashZipUpload(view: View) {
|
||||
@@ -197,47 +216,53 @@ internal fun startAnrCrashZipUpload(view: View) {
|
||||
checkAndInitiateFileUploadWorkManager()
|
||||
}
|
||||
}
|
||||
AlfredManager.coroutineScope.launch(Dispatchers.IO) {
|
||||
if (AlfredManager.zipDetailsDao.getZipFilesDetailsCount() > 0) {
|
||||
val zipFileDetailList = AlfredManager.zipDetailsDao.fetchAllZipFilesDetails()
|
||||
val requestData =
|
||||
Data.Builder()
|
||||
.putString(
|
||||
AlfredConstants.ZIP_FILE_DETAIL_LIST,
|
||||
Gson().toJson(zipFileDetailList)
|
||||
)
|
||||
.build()
|
||||
buildWorkManagerForZipUploadRetry(requestData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun uploadWorkManagerFailedZip(
|
||||
alfredEventId: String? = null,
|
||||
latestScreenshotTimestamp: Long? = null
|
||||
) {
|
||||
if (AlfredManager.workFailureData.size > 0) {
|
||||
val inputData = AlfredManager.workFailureData[0]
|
||||
if (AlfredManager.zipUploadRetryCount < 3) {
|
||||
WorkManager.getInstance(AlfredManager.applicationContext).cancelWorkById(inputData.id)
|
||||
val failedZipDetails = AlfredManager.workFailureData.find { it.alfredEventId == alfredEventId }
|
||||
if (failedZipDetails != null) {
|
||||
if (failedZipDetails.retryCount < 3) {
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.cancelWorkById(failedZipDetails.id)
|
||||
val requestData =
|
||||
Data.Builder()
|
||||
.putString(AlfredConstants.ALFRED_SESSION_ID, inputData.alfredSessionId)
|
||||
.putString(AlfredConstants.ALFRED_SESSION_ID, failedZipDetails.alfredSessionId)
|
||||
.putLong(
|
||||
AlfredConstants.SESSION_START_RECORDING_TIME,
|
||||
inputData.sessionStartRecordingTime
|
||||
failedZipDetails.sessionStartRecordingTime
|
||||
)
|
||||
.putLong(
|
||||
AlfredConstants.EVENT_START_RECORDING_TIME,
|
||||
inputData.eventStartRecordingTime
|
||||
failedZipDetails.eventStartRecordingTime
|
||||
)
|
||||
.putString(
|
||||
AlfredConstants.SCREENSHOT_LIST,
|
||||
Gson().toJson(inputData.screenShots)
|
||||
.putString(AlfredConstants.ALFRED_EVENT_ID, failedZipDetails.alfredEventId)
|
||||
.putLong(
|
||||
AlfredConstants.LATEST_SCREENSHOT_TIMESTAMP,
|
||||
latestScreenshotTimestamp ?: 0L
|
||||
)
|
||||
.build()
|
||||
buildWorkManagerForZip(requestData)
|
||||
AlfredManager.zipUploadRetryCount += 1
|
||||
AlfredManager.workFailureData.removeAt(0)
|
||||
failedZipDetails.retryCount += 1
|
||||
} else {
|
||||
AlfredManager.zipUploadRetryCount = 0
|
||||
WorkManager.getInstance(AlfredManager.applicationContext).cancelWorkById(inputData.id)
|
||||
WorkManager.getInstance(AlfredManager.applicationContext)
|
||||
.cancelWorkById(failedZipDetails.id)
|
||||
AlfredManager.workFailureData.removeAt(0)
|
||||
insertZipDetailsToDbForDumpingLater(
|
||||
alfredSessionId = inputData.alfredSessionId.toString(),
|
||||
eventStartRecordingTime = inputData.eventStartRecordingTime,
|
||||
sessionStartRecordingTime = inputData.sessionStartRecordingTime,
|
||||
zipFileName = inputData.zipFileName,
|
||||
alfredEventId = alfredEventId.toString(),
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp ?: 0L
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@ package com.navi.alfred.worker
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.navi.alfred.AlfredManager
|
||||
import com.navi.alfred.AlfredManager.workFailureData
|
||||
import com.navi.alfred.db.AlfredDatabaseHelper
|
||||
import com.navi.alfred.db.model.ScreenShotPathHelper
|
||||
import com.navi.alfred.model.WorkManagerFailureInputData
|
||||
import com.navi.alfred.utils.AlfredConstants
|
||||
import com.navi.alfred.utils.getPreSignedUrl
|
||||
import com.navi.alfred.utils.sendAlfredSessionEvent
|
||||
import com.navi.alfred.utils.toZipForWorkManager
|
||||
import java.lang.reflect.Type
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -27,53 +27,73 @@ class UploadFileWorker(context: Context, workerParams: WorkerParameters) :
|
||||
|
||||
override suspend fun doWork(): Result =
|
||||
withContext(Dispatchers.IO) {
|
||||
AlfredManager.alfredDataBase =
|
||||
AlfredDatabaseHelper.getAnalyticsDatabase(AlfredManager.applicationContext)
|
||||
AlfredManager.zipDetailsDao = AlfredManager.alfredDataBase.zipDetailsDao()
|
||||
AlfredManager.screenShotDao = AlfredManager.alfredDataBase.screenShotDao()
|
||||
|
||||
val alfredSessionId = inputData.getString(AlfredConstants.ALFRED_SESSION_ID)
|
||||
val sessionStartRecordingTime =
|
||||
inputData.getLong(AlfredConstants.SESSION_START_RECORDING_TIME, 0L)
|
||||
val eventStartRecordingTime =
|
||||
inputData.getLong(AlfredConstants.EVENT_START_RECORDING_TIME, 0L)
|
||||
val screenShotList = inputData.getString(AlfredConstants.SCREENSHOT_LIST)
|
||||
val listType: Type = object : TypeToken<List<ScreenShotPathHelper?>?>() {}.type
|
||||
val screenShots: List<ScreenShotPathHelper> =
|
||||
Gson().fromJson(screenShotList.toString(), listType)
|
||||
val zipFileName =
|
||||
alfredSessionId +
|
||||
sessionStartRecordingTime +
|
||||
AlfredConstants.WORKER_ZIP_FILENAME_ENDPOINT
|
||||
val alfredEventId =
|
||||
inputData.getString(AlfredConstants.ALFRED_EVENT_ID)
|
||||
?: UUID.randomUUID().toString().plus(AlfredConstants.ALFRED_EVENT_ID)
|
||||
val screenshotIdArray = inputData.getIntArray(AlfredConstants.SCREENSHOT_ID_LIST)
|
||||
val screenshotIdList = screenshotIdArray?.toList()
|
||||
var screenShots = emptyList<ScreenShotPathHelper>()
|
||||
if (screenshotIdList?.isNotEmpty() == true) {
|
||||
screenShots =
|
||||
AlfredManager.screenShotDao.fetchAllScreenShotsPathFromIds(screenshotIdList)
|
||||
}
|
||||
val alfredEventId = inputData.getString(AlfredConstants.ALFRED_EVENT_ID)
|
||||
val latestScreenshotTimestamp =
|
||||
inputData.getLong(AlfredConstants.LATEST_SCREENSHOT_TIMESTAMP, 0L)
|
||||
workFailureData.add(
|
||||
WorkManagerFailureInputData(
|
||||
alfredSessionId = alfredSessionId,
|
||||
sessionStartRecordingTime,
|
||||
eventStartRecordingTime,
|
||||
screenShots,
|
||||
id,
|
||||
zipFileName,
|
||||
alfredEventId,
|
||||
latestScreenshotTimestamp
|
||||
)
|
||||
)
|
||||
|
||||
try {
|
||||
if (
|
||||
alfredSessionId == null ||
|
||||
sessionStartRecordingTime == 0L ||
|
||||
eventStartRecordingTime == 0L
|
||||
eventStartRecordingTime == 0L ||
|
||||
alfredEventId == null
|
||||
) {
|
||||
Result.failure()
|
||||
} else {
|
||||
toZipForWorkManager(
|
||||
imagePathList = screenShots,
|
||||
zipFileName = zipFileName,
|
||||
sessionStartRecordingTime = sessionStartRecordingTime,
|
||||
alfredSessionId = alfredSessionId,
|
||||
eventStartRecordingTime = eventStartRecordingTime,
|
||||
alfredEventId = alfredEventId,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp
|
||||
)
|
||||
if (workFailureData.find { it.alfredEventId == alfredEventId } == null) {
|
||||
workFailureData.add(
|
||||
WorkManagerFailureInputData(
|
||||
alfredSessionId = alfredSessionId,
|
||||
sessionStartRecordingTime,
|
||||
eventStartRecordingTime,
|
||||
id,
|
||||
alfredEventId,
|
||||
latestScreenshotTimestamp
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (screenShots.isNotEmpty()) {
|
||||
toZipForWorkManager(
|
||||
imagePathList = screenShots,
|
||||
sessionStartRecordingTime = sessionStartRecordingTime,
|
||||
alfredSessionId = alfredSessionId,
|
||||
eventStartRecordingTime = eventStartRecordingTime,
|
||||
alfredEventId = alfredEventId,
|
||||
latestScreenshotTimestamp = latestScreenshotTimestamp
|
||||
)
|
||||
} else {
|
||||
val zipFileDetail =
|
||||
AlfredManager.zipDetailsDao.getZipFileDetail(alfredEventId)
|
||||
if (zipFileDetail != null) {
|
||||
if (zipFileDetail.zipUploadStatus) {
|
||||
sendAlfredSessionEvent(zipFileDetail, dumpFlow = true)
|
||||
} else {
|
||||
getPreSignedUrl(
|
||||
zipFileDetail = zipFileDetail,
|
||||
workManagerFlow = true,
|
||||
dumpFlow = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Result.success()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2023 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.alfred.worker
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.Data
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.navi.alfred.db.model.ZipDetailsHelper
|
||||
import com.navi.alfred.utils.AlfredConstants
|
||||
import com.navi.alfred.utils.buildWorkManagerForZip
|
||||
import com.navi.alfred.utils.sendAlfredSessionEvent
|
||||
import java.lang.reflect.Type
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ZipUploadRetryWorker(context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
|
||||
override suspend fun doWork(): Result =
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val zipDetailList = inputData.getString(AlfredConstants.ZIP_FILE_DETAIL_LIST)
|
||||
val listType: Type = object : TypeToken<List<ZipDetailsHelper?>?>() {}.type
|
||||
val zipFileDetailList: List<ZipDetailsHelper> =
|
||||
if (zipDetailList != null) {
|
||||
Gson().fromJson(zipDetailList, listType)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
zipFileDetailList.forEach {
|
||||
if (it.zipUploadStatus) {
|
||||
sendAlfredSessionEvent(it, true)
|
||||
} else {
|
||||
val requestDataForZip =
|
||||
Data.Builder()
|
||||
.putString(AlfredConstants.ALFRED_SESSION_ID, it.alfredSessionId)
|
||||
.putLong(
|
||||
AlfredConstants.SESSION_START_RECORDING_TIME,
|
||||
it.sessionStartRecordingTime
|
||||
)
|
||||
.putLong(
|
||||
AlfredConstants.EVENT_START_RECORDING_TIME,
|
||||
it.sessionStartRecordingTime
|
||||
)
|
||||
.putString(AlfredConstants.ALFRED_EVENT_ID, it.alfredEventId)
|
||||
.putLong(
|
||||
AlfredConstants.LATEST_SCREENSHOT_TIMESTAMP,
|
||||
it.latestScreenshotTimestamp
|
||||
)
|
||||
.build()
|
||||
buildWorkManagerForZip(requestDataForZip)
|
||||
}
|
||||
}
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
Result.retry()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user