diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index e1eea1d..7e340a7 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 9f71c83..773fe0f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/navi-alfred/src/main/java/com/navi/alfred/AlfredConfig.kt b/navi-alfred/src/main/java/com/navi/alfred/AlfredConfig.kt index 4dac4b7..35c62bf 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/AlfredConfig.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/AlfredConfig.kt @@ -10,7 +10,8 @@ package com.navi.alfred import android.os.Build import android.provider.Settings import android.text.TextUtils -import com.navi.alfred.utils.* +import com.navi.alfred.utils.AlfredConstants +import com.navi.alfred.utils.getCarrierName import java.util.* data class AlfredConfig( private var appVersionCode: String = "", @@ -39,13 +40,17 @@ data class AlfredConfig( private var cpuEnableStatus: Boolean = false, private var memoryEnableStatus: Boolean = false, private var metricsApiEnableStatus: Boolean = false, + private var apiResponseEnableStatus:Boolean=false, + private var apiRequestEnableStatus:Boolean=false, + private var enabledApiPath: List? = null, var batteryPercentageBeforeEventStart: Float? = null, private var disableScreenList: List? = null, private var disableModuleList: List? = null, private var snapshotPerSecond: Int = 1, private var enableAlfred: Boolean = false, - private var firebaseControlledCruise: Boolean = false, - private var disableChucker:Boolean=true + private var disableDialogScreenShot: Boolean = false, + private var zipFileName: String? = null, + private var disableAlfredLogs: Boolean = true ) { @@ -96,11 +101,6 @@ data class AlfredConfig( fun setAlfredStatus(enable: Boolean) { this.enableAlfred = enable } - fun getFirebaseControlledCruise(): Boolean = this.firebaseControlledCruise - - fun setFirebaseControlledCruise(firebaseControlledCruise: Boolean) { - this.firebaseControlledCruise = firebaseControlledCruise - } fun setEnableRecordingStatus(enableRecording: Boolean) { this.enableRecording = enableRecording @@ -133,10 +133,10 @@ data class AlfredConfig( fun getNetworkCarrier(): String? = getCarrierName(AlfredManager.applicationContext) - fun getNetworkType(): String = getNetworkType(AlfredManager.applicationContext) + fun getNetworkType(): String = com.navi.alfred.utils.getNetworkType(AlfredManager.applicationContext) fun getBatteryPercentage(): Float = - getBatteryPercentageForDevice(AlfredManager.applicationContext) + com.navi.alfred.utils.getBatteryPercentage(AlfredManager.applicationContext) fun getUserId(): String? = userId @@ -173,12 +173,12 @@ data class AlfredConfig( this.batteryPercentageBeforeEventStart = getBatteryPercentage() } - fun getCpuUsage(): Float = getCpuUsageForDevice() + fun getCpuUsage(): Float = getCpuUsage() - fun getMemoryUsage(): Float = getMemoryUsageForDevice() + fun getMemoryUsage(): Float = getMemoryUsage() fun getStorageUsage(): Float { - val (totalSize, freeSize) = getStorageUsageForDevice(context = AlfredManager.applicationContext) + val (totalSize, freeSize) = com.navi.alfred.utils.getStorageUsage(context = AlfredManager.applicationContext) return (totalSize - freeSize).toFloat() } @@ -229,5 +229,38 @@ data class AlfredConfig( } fun getMetricsApiEnableStatus(): Boolean = this.metricsApiEnableStatus - fun getDisableChuckerStatus(): Boolean = this.disableChucker + fun setApiResponseEnableStatus(status: Boolean) { + this.apiResponseEnableStatus = status + } + fun getApiResponseEnableStatus(): Boolean = this.apiResponseEnableStatus + + fun setApiRequestEnableStatus(status: Boolean) { + this.apiRequestEnableStatus = status + } + fun getApiRequestEnableStatus(): Boolean = this.apiRequestEnableStatus + + fun setEnabledApiPath(enabledApiPath: List?) { + this.enabledApiPath = enabledApiPath + } + fun getEnabledApiPath():Set? = this.enabledApiPath?.toSet() + + fun setDisableDialogScreenShot(status: Boolean){ + this.disableDialogScreenShot=status + } + + fun getDisableDialogScreenShot(): Boolean { + return this.disableDialogScreenShot + } + + fun setZipFileName(zipFileName: String) { + this.zipFileName = zipFileName + } + + fun getZipFileName(): String? = this.zipFileName + + fun clearZipFileName() { + this.zipFileName = "" + } + + fun getDisableAlfredLogsStatus(): Boolean = this.disableAlfredLogs } 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 f1ef8ad..ab8e383 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt @@ -11,6 +11,7 @@ import android.app.Dialog import android.content.Context import android.graphics.Bitmap import android.graphics.Canvas +import android.util.Log import android.view.KeyEvent.ACTION_DOWN import android.view.KeyEvent.ACTION_UP import android.view.MotionEvent @@ -20,7 +21,6 @@ import androidx.annotation.WorkerThread import androidx.work.* import com.google.gson.Gson import com.google.gson.reflect.TypeToken -import com.navi.alfred.utils.ScreenShotStorageHelper import com.navi.alfred.db.AlfredDatabase import com.navi.alfred.db.AlfredDatabaseHelper import com.navi.alfred.db.dao.ApiMetricDao @@ -31,15 +31,38 @@ import com.navi.alfred.db.model.ApiMetricHelper import com.navi.alfred.db.model.ScreenShotPathHelper import com.navi.alfred.db.model.ZipDetailsHelper import com.navi.alfred.dispatcher.AlfredDispatcher -import com.navi.alfred.model.* +import com.navi.alfred.model.AnalyticsRequest +import com.navi.alfred.model.BaseAttribute +import com.navi.alfred.model.DeviceAttributes +import com.navi.alfred.model.EventAttribute +import com.navi.alfred.model.EventMetricRequest +import com.navi.alfred.model.MetricAttribute +import com.navi.alfred.model.NaviMotionEvent +import com.navi.alfred.model.SessionEventAttribute +import com.navi.alfred.model.SessionRequest +import com.navi.alfred.model.WorkManagerFailureInputData import com.navi.alfred.network.AlfredNetworkRepository import com.navi.alfred.network.AlfredRetrofitProvider import com.navi.alfred.network.model.CruiseResponse -import com.navi.alfred.utils.* +import com.navi.alfred.utils.AlfredConstants +import com.navi.alfred.utils.AlfredConstants.ALFRED import com.navi.alfred.utils.AlfredConstants.API_METRICS +import com.navi.alfred.utils.AlfredConstants.CODE_API_BAD_REQUEST import com.navi.alfred.utils.AlfredConstants.CODE_API_SUCCESS import com.navi.alfred.utils.AlfredConstants.THIRD_PARTY_MODULE +import com.navi.alfred.utils.AlfredConstants.X_TARGET import com.navi.alfred.utils.AlfredConstants.ZIP_FILE_EXTENSION +import com.navi.alfred.utils.ScreenShotStorageHelper +import com.navi.alfred.utils.captureScreen +import com.navi.alfred.utils.captureScreenshotOfCustomView +import com.navi.alfred.utils.checkFileExists +import com.navi.alfred.utils.clearScreenShot +import com.navi.alfred.utils.combineScreenshots +import com.navi.alfred.utils.createBitmapForView +import com.navi.alfred.utils.getTouchEvent +import com.navi.alfred.utils.insertScreenShotPathInDb +import com.navi.alfred.utils.log +import com.navi.alfred.utils.zip import com.navi.alfred.worker.AddEventTask import com.navi.alfred.worker.AddMetricTask import com.navi.alfred.worker.UploadFileWorker @@ -50,7 +73,6 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Request import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.Response -import okio.Buffer import java.io.File import java.lang.reflect.Type import java.util.* @@ -63,7 +85,7 @@ object AlfredManager { lateinit var config: AlfredConfig lateinit var applicationContext: Context private var previousTouchEvent: NaviMotionEvent = NaviMotionEvent() - private val mutex = Mutex() + val mutex = Mutex() private val coroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private val repository = AlfredNetworkRepository() private val completableJob = Job() @@ -75,7 +97,6 @@ object AlfredManager { private const val imageThreshHoldValue: Int = 60 private const val imageSecondThreshHoldValue: Int = 100 private var screenShotCaptureDelay: Long = 1000L - private var isAppInBackground: Boolean = false private var hasRecordingStarted: Boolean = false var dialog: Dialog? = null private var timer: Timer? = null @@ -83,23 +104,22 @@ object AlfredManager { private var viewLayoutDelay: Long = 1000 private var currentScreenName: String? = null private var currentModuleName: String? = null - private var sessionIdForCrash: String? = null - private var sessionStartRecordingTimeForCrash: Long? = null - private var eventStartRecordingTimeForCrash: Long? = null + val workFailureData: MutableList = mutableListOf() + private var retryCount: Int = 0 private lateinit var zipFileDetails: List - private val exceptionHandler = CoroutineExceptionHandler { _, exception -> + private val exceptionHandler = CoroutineExceptionHandler { _, _ -> } private val coroutineScope = CoroutineScope(Dispatchers.IO + completableJob + exceptionHandler) fun init(config: AlfredConfig, context: Context) { - this.config = config - this.applicationContext = context + AlfredManager.config = config + applicationContext = context AlfredRetrofitProvider.init(applicationContext) startSyncEvents(context) alfredDataBase = AlfredDatabaseHelper.getAnalyticsDatabase(applicationContext) - this.screenShotDao = alfredDataBase.screenShotDao() - this.zipDetailsDao = alfredDataBase.zipDetailsDao() - this.apiMetricDao = alfredDataBase.apiMetricDao() + screenShotDao = alfredDataBase.screenShotDao() + zipDetailsDao = alfredDataBase.zipDetailsDao() + apiMetricDao = alfredDataBase.apiMetricDao() } fun startRecording( @@ -188,15 +208,16 @@ object AlfredManager { } private suspend fun checkToStartZipUpload() { - val zipFileName = - config.getAlfredSessionId() + config.getSessionStartRecordingTime().toString() - if (checkFileExists( - zipFileName, applicationContext - ) != null - ) { - if (!hasUploadFlowStarted) { - hasUploadFlowStarted = true - getPreSignedUrl(zipFileName) + val zipFileName = config.getZipFileName().toString() + if (zipFileName.isNotEmpty()) { + if (checkFileExists( + zipFileName, applicationContext + ) != null + ) { + if (!hasUploadFlowStarted) { + hasUploadFlowStarted = true + getPreSignedUrl(zipFileName) + } } } } @@ -209,6 +230,9 @@ object AlfredManager { rootBmp: Bitmap? = null, moduleName: String? = null ) { + if(config.getDisableDialogScreenShot()){ + return + } val bottomSheetView = dialog?.window?.decorView?.rootView if (bottomSheetView != null) { val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView) @@ -249,16 +273,17 @@ object AlfredManager { private fun checkDbBeforeStartRecording() { coroutineScope.launch(Dispatchers.IO) { if (screenShotDao.getScreenShotCount() > 0) { - clearScreenShot(screenShotDao.fetchAllScreenShotsPath()) screenShotDao.deleteAllScreenShot() } if (zipDetailsDao.getZipFilesDetailsCount() > 0) { zipFileDetails = zipDetailsDao.fetchAllZipFilesDetails() zipFileDetails.forEachIndexed { index, DumpZipDetailsHelper -> - val zipFileName = - DumpZipDetailsHelper.alfredSessionId + DumpZipDetailsHelper.sessionStartRecordingTime.toString() - if (checkFileExists(fileName = zipFileName, applicationContext) != null) { - getPreSignedUrl(zipFileName, true, index) + if (checkFileExists( + fileName = DumpZipDetailsHelper.zipFileName, + applicationContext + ) != null + ) { + getPreSignedUrl(DumpZipDetailsHelper.zipFileName, true, index) } else { zipDetailsDao.deleteZipFileDetail(DumpZipDetailsHelper.id) } @@ -287,8 +312,12 @@ object AlfredManager { config.getDeviceId() ) if (response.isSuccessful && response.code() == CODE_API_SUCCESS) { - response.body()?.let { cruiseResponse -> - setCruiseConfig(cruiseResponse) + try { + response.body()?.let { cruiseResponse -> + setCruiseConfig(cruiseResponse) + } + } catch (e: Exception) { + e.log() } } } @@ -334,6 +363,15 @@ object AlfredManager { metricsConfig.disableApiPerformance?.let { api_monitor_status -> config.setApiMetricsEnableStatus(!api_monitor_status) } + metricsConfig.disableApiRequest?.let { api_request_status -> + config.setApiRequestEnableStatus(!api_request_status) + } + metricsConfig.disableApiResponse?.let { api_response_status -> + config.setApiResponseEnableStatus(!api_response_status) + } + metricsConfig.enabledApiPaths.let {enabled_api_path -> + config.setEnabledApiPath(enabled_api_path) + } } } } @@ -341,35 +379,98 @@ object AlfredManager { } private suspend fun getPreSignedUrl( - zipFileName: String, dumpFlow: Boolean = false, index: Int? = null + zipFileName: String, + dumpFlow: Boolean = false, + index: Int? = null, + workManagerFlow: Boolean? = false, + alfredSessionId: String? = null, + eventStartRecordingTime: Long? = null, + sessionStartRecordingTime: Long? = null ) { - config.setAlfredEventId() - val bucketKey = config.getAlfredEventId().plus(ZIP_FILE_EXTENSION) - val response = repository.getPreSignedUrl(bucketKey) - if (response.isSuccessful && response.code() == CODE_API_SUCCESS) { - checkFileExists( - zipFileName, applicationContext - )?.let { file -> - response.body()?.data?.let { uploadFile(file, it, dumpFlow, index) } - } - } else { - if (!dumpFlow) { - hasUploadFlowStarted = false - config.getEventStartRecordingTime()?.let { eventStartRecordingTime -> - insertZipDetailsToDbForDumpingLater( - config.getAlfredSessionId(), - config.getSessionStartRecordingTime(), - eventStartRecordingTime - ) + mutex.withLock { + config.setAlfredEventId() + val bucketKey = config.getAlfredEventId().plus(ZIP_FILE_EXTENSION) + val response = repository.getPreSignedUrl(bucketKey) + if (response.isSuccessful && response.code() == CODE_API_SUCCESS) { + if (workManagerFlow == true) { + retryCount = 0 + } + checkFileExists( + zipFileName, applicationContext + )?.let { file -> + response.body()?.data?.let { url -> + uploadFile( + file, + url, + dumpFlow, + index, + zipFileName, + workManagerFlow = workManagerFlow, + alfredSessionId = alfredSessionId, + eventStartRecordingTime = eventStartRecordingTime, + sessionStartRecordingTime = sessionStartRecordingTime + ) + } + } + } else { + if (!dumpFlow) { + hasUploadFlowStarted = false + config.getEventStartRecordingTime()?.let { eventStartRecordingTime -> + insertZipDetailsToDbForDumpingLater( + config.getAlfredSessionId(), + config.getSessionStartRecordingTime(), + eventStartRecordingTime, + zipFileName + ) + } + config.setEventStartRecordingTime(true) + } + if (workManagerFlow == true) { + uploadWorkManagerFailedZip(zipFileName) + } else { } - config.setEventStartRecordingTime(true) } } } + private fun uploadWorkManagerFailedZip(zipFileName: String) { + retryCount +=1 + if (workFailureData.size > 0) { + val inputData = workFailureData[0] + WorkManager.getInstance(applicationContext).cancelWorkById(inputData.id) + if (retryCount <= 3) { + val requestData = Data.Builder() + .putString(AlfredConstants.ALFRED_SESSION_ID, inputData.alfredSessionId) + .putLong( + AlfredConstants.SESSION_START_RECORDING_TIME, + inputData.sessionStartRecordingTime + ).putLong( + AlfredConstants.EVENT_START_RECORDING_TIME, + inputData.eventStartRecordingTime + ).putString( + AlfredConstants.SCREENSHOT_LIST, + Gson().toJson(inputData.screenShots) + ) + .build() + buildWorkManager(requestData) + } else { + insertZipDetailsToDbForDumpingLater( + inputData.alfredSessionId.toString(), + inputData.sessionStartRecordingTime, + inputData.eventStartRecordingTime, + zipFileName + ) + } + workFailureData.removeAt(0) + } + } + private fun checkAndInitiateFileUploadWorkManager() { val screenShotList = screenShotDao.fetchAllScreenShotsPath() screenShotDao.deleteAllScreenShot() + ScreenShotStorageHelper.clearAll() + hasRecordingStarted = false + config.clearZipFileName() val requestData = Data.Builder() .putString(AlfredConstants.ALFRED_SESSION_ID, config.getAlfredSessionId()) .putLong( @@ -380,6 +481,10 @@ object AlfredManager { config.getEventStartRecordingTime() ?: 0L ).putString(AlfredConstants.SCREENSHOT_LIST, Gson().toJson(screenShotList)) .build() + buildWorkManager(requestData) + } + + private fun buildWorkManager(requestData: Data) { val constraints = Constraints.Builder().setRequiresBatteryNotLow(false) .setRequiredNetworkType(NetworkType.CONNECTED).build() val uniqueWorkName = "YOUR_UNIQUE_WORK_NAME" @@ -391,7 +496,7 @@ object AlfredManager { .setInputData(requestData) .build() WorkManager.getInstance(applicationContext) - .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.REPLACE, uniqueWorkRequest) + .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.KEEP, uniqueWorkRequest) .enqueue() } else { val workRequest = OneTimeWorkRequestBuilder() @@ -404,7 +509,15 @@ object AlfredManager { } private suspend fun uploadFile( - uploadFile: File, url: String, dumpFlow: Boolean = false, index: Int? = null + uploadFile: File, + url: String, + dumpFlow: Boolean = false, + index: Int? = null, + zipFileName: String, + workManagerFlow: Boolean? = false, + alfredSessionId: String? = null, + eventStartRecordingTime: Long? = null, + sessionStartRecordingTime: Long? = null ) { val requestBody = uploadFile.asRequestBody("application/zip".toMediaTypeOrNull()) val uploadResponse = repository.uploadZipToS3( @@ -412,18 +525,30 @@ object AlfredManager { ) if (uploadResponse.isSuccessful && uploadResponse.code() == CODE_API_SUCCESS) { uploadFile.delete() - sendAlfredSessionEvent(dumpFlow, index) + if (workManagerFlow == true) { + retryCount = 0 + } + sendAlfredSessionEvent( + dumpFlow, index, + alfredSessionId = alfredSessionId, + eventStartRecordingTime = eventStartRecordingTime, + sessionStartRecordingTime = sessionStartRecordingTime + ) } else { if (!dumpFlow) { config.getEventStartRecordingTime()?.let { eventStartRecordingTime -> insertZipDetailsToDbForDumpingLater( config.getAlfredSessionId(), config.getSessionStartRecordingTime(), - eventStartRecordingTime + eventStartRecordingTime, + zipFileName ) } config.setEventStartRecordingTime(true) } + if (workManagerFlow == true) { + uploadWorkManagerFailedZip(zipFileName) + } } if (!dumpFlow) { hasUploadFlowStarted = false @@ -434,42 +559,24 @@ object AlfredManager { alfredSessionId: String, sessionStartRecordingTime: Long, eventStartRecordingTime: Long, - screenShotList: String? = null + zipFileName: String ) { zipDetailsDao.insert( data = ZipDetailsHelper( alfredSessionId = alfredSessionId, sessionStartRecordingTime = sessionStartRecordingTime, eventStartRecordingTime = eventStartRecordingTime, - screenShotList = screenShotList + zipFileName = zipFileName ) ) } - private fun deleteScreenShot() { + private fun deleteScreenShot(imagePathList: List) { try { - val screenShotPathList: List = - if (screenShotDao.getScreenShotCount() >= imageThreshHoldValue) { - screenShotDao.fetchScreenShotsPath(imageThreshHoldValue) - } else { - screenShotDao.fetchAllScreenShotsPath() - } - if (clearScreenShot(screenShotPathList)) { - try { - if (isAppInBackground) { - ScreenShotStorageHelper.clearAll() - screenShotDao.deleteAllScreenShot() - isAppInBackground = false - hasRecordingStarted = false - } else { - val idList = screenShotPathList.map { it.id } - screenShotDao.deleteScreenShot(idList) - ScreenShotStorageHelper.deleteKItems(idList.size) - } - } catch (e: Exception) { - e.log() - } - } + clearScreenShot(imagePathList) + val idList = imagePathList.map { it.id } + screenShotDao.deleteScreenShot(idList) + ScreenShotStorageHelper.deleteKItems(idList.size) } catch (e: Exception) { e.log() } @@ -496,7 +603,7 @@ object AlfredManager { AlfredConstants.DEFAULT_INGEST_METRIC_URL, metricRequestBody = request ) - return if (response.isSuccessful && response.code() == CODE_API_SUCCESS) { + return if ((response.isSuccessful && response.code() == CODE_API_SUCCESS) or (response.code() == CODE_API_BAD_REQUEST)) { apiMetricDao.deleteApiMetric(metricEventList.map { it.id }) true } else { @@ -531,6 +638,31 @@ object AlfredManager { } } + fun handleScreenTransitionEvent( + currentScreenName: String?, previousScreenName: String?, screenTransitionDuration: Long + ) { + if (config.getAlfredStatus() && config.getMetricsApiEnableStatus()) { + val screenTransitionAttributes: HashMap = hashMapOf() + screenTransitionAttributes.apply { + this[AlfredConstants.CURRENT_SCREEN_NAME] = currentScreenName.orEmpty() + this[AlfredConstants.PREVIOUS_SCREEN_NAME] = previousScreenName.orEmpty() + this[AlfredConstants.LOAD_TIME] = screenTransitionDuration + } + coroutineDispatcher.executor.execute { + val screenTransitionEvent = buildAppPerformanceEvent( + eventName = AlfredConstants.SCREEN_TRANSITION_METRICS, + attribute = screenTransitionAttributes, + eventType = AlfredConstants.SCREEN_TRANSITION_METRICS + ) + AlfredDispatcher.addTaskToQueue( + AddMetricTask( + screenTransitionEvent, applicationContext + ) + ) + } + } + } + suspend fun sendEventsToServer( applicationContext: Context @@ -557,7 +689,7 @@ object AlfredManager { val response = repository.sendEvents( config.getPostUrl(), request ) - return if (response.isSuccessful && response.code() == CODE_API_SUCCESS) { + return if ((response.isSuccessful && response.code() == CODE_API_SUCCESS) or (response.code() == CODE_API_BAD_REQUEST)) { analyticsDao.deleteEvents(analyticsEvents.map { it.eventId }) true } else { @@ -578,7 +710,12 @@ object AlfredManager { return false } - private fun sendAlfredSessionEvent(dumpFlow: Boolean = false, index: Int? = null) { + private fun sendAlfredSessionEvent( + dumpFlow: Boolean = false, index: Int? = null, + alfredSessionId: String? = null, + eventStartRecordingTime: Long? = null, + sessionStartRecordingTime: Long? = null + ) { var request: SessionRequest? = null if (dumpFlow) { var clientTs: Long? = null @@ -590,9 +727,9 @@ object AlfredManager { sessionTimeStamp = zipFileDetail.sessionStartRecordingTime sessionId = zipFileDetail.alfredSessionId } else { - clientTs = eventStartRecordingTimeForCrash - sessionTimeStamp = sessionStartRecordingTimeForCrash - sessionId = sessionIdForCrash + clientTs = eventStartRecordingTime + sessionTimeStamp = sessionStartRecordingTime + sessionId = alfredSessionId } request = SessionRequest( base_attribute = BaseAttribute( @@ -633,6 +770,13 @@ object AlfredManager { if (!dumpFlow) { config.setEventStartRecordingTime(true) handleDeviceAttributes() + } else { + index?.let { index -> + zipDetailsDao.deleteZipFileDetail(zipFileDetails[index].id) + } + if(workFailureData.size>0){ + workFailureData.removeAt(0) + } } } } @@ -669,8 +813,19 @@ object AlfredManager { } else { null } + var url= request.url.toString().split("?").first() + request.header(ALFRED)?.let {pathParam -> + if(url.contains(pathParam)){ + url=url.replace(pathParam,"Path_Param_Value") + request.header(X_TARGET)?.let {xTarget -> + url=xTarget.plus(url) + } + } + } + Log.d("ALFREDAPI","URL$url") + Log.d("ALFREDAPI","RESPONSE BODY${response.message}") val attributes = hashMapOf().apply { - this[AlfredConstants.URL] = request.url.toString() + this[AlfredConstants.URL] = url this[AlfredConstants.METHOD] = request.method this[AlfredConstants.RESPONSE_CODE] = response.code this[AlfredConstants.ERROR_MESSAGE] = errorMessage.toString() @@ -680,6 +835,22 @@ object AlfredManager { this[AlfredConstants.DURATION_IN_MS] = duration.toDouble() this[AlfredConstants.BYTES_RECEIVED] = byteReceived this[AlfredConstants.BYTES_SENT] = byteSent + if (config.getApiResponseEnableStatus()) { + if (response.code != CODE_API_SUCCESS || config.getEnabledApiPath() + ?.contains(url) == true + ) { + Log.d("ALFREDAPI","response${response.body.toString()}") + this[AlfredConstants.RESPONSE_BODY] = response.body.toString() + } + } + if (config.getApiRequestEnableStatus()) { + if (response.code != CODE_API_SUCCESS || config.getEnabledApiPath() + ?.contains(url) == true + ) { + Log.d("ALFREDAPI","request${request.body.toString()}") + this[AlfredConstants.REQUEST_BODY] = request.body.toString() + } + } } val appPerformanceEvent = buildAppPerformanceEvent( AlfredConstants.API_METRIC_EVENT_NAME, API_METRICS, attributes @@ -693,18 +864,12 @@ object AlfredManager { } } - fun stopRecording(appBackgroundView: View) { - isAppInBackground = true hasRecordingStarted = false screenShotTimer?.cancel() - if (config.getFirebaseControlledCruise()) { - deleteScreenShot() - } else { - if (config.getAlfredStatus() && config.getEnableRecordingStatus()) { - if (ScreenShotStorageHelper.images.size > 0) { - startAnrCrashZipUpload(appBackgroundView) - } + if (config.getAlfredStatus() && config.getEnableRecordingStatus()) { + if (ScreenShotStorageHelper.images.size > 0) { + startAnrCrashZipUpload(appBackgroundView) } } } @@ -717,9 +882,6 @@ object AlfredManager { eventStartRecordingTime: Long? = null, index: Int? = null ) { - sessionIdForCrash = alfredSessionId - sessionStartRecordingTimeForCrash = sessionStartRecordingTime - eventStartRecordingTimeForCrash = eventStartRecordingTime val fileList = ArrayList() imagePathList.forEach { screenShotPathHelper -> screenShotPathHelper.screenShotPath?.let { screenShotPath -> @@ -731,15 +893,25 @@ object AlfredManager { val zipFilePath = applicationContext.filesDir.path + "/" + zipFileName if (zip(fileList, zipFilePath) == true) { clearScreenShot(imagePathList) - getPreSignedUrl(zipFileName, true, index = index) + getPreSignedUrl( + zipFileName, + index = index, + workManagerFlow = true, + dumpFlow = true, + alfredSessionId = alfredSessionId, + eventStartRecordingTime = eventStartRecordingTime, + sessionStartRecordingTime = sessionStartRecordingTime + ) } } private fun toZip( imagePathList: List ) { + val zipFileName = config.getAlfredSessionId() + UUID.randomUUID().toString() + config.setZipFileName(zipFileName) val zipFilePath: String = - applicationContext.filesDir.path + "/" + config.getAlfredSessionId() + config.getSessionStartRecordingTime() + applicationContext.filesDir.path + "/" + zipFileName val fileList = ArrayList() imagePathList.forEach { screenShotPathHelper -> screenShotPathHelper.screenShotPath?.let { screenShotPath -> @@ -749,12 +921,11 @@ object AlfredManager { } } if (zip(fileList, zipFilePath) == true) { - deleteScreenShot() + deleteScreenShot(imagePathList) config.setNextEventStartRecordingTime() } } - fun handleTouchEvent( currentTouchEvent: MotionEvent?, screenName: String? = null, @@ -807,46 +978,22 @@ object AlfredManager { } } - fun handleScreenTransitionEvent( - currentScreenName: String?, previousScreenName: String?, screenTransitionDuration: Long - ) { - if (config.getAlfredStatus() && config.getMetricsApiEnableStatus()) { - val screenTransitionAttributes: HashMap = hashMapOf() - screenTransitionAttributes.apply { - this[AlfredConstants.CURRENT_SCREEN_NAME] = currentScreenName.orEmpty() - this[AlfredConstants.PREVIOUS_SCREEN_NAME] = previousScreenName.orEmpty() - this[AlfredConstants.LOAD_TIME] = screenTransitionDuration - } - coroutineDispatcher.executor.execute { - val screenTransitionEvent = buildAppPerformanceEvent( - eventName = AlfredConstants.SCREEN_TRANSITION_METRICS, - attribute = screenTransitionAttributes, - eventType = AlfredConstants.SCREEN_TRANSITION_METRICS - ) - AlfredDispatcher.addTaskToQueue( - AddMetricTask( - screenTransitionEvent, applicationContext - ) - ) - } - } - } - fun handleAnrEvent( anrEventProperties: Map, anrView: View, screenName: String? = null ) { startAnrCrashZipUpload(anrView) - coroutineDispatcher.executor.execute { - val event = buildEvent( - AlfredConstants.ANR_EVENT, anrEventProperties as HashMap, - screenName = screenName, - moduleName = currentModuleName - ) - AlfredDispatcher.addTaskToQueue(AddEventTask(event, this.applicationContext)) + if (config.getAlfredSessionId().isNotEmpty()) { + coroutineDispatcher.executor.execute { + val event = buildEvent( + AlfredConstants.ANR_EVENT, anrEventProperties as HashMap, + screenName = screenName, + moduleName = currentModuleName + ) + AlfredDispatcher.addTaskToQueue(AddEventTask(event, applicationContext)) + } } } - fun handleSWWEvent( screenName: String? = null, swwEventProperties: Map ) { @@ -871,14 +1018,16 @@ object AlfredManager { screenName: String? = null ) { startAnrCrashZipUpload(crashView) - coroutineDispatcher.executor.execute { - val event = buildEvent( - AlfredConstants.CRASH_ANALYTICS_EVENT, - crashEventProperties as HashMap, - screenName = screenName, - moduleName = currentModuleName - ) - AlfredDispatcher.addTaskToQueue(AddEventTask(event, applicationContext)) + if (config.getAlfredSessionId().isNotEmpty()) { + coroutineDispatcher.executor.execute { + val event = buildEvent( + AlfredConstants.CRASH_ANALYTICS_EVENT, + crashEventProperties as HashMap, + screenName = screenName, + moduleName = currentModuleName + ) + AlfredDispatcher.addTaskToQueue(AddEventTask(event, applicationContext)) + } } } 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 9f6b06a..1678581 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 @@ -19,7 +19,7 @@ import com.navi.alfred.db.model.ZipDetailsHelper @Database( entities = [AnalyticsEvent::class, ScreenShotPathHelper::class, ZipDetailsHelper::class, ApiMetricHelper::class], - version = 2, + version = 3, exportSchema = true ) 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 e4f5a99..2001a8b 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 @@ -35,7 +35,7 @@ data class ZipDetailsHelper( @ColumnInfo(name = "alfredSessionId") val alfredSessionId: String, @ColumnInfo(name = "sessionStartRecordingTime") val sessionStartRecordingTime: Long, @ColumnInfo(name = "eventStartRecordingTime") val eventStartRecordingTime: Long, - @ColumnInfo(name = "screenShotList") val screenShotList: String? + @ColumnInfo(name = "zipFileName") val zipFileName: String ) { @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 new file mode 100644 index 0000000..c6fa755 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/model/ErrorMessage.kt @@ -0,0 +1,18 @@ +package com.navi.alfred.model + +import com.navi.alfred.db.model.ScreenShotPathHelper +import java.util.* + + +data class ErrorMessage( + var statusCode: Int? = null, + var message: String? = null +) + +data class WorkManagerFailureInputData( + var alfredSessionId: String? = "", + var sessionStartRecordingTime: Long = 0L, + var eventStartRecordingTime: Long = 0L, + var screenShots: List = emptyList(), + val id: UUID +) \ No newline at end of file diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredNetworkRepository.kt b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredNetworkRepository.kt index b66c439..904ae9a 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredNetworkRepository.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredNetworkRepository.kt @@ -7,6 +7,7 @@ package com.navi.alfred.network +import com.navi.alfred.model.AnalyticsRequest import com.navi.alfred.model.EventMetricRequest import com.navi.alfred.model.SessionRequest import com.navi.alfred.network.model.CruiseResponse @@ -17,7 +18,7 @@ import retrofit2.Response class AlfredNetworkRepository { suspend fun sendEvents( - url: String, analyticsRequest: com.navi.alfred.model.AnalyticsRequest + url: String, analyticsRequest: AnalyticsRequest ): Response { return AlfredRetrofitProvider.getApiService() .sendEvents(url, "application/json", ALFRED, analyticsRequest) 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 1c2971d..80842be 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 @@ -12,21 +12,19 @@ import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerInterceptor import com.google.gson.GsonBuilder import com.navi.alfred.AlfredManager -import com.navi.alfred.BuildConfig import com.navi.alfred.deserializer.AnalyticsDataDeserializer import com.navi.alfred.deserializer.MetricsDataDeserializer import com.navi.alfred.deserializer.SessionDataDeserializer +import com.navi.alfred.model.AnalyticsRequest import com.navi.alfred.model.EventMetricRequest import com.navi.alfred.model.SessionRequest -import com.navi.alfred.utils.AlfredConstants.API_FAILURE -import com.navi.alfred.utils.AlfredConstants.API_FAILURE_CODE -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.Response +import com.navi.alfred.utils.handleException +import com.navi.alfred.utils.orZero +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -import java.io.IOException import java.util.concurrent.TimeUnit object AlfredRetrofitProvider { @@ -36,26 +34,28 @@ object AlfredRetrofitProvider { private lateinit var apiService: AlfredRetrofitService private lateinit var okHttpClient: OkHttpClient + private val headerInterceptor: Interceptor get() = Interceptor { chain -> val request = chain.request() - val response: Response = try { + val response = try { chain.proceed(request).newBuilder().build() - } catch (e: IOException) { + } catch (e: Exception) { + val errorMessage = handleException(e) // A mocked response in case of n/w exception - Response.Builder() - .request(request) - .code(API_FAILURE_CODE) - .message(API_FAILURE) - .build() + Response.Builder().request(request).protocol(Protocol.HTTP_2) + .code(errorMessage.statusCode.orZero()) + .body(ResponseBody.create("application/json".toMediaTypeOrNull(), "{}")) + .message(errorMessage.message.orEmpty()).build() } response } + fun init(context: Context) { okHttpClient = OkHttpClient.Builder().apply { - connectTimeout(20, TimeUnit.SECONDS).readTimeout(20, TimeUnit.SECONDS) - if (BuildConfig.DEBUG && !AlfredManager.config.getDisableChuckerStatus()) { + connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30,TimeUnit.SECONDS) + if (BuildConfig.DEBUG && !AlfredManager.config.getDisableAlfredLogsStatus()) { addInterceptor(loggingInterceptor()) addInterceptor( ChuckerInterceptor.Builder(context).collector(ChuckerCollector(context)) @@ -63,6 +63,8 @@ object AlfredRetrofitProvider { ) } addInterceptor(headerInterceptor) + connectionPool(ConnectionPool(0, 5, TimeUnit.MINUTES)) + .protocols(listOf(Protocol.HTTP_1_1)) }.build() apiService = getRetrofit().create(AlfredRetrofitService::class.java) } @@ -76,7 +78,7 @@ object AlfredRetrofitProvider { BASE_URL_DEBUG } val providesDeserializer = GsonBuilder().registerTypeAdapter( - com.navi.alfred.model.AnalyticsRequest::class.java, + AnalyticsRequest::class.java, AnalyticsDataDeserializer() ).registerTypeAdapter(SessionRequest::class.java, SessionDataDeserializer()) .registerTypeAdapter(EventMetricRequest::class.java, MetricsDataDeserializer()).create() diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitService.kt b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitService.kt index 9b82778..7dc8ee3 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitService.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/network/AlfredRetrofitService.kt @@ -7,6 +7,7 @@ package com.navi.alfred.network +import com.navi.alfred.model.AnalyticsRequest import com.navi.alfred.model.EventMetricRequest import com.navi.alfred.model.SessionRequest import com.navi.alfred.network.model.CruiseResponse @@ -27,7 +28,7 @@ interface AlfredRetrofitService { @Url url: String, @Header(CONTENT_TYPE) contentType: String, @Header(X_TARGET) target: String, - @Body analyticsRequest: com.navi.alfred.model.AnalyticsRequest + @Body analyticsRequest: AnalyticsRequest ): Response @GET("ingest/session/pre-sign/{sessionId}") diff --git a/navi-alfred/src/main/java/com/navi/alfred/network/model/CruiseResponse.kt b/navi-alfred/src/main/java/com/navi/alfred/network/model/CruiseResponse.kt index 6bade16..ee995f8 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/network/model/CruiseResponse.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/network/model/CruiseResponse.kt @@ -37,6 +37,9 @@ data class OsConfig( data class MetricsConfig( @SerializedName("disable_api_performance") val disableApiPerformance: Boolean? = null, + @SerializedName("disable_api_response") val disableApiResponse: Boolean? = null, + @SerializedName("enabled_api_paths") val enabledApiPaths: List? = null, + @SerializedName("disable_api_request") val disableApiRequest: Boolean? = null, @SerializedName("disable_cpu_monitoring") val disableCpuMonitoring: Boolean? = null, @SerializedName("disable_memory_monitoring") val disableMemoryMonitoring: Boolean? = null, @SerializedName("disable_remote_logging") val disableRemoteLogging: Boolean? = null, 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 7a412c5..93e7e4c 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 @@ -9,6 +9,7 @@ package com.navi.alfred.utils object AlfredConstants { const val CODE_API_SUCCESS = 200 + const val CODE_API_BAD_REQUEST = 400 const val UNDERSCORE = "_" const val ADD_EVENT_TASK = "AddEventTask" const val ADD_API_METRIC_TASK = "AddMetricTask" @@ -24,8 +25,6 @@ object AlfredConstants { const val QA = "qa" const val TOUCH_EVENT = "TOUCH_EVENT" const val SCROLL_EVENT = "SCROLL_EVENT" - const val SCREEN_WIDTH = "SCREEN_WIDTH" - const val SCREEN_HEIGHT = "SCREEN_HEIGHT" const val START_X = "START_X" const val START_Y = "START_Y" const val END_X = "END_X" @@ -37,7 +36,6 @@ object AlfredConstants { const val ALFRED_SESSION_ID = "ALFRED_SESSION_ID" const val API_METRIC_EVENT_NAME = "API_METRIC_EVENT" const val API_METRICS = "API_METRICS" - const val SCREEN_TRANSITION_METRICS = "SCREEN_TRANSITION_METRICS" const val SESSION_START_RECORDING_TIME = "SESSION_START_RECORDING_TIME" const val EVENT_START_RECORDING_TIME = "EVENT_START_RECORDING_TIME" const val URL = "url" @@ -50,6 +48,8 @@ object AlfredConstants { const val DURATION_IN_MS = "duration_in_ms" const val BYTES_RECEIVED = "bytes_received" const val BYTES_SENT = "bytes_sent" + const val RESPONSE_BODY = "response_body" + const val REQUEST_BODY = "request_body" const val ALFRED = "ALFRED" const val CONTENT_TYPE = "Content-Type" const val X_TARGET = "X-Target" @@ -65,15 +65,15 @@ object AlfredConstants { const val REASON = "REASON" const val CODE = "CODE" const val STATUS_CODE = "STATUS_CODE" - const val EVENT_DB_NAME = "navi-analytics" - const val ZIP_FILE_EXTENSION = ".zip" - const val IMAGE_FILE_EXTENSION = ".jpeg" - const val SYNC_EVENT_TASK = "SyncEventTask" + const val WORKER_ZIP_FILENAME_ENDPOINT = "WORKER_ZIP_FILENAME_ENDPOINT" + const val DISABLE_ALFRED_LOGS = "DISABLE_ALFRED_LOGS" + const val DISABLE_CDN_LOGS = "DISABLE_CDN_LOGS" const val CURRENT_SCREEN_NAME = "current_screen_name" const val PREVIOUS_SCREEN_NAME = "previous_screen_name" const val LOAD_TIME = "load_time_in_ms" - const val DISABLE_CHUCKER = "DISABLE_CHUCKER" - const val API_FAILURE = "API_FAILURE" - const val API_FAILURE_CODE = 300 - + const val SCREEN_TRANSITION_METRICS = "SCREEN_TRANSITION_METRICS" + const val ZIP_FILE_EXTENSION = ".zip" + const val SYNC_EVENT_TASK = "SyncEventTask" + const val IMAGE_FILE_EXTENSION = ".jpeg" + const val EVENT_DB_NAME = "navi-analytics" } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredHelper.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredHelper.kt deleted file mode 100644 index c7f2584..0000000 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/AlfredHelper.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.navi.alfred.utils - -import com.google.firebase.crashlytics.FirebaseCrashlytics - -object AlfredHelper { - - fun recordException(e: Throwable) { - FirebaseCrashlytics.getInstance().recordException(e) - } -} \ No newline at end of file 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 new file mode 100644 index 0000000..03bc444 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ApiConstants.kt @@ -0,0 +1,13 @@ +package com.navi.alfred.utils + +object ApiConstants { + const val API_CONNECT_TIMEOUT_VALUE = 20L + const val API_SUCCESS_CODE = 200 + const val API_CODE_ERROR = 20 + const val NO_INTERNET = 23 + const val API_CODE_SOCKET_TIMEOUT = 24 + const val API_WRONG_ERROR_RESPONSE = 25 + const val API_CODE_UNKNOWN_HOST = 21 + const val API_CODE_CONNECT_EXCEPTION = 22 + const val API_CODE_SOCKET = 26 +} \ No newline at end of file diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/Ext.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/Ext.kt new file mode 100644 index 0000000..f6fdec8 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/Ext.kt @@ -0,0 +1,5 @@ +package com.navi.alfred.utils + +fun Int?.orZero() = this ?: 0 + +fun Boolean?.orFalse() = this ?: false \ No newline at end of file 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 new file mode 100644 index 0000000..7313e49 --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/NetworkUtils.kt @@ -0,0 +1,59 @@ +/* + * + * * Copyright © 2022-2023 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.alfred.utils + +import com.google.gson.JsonParseException +import com.navi.alfred.AlfredManager +import com.navi.alfred.model.ErrorMessage +import java.net.ConnectException +import java.net.NetworkInterface +import java.net.SocketException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + + +fun handleException(e: Throwable): ErrorMessage { + val errorMessage = ErrorMessage() + if (!isNetworkAvailable(AlfredManager.applicationContext)) { + errorMessage.statusCode = ApiConstants.NO_INTERNET + } else if (e is ConnectException) { + errorMessage.statusCode = ApiConstants.API_CODE_CONNECT_EXCEPTION + } else if (e is UnknownHostException) { + errorMessage.statusCode = ApiConstants.API_CODE_UNKNOWN_HOST + } else if (e is SocketTimeoutException) { + errorMessage.statusCode = ApiConstants.API_CODE_SOCKET_TIMEOUT + } else if (e is JsonParseException) { + errorMessage.statusCode = ApiConstants.API_WRONG_ERROR_RESPONSE + } else if (e is SocketException) { + errorMessage.statusCode = ApiConstants.API_CODE_SOCKET + } + else { + errorMessage.statusCode = ApiConstants.API_CODE_ERROR + } + errorMessage.message = e.message.toString() + return errorMessage +} + +fun getIPAddress(): String? { + try { + val networkInterfaces = NetworkInterface.getNetworkInterfaces() + while (networkInterfaces.hasMoreElements()) { + val networkInterface = networkInterfaces.nextElement() + val inetAddresses = networkInterface.inetAddresses + while (inetAddresses.hasMoreElements()) { + val inetAddress = inetAddresses.nextElement() + if (!inetAddress.isLoopbackAddress && !inetAddress.isLinkLocalAddress && inetAddress.isSiteLocalAddress) { + return inetAddress.hostAddress + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return null +} \ No newline at end of file diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/Utils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/Utils.kt index 0fb6b88..e1ad44c 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/Utils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/Utils.kt @@ -19,12 +19,13 @@ import android.os.StatFs import android.telephony.TelephonyManager import android.view.MotionEvent import android.view.View -import com.navi.alfred.utils.ScreenShotStorageHelper +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.navi.alfred.AlfredManager import com.navi.alfred.db.AlfredDatabaseHelper import com.navi.alfred.db.model.ScreenShotPathHelper import com.navi.alfred.model.NaviMotionEvent import com.navi.alfred.utils.AlfredConstants.IMAGE_FILE_EXTENSION +import com.navi.alfred.utils.AlfredConstants.UNDERSCORE import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -34,7 +35,6 @@ import java.io.* import java.lang.Math.subtractExact import java.util.* import java.util.zip.ZipEntry -import java.util.zip.ZipFile import java.util.zip.ZipOutputStream import kotlin.math.abs @@ -82,17 +82,21 @@ fun combineScreenshots( } var screenWidth = Resources.getSystem().displayMetrics.widthPixels / 2 var screenHeight = Resources.getSystem().displayMetrics.heightPixels / 2 - if (!isResolutionEven(screenWidth, screenHeight)) { - screenHeight += 1 + if (!isResolutionEven(screenWidth)) { screenWidth += 1 } + if (!isResolutionEven(screenHeight)) { + screenHeight += 1 + } val combinedBitmap = Bitmap.createBitmap(screenWidth, screenHeight, Bitmap.Config.ARGB_8888) val canvas = Canvas(combinedBitmap) canvas.drawBitmap(backgroundScreenshot, 0f, 0f, null) var bottomSheetLeft = (screenWidth - bottomSheetScreenshot.width) / 2f var bottomSheetTop = screenHeight - bottomSheetScreenshot.height.toFloat() - if (!isResolutionEven(bottomSheetLeft.toInt(), bottomSheetTop.toInt())) { + if (!isResolutionEven(bottomSheetLeft.toInt())) { bottomSheetLeft = (bottomSheetLeft.toInt() + 1).toFloat() + } + if (!isResolutionEven(bottomSheetTop.toInt())) { bottomSheetTop = (bottomSheetTop.toInt() + 1).toFloat() } canvas.drawBitmap(bottomSheetScreenshot, bottomSheetLeft, bottomSheetTop, null) @@ -169,8 +173,10 @@ fun createBitmapForView(view: View): Pair? { var width = view.width / 2 var height = view.height / 2 if (width > 0 && height > 0) { - if (!isResolutionEven(width, height)) { + if (!isResolutionEven(width)) { width += 1 + } + if (!isResolutionEven(height)) { height += 1 } val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) @@ -185,8 +191,12 @@ fun createBitmapForView(view: View): Pair? { fun zip(_files: ArrayList, zipFilePath: String?): Boolean? { val buffer = 1000 try { - zipFilePath?.let { zipFile -> - File(zipFile) + zipFilePath?.let { path -> + if (File(path).exists()) + return true + else { + File(path) + } } var origin: BufferedInputStream? = null val dest = FileOutputStream(zipFilePath) @@ -215,14 +225,13 @@ fun zip(_files: ArrayList, zipFilePath: String?): Boolean? { return null } -fun clearScreenShot(path: List): Boolean { +fun clearScreenShot(path: List) { path.forEach { data -> val file = data.screenShotPath?.let { File(it) } if (file?.exists() == true) { file.delete() } } - return true } fun isScreenDisabled(screenName: String? = null, moduleName: String? = null): Boolean { @@ -293,7 +302,7 @@ fun getNetworkType(context: Context): String { TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyManager.NETWORK_TYPE_HSDPA, TelephonyManager.NETWORK_TYPE_HSUPA, TelephonyManager.NETWORK_TYPE_HSPA, TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyManager.NETWORK_TYPE_HSPAP -> "3G" TelephonyManager.NETWORK_TYPE_LTE -> "4G" TelephonyManager.NETWORK_TYPE_NR -> "5G" - else -> "Unknown" + AlfredConstants.UNDERSCORE + mTelephonyManager.networkType + else -> "Unknown" + UNDERSCORE + mTelephonyManager.networkType } } catch (e: Exception) { e.log() @@ -305,27 +314,23 @@ fun getCarrierName(context: Context): String? { return try { (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).networkOperatorName } catch (e: Exception) { - e.log() + e.log() null } } -fun Int?.orZero() = this ?: 0 - -fun Float?.orZero() = this ?: 0F - -fun getBatteryPercentageForDevice(context: Context): Float { +fun getBatteryPercentage(context: Context): Float { val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY).toFloat() } -fun getMemoryUsageForDevice(): Float { +fun getMemoryUsage(): Float { val runtime = Runtime.getRuntime() val usedMemory = runtime.totalMemory() - runtime.freeMemory() return usedMemory / (1024f * 1024f) } -fun getCpuUsageForDevice(): Float { +fun getCpuUsage(): Float { val command = "top -n 1 -d 1" val process = Runtime.getRuntime().exec(command) val reader = BufferedReader(InputStreamReader(process.inputStream)) @@ -345,15 +350,7 @@ fun getCpuUsageForDevice(): Float { } -fun hasFilesInZip(zipFilePath: String): Boolean { - val zipFile = ZipFile(zipFilePath) - val entries = zipFile.entries() - val hasEntries = entries.hasMoreElements() - zipFile.close() - return hasEntries -} - -fun getStorageUsageForDevice(context: Context): Pair { +fun getStorageUsage(context: Context): Pair { val path = context.filesDir.path val stat = StatFs(path) val blockSize = stat.blockSizeLong @@ -376,24 +373,23 @@ private fun Any.tag(): String { } } -private fun isResolutionEven(width: Int, height: Int): Boolean { - if (width.mod(2) == 0 && height.mod(2) == 0) { - return true - } - return false -} - fun java.lang.Exception.log() { if (AlfredManager.config.isQa()) { Timber.tag(tag()).e(this) } - AlfredHelper.recordException(this) + recordException(this) } -fun getScreenWidth(): Int { - return Resources.getSystem().displayMetrics.widthPixels +fun isNetworkAvailable(context: Context): Boolean { + val connectivityManager = + context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager + return connectivityManager?.activeNetworkInfo?.isConnected.orFalse() } -fun getScreenHeight(): Int { - return Resources.getSystem().displayMetrics.heightPixels +private fun isResolutionEven(size: Int): Boolean { + return size.mod(2) == 0 } + +fun recordException(e: Throwable) { + FirebaseCrashlytics.getInstance().recordException(e) +} \ No newline at end of file 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 95888fc..3ce52bb 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 @@ -1,15 +1,15 @@ package com.navi.alfred.worker import android.content.Context -import android.util.Log 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.model.ScreenShotPathHelper +import com.navi.alfred.model.WorkManagerFailureInputData import com.navi.alfred.utils.AlfredConstants -import com.navi.alfred.utils.checkFileExists import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.lang.reflect.Type @@ -29,10 +29,19 @@ class UploadFileWorker( val listType: Type = object : TypeToken?>() {}.type val screenShots: List = Gson().fromJson(screenShotList.toString(), listType) - val zipFileName = alfredSessionId + sessionStartRecordingTime.toString() + val zipFileName = + alfredSessionId + sessionStartRecordingTime + AlfredConstants.WORKER_ZIP_FILENAME_ENDPOINT + workFailureData.add( + WorkManagerFailureInputData( + alfredSessionId = alfredSessionId, + sessionStartRecordingTime, + eventStartRecordingTime, + screenShots, + id + ) + ) try { if (alfredSessionId == null || sessionStartRecordingTime == 0L || eventStartRecordingTime == 0L) { - handleWorkFailure(zipFileName) Result.failure() } else { AlfredManager.toZipForWorkManager(screenShots, zipFileName,sessionStartRecordingTime,alfredSessionId, eventStartRecordingTime) @@ -40,13 +49,8 @@ class UploadFileWorker( } } catch (e: Exception) { e.printStackTrace() - handleWorkFailure(zipFileName) - Result.failure() + Result.retry() } } - - private fun handleWorkFailure(zipFileName:String) { - checkFileExists(zipFileName, context = applicationContext)?.delete() - } }