AssimilateTranslate/app/src/main/java/com/assimilate/alltrans/curview/DistrictView.kt
2024-08-16 18:29:50 +08:00

277 lines
11 KiB
Kotlin

package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.graphics.Rect
import android.media.ImageReader
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.common.MyTextTools
import com.assimilate.alltrans.databinding.LayoutSusDistrictBinding
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.assimilate.alltrans.model.LanguagesConstants
import com.assimilate.alltrans.model.PreferenceLanguageUtils
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.nio.ByteBuffer
class DistrictView(
private val context: Context
) {
private lateinit var bindingSusDistrict: LayoutSusDistrictBinding
private lateinit var districtView: View
private lateinit var overlayView: View
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
fun showDistrictView(imageReader: ImageReader, startX: Int, startY: Int, endX: Int, endY: Int) {
// 添加一个全屏的透明背景视图
overlayView = View(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setBackgroundColor(0x00000000) // 全透明
setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
removeDistrictView()
performClick()
}
true
}
}
val overlayParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
windowManager.addView(overlayView, overlayParams)
// 初始化 DistrictView
bindingSusDistrict = LayoutSusDistrictBinding.inflate(LayoutInflater.from(context))
districtView = bindingSusDistrict.root
val displayMetrics = context.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
val density = displayMetrics.density
val marginDp = 16
val marginPx = (marginDp * density).toInt()
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = marginPx
layoutParams.y = 100
layoutParams.width = screenWidth - 2 * marginPx
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
windowManager.addView(districtView, layoutParams)
initDistrictClick()
// 获取最新的图像并处理
val image = imageReader.acquireLatestImage()
image?.let {
val planes = it.planes
if (planes.isNotEmpty()) {
val buffer: ByteBuffer = planes[0].buffer
val pixelStride: Int = planes[0].pixelStride
val rowStride: Int = planes[0].rowStride
val rowPadding: Int = rowStride - pixelStride * image.width
// 从缓冲区创建位图
val bitmap = Bitmap.createBitmap(
image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
// 根据用户框选区域裁剪位图
val rect = Rect(startX, startY, endX, endY)
takeDistrictScreenshot(bitmap, rect)
}
image.close()
}
}
private fun initDistrictClick() {
bindingSusDistrict.ivSourceTts.setOnClickListener {
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_source"),
MyApp.applicationContext()
)
MyTextTools.ttsReadText(
bindingSusDistrict.tvTrSource.text.toString(), lanSourceCode, context
)
}
bindingSusDistrict.ivTrCopy.setOnClickListener {
MyTextTools.copyToClipboard(context, bindingSusDistrict.tvTrTarget.text.toString())
}
bindingSusDistrict.ivTrTargetDic.setOnClickListener {
if (bindingSusDistrict.tvTrTarget.text.toString().isNotEmpty()) {
MyTextTools.makeWordsClickableWithUnderline(
bindingSusDistrict.tvTrTarget,
bindingSusDistrict.tvTrTarget.text.toString(),
object : MyTextTools.OnWordClickListener {
override fun onWordClick(word: String?) {
Log.d("gvsdacd", "$word is clicked")
}
})
}
}
// bindingSusDistrict.ivTrTargetShare.setOnClickListener {
// MyTextTools.shareText(
// context,
// bindingSusDistrict.tvTrTarget.text.toString()
// )
// }
bindingSusDistrict.ivTargetTts.setOnClickListener {
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
MyTextTools.ttsReadText(
bindingSusDistrict.tvTrTarget.text.toString(), lanTargetCode, context
)
}
bindingSusDistrict.ivTrCollect.setOnClickListener { addCollect() }
bindingSusDistrict.ivSourceClear.setOnClickListener {
bindingSusDistrict.tvTrSource.text = ""
bindingSusDistrict.tvTrTarget.text = ""
removeDistrictView()
}
}
fun removeDistrictView() {
windowManager.removeView(districtView)
windowManager.removeView(overlayView)
}
private fun takeDistrictScreenshot(image: Bitmap, rect: Rect) {
if (rect.width() > 0 && rect.height() > 0) {
val croppedBitmap = Bitmap.createBitmap(
image,
rect.left,
rect.top,
rect.width(),
rect.height()
)
val recognizer =
TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
val imageNew = InputImage.fromBitmap(croppedBitmap, 0)
recognizer.process(imageNew)
.addOnSuccessListener { visionText ->
bindingSusDistrict.tvTrSource.text = visionText.text
// Add translation functionality here if needed
translate(visionText.text)
}
.addOnFailureListener { e ->
Log.e("DistrictView", "Text recognition failed", e)
}
} else {
Log.e("DistrictView", "Rect width or height is 0")
}
}
private fun addCollect() {
// Add collect functionality here
bindingSusDistrict.ivTrCollect.setImageResource(R.drawable.ic_like_yes)
}
private fun translate(text: String) {
if (text.isEmpty()) {
Logger.d("log", "translating(not post data)...")
return
}
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_source"),
MyApp.applicationContext()
)
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
val param = HashMap<String, String>().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", text)
}
Handler(Looper.getMainLooper()).post {
bindingSusDistrict.tvTrTarget.text = "translating..."
}
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator()
translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
override fun onResponse(result: String?, errorMessage: String?) {
Handler(Looper.getMainLooper()).post {
if (!result.isNullOrEmpty()) {
bindingSusDistrict.tvTrTarget.text = result
} else if (!errorMessage.isNullOrEmpty()) {
Log.e("TranslationError", errorMessage)
bindingSusDistrict.tvTrTarget.text = "Translation error: $errorMessage"
}
}
}
override fun onFailure(errorMessage: String?) {
Handler(Looper.getMainLooper()).post {
FirebaseAnalyticsHelper.hoverDistrictResultEvent(
MyApp.Config.FAIL_REASON,
"district" + errorMessage.toString()
)
bindingSusDistrict.tvTrTarget.text = "Translation failed: $errorMessage"
}
}
})
}
}