Files
alfred-android/navi-alfred/src/main/java/com/navi/alfred/utils/ScreenShotUtils.kt
dependabot[bot] 9fc691bf71 NTP-7562 | Bump com.diffplug.spotless from 6.25.0 to 7.0.0 (#288)
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>
2025-01-07 11:29:29 +00:00

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)
}