SEPARATE MODULE CREATION (#7)
This commit is contained in:
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.7.20" />
|
||||
<option name="version" value="1.6.21" />
|
||||
</component>
|
||||
</project>
|
||||
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
|
||||
@@ -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<String>? = null,
|
||||
var batteryPercentageBeforeEventStart: Float? = null,
|
||||
private var disableScreenList: List<String>? = null,
|
||||
private var disableModuleList: List<String>? = 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<String>?) {
|
||||
this.enabledApiPath = enabledApiPath
|
||||
}
|
||||
fun getEnabledApiPath():Set<String>? = 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
|
||||
}
|
||||
|
||||
@@ -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<WorkManagerFailureInputData> = mutableListOf()
|
||||
private var retryCount: Int = 0
|
||||
private lateinit var zipFileDetails: List<ZipDetailsHelper>
|
||||
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<UploadFileWorker>()
|
||||
@@ -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<ScreenShotPathHelper>) {
|
||||
try {
|
||||
val screenShotPathList: List<ScreenShotPathHelper> =
|
||||
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<String, Any> = 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<String, Any>().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<String>()
|
||||
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<ScreenShotPathHelper>
|
||||
) {
|
||||
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<String>()
|
||||
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<String, Any> = 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<String, String>, anrView: View, screenName: String? = null
|
||||
) {
|
||||
startAnrCrashZipUpload(anrView)
|
||||
coroutineDispatcher.executor.execute {
|
||||
val event = buildEvent(
|
||||
AlfredConstants.ANR_EVENT, anrEventProperties as HashMap<String, String>,
|
||||
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<String, String>,
|
||||
screenName = screenName,
|
||||
moduleName = currentModuleName
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(AddEventTask(event, applicationContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun handleSWWEvent(
|
||||
screenName: String? = null, swwEventProperties: Map<String, String>
|
||||
) {
|
||||
@@ -871,14 +1018,16 @@ object AlfredManager {
|
||||
screenName: String? = null
|
||||
) {
|
||||
startAnrCrashZipUpload(crashView)
|
||||
coroutineDispatcher.executor.execute {
|
||||
val event = buildEvent(
|
||||
AlfredConstants.CRASH_ANALYTICS_EVENT,
|
||||
crashEventProperties as HashMap<String, String>,
|
||||
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<String, String>,
|
||||
screenName = screenName,
|
||||
moduleName = currentModuleName
|
||||
)
|
||||
AlfredDispatcher.addTaskToQueue(AddEventTask(event, applicationContext))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<ScreenShotPathHelper> = emptyList(),
|
||||
val id: UUID
|
||||
)
|
||||
@@ -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<Unit> {
|
||||
return AlfredRetrofitProvider.getApiService()
|
||||
.sendEvents(url, "application/json", ALFRED, analyticsRequest)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<Unit>
|
||||
|
||||
@GET("ingest/session/pre-sign/{sessionId}")
|
||||
|
||||
@@ -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<String>? = 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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
5
navi-alfred/src/main/java/com/navi/alfred/utils/Ext.kt
Normal file
5
navi-alfred/src/main/java/com/navi/alfred/utils/Ext.kt
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.navi.alfred.utils
|
||||
|
||||
fun Int?.orZero() = this ?: 0
|
||||
|
||||
fun Boolean?.orFalse() = this ?: false
|
||||
@@ -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
|
||||
}
|
||||
@@ -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<Canvas, Bitmap>? {
|
||||
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<Canvas, Bitmap>? {
|
||||
fun zip(_files: ArrayList<String>, 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<String>, zipFilePath: String?): Boolean? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun clearScreenShot(path: List<ScreenShotPathHelper>): Boolean {
|
||||
fun clearScreenShot(path: List<ScreenShotPathHelper>) {
|
||||
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<Long, Long> {
|
||||
fun getStorageUsage(context: Context): Pair<Long, Long> {
|
||||
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)
|
||||
}
|
||||
@@ -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<List<ScreenShotPathHelper?>?>() {}.type
|
||||
val screenShots: List<ScreenShotPathHelper> =
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user