From 0db7eb2db4ddd26d784f03c070b43ffcdd87fee8 Mon Sep 17 00:00:00 2001 From: Sayed Owais Ali Date: Wed, 26 Jun 2024 13:49:06 +0530 Subject: [PATCH] TP-67637 | PixelCopy Capture (#195) Co-authored-by: Varun Jain --- .../navi/alfred/demo/AlfredDemoApplication.kt | 99 ++++---- .../java/com/navi/alfred/AlfredManager.kt | 167 +++++++------ .../com/navi/alfred/utils/CanvasDrawUtils.kt | 68 ++++++ .../java/com/navi/alfred/utils/EventUtils.kt | 3 +- .../com/navi/alfred/utils/MaskingUtils.kt | 13 +- .../com/navi/alfred/utils/PixelCopyUtils.kt | 230 ++++++++++++++++++ .../com/navi/alfred/utils/ScreenShotUtils.kt | 173 ++++++------- .../java/com/navi/alfred/utils/UploadUtils.kt | 12 +- .../java/com/navi/alfred/utils/ZipUtils.kt | 11 +- .../com/navi/alfred/worker/AddEventTask.kt | 2 +- .../com/navi/alfred/worker/AddFailureTask.kt | 2 +- .../com/navi/alfred/worker/AddMetricTask.kt | 2 +- .../com/navi/alfred/worker/AddNegativeCase.kt | 2 +- .../com/navi/alfred/worker/AnalyticsTask.kt | 2 +- .../alfred/worker/AnalyticsTaskProcessor.kt | 16 +- .../com/navi/alfred/worker/SyncDataTask.kt | 8 +- .../com/navi/alfred/worker/SyncFailureTask.kt | 5 +- .../alfred/worker/SyncNegativeCaseTask.kt | 5 +- .../navi/alfred/worker/UploadEventsWorker.kt | 1 - .../navi/alfred/worker/UploadFileWorker.kt | 1 - .../alfred/worker/ZipUploadRetryWorker.kt | 17 +- 21 files changed, 540 insertions(+), 299 deletions(-) create mode 100644 navi-alfred/src/main/java/com/navi/alfred/utils/CanvasDrawUtils.kt create mode 100644 navi-alfred/src/main/java/com/navi/alfred/utils/PixelCopyUtils.kt diff --git a/app/src/main/java/com/navi/alfred/demo/AlfredDemoApplication.kt b/app/src/main/java/com/navi/alfred/demo/AlfredDemoApplication.kt index b4d5611..3e47c47 100644 --- a/app/src/main/java/com/navi/alfred/demo/AlfredDemoApplication.kt +++ b/app/src/main/java/com/navi/alfred/demo/AlfredDemoApplication.kt @@ -9,17 +9,14 @@ package com.navi.alfred.demo import android.app.Activity import android.app.Application -import android.content.IntentFilter import android.os.Bundle import android.util.Log import com.github.anrwatchdog.ANRWatchDog import com.navi.alfred.AlfredConfig import com.navi.alfred.AlfredManager -import com.navi.alfred.AlfredManager.isAlfredRecordingEnabled import com.navi.alfred.demo.activity.MainActivity import com.navi.alfred.demo.activity.SWWActivity import com.navi.alfred.utils.AlfredConstants -import com.navi.alfred.utils.AlfredConstants.BROADCAST_ACTION_TYPE import com.navi.alfred.utils.log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -40,8 +37,22 @@ class AlfredDemoApplication : Application(), Application.ActivityLifecycleCallba "qa", "oMv77fgpBg9NFGom0Psizbf7lbrdBVJz" ) - AlfredManager.init(alfredConfig, this, 27, "Critical Journey is active") - getAlfredCruiseInfo() + AlfredManager.init(alfredConfig, this, 27, "Critical Journey") + + CoroutineScope(Dispatchers.IO).launch { + try { + AlfredManager.getAlfredCruiseInfo( + cruiseApiSuccessful = { response -> + Log.d( + "Alfred", + "Alfred - getAlfredCruiseInfo cruiseApiSuccessful h response = $response" + ) + } + ) + } catch (e: Exception) { + e.log() + } + } // Dumping anr data to click stream ANRWatchDog() @@ -97,7 +108,7 @@ class AlfredDemoApplication : Application(), Application.ActivityLifecycleCallba override fun onActivityResumed(activity: Activity) { if (appForegroundCounter > 0) { - startAlfredRecording(activity) + alfredOnActivityResumed(activity) } } @@ -114,64 +125,36 @@ class AlfredDemoApplication : Application(), Application.ActivityLifecycleCallba override fun onActivityDestroyed(activity: Activity) {} - private fun startAlfredRecording(activity: Activity) { - if (isAlfredRecordingEnabled()) { - val screenName: String? - val moduleName: String? - when (activity) { - is MainActivity -> { - screenName = "MainActivity" - moduleName = "Demo App" - } - is SWWActivity -> { - screenName = "SWWActivity" - moduleName = "Demo App" - } - else -> { - screenName = AlfredConstants.THIRD_PARTY_SCREEN - moduleName = AlfredConstants.THIRD_PARTY_MODULE - } + private fun alfredOnActivityResumed(activity: Activity) { + val screenName: String? + val moduleName: String? + when (activity) { + is MainActivity -> { + screenName = "NaviApp_HomePage_Lands" + moduleName = "Demo App" } - try { - AlfredManager.startRecording( - activity.applicationContext, - activity.window.decorView.rootView, - screenName, - moduleName - ) - } catch (e: Exception) { - e.log() + is SWWActivity -> { + screenName = "NaviApp_HomePage_Lands" + moduleName = "Demo App" } + else -> { + screenName = AlfredConstants.THIRD_PARTY_SCREEN + moduleName = AlfredConstants.THIRD_PARTY_MODULE + } + } + try { + AlfredManager.onActivityResumed( + activity = activity, + screenName = screenName, + moduleName = moduleName, + applicationContext = applicationContext + ) + } catch (e: Exception) { + e.log() } } private fun isAppInForeground(): Boolean { return appForegroundCounter >= 1 } - - private fun getAlfredCruiseInfo() { - Log.d("Alfred", "Alfred - getAlfredCruiseInfo started") - val appNetworkReceiver = AlfredNetworkFailureReceiver() - val intentFilter = IntentFilter(BROADCAST_ACTION_TYPE) - registerReceiver(appNetworkReceiver, intentFilter) - CoroutineScope(Dispatchers.IO).launch { - try { - Log.d( - "Alfred", - "Alfred - getAlfredCruiseInfo started inside try thread = ${Thread.currentThread().name}" - ) - AlfredManager.getAlfredCruiseInfo( - cruiseApiSuccessful = { response -> - Log.d( - "Alfred", - "Alfred - getAlfredCruiseInfo cruiseApiSuccessful h response = $response" - ) - } - ) - } catch (e: Exception) { - Log.d("Alfred", "Alfred - getAlfredCruiseInfo catch e = $e") - e.log() - } - } - } } 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 4ebff46..4123f4d 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/AlfredManager.kt @@ -43,7 +43,6 @@ import com.navi.alfred.utils.ScreenShotStorageHelper import com.navi.alfred.utils.buildAppPerformanceEvent import com.navi.alfred.utils.buildEvent import com.navi.alfred.utils.buildNegativeCaseEvent -import com.navi.alfred.utils.captureBottomSheet import com.navi.alfred.utils.captureScreen import com.navi.alfred.utils.captureScreenshotOfCustomView import com.navi.alfred.utils.checkDbAndDeleteCorruptScreenshots @@ -78,7 +77,8 @@ object AlfredManager { private val exceptionHandler = CoroutineExceptionHandler { _, _ -> } private var previousTouchEvent: NaviMotionEvent = NaviMotionEvent() private var screenShotCaptureDelay: Long = 1000L - private var reactBottomSheetView: WeakReference? = null + private var screenShotTimerStartDelay: Long = 500L + internal var reactBottomSheetView: WeakReference? = null private var screenShotTimer: Timer? = null private var viewLayoutDelay: Long = 1000 internal val mutex = Mutex() @@ -113,6 +113,11 @@ object AlfredManager { internal var hasCheckedDb: Boolean = true internal var criticalJourneyResponseCode: Int = 0 internal var criticalJourneyResponseMessage: String = "" + var bmpForCanvas: Pair? = null + private var isCruiseApiCalled: Boolean = false + private var isActivityResumed: Boolean = false + internal var currentView: WeakReference? = null + internal var bmpForThirdPartySdkScreen: Bitmap? = null fun init( config: AlfredConfig, @@ -133,18 +138,32 @@ object AlfredManager { return config.getAlfredStatus() && config.getEnableRecordingStatus() } - fun startRecording( - context: Context, - view: View, + fun onActivityResumed( screenName: String? = null, moduleName: String, - activity: Activity? = null + activity: Activity? = null, + applicationContext: Context ) { + isActivityResumed = true + if (isAlfredRecordingEnabled() || !isCruiseApiCalled) { + currentActivity = WeakReference(activity) + setCurrentScreenName(screenName) + currentModuleName = moduleName + currentView = WeakReference(activity?.window?.decorView?.rootView) + if (moduleName == THIRD_PARTY_MODULE) { + handleScreenTransitionEvent(activity?.localClassName.toString(), moduleName) + } + } + if (isCruiseApiCalled && isAlfredRecordingEnabled()) { + initAlfredRecording(applicationContext) + startRecording() + } + } + + private fun initAlfredRecording(context: Context) { if (config.getEnableRecordingStatus().not()) { return } - screenShotTimer?.cancel() - screenShotTimer = Timer() if (!hasRecordingStarted) { checkDbAndDeleteCorruptScreenshots() config.setAlfredSessionId() @@ -155,28 +174,32 @@ object AlfredManager { val startRecordingEvent = buildEvent( AlfredConstants.START_RECORDING_EVENT, - screenName = screenName, - moduleName = moduleName + screenName = currentScreenName, + moduleName = currentModuleName ) AlfredDispatcher.addTaskToQueue(AddEventTask(startRecordingEvent, context)) + hasRecordingStarted = true hasCheckedDb = false } - currentActivity = WeakReference(activity) - setCurrentScreenName(screenName) - currentModuleName = moduleName - hasRecordingStarted = true - var bmpForCanvas: Pair? = null - var bmpForThirdPartySdkScreen: Bitmap? = null - if (moduleName == THIRD_PARTY_MODULE) { - handleScreenTransitionEvent(activity?.localClassName.toString(), moduleName) + } + + private fun startRecording() { + if ( + config.getEnableRecordingStatus().not() || + currentActivity == null || + currentScreenName == null || + currentModuleName == null + ) { + return } + screenShotTimer?.cancel() + screenShotTimer = Timer() val timerTask: TimerTask = object : TimerTask() { override fun run() { - coroutineScope.launch(Dispatchers.IO) { - if (moduleName == THIRD_PARTY_MODULE) { - currentScreenName = activity?.localClassName.toString() - if (isScreenDisabled(currentScreenName, moduleName)) { + coroutineScope.launch { + if (currentModuleName == THIRD_PARTY_MODULE) { + if (isScreenDisabled(currentScreenName, currentModuleName)) { if (bmpForThirdPartySdkScreen == null) { val thirdPartyScreenView = LayoutInflater.from(applicationContext) @@ -184,7 +207,7 @@ object AlfredManager { measureInflatedView(thirdPartyScreenView) thirdPartyScreenView .findViewById(R.id.tv_third_party_name) - .text = screenName + .text = currentScreenName bmpForThirdPartySdkScreen = thirdPartyScreenView?.let { captureScreenshotOfCustomView(it) @@ -198,18 +221,20 @@ object AlfredManager { } else { if (bmpForCanvas == null) { delay(viewLayoutDelay) - bmpForCanvas = createBitmapForView(view) + bmpForCanvas = + currentView?.get()?.let { createBitmapForView(it) } } try { - captureScreen( - view, - context, - screenName = screenName, - scope = coroutineScope, - canvas = bmpForCanvas?.first, - bmp = bmpForCanvas?.second, - moduleName = moduleName - ) + currentView?.get()?.let { view -> + captureScreen( + view, + screenName = currentScreenName, + canvas = bmpForCanvas?.first, + bmp = bmpForCanvas?.second, + moduleName = currentModuleName, + activity = currentActivity?.get() + ) + } } catch (e: Exception) { e.log() } @@ -217,31 +242,17 @@ object AlfredManager { } else { if (bmpForCanvas == null) { delay(viewLayoutDelay) - bmpForCanvas = createBitmapForView(view) + bmpForCanvas = currentView?.get()?.let { createBitmapForView(it) } } try { - val bottomSheetView = - reactBottomSheetView?.get() - ?: dialog?.window?.decorView?.rootView - if (bottomSheetView != null) { - captureBottomSheet( - view, - bottomSheetView, - context, - screenName, - bmpForCanvas?.first, - rootBmp = bmpForCanvas?.second, - moduleName = moduleName - ) - } else { + currentView?.get()?.let { view -> captureScreen( view, - context, - screenName = screenName, - scope = coroutineScope, + screenName = currentScreenName, canvas = bmpForCanvas?.first, bmp = bmpForCanvas?.second, - moduleName = moduleName + moduleName = currentModuleName, + activity = currentActivity?.get() ) } } catch (e: Exception) { @@ -253,13 +264,14 @@ object AlfredManager { } } screenShotCaptureDelay = (1000 / config.getSnapshotPerSecond().toLong()) - screenShotTimer?.schedule(timerTask, 0, screenShotCaptureDelay) + screenShotTimer?.schedule(timerTask, screenShotTimerStartDelay, screenShotCaptureDelay) } fun stopRecording() { if (isAlfredRecordingEnabled()) { isAppInBackground = true hasRecordingStarted = false + isActivityResumed = false screenShotTimer?.cancel() isCriticalUserJourneyActive.set(false) val appBackgroundView = @@ -280,25 +292,34 @@ object AlfredManager { } fun getAlfredCruiseInfo(cruiseApiSuccessful: (response: CruiseResponse) -> Unit) { - CoroutineScope(Dispatchers.IO).launch { - try { - getCruiseConfig( - cruiseApiSuccessful = { response -> - alfredDataBase = - AlfredDatabaseHelper.getAnalyticsDatabase(applicationContext) - analyticsDao = alfredDataBase.analyticsDao() - screenShotDao = alfredDataBase.screenShotDao() - zipDetailsDao = alfredDataBase.zipDetailsDao() - apiMetricDao = alfredDataBase.apiMetricDao() - negativeCaseDao = alfredDataBase.negativeCaseDao() - failureEventDao = alfredDataBase.failureEventDao() - sensitiveComposeRepository = ComposeMaskingRepoImpl() - startSyncEvents() - cruiseApiSuccessful(response) - } - ) - } catch (e: Exception) { - e.log() + if (!isCruiseApiCalled) { + coroutineScope.launch { + try { + getCruiseConfig( + cruiseApiSuccessful = { response -> + isCruiseApiCalled = true + alfredDataBase = + AlfredDatabaseHelper.getAnalyticsDatabase(applicationContext) + analyticsDao = alfredDataBase.analyticsDao() + screenShotDao = alfredDataBase.screenShotDao() + zipDetailsDao = alfredDataBase.zipDetailsDao() + apiMetricDao = alfredDataBase.apiMetricDao() + negativeCaseDao = alfredDataBase.negativeCaseDao() + failureEventDao = alfredDataBase.failureEventDao() + sensitiveComposeRepository = ComposeMaskingRepoImpl() + startSyncEvents() + cruiseApiSuccessful(response) + if (isAlfredRecordingEnabled() && isActivityResumed) { + if (!hasRecordingStarted) { + initAlfredRecording(context = applicationContext) + } + startRecording() + } + } + ) + } catch (e: Exception) { + e.log() + } } } } @@ -399,7 +420,7 @@ object AlfredManager { } fun handleScreenTransitionEvent(screenName: String?, moduleName: String? = null) { - if (isAlfredRecordingEnabled()) { + if (isAlfredRecordingEnabled() && screenName != null && moduleName != null) { if (config.getAlfredSessionId().isNotEmpty()) { coroutineDispatcher.executor.execute { try { diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/CanvasDrawUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/CanvasDrawUtils.kt new file mode 100644 index 0000000..7ca480f --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/CanvasDrawUtils.kt @@ -0,0 +1,68 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.alfred.utils + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.View +import com.navi.alfred.AlfredManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +internal suspend fun getScreenShotUsingCanvasDraw( + view: View, + bitmap: Bitmap, + canvas: Canvas, + shouldMaskComposeScreen: Boolean? = false, + shouldMaskXmlScreen: Boolean? = false +) { + try { + withContext(Dispatchers.Main) { view.draw(canvas) } + if (shouldMaskComposeScreen == true) { + val sensitiveCoordinates = AlfredManager.sensitiveComposeRepository.sensitiveCoordinates + captureComposeViewWithMasking( + canvas = canvas, + rootView = view, + sensitiveCoordinates = sensitiveCoordinates + ) + } + if (shouldMaskXmlScreen == true) { + captureXmlViewWithMasking(canvas = canvas, view = view) + } + + val bottomSheetView = + AlfredManager.reactBottomSheetView?.get() + ?: AlfredManager.dialog?.window?.decorView?.rootView + if (bottomSheetView != null && !AlfredManager.config.getDisableDialogScreenShot()) { + val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView) + withContext(Dispatchers.Main) { + bottomSheetCanvasForBitmap?.first?.let { bottomSheetView.draw(it) } + } + + if (bottomSheetCanvasForBitmap?.second != null) { + combineScreenshots( + backgroundScreenshot = bitmap, + bottomSheetScreenshot = bottomSheetCanvasForBitmap.second, + context = AlfredManager.applicationContext, + moduleName = AlfredManager.currentModuleName, + screenName = AlfredManager.currentScreenName, + scope = AlfredManager.coroutineScope + ) + } + } else { + insertScreenShotPathInDb( + scope = AlfredManager.coroutineScope, + context = AlfredManager.applicationContext, + bitmap = bitmap, + bottomSheetFlow = false + ) + } + } catch (e: Exception) { + e.log() + } +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt index 8906f59..b5913b4 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/EventUtils.kt @@ -47,7 +47,6 @@ import java.lang.reflect.Type import java.util.UUID import kotlin.concurrent.fixedRateTimer import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.withLock internal suspend fun sendEventsToServer( @@ -498,7 +497,7 @@ internal fun startSyncEvents() { AlfredConstants.DEFAULT_INITIAL_DELAY, AlfredManager.config.getEventsDelayInMilliseconds() ) { - runBlocking { + AlfredManager.coroutineScope.launch { AddMetricTask.resetEventCount() sendIngestMetric() AddEventTask.resetEventCount() diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/MaskingUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/MaskingUtils.kt index 181b603..f381b29 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/MaskingUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/MaskingUtils.kt @@ -10,20 +10,19 @@ package com.navi.alfred.utils import android.view.View import android.view.ViewGroup -internal fun findViewWithTagRecursive( - view: View, - tag: String, - maskedViews: MutableList -): List { +internal fun findViewWithTagRecursive(view: View, tag: String, maskedViews: MutableList) { if (view.tag == tag) { maskedViews.add(view) + return } if (view is ViewGroup) { for (i in 0 until view.childCount) { val childView = view.getChildAt(i) - findViewWithTagRecursive(childView, tag, maskedViews) + if (childView != null) { + findViewWithTagRecursive(childView, tag, maskedViews) + } } } - return maskedViews + return } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/PixelCopyUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/PixelCopyUtils.kt new file mode 100644 index 0000000..f99ff1e --- /dev/null +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/PixelCopyUtils.kt @@ -0,0 +1,230 @@ +/* + * + * * Copyright © 2024 by Navi Technologies Limited + * * All rights reserved. Strictly confidential + * + */ + +package com.navi.alfred.utils + +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.os.Build +import android.os.Handler +import android.os.HandlerThread +import android.view.PixelCopy +import android.view.View +import com.navi.alfred.AlfredManager +import com.navi.alfred.model.MaskingBounds +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.runBlocking + +fun getScreenShotUsingPixelCopy( + view: View, + bitmap: Bitmap?, + isBottomSheet: Boolean? = false, + activity: Activity?, + shouldMaskComposeScreen: Boolean? = false, + shouldMaskXmlScreen: Boolean? = false, + bitmapForBackground: Bitmap? = null, + canvas: Canvas +) { + if (isViewCaptureNotReady(activity, view)) return + + val handlerThread = HandlerThread("PixelCopyThread") + handlerThread.start() + + val captureWindow = + if (isBottomSheet == false) { + activity?.window + } else { + AlfredManager.dialog?.window + } + captureWindow?.let { window -> + val locationOfViewInWindow = IntArray(2) + view.getLocationInWindow(locationOfViewInWindow) + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + if (bitmap != null) { + var sensitiveCoordinates: Map = emptyMap() + if (shouldMaskComposeScreen == true) { + sensitiveCoordinates = + AlfredManager.sensitiveComposeRepository.sensitiveCoordinates + } + PixelCopy.request( + window, + Rect( + locationOfViewInWindow[0], + locationOfViewInWindow[1], + locationOfViewInWindow[0] + view.width, + locationOfViewInWindow[1] + view.height + ), + bitmap, + { copyResult -> + if (copyResult == PixelCopy.SUCCESS) { + if (shouldMaskComposeScreen == true) { + captureComposeViewWithMasking( + canvas, + view, + sensitiveCoordinates + ) + } + onPixelCopySuccess( + bitmap = bitmap, + isBottomSheetCaptured = isBottomSheet, + activity = activity, + scope = AlfredManager.coroutineScope, + bitmapForBackground = bitmapForBackground, + shouldMaskComposeScreen = shouldMaskComposeScreen, + shouldMaskXmlScreen = shouldMaskXmlScreen, + canvas = canvas, + view = view + ) + } else { + onPixelCopyError() + } + handlerThread.quitSafely() + }, + Handler(handlerThread.looper) + ) + } + } + } catch (e: IllegalArgumentException) { + e.log() + } + } +} + +fun onPixelCopySuccess( + bitmap: Bitmap?, + isBottomSheetCaptured: Boolean?, + activity: Activity?, + scope: CoroutineScope, + bitmapForBackground: Bitmap?, + shouldMaskComposeScreen: Boolean? = false, + shouldMaskXmlScreen: Boolean? = false, + canvas: Canvas, + view: View +) { + if (shouldMaskComposeScreen == true) { + val sensitiveCoordinates = AlfredManager.sensitiveComposeRepository.sensitiveCoordinates + captureComposeViewWithMasking(canvas, view, sensitiveCoordinates) + } + + if (shouldMaskXmlScreen == true) { + captureXmlViewWithMasking(view, canvas) + } + + if (isBottomSheetCaptured == true) { + combineScreenshots( + backgroundScreenshot = bitmapForBackground, + bottomSheetScreenshot = bitmap, + context = AlfredManager.applicationContext, + moduleName = AlfredManager.currentModuleName, + screenName = AlfredManager.currentScreenName, + scope = scope + ) + } else { + val bottomSheetView = + AlfredManager.reactBottomSheetView?.get() + ?: AlfredManager.dialog?.window?.decorView?.rootView + if (!AlfredManager.config.getDisableDialogScreenShot() && bottomSheetView != null) { + captureBottomSheetUsingPixelCopy( + bitmap = bitmap, + activity = activity, + bottomSheetView = bottomSheetView, + canvas = canvas + ) + } else { + insertScreenShotPathInDb( + scope = scope, + context = AlfredManager.applicationContext, + bitmap = bitmap, + bottomSheetFlow = false + ) + } + } +} + +fun captureBottomSheetUsingPixelCopy( + bitmap: Bitmap?, + activity: Activity?, + bottomSheetView: View, + canvas: Canvas +) { + val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView) + getScreenShotUsingPixelCopy( + view = bottomSheetView, + bitmap = bottomSheetCanvasForBitmap?.second, + isBottomSheet = true, + activity = activity, + bitmapForBackground = bitmap, + canvas = canvas + ) +} + +fun onPixelCopyError() { + return +} + +internal fun captureComposeViewWithMasking( + canvas: Canvas, + rootView: View, + sensitiveCoordinates: Map +) { + try { + val paint = Paint() + paint.color = Color.BLACK + if (sensitiveCoordinates.isNotEmpty()) { + sensitiveCoordinates.forEach { (key, bounds) -> + if ( + bounds.bottom > 0 && + bounds.top < rootView.height && + bounds.height > 0 && + bounds.width > 0 + ) { + canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, paint) + } + } + } + } catch (e: Exception) { + e.log() + } +} + +internal fun captureXmlViewWithMasking(view: View, canvas: Canvas) { + runBlocking { + try { + val maskedViewsList: MutableList = mutableListOf() + findViewWithTagRecursive(view, AlfredConstants.SENSITIVE_VIEW_TAG, maskedViewsList) + + val sensitiveCoordinatesList: MutableList = mutableListOf() + maskedViewsList.forEach { view -> + val bounds = + MaskingBounds( + left = view.left.toFloat(), + top = view.top.toFloat(), + right = view.right.toFloat(), + bottom = view.bottom.toFloat() + ) + sensitiveCoordinatesList.add(bounds) + } + + sensitiveCoordinatesList.forEach { + canvas.drawRect( + it.left, + it.top, + it.right, + it.bottom, + Paint().apply { color = Color.BLACK } + ) + } + } catch (e: Exception) { + e.log() + } + } +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt index d78554b..7faf0e9 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt @@ -7,14 +7,17 @@ package com.navi.alfred.utils +import android.app.Activity import android.content.Context import android.content.res.Resources import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.os.Build import android.view.View import android.view.ViewGroup +import androidx.core.view.isVisible import com.navi.alfred.AlfredManager import com.navi.alfred.db.AlfredDatabaseHelper import com.navi.alfred.db.model.ScreenShotPathHelper @@ -24,7 +27,6 @@ import java.io.IOException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext internal suspend fun handleScreenShot() { if ( @@ -133,7 +135,7 @@ internal fun insertScreenShotPathInDb( ) ScreenShotStorageHelper.addImage(path) } catch (e: Exception) { - e.printStackTrace() + e.log() } } } catch (e: Exception) { @@ -203,117 +205,95 @@ enum class VideoQuality { internal suspend fun captureScreen( v: View, - context: Context, - bottomSheetFlow: Boolean? = false, screenName: String? = null, - scope: CoroutineScope, canvas: Canvas? = null, bmp: Bitmap? = null, - moduleName: String? = null + moduleName: String? = null, + activity: Activity? = null ): Bitmap? { if ( isScreenDisabled(screenName, moduleName) || isScreenDisabled(AlfredManager.currentScreenName, moduleName) || canvas == null || - bmp == null + bmp == null || + activity == null ) { return null } - withContext(Dispatchers.Main) { - val rootView = AlfredManager.sensitiveComposeRepository.getRootViewOfComposeScreen() - if (isMaskingEnabled(screenName)) { + AlfredManager.coroutineScope.launch { + if (isMaskingEnabled(screenName) || isMaskingEnabled(AlfredManager.currentScreenName)) { try { + val rootView = AlfredManager.sensitiveComposeRepository.getRootViewOfComposeScreen() if (rootView != null) { if (AlfredManager.sensitiveComposeRepository.getBlurSensitiveScreenStatus()) { blurScreen(canvas, rootView) } else { - captureComposeViewWithMasking(canvas, rootView) + captureScreenManager( + view = rootView, + bitmap = bmp, + canvas = canvas, + shouldMaskComposeScreen = true, + activity = activity + ) } } else { if (v.tag == AlfredConstants.SENSITIVE_VIEW_TAG) { - captureXmlViewWithMasking(canvas, v) + captureScreenManager( + view = v, + bitmap = bmp, + canvas = canvas, + shouldMaskXmlScreen = true, + activity = activity + ) } else { - v.draw(canvas) + captureScreenManager( + view = v, + bitmap = bmp, + canvas = canvas, + activity = activity + ) } } } catch (e: Exception) { e.log() } } else { - v.draw(canvas) + captureScreenManager(view = v, bitmap = bmp, canvas = canvas, activity = activity) } } - insertScreenShotPathInDb(scope, context, bmp, bottomSheetFlow) return bmp } -internal suspend fun captureBottomSheet( +internal suspend fun captureScreenManager( view: View, - bottomSheetView: View, - context: Context, - screenName: String? = null, - rootCanvas: Canvas? = null, - rootBmp: Bitmap? = null, - moduleName: String? = null + bitmap: Bitmap, + canvas: Canvas, + shouldMaskComposeScreen: Boolean? = false, + shouldMaskXmlScreen: Boolean? = false, + activity: Activity? ) { - if (AlfredManager.config.getDisableDialogScreenShot()) { - return - } - val bottomSheetCanvasForBitmap = createBitmapForView(bottomSheetView) - val bottomSheetScreenShot = - captureScreen( - bottomSheetView, - context, - true, - screenName, - AlfredManager.coroutineScope, - bottomSheetCanvasForBitmap?.first, - bottomSheetCanvasForBitmap?.second, - moduleName = moduleName - ) - val backgroundScreenShot = - captureScreen( - view, - context, - true, - screenName, - AlfredManager.coroutineScope, - rootCanvas, - rootBmp, - moduleName = moduleName - ) - if (bottomSheetScreenShot != null && backgroundScreenShot != null) { - combineScreenshots( - backgroundScreenShot, - bottomSheetScreenShot, - context, - screenName, - moduleName = moduleName, - scope = AlfredManager.coroutineScope - ) - } -} - -internal fun captureComposeViewWithMasking(canvas: Canvas, rootView: View) { - rootView.draw(canvas) - - val sensitiveCoordinates = AlfredManager.sensitiveComposeRepository.sensitiveCoordinates - - val paint = Paint() - paint.color = Color.BLACK - - if (sensitiveCoordinates.isNotEmpty()) { - sensitiveCoordinates.forEach { (key, bounds) -> - if ( - bounds.bottom > 0 && - bounds.top < rootView.height && - bounds.height > 0 && - bounds.width > 0 - ) { - canvas.drawRect(bounds.left, bounds.top, bounds.right, bounds.bottom, paint) - } + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + getScreenShotUsingPixelCopy( + view = view, + bitmap = bitmap, + activity = activity, + canvas = canvas, + shouldMaskComposeScreen = shouldMaskComposeScreen, + shouldMaskXmlScreen = shouldMaskXmlScreen + ) + } else { + getScreenShotUsingCanvasDraw( + view = view, + bitmap = bitmap, + canvas = canvas, + shouldMaskComposeScreen = shouldMaskComposeScreen, + shouldMaskXmlScreen = shouldMaskXmlScreen + ) } + } catch (e: Exception) { + e.log() } } @@ -330,33 +310,6 @@ internal fun blurScreen(canvas: Canvas, rootView: View) { ) } -internal fun captureXmlViewWithMasking(canvas: Canvas, v: View) { - val maskedViewsList = - findViewWithTagRecursive(v, AlfredConstants.SENSITIVE_VIEW_TAG, mutableListOf()) - try { - if (maskedViewsList.isEmpty()) { - v.draw(canvas) - } else { - val alphaList = mutableListOf>() - - for (maskedView in maskedViewsList) { - alphaList.add(Pair(maskedView, maskedView.alpha)) - maskedView.alpha = 0.0f - } - - v.draw(canvas) - - for (alphaView in alphaList) { - val view = alphaView.first - view.alpha = alphaView.second - } - alphaList.clear() - } - } catch (e: Exception) { - e.log() - } -} - @JvmOverloads internal fun measureInflatedView(view: View, width: Int = 1080, height: Int = 1920) { view.layoutParams = ViewGroup.LayoutParams(width, height) @@ -386,3 +339,13 @@ internal fun createBitmapForView(view: View): Pair? { internal fun isResolutionEven(width: Int, height: Int): Boolean { return width.mod(2) == 0 && height.mod(2) == 0 } + +fun isViewCaptureNotReady(activity: Activity?, view: View): Boolean { + return activity == null || + !view.isVisible || + !view.isAttachedToWindow || + view.width == 0 || + view.height == 0 || + activity.isFinishing || + activity.isDestroyed +} diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt index c3afcd3..5f66234 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/UploadUtils.kt @@ -226,7 +226,7 @@ internal suspend fun uploadFile( } } -internal fun buildWorkManagerForZipUploadRetry(requestData: Data) { +internal fun buildWorkManagerForZipUploadRetry() { val constraints = Constraints.Builder() .setRequiresBatteryNotLow(false) @@ -239,19 +239,13 @@ internal fun buildWorkManagerForZipUploadRetry(requestData: Data) { .get() if (uniqueWorkStatus.isNullOrEmpty()) { val uniqueWorkRequest = - OneTimeWorkRequestBuilder() - .setConstraints(constraints) - .setInputData(requestData) - .build() + OneTimeWorkRequestBuilder().setConstraints(constraints).build() WorkManager.getInstance(AlfredManager.applicationContext) .beginUniqueWork(uniqueWorkName, ExistingWorkPolicy.KEEP, uniqueWorkRequest) .enqueue() } else { val workRequest = - OneTimeWorkRequestBuilder() - .setConstraints(constraints) - .setInputData(requestData) - .build() + OneTimeWorkRequestBuilder().setConstraints(constraints).build() WorkManager.getInstance(AlfredManager.applicationContext).enqueue(workRequest) } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt b/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt index c758d81..cf531ac 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/utils/ZipUtils.kt @@ -12,7 +12,6 @@ import android.graphics.Bitmap import android.view.View import androidx.work.Data import androidx.work.WorkManager -import com.google.gson.Gson import com.navi.alfred.AlfredManager import com.navi.alfred.db.model.ScreenShotPathHelper import com.navi.alfred.db.model.ZipDetailsHelper @@ -218,15 +217,7 @@ internal fun startAnrCrashZipUpload(view: View) { } AlfredManager.coroutineScope.launch(Dispatchers.IO) { if (AlfredManager.zipDetailsDao.getZipFilesDetailsCount() > 0) { - val zipFileDetailList = AlfredManager.zipDetailsDao.fetchAllZipFilesDetails() - val requestData = - Data.Builder() - .putString( - AlfredConstants.ZIP_FILE_DETAIL_LIST, - Gson().toJson(zipFileDetailList) - ) - .build() - buildWorkManagerForZipUploadRetry(requestData) + buildWorkManagerForZipUploadRetry() } } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AddEventTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AddEventTask.kt index 434ae7e..d6c7bed 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AddEventTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AddEventTask.kt @@ -27,7 +27,7 @@ class AddEventTask(private val event: AnalyticsEvent, private val context: Conte } @WorkerThread - override fun execute(): Boolean { + override suspend fun execute(): Boolean { val db = AlfredDatabaseHelper.getAnalyticsDatabase(context) val analyticsDao = db.analyticsDao() return try { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AddFailureTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AddFailureTask.kt index 5347067..ab4defe 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AddFailureTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AddFailureTask.kt @@ -27,7 +27,7 @@ class AddFailureTask(private val event: FailureEvent, private val context: Conte } @WorkerThread - override fun execute(): Boolean { + override suspend fun execute(): Boolean { val db = AlfredDatabaseHelper.getAnalyticsDatabase(context) val failureDao = db.failureEventDao() return try { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AddMetricTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AddMetricTask.kt index bba3a5f..750db7d 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AddMetricTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AddMetricTask.kt @@ -27,7 +27,7 @@ class AddMetricTask(private val event: ApiMetricHelper, private val context: Con } @WorkerThread - override fun execute(): Boolean { + override suspend fun execute(): Boolean { val db = AlfredDatabaseHelper.getAnalyticsDatabase(context) val analyticsDao = db.apiMetricDao() return try { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AddNegativeCase.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AddNegativeCase.kt index df74054..fab1583 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AddNegativeCase.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AddNegativeCase.kt @@ -27,7 +27,7 @@ class AddNegativeCase(private val negativeCase: NegativeCase, private val contex } @WorkerThread - override fun execute(): Boolean { + override suspend fun execute(): Boolean { val db = AlfredDatabaseHelper.getAnalyticsDatabase(context) val negativeCaseDao = db.negativeCaseDao() return try { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTask.kt index 90f3cb0..702944a 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTask.kt @@ -10,7 +10,7 @@ package com.navi.alfred.worker import androidx.annotation.WorkerThread interface AnalyticsTask { - @WorkerThread fun execute(): Boolean + @WorkerThread suspend fun execute(): Boolean fun getTaskName(): String } diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTaskProcessor.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTaskProcessor.kt index 2e9880c..4e55530 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTaskProcessor.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/AnalyticsTaskProcessor.kt @@ -7,10 +7,12 @@ package com.navi.alfred.worker +import com.navi.alfred.AlfredManager import java.util.concurrent.BlockingDeque import java.util.concurrent.Executors import java.util.concurrent.LinkedBlockingDeque import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.launch class AnalyticsTaskProcessor { private val taskQueue: BlockingDeque = LinkedBlockingDeque() @@ -43,14 +45,16 @@ class AnalyticsTaskProcessor { if (taskQueue.poll().also { active = it } != null) { coroutineDispatcher.executor.execute { active?.let { - executeTask(it) - scheduleNext() + AlfredManager.coroutineScope.launch { + executeTask(it) + scheduleNext() + } } } } } - private fun executeTask(task: AnalyticsTask) { + private suspend fun executeTask(task: AnalyticsTask) { task.execute() } @@ -58,8 +62,10 @@ class AnalyticsTaskProcessor { if (isSyncEventTaskExecuted) { isSyncEventTaskExecuted = false singleThreadExecutorForSync.execute { - task.execute() - isSyncEventTaskExecuted = true + AlfredManager.coroutineScope.launch { + task.execute() + isSyncEventTaskExecuted = true + } } } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncDataTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncDataTask.kt index ee31e5d..50a60ba 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncDataTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncDataTask.kt @@ -11,15 +11,11 @@ import androidx.annotation.WorkerThread import com.navi.alfred.utils.AlfredConstants.SYNC_EVENT_TASK import com.navi.alfred.utils.sendEventsToServer import com.navi.alfred.utils.sendIngestMetric -import kotlinx.coroutines.runBlocking class SyncDataTask() : AnalyticsTask { @WorkerThread - override fun execute(): Boolean { - return runBlocking { - sendIngestMetric() - sendEventsToServer() - } + override suspend fun execute(): Boolean { + return sendEventsToServer() && sendIngestMetric() } override fun getTaskName(): String { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncFailureTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncFailureTask.kt index 33cfe6b..4070dbe 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncFailureTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncFailureTask.kt @@ -10,12 +10,11 @@ package com.navi.alfred.worker import androidx.annotation.WorkerThread import com.navi.alfred.utils.AlfredConstants.SYNC_FAILURE_TASK import com.navi.alfred.utils.sendFailureEventsToServer -import kotlinx.coroutines.runBlocking class SyncFailureTask() : AnalyticsTask { @WorkerThread - override fun execute(): Boolean { - return runBlocking { sendFailureEventsToServer() } + override suspend fun execute(): Boolean { + return sendFailureEventsToServer() } override fun getTaskName(): String { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncNegativeCaseTask.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncNegativeCaseTask.kt index 1021ec7..137a7bb 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/SyncNegativeCaseTask.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/SyncNegativeCaseTask.kt @@ -10,12 +10,11 @@ package com.navi.alfred.worker import androidx.annotation.WorkerThread import com.navi.alfred.utils.AlfredConstants.SYNC_NEGATIVE_CASE_TASK import com.navi.alfred.utils.sendNegativeCaseToServer -import kotlinx.coroutines.runBlocking class SyncNegativeCaseTask() : AnalyticsTask { @WorkerThread - override fun execute(): Boolean { - return runBlocking { sendNegativeCaseToServer() } + override suspend fun execute(): Boolean { + return sendNegativeCaseToServer() } override fun getTaskName(): String { diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/UploadEventsWorker.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/UploadEventsWorker.kt index 798c27f..516aa47 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/UploadEventsWorker.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/UploadEventsWorker.kt @@ -53,7 +53,6 @@ class UploadEventsWorker(context: Context, workerParams: WorkerParameters) : Result.success() } } catch (e: Exception) { - e.printStackTrace() Result.retry() } } 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 d863dd8..a2ee01a 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 @@ -97,7 +97,6 @@ class UploadFileWorker(context: Context, workerParams: WorkerParameters) : Result.success() } } catch (e: Exception) { - e.printStackTrace() Result.retry() } } diff --git a/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt b/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt index 18efe31..e62e662 100644 --- a/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt +++ b/navi-alfred/src/main/java/com/navi/alfred/worker/ZipUploadRetryWorker.kt @@ -11,13 +11,12 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.WorkerParameters -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken +import com.navi.alfred.AlfredManager +import com.navi.alfred.db.AlfredDatabaseHelper import com.navi.alfred.db.model.ZipDetailsHelper import com.navi.alfred.utils.AlfredConstants import com.navi.alfred.utils.buildWorkManagerForZip import com.navi.alfred.utils.sendAlfredSessionEvent -import java.lang.reflect.Type import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -27,14 +26,11 @@ class ZipUploadRetryWorker(context: Context, workerParams: WorkerParameters) : override suspend fun doWork(): Result = withContext(Dispatchers.IO) { try { - val zipDetailList = inputData.getString(AlfredConstants.ZIP_FILE_DETAIL_LIST) - val listType: Type = object : TypeToken?>() {}.type + AlfredManager.alfredDataBase = + AlfredDatabaseHelper.getAnalyticsDatabase(AlfredManager.applicationContext) + AlfredManager.zipDetailsDao = AlfredManager.alfredDataBase.zipDetailsDao() val zipFileDetailList: List = - if (zipDetailList != null) { - Gson().fromJson(zipDetailList, listType) - } else { - emptyList() - } + AlfredManager.zipDetailsDao.fetchAllZipFilesDetails() zipFileDetailList.forEach { if (it.zipUploadStatus) { @@ -62,7 +58,6 @@ class ZipUploadRetryWorker(context: Context, workerParams: WorkerParameters) : } Result.success() } catch (e: Exception) { - e.printStackTrace() Result.retry() } }