From 2253fd922603c081016048f79ae26efe1188f0b7 Mon Sep 17 00:00:00 2001 From: Sayed Owais Ali Date: Wed, 29 May 2024 20:48:34 +0530 Subject: [PATCH] TP-66620 | Alfred resource manager (#179) Co-authored-by: Varun Jain --- .../java/com/navi/alfred/AlfredManager.kt | 27 ++- .../java/com/navi/alfred/db/AlfredDatabase.kt | 18 +- .../navi/alfred/db/AlfredDatabaseHelper.kt | 1 + .../java/com/navi/alfred/db/dao/AlfredDao.kt | 19 ++ .../com/navi/alfred/db/model/AlfredEntity.kt | 4 +- .../com/navi/alfred/model/ErrorMessage.kt | 6 +- .../network/AlfredFailureRetrofitProvider.kt | 11 +- .../AlfredResourceManagerInterceptor.kt | 80 ++++++++ .../alfred/network/AlfredRetrofitProvider.kt | 11 +- .../com/navi/alfred/utils/AlfredConstants.kt | 9 + .../com/navi/alfred/utils/ApiConstants.kt | 1 + .../java/com/navi/alfred/utils/EventUtils.kt | 135 ++++++------- .../com/navi/alfred/utils/NetworkUtils.kt | 4 + .../com/navi/alfred/utils/ScreenShotUtils.kt | 66 ++++--- .../java/com/navi/alfred/utils/UploadUtils.kt | 181 +++++++++--------- .../java/com/navi/alfred/utils/ZipUtils.kt | 175 +++++++++-------- .../navi/alfred/worker/UploadFileWorker.kt | 94 +++++---- .../alfred/worker/ZipUploadRetryWorker.kt | 69 +++++++ 18 files changed, 574 insertions(+), 337 deletions(-) create mode 100644 navi-alfred/src/main/java/com/navi/alfred/network/AlfredResourceManagerInterceptor.kt create mode 100644 navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt diff --git a/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt b/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt index 13a64e3..4ebff46 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt @@ -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? = 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 + internal lateinit var zipFileDetailsList: List 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) + } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabase.kt b/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabase.kt index c2d97a8..f159531 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabase.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabase.kt @@ -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" + ) + } + } diff --git a/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabaseHelper.kt b/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabaseHelper.kt index 818f225..74d86a5 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabaseHelper.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/db/AlfredDatabaseHelper.kt @@ -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 diff --git a/navi-alfred/src/main/java/com/navi/alfred/db/dao/AlfredDao.kt b/navi-alfred/src/main/java/com/navi/alfred/db/dao/AlfredDao.kt index e8f355c..31c90e0 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/db/dao/AlfredDao.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/db/dao/AlfredDao.kt @@ -43,6 +43,11 @@ interface ScreenShotDao { @Query("SELECT * FROM ScreenShotPathHelper") fun fetchAllScreenShotsPath(): List + @Query("SELECT id FROM ScreenShotPathHelper") fun fetchAllScreenShotIds(): List + + @Query("SELECT * FROM ScreenShotPathHelper WHERE id IN (:idList)") + fun fetchAllScreenShotsPathFromIds(idList: List): List + @Query("DELETE FROM ScreenShotPathHelper WHERE id IN (:idList)") fun deleteScreenShot(idList: List) @@ -62,6 +67,20 @@ interface ZipDetailsDao { fun fetchAllZipFilesDetails(): List @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 diff --git a/navi-alfred/src/main/java/com/navi/alfred/db/model/AlfredEntity.kt b/navi-alfred/src/main/java/com/navi/alfred/db/model/AlfredEntity.kt index fe04869..577412f 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/db/model/AlfredEntity.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/db/model/AlfredEntity.kt @@ -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 } diff --git a/navi-alfred/src/main/java/com/navi/alfred/model/ErrorMessage.kt b/navi-alfred/src/main/java/com/navi/alfred/model/ErrorMessage.kt index 96ab227..46fd83f 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/model/ErrorMessage.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/model/ErrorMessage.kt @@ -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 = emptyList(), val id: UUID, - val zipFileName: String, val alfredEventId: String, - val latestScreenshotTimestamp: Long = 0L + val latestScreenshotTimestamp: Long = 0L, + var retryCount: Int = 0 ) diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredFailureRetrofitProvider.kt b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredFailureRetrofitProvider.kt index 49c7da0..285f757 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredFailureRetrofitProvider.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredFailureRetrofitProvider.kt @@ -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)) } diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredResourceManagerInterceptor.kt b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredResourceManagerInterceptor.kt new file mode 100644 index 0000000..4d330f9 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredResourceManagerInterceptor.kt @@ -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) + } +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitProvider.kt b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitProvider.kt index c324a28..df92cca 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitProvider.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitProvider.kt @@ -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)) } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredConstants.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredConstants.kt index 2cfc6bd..32c1d02 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredConstants.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredConstants.kt @@ -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 } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/ApiConstants.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/ApiConstants.kt index 07d1f7f..6e00117 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/ApiConstants.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ApiConstants.kt @@ -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 } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt index e6047b4..9f5c72c 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt @@ -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 = mutableListOf() val eventIdList: MutableList = 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, diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/NetworkUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/NetworkUtils.kt index d7a20fc..a77016c 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/NetworkUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/NetworkUtils.kt @@ -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 +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt index e8146fe..ee0a609 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt @@ -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 + ) } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt index 0d6b51f..c3afcd3 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt @@ -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() + .setConstraints(constraints) + .setInputData(requestData) + .build() + WorkManager.getInstance(AlfredManager.applicationContext) + .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.KEEP, uniqueWorkRequest) + .enqueue() + } else { + val workRequest = + OneTimeWorkRequestBuilder() + .setConstraints(constraints) + .setInputData(requestData) + .build() + WorkManager.getInstance(AlfredManager.applicationContext).enqueue(workRequest) + } +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt index 30c2d36..c758d81 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt @@ -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) { - 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() @@ -42,7 +42,15 @@ internal fun toZip(imagePathList: List) { } 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, 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, - 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 - ) } } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/UploadFileWorker.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/UploadFileWorker.kt index f337d7b..d863dd8 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/UploadFileWorker.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/UploadFileWorker.kt @@ -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?>() {}.type - val screenShots: List = - 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() + 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) { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt new file mode 100644 index 0000000..18efe31 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt @@ -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?>() {}.type + val zipFileDetailList: List = + 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() + } + } +}