Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
364 lines
12 KiB
Kotlin
364 lines
12 KiB
Kotlin
/*
|
|
*
|
|
* * Copyright © 2023-2025 by Navi Technologies Limited
|
|
* * All rights reserved. Strictly confidential
|
|
*
|
|
*/
|
|
|
|
package com.navi.alfred.utils
|
|
|
|
import android.app.Activity
|
|
import android.content.Context
|
|
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 android.view.Window
|
|
import androidx.core.view.ViewCompat
|
|
import androidx.core.view.isVisible
|
|
import com.navi.alfred.AlfredManager
|
|
import com.navi.alfred.db.AlfredDatabaseHelper
|
|
import com.navi.alfred.db.model.ScreenShotPathHelper
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
import java.io.IOException
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
|
|
internal suspend fun handleScreenShot() {
|
|
if (
|
|
ScreenShotStorageHelper.images.size == AlfredManager.imageThreshHoldValue ||
|
|
ScreenShotStorageHelper.images.size == AlfredManager.imageSecondThreshHoldValue
|
|
) {
|
|
toZip(AlfredManager.screenShotDao.fetchScreenShotsPath(AlfredManager.imageThreshHoldValue))
|
|
} else {
|
|
if (ScreenShotStorageHelper.images.size == 1) {
|
|
checkToStartZipUpload()
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun combineScreenshots(
|
|
backgroundScreenshot: Bitmap?,
|
|
bottomSheetScreenshot: Bitmap?,
|
|
context: Context,
|
|
screenName: String? = null,
|
|
moduleName: String? = null,
|
|
scope: CoroutineScope,
|
|
view: View,
|
|
): Bitmap? {
|
|
if (
|
|
backgroundScreenshot == null ||
|
|
bottomSheetScreenshot == null ||
|
|
isScreenDisabled(screenName, moduleName)
|
|
) {
|
|
return null
|
|
}
|
|
var screenWidth = view.width / 2
|
|
var screenHeight = view.height / 2
|
|
if (!isResolutionEven(screenWidth, screenHeight)) {
|
|
screenHeight += 1
|
|
screenWidth += 1
|
|
}
|
|
val canvas = Canvas(backgroundScreenshot)
|
|
var bottomSheetLeft = (screenWidth - bottomSheetScreenshot.width) / 2f
|
|
var bottomSheetTop = screenHeight - bottomSheetScreenshot.height.toFloat()
|
|
if (!isResolutionEven(bottomSheetLeft.toInt(), bottomSheetTop.toInt())) {
|
|
bottomSheetLeft = (bottomSheetLeft.toInt() + 1).toFloat()
|
|
bottomSheetTop = (bottomSheetTop.toInt() + 1).toFloat()
|
|
}
|
|
canvas.drawBitmap(bottomSheetScreenshot, bottomSheetLeft, bottomSheetTop, null)
|
|
insertScreenShotPathInDb(scope, context, backgroundScreenshot)
|
|
return backgroundScreenshot
|
|
}
|
|
|
|
internal fun insertScreenShotPathInDb(
|
|
scope: CoroutineScope,
|
|
context: Context,
|
|
bitmap: Bitmap?,
|
|
bottomSheetFlow: Boolean? = false,
|
|
) {
|
|
scope.launch(Dispatchers.IO) {
|
|
try {
|
|
val fileDir = context.filesDir
|
|
val currentTime = AlfredManager.config.getAlfredCurrentTimeMillis()
|
|
AlfredManager.config.setLatestScreenshotTimestamp(currentTime)
|
|
|
|
val imageFileExtension =
|
|
if (AlfredManager.config.imageType == AlfredConstants.IMAGE_TYPE_WEBP) {
|
|
AlfredConstants.IMAGE_FILE_EXTENSION_WEBP
|
|
} else {
|
|
AlfredConstants.IMAGE_FILE_EXTENSION_JPEG
|
|
}
|
|
|
|
val fileName = currentTime.toString() + imageFileExtension
|
|
val path = fileDir.path.plus("/").plus(fileName)
|
|
val imageUrl = File(path)
|
|
val fos = FileOutputStream(imageUrl)
|
|
|
|
if (AlfredManager.config.imageType == AlfredConstants.IMAGE_TYPE_WEBP) {
|
|
bitmap?.compress(Bitmap.CompressFormat.WEBP, 0, fos)
|
|
} else {
|
|
val videoQuality: Int =
|
|
when (AlfredManager.config.getVideoQuality()) {
|
|
VideoQuality.HIGH.name -> {
|
|
10
|
|
}
|
|
VideoQuality.LOW.name -> {
|
|
6
|
|
}
|
|
VideoQuality.MEDIUM.name -> {
|
|
8
|
|
}
|
|
else -> {
|
|
10
|
|
}
|
|
}
|
|
bitmap?.compress(Bitmap.CompressFormat.JPEG, videoQuality, fos)
|
|
}
|
|
|
|
fos.flush()
|
|
fos.close()
|
|
if (bottomSheetFlow == false) {
|
|
val db = AlfredDatabaseHelper.getAnalyticsDatabase(context)
|
|
val screenShotDao = db.screenShotDao()
|
|
try {
|
|
screenShotDao.insertScreenShotPath(
|
|
ScreenShotPathHelper(
|
|
AlfredManager.config.getAlfredCurrentTimeMillis(),
|
|
path,
|
|
)
|
|
)
|
|
ScreenShotStorageHelper.addImage(path)
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
} catch (e: IOException) {
|
|
e.log()
|
|
}
|
|
}
|
|
}
|
|
|
|
internal fun captureScreenshotOfCustomView(view: View): Bitmap? {
|
|
view.draw(Canvas())
|
|
val bitmapForCanvas = createBitmapForView(view)
|
|
try {
|
|
bitmapForCanvas?.first?.let { view.draw(it) }
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
}
|
|
return bitmapForCanvas?.second
|
|
}
|
|
|
|
internal fun deleteScreenShot() {
|
|
try {
|
|
val screenShotPathList: List<ScreenShotPathHelper> =
|
|
if (
|
|
AlfredManager.screenShotDao.getScreenShotCount() >=
|
|
AlfredManager.imageThreshHoldValue
|
|
) {
|
|
AlfredManager.screenShotDao.fetchScreenShotsPath(AlfredManager.imageThreshHoldValue)
|
|
} else {
|
|
AlfredManager.screenShotDao.fetchAllScreenShotsPath()
|
|
}
|
|
clearScreenShot(screenShotPathList)
|
|
try {
|
|
if (AlfredManager.isAppInBackground) {
|
|
ScreenShotStorageHelper.clearAll()
|
|
AlfredManager.screenShotDao.deleteAllScreenShot()
|
|
AlfredManager.isAppInBackground = false
|
|
AlfredManager.hasRecordingStarted = false
|
|
} else {
|
|
val idList = screenShotPathList.map { it.id }
|
|
AlfredManager.screenShotDao.deleteScreenShot(idList)
|
|
ScreenShotStorageHelper.deleteKItems(idList.size)
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
}
|
|
}
|
|
|
|
internal fun clearScreenShot(path: List<ScreenShotPathHelper>) {
|
|
path.forEach { data ->
|
|
val file = data.screenShotPath?.let { File(it) }
|
|
if (file?.exists() == true) {
|
|
file.delete()
|
|
}
|
|
}
|
|
}
|
|
|
|
enum class VideoQuality {
|
|
LOW,
|
|
HIGH,
|
|
MEDIUM,
|
|
}
|
|
|
|
internal suspend fun captureScreen(
|
|
v: View,
|
|
screenName: String? = null,
|
|
canvas: Canvas? = null,
|
|
bmp: Bitmap? = null,
|
|
moduleName: String? = null,
|
|
activity: Activity? = null,
|
|
): Bitmap? {
|
|
if (
|
|
isScreenDisabled(screenName, moduleName) ||
|
|
isScreenDisabled(AlfredManager.currentScreenName, moduleName) ||
|
|
canvas == null ||
|
|
bmp == null ||
|
|
activity == null
|
|
) {
|
|
return null
|
|
}
|
|
|
|
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 {
|
|
captureScreenManager(
|
|
view = rootView,
|
|
bitmap = bmp,
|
|
canvas = canvas,
|
|
shouldMaskComposeScreen = true,
|
|
activity = activity,
|
|
)
|
|
}
|
|
} else {
|
|
if (v.tag == AlfredConstants.SENSITIVE_VIEW_TAG) {
|
|
captureScreenManager(
|
|
view = v,
|
|
bitmap = bmp,
|
|
canvas = canvas,
|
|
shouldMaskXmlScreen = true,
|
|
activity = activity,
|
|
)
|
|
} else {
|
|
captureScreenManager(
|
|
view = v,
|
|
bitmap = bmp,
|
|
canvas = canvas,
|
|
activity = activity,
|
|
)
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.log()
|
|
}
|
|
} else {
|
|
captureScreenManager(view = v, bitmap = bmp, canvas = canvas, activity = activity)
|
|
}
|
|
}
|
|
return bmp
|
|
}
|
|
|
|
internal suspend fun captureScreenManager(
|
|
view: View,
|
|
bitmap: Bitmap,
|
|
canvas: Canvas,
|
|
shouldMaskComposeScreen: Boolean? = false,
|
|
shouldMaskXmlScreen: Boolean? = false,
|
|
activity: Activity?,
|
|
) {
|
|
try {
|
|
if (
|
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
AlfredManager.config.getPixelCopyStatus()
|
|
) {
|
|
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()
|
|
}
|
|
}
|
|
|
|
internal fun blurScreen(canvas: Canvas, rootView: View) {
|
|
val paint = Paint()
|
|
paint.color = Color.GRAY
|
|
paint.alpha = 240
|
|
canvas.drawRect(
|
|
rootView.left.toFloat(),
|
|
rootView.top.toFloat(),
|
|
rootView.right.toFloat(),
|
|
rootView.bottom.toFloat(),
|
|
paint,
|
|
)
|
|
}
|
|
|
|
@JvmOverloads
|
|
internal fun measureInflatedView(view: View, width: Int = 1080, height: Int = 1920) {
|
|
view.layoutParams = ViewGroup.LayoutParams(width, height)
|
|
view.measure(
|
|
View.MeasureSpec.makeMeasureSpec(view.layoutParams.width, View.MeasureSpec.EXACTLY),
|
|
View.MeasureSpec.makeMeasureSpec(view.layoutParams.height, View.MeasureSpec.EXACTLY),
|
|
)
|
|
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
|
|
}
|
|
|
|
internal 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)) {
|
|
width += 1
|
|
height += 1
|
|
}
|
|
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
val canvas = Canvas(bitmap)
|
|
canvas.scale(0.5f, 0.5f)
|
|
return Pair(canvas, bitmap)
|
|
}
|
|
return null
|
|
}
|
|
|
|
internal fun isResolutionEven(width: Int, height: Int): Boolean {
|
|
return width.mod(2) == 0 && height.mod(2) == 0
|
|
}
|
|
|
|
internal fun isViewCaptureReady(activity: Activity?, view: View): Boolean {
|
|
return activity != null &&
|
|
view.isVisible &&
|
|
view.isAttachedToWindow &&
|
|
view.width != 0 &&
|
|
view.height != 0 &&
|
|
!activity.isFinishing &&
|
|
!activity.isDestroyed
|
|
}
|
|
|
|
internal fun isWindowCaptureReady(window: Window?): Boolean {
|
|
return window != null &&
|
|
window.decorView.width != 0 &&
|
|
window.decorView.height != 0 &&
|
|
window.peekDecorView() != null &&
|
|
window.isActive &&
|
|
ViewCompat.isLaidOut(window.decorView)
|
|
}
|