diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef3..33dccf9 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2479661..8cc1855 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -8,7 +8,6 @@
-
@@ -18,6 +17,7 @@
+
= Build.VERSION_CODES.P) {
diff --git a/app/src/main/java/com/assimilate/alltrans/adapters/LanguageAdapter.java b/app/src/main/java/com/assimilate/alltrans/adapters/LanguageAdapter.java
index 9931619..455413b 100644
--- a/app/src/main/java/com/assimilate/alltrans/adapters/LanguageAdapter.java
+++ b/app/src/main/java/com/assimilate/alltrans/adapters/LanguageAdapter.java
@@ -5,6 +5,8 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Filter;
+import android.widget.Filterable;
import android.widget.ImageView;
import androidx.annotation.NonNull;
@@ -17,24 +19,21 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
+import java.util.List;
-public class LanguageAdapter extends RecyclerView.Adapter {
+public class LanguageAdapter extends RecyclerView.Adapter implements Filterable {
private final Activity mActivity;
private final ArrayList languages;
+ private final ArrayList filteredLanguages;
private final OnClickListener listener;
private final RequestOptions options;
- private boolean sorting = false;
-
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList languageList, final OnClickListener onClickListener) {
this.mActivity = activity;
- this.languages = new ArrayList();
+ this.languages = new ArrayList<>(languageList);
+ this.filteredLanguages = new ArrayList<>(languageList);
this.listener = onClickListener;
- if (!languageList.isEmpty()) {
- languages.addAll(languageList);
- }
-
options = new RequestOptions()
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
@@ -49,9 +48,9 @@ public class LanguageAdapter extends RecyclerView.Adapter {
+ final int pos = holder.getAdapterPosition();
+ if (listener != null)
+ listener.click(pos, currentLanguage);
});
}
@Override
public int getItemCount() {
- return languages.size();
+ return filteredLanguages.size();
}
private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
@@ -90,6 +86,37 @@ public class LanguageAdapter extends RecyclerView.Adapter filteredList = new ArrayList<>();
+ if (TextUtils.isEmpty(constraint)) {
+ filteredList.addAll(languages);
+ } else {
+ String filterPattern = constraint.toString().toLowerCase().trim();
+ for (Language language : languages) {
+ if (language.getLanguage().toLowerCase().contains(filterPattern)) {
+ filteredList.add(language);
+ }
+ }
+ }
+ FilterResults results = new FilterResults();
+ results.values = filteredList;
+ return results;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ filteredLanguages.clear();
+ filteredLanguages.addAll((List) results.values);
+ notifyDataSetChanged();
+ }
+ };
+ }
+
public static class LanguageHolder extends RecyclerView.ViewHolder {
public LanguageItemLayoutBinding mBinding;
diff --git a/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt b/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt
index ada6964..e30f3ab 100644
--- a/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt
+++ b/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt
@@ -1,34 +1,47 @@
package com.assimilate.alltrans.allservice
-import android.app.*
+import android.app.Activity
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
+import android.media.Image
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
-import android.os.*
+import android.os.Build
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
-import android.view.*
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.WindowManager
import androidx.core.app.NotificationCompat
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
-import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
+import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
class SusService : Service() {
private var imageProcessor: VisionImageProcessor? = null
private lateinit var mediaProjectionManager: MediaProjectionManager
- private lateinit var mediaProjection: MediaProjection
- private lateinit var virtualDisplay: VirtualDisplay
+ private var mediaProjection: MediaProjection? = null
+ private var virtualDisplay: VirtualDisplay? = null
private lateinit var displayMetrics: DisplayMetrics
+ private lateinit var imageReader: ImageReader
private lateinit var windowManager: WindowManager
private lateinit var floatingView: View
@@ -37,34 +50,79 @@ class SusService : Service() {
private lateinit var bindingSusControl: SusControlViewBinding
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
+ private var mResultCode = 0
+ private var mResultData: Intent? = null
+ private val mpResultCode = "mpResultCode"
+ private val mpData = "mpData"
+ private val ball_time = 5000L
+
+ private val handler = Handler(Looper.getMainLooper())
+ private val hideRunnable = Runnable {
+// floatingView.visibility = View.GONE
+// val layoutParams = floatingView.layoutParams as WindowManager.LayoutParams
+// val iconLayoutParams = WindowManager.LayoutParams(
+// 100, 100, // Set the size of the icon as needed
+// layoutParams.type,
+// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+// PixelFormat.TRANSLUCENT
+// )
+// iconLayoutParams.gravity = layoutParams.gravity
+// iconLayoutParams.x = layoutParams.x
+// iconLayoutParams.y = layoutParams.y
+//
+// val iconView = ImageView(this).apply {
+// setImageResource(R.drawable.ic_like_yes) // Set your icon drawable here
+// setOnClickListener {
+// floatingView.visibility = View.VISIBLE
+// windowManager.removeView(this)
+// windowManager.addView(floatingView, layoutParams)
+// startHideTimer()
+// }
+// }
+// windowManager.addView(iconView, iconLayoutParams)
+ }
+
override fun onBind(intent: Intent?): IBinder? {
return null
}
- override fun onCreate() {
- super.onCreate()
- mediaProjectionManager =
- getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
- displayMetrics = DisplayMetrics()
- windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
- windowManager.defaultDisplay?.getMetrics(displayMetrics)
-
- initSusView()
- startForeground(1, createNotification())
- }
-
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- val resultCode = intent?.getIntExtra("resultCode", Activity.RESULT_OK)
- val data: Intent? = intent?.getParcelableExtra("data")
- if (resultCode != null && data != null) {
- startScreenshot(resultCode, data)
+ if (intent == null) {
+ return START_NOT_STICKY
}
+
+ val resultCode = intent.getIntExtra(mpResultCode, Activity.RESULT_CANCELED)
+ val data = intent.getParcelableExtra(mpData)
+
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ mResultCode = resultCode
+ mResultData = data
+ startProjection()
+ } else {
+ Log.e("SusService", "Intent or data is null")
+ }
+
return START_NOT_STICKY
}
- private fun initSusView() {
+ override fun onCreate() {
+ super.onCreate()
+ displayMetrics = DisplayMetrics()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
+ initControlView()
+
+
+ imageProcessor = TextRecognitionProcessor(
+ this,
+ ChineseTextRecognizerOptions.Builder().build()
+ )
+
+ startForeground(1, createNotification())
+
+ }
+
+ private fun initControlView() {
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this))
floatingView = bindingSusControl.root
@@ -85,31 +143,7 @@ class SusService : Service() {
windowManager.addView(floatingView, layoutParams)
- // 设置点击事件
- bindingSusControl.tvSusGlobal.setOnClickListener {
- // 处理全局翻译点击事件
- addGlobalView()
- }
-
- bindingSusControl.tvSusCopy.setOnClickListener {
- // 处理复制文本点击事件
- }
-
- bindingSusControl.tvSusPhoto.setOnClickListener {
- // 处理照片翻译点击事件
- }
-
- bindingSusControl.tvSusDistrict.setOnClickListener {
- // 处理地区翻译点击事件
- }
-
- bindingSusControl.ivSusHome.setOnClickListener {
- // 处理返回主页点击事件
- }
-
- bindingSusControl.ivSusMove.setOnClickListener {
- // 处理移动窗口点击事件
- }
+ initControlClick()
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener {
private var startX = 0f
@@ -122,9 +156,9 @@ class SusService : Service() {
MotionEvent.ACTION_DOWN -> {
startX = layoutParams.x.toFloat()
startY = layoutParams.y.toFloat()
-
touchX = event.rawX
touchY = event.rawY
+ handler.removeCallbacks(hideRunnable)
return true
}
@@ -135,26 +169,78 @@ class SusService : Service() {
return true
}
- MotionEvent.ACTION_UP -> return true
+ MotionEvent.ACTION_UP -> {
+ val screenWidth = resources.displayMetrics.widthPixels
+ if (layoutParams.x + floatingView.width / 2 <= screenWidth / 2) {
+ layoutParams.x = 0
+ } else {
+ layoutParams.x = screenWidth - floatingView.width
+ }
+
+ v.performClick() // 手动触发点击事件
+ windowManager.updateViewLayout(floatingView, layoutParams)
+ startHideTimer()
+ return true
+ }
}
return false
}
})
+
+ startHideTimer() // Start the timer when the view is initialized
+ }
+
+ private fun startHideTimer() {
+ handler.removeCallbacks(hideRunnable)
+
+ handler.postDelayed(hideRunnable, ball_time)
+ }
+
+ private fun initControlClick() {
+
+ bindingSusControl.ivSusMove.setOnClickListener {
+ bindingSusControl.ivSusMove.setImageResource(R.drawable.ic_little_ball)
+ bindingSusControl.susLl1.visibility = View.GONE
+ bindingSusControl.susLl2.visibility = View.GONE
+ bindingSusControl.ivSusHome.visibility = View.GONE
+ }
+
+ bindingSusControl.tvSusGlobal.setOnClickListener {
+ bindingSusControl.susControlRoot.visibility = View.INVISIBLE
+
+ Handler(Looper.getMainLooper()).postDelayed({
+ addGlobalView()
+ captureScreenshot()
+ }, 555)
+ }
+
+ bindingSusControl.tvSusCopy.setOnClickListener {
+ Log.d("SusService", "Copy text clicked")
+ }
+
+ bindingSusControl.tvSusPhoto.setOnClickListener {
+ Log.d("SusService", "Photo translation clicked")
+ }
+
+ bindingSusControl.tvSusDistrict.setOnClickListener {
+ Log.d("SusService", "District translation clicked")
+ }
+
+ bindingSusControl.ivSusHome.setOnClickListener {
+ Log.d("SusService", "Home clicked")
+
+ }
+
+
}
private fun addGlobalView() {
- windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
-
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
globalView = bindingSubGlobal.root
- imageProcessor = TextRecognitionProcessor(
- this,
- ChineseTextRecognizerOptions.Builder().build()
- )
val layoutParams = WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
@@ -165,22 +251,113 @@ class SusService : Service() {
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = 0
- layoutParams.y = 100
+ layoutParams.y = 0
windowManager.addView(globalView, layoutParams)
+
+ initGlobalClick()
+
}
- override fun onDestroy() {
- super.onDestroy()
- if (floatingView.isAttachedToWindow) {
- windowManager.removeView(floatingView)
- }
- if (::globalView.isInitialized && globalView.isAttachedToWindow) {
+ private fun initGlobalClick() {
+ bindingSubGlobal.susGlobalClose.setOnClickListener {
windowManager.removeView(globalView)
+ bindingSusControl.susControlRoot.visibility = View.VISIBLE
}
+
+ }
+
+ private fun startProjection() {
+ if (mResultData == null) {
+ Log.e("SusService", "mResultData is null, cannot start projection")
+ return
+ }
+
+ if (mediaProjection == null) {
+ mediaProjectionManager =
+ getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ mediaProjection =
+ mediaProjectionManager.getMediaProjection(mResultCode, mResultData!!)
+ mediaProjection?.registerCallback(object : MediaProjection.Callback() {}, null)
+
+ Handler(Looper.getMainLooper()).postDelayed({
+ try {
+ val displayMetrics = DisplayMetrics()
+ windowManager.defaultDisplay.getMetrics(displayMetrics)
+
+ val density = displayMetrics.densityDpi
+ val width = displayMetrics.widthPixels
+ val height = displayMetrics.heightPixels
+
+ imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)
+
+ virtualDisplay = mediaProjection?.createVirtualDisplay(
+ "ScreenCapture",
+ width,
+ height,
+ density,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
+ imageReader.surface,
+ null,
+ null
+ )
+
+
+ } catch (e: Exception) {
+ Log.e("SusService", "Error starting projection", e)
+ }
+ }, 1234)
+ }
+
+ }
+
+ private fun stopProjection() {
+ // Release the virtual display
+ try {
+ virtualDisplay?.release()
+ virtualDisplay = null
+ } catch (e: Exception) {
+ Log.e("SusService", "Error releasing virtual display", e)
+ }
+
+ // Stop the media projection
+ try {
+ mediaProjection?.stop()
+ mediaProjection = null
+ } catch (e: Exception) {
+ Log.e("SusService", "Error stopping media projection", e)
+ }
+ }
+
+ private fun captureScreenshot() {
+ val image: Image? = imageReader.acquireLatestImage()
+ image?.let {
+ val bitmap = imageToBitmap(it)
+ image.close()
+ bindingSubGlobal.susPreview.setImageBitmap(bitmap)
+ tryReloadAndDetectInImage(bitmap)
+
+ }
+ }
+
+ private fun imageToBitmap(image: Image): Bitmap {
+ val planes = image.planes
+ val buffer = planes[0].buffer
+ val pixelStride = planes[0].pixelStride
+ val rowStride = planes[0].rowStride
+ val rowPadding = rowStride - pixelStride * image.width
+
+ val bitmap = Bitmap.createBitmap(
+ image.width + rowPadding / pixelStride,
+ image.height,
+ Bitmap.Config.ARGB_8888
+ )
+ bitmap.copyPixelsFromBuffer(buffer)
+ return Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
}
private fun createNotification(): Notification {
+ Log.d("SusService", "Creating notification")
val notificationChannelId = "FOREGROUND_SERVICE_CHANNEL"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -206,7 +383,6 @@ class SusService : Service() {
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
- // Clear the overlay first
bindingSubGlobal.susGraphicOverlay.clear()
if (imageProcessor != null) {
@@ -223,53 +399,20 @@ class SusService : Service() {
)
}
} catch (e: IOException) {
- Log.e("SusService", "Error retrieving saved image")
+ Log.e("SusService", "Error retrieving saved image", e)
}
}
- fun startScreenshot(resultCode: Int, data: Intent?) {
- mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
- val imageReader = ImageReader.newInstance(
- displayMetrics.widthPixels,
- displayMetrics.heightPixels,
- PixelFormat.RGBA_8888,
- 2
- )
-
- virtualDisplay = mediaProjection.createVirtualDisplay(
- "Screenshot",
- displayMetrics.widthPixels,
- displayMetrics.heightPixels,
- displayMetrics.densityDpi,
- DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
- imageReader.surface,
- null,
- null
- )
-
- // 初始化 globalView
- addGlobalView()
-
- Handler(Looper.getMainLooper()).postDelayed({
- val image = imageReader.acquireLatestImage()
- if (image != null) {
- val planes = image.planes
- val buffer = planes[0].buffer
- val pixelStride = planes[0].pixelStride
- val rowStride = planes[0].rowStride
- val rowPadding = rowStride - pixelStride * displayMetrics.widthPixels
-
- val bitmap = Bitmap.createBitmap(
- displayMetrics.widthPixels + rowPadding / pixelStride,
- displayMetrics.heightPixels,
- Bitmap.Config.ARGB_8888
- )
- bitmap.copyPixelsFromBuffer(buffer)
- image.close()
- bindingSubGlobal.susPreview.setImageBitmap(bitmap)
- tryReloadAndDetectInImage(bitmap)
- }
- stopSelf()
- }, 1000)
+ override fun onDestroy() {
+ super.onDestroy()
+ Log.d("SusService", "Service onDestroy")
+ if (floatingView.isAttachedToWindow) {
+ windowManager.removeView(floatingView)
+ }
+ if (::globalView.isInitialized && globalView.isAttachedToWindow) {
+ windowManager.removeView(globalView)
+ }
+ stopProjection()
}
+
}
diff --git a/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java b/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java
index 458e699..c53eac1 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java
+++ b/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java
@@ -62,6 +62,11 @@ public class LanguagesConstants {
// 根据语言代码获取 Language 对象
public Language getLanguageByLanguageCode(@NonNull String languageCode, @NonNull Context context) {
ArrayList languages = getList(context);
+
+ if (languageCode.equals("zh")) {
+ languageCode = "zh_CN";
+ }
+
for (Language lang : languages) {
if (lang.getLanguageCode().equalsIgnoreCase(languageCode)) {
return lang;
diff --git a/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt b/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt
index 8433f2b..0a856a6 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt
+++ b/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt
@@ -4,8 +4,11 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
+import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
+import android.text.Layout
+import android.text.StaticLayout
import android.text.TextPaint
import android.util.Log
import com.assimilate.alltrans.MyApp
@@ -13,7 +16,7 @@ import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.google.mlkit.vision.text.Text
-import java.util.Arrays
+import java.util.concurrent.Executors
import kotlin.math.max
import kotlin.math.min
@@ -27,8 +30,10 @@ class TextGraphic(
private val rectPaint: Paint = Paint()
private val textPaint: TextPaint
+
private val labelPaint: Paint
private val handler = Handler(Looper.getMainLooper())
+ private val executor = Executors.newSingleThreadExecutor()
init {
prepareTranslation()
@@ -37,6 +42,9 @@ class TextGraphic(
rectPaint.strokeWidth = STROKE_WIDTH
textPaint = TextPaint()
textPaint.color = TEXT_COLOR
+ textPaint.textSize = TEXT_SIZE
+ textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
+ textPaint.isFakeBoldText = true
labelPaint = Paint()
labelPaint.color = MARKER_COLOR
labelPaint.style = Paint.Style.FILL
@@ -48,7 +56,10 @@ class TextGraphic(
// Method to prepare translation before drawing
private fun prepareTranslation() {
- Thread {
+
+
+ executor.execute {
+
val textToTranslate = StringBuilder()
// Collect all text to be translated and append delimiter
@@ -64,13 +75,22 @@ class TextGraphic(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
+
+ if (lanTargetCode == null || lanSourceCode == null || textToTranslate.toString()
+ .isEmpty()
+ ) {
+ return@execute
+ }
+
// Define translation parameters
val param = HashMap().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", textToTranslate.toString())
}
-
+ Log.d("fdasfas_sou", lanSourceCode)
+ Log.d("fdasfas_tar", lanTargetCode)
+ Log.d("fdasfas_tex", textToTranslate.toString())
val translator: Translator =
GoogleTranslator()
@@ -78,27 +98,23 @@ class TextGraphic(
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
// Split translated text by delimiter
translatedTextBlocks =
- translatedText.split(DELIMITER.toRegex()).dropLastWhile { it.isEmpty() }
+ translatedText.split(DELIMITER.toRegex()).filter { it.isNotEmpty() }
// Update UI thread
handler.post {
- postInvalidate() // Notify to redraw
+ postInvalidate()
}
})
- }.start()
+ }
}
override fun draw(canvas: Canvas) {
- Log.d(TAG, "Text is: " + text.text)
+
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
val translatedBlockText =
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text
- val height1 = ((textBlock.boundingBox?.bottom?.toFloat()
- ?: 30f) - (textBlock.boundingBox?.top?.toFloat()
- ?: 0f)) / textBlock.lines.size
-
if (shouldGroupTextInBlocks) {
drawText(
getFormattedText(
@@ -107,53 +123,16 @@ class TextGraphic(
confidence = null
),
RectF(textBlock.boundingBox),
- height1 - 3 * STROKE_WIDTH,
canvas
)
- } else {
- for (line in textBlock.lines) {
- Log.d(TAG, "Line text is: " + line.text)
- Log.d(TAG, "Line boundingbox is: " + line.boundingBox)
- Log.d(TAG, "Line cornerpoint is: " + Arrays.toString(line.cornerPoints))
- Log.d(TAG, "Line confidence is: " + line.confidence)
- Log.d(TAG, "Line angle is: " + line.angle)
- // Draw the bounding box around the TextBlock.
- val rect = RectF(line.boundingBox)
- drawText(
- getFormattedText(
- translatedBlockText,
- line.recognizedLanguage,
- line.confidence
- ),
- rect,
- ((line.boundingBox?.bottom?.toFloat()
- ?: 20f) - (line.boundingBox?.top?.toFloat()
- ?: 0f)) - 2 * STROKE_WIDTH,
- canvas
+ Log.d(
+ "fdgfsdfsdfas", getFormattedText(
+ translatedBlockText,
+ textBlock.recognizedLanguage,
+ confidence = null
)
- for (element in line.elements) {
- Log.d(TAG, "Element text is: " + element.text)
- Log.d(TAG, "Element boundingbox is: " + element.boundingBox)
- Log.d(
- TAG,
- "Element cornerpoint is: " + Arrays.toString(element.cornerPoints)
- )
- Log.d(TAG, "Element language is: " + element.recognizedLanguage)
- Log.d(TAG, "Element confidence is: " + element.confidence)
- Log.d(TAG, "Element angle is: " + element.angle)
- for (symbol in element.symbols) {
- Log.d(TAG, "Symbol text is: " + symbol.text)
- Log.d(TAG, "Symbol boundingbox is: " + symbol.boundingBox)
- Log.d(
- TAG,
- "Symbol cornerpoint is: " + Arrays.toString(symbol.cornerPoints)
- )
- Log.d(TAG, "Symbol confidence is: " + symbol.confidence)
- Log.d(TAG, "Symbol angle is: " + symbol.angle)
- }
- }
- }
- }
+ )
+ } // 不需要单行(删除)
}
}
@@ -168,78 +147,60 @@ class TextGraphic(
else res
}
- private fun drawText(text: String, rect: RectF, textSize: Float, canvas: Canvas) {
+ private fun drawText(text: String, rect: RectF, canvas: Canvas) {
+ // 如果图像是翻转的,将左边翻译到右边,右边翻译到左边。
val x0 = translateX(rect.left)
val x1 = translateX(rect.right)
rect.left = min(x0, x1)
rect.right = max(x0, x1)
rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom)
+ canvas.drawRect(rect, labelPaint)
- // Set initial text size
- textPaint.textSize = textSize
+ // 设置文本大小以适应矩形
+ val textPaintCopy = TextPaint(textPaint)
+ val availableWidth = rect.width().toInt()
+ val availableHeight = rect.height().toInt()
+ var textSize = textPaintCopy.textSize
- // Break the text into multiple lines if necessary
- var lines = wrapText(text.trim(), rect.width())
+ // 调整文本大小以适应矩形
+ var textLayout: StaticLayout
+ while (textSize > 0) {
+ textPaintCopy.textSize = textSize
+ textLayout =
+ StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
+ .setAlignment(Layout.Alignment.ALIGN_NORMAL)
+ .setLineSpacing(0.0f, 1.0f)
+ .setIncludePad(false)
+ .build()
- // Calculate total height of the text
- val totalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
- val totalTextHeightWithSpacing = totalTextHeight * lines.size
-
- // Adjust the text size if the total height is greater than the rectangle height
- if (totalTextHeightWithSpacing > rect.height()) {
- textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
- lines = wrapText(text.trim(), rect.width())
- } else if (totalTextHeightWithSpacing < rect.height()) {
- // If the total text height is less than the rectangle height, increase text size
- textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
- lines = wrapText(text.trim(), rect.width())
+ if (textLayout.height <= availableHeight) {
+ break
+ }
+ textSize -= 1
}
- // Calculate new total height with adjusted text size
- val finalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
- val finalTotalTextHeightWithSpacing = finalTextHeight * lines.size
+ // 使用 StaticLayout 绘制文本
+ textLayout =
+ StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
+ .setAlignment(Layout.Alignment.ALIGN_NORMAL)
+ .setLineSpacing(0.0f, 1.0f)
+ .setIncludePad(false)
+ .build()
- // Calculate starting Y coordinate to center the text vertically
- var textY =
- rect.top + ((rect.height() - finalTotalTextHeightWithSpacing) / 2) - textPaint.fontMetrics.ascent
-
- // Draw the background rectangle
- canvas.drawRect(
- rect.left - STROKE_WIDTH,
- rect.top - STROKE_WIDTH,
- rect.right + STROKE_WIDTH,
- rect.bottom + STROKE_WIDTH,
- labelPaint
- )
-
- // Draw each line of text
- for (line in lines) {
- canvas.drawText(line, rect.left, textY, textPaint)
- textY += finalTextHeight
- }
- }
-
- private fun wrapText(text: String, maxWidth: Float): List {
- val lines = mutableListOf()
- var remainingText = text.trim()
-
- while (remainingText.isNotEmpty()) {
- val breakPoint = textPaint.breakText(remainingText, true, maxWidth, null)
- val line = remainingText.substring(0, breakPoint)
- lines.add(line)
- remainingText = remainingText.substring(breakPoint)
- }
-
- return lines
+ canvas.save()
+ canvas.translate(rect.left, rect.top)
+ textLayout.draw(canvas)
+ canvas.restore()
}
companion object {
- private const val DELIMITER = "`0_.._0`"
+ private const val DELIMITER = "0`~`0"
private const val TAG = "TextGraphic"
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
private val TEXT_COLOR = Color.parseColor("#FF474747")
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
private const val STROKE_WIDTH = 2.0f
+ private const val TEXT_SIZE = 44.0f
}
}
diff --git a/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt b/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt
index ac1e6f4..df2b24e 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt
+++ b/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt
@@ -1,13 +1,9 @@
-
package com.assimilate.alltrans.common
import android.content.Context
-import android.text.TextUtils
import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.curview.GraphicOverlay
-import com.assimilate.alltrans.http.GoogleTranslator
-import com.assimilate.alltrans.http.Translator
import com.google.android.gms.tasks.Task
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.Text
@@ -17,99 +13,134 @@ import com.google.mlkit.vision.text.TextRecognizerOptionsInterface
/** Processor for the text detector demo. */
class TextRecognitionProcessor(
- private val context: Context,
- textRecognizerOptions: TextRecognizerOptionsInterface
+ private val context: Context,
+ textRecognizerOptions: TextRecognizerOptionsInterface
) : VisionProcessorBase(context) {
- private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
- private val shouldGroupRecognizedTextInBlocks: Boolean =
- PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
- private val showLanguageTag: Boolean = PreferenceUtils.showLanguageTag(context)
- private val showConfidence: Boolean = PreferenceUtils.shouldShowTextConfidence(context)
+ private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
+ private val shouldGroupRecognizedTextInBlocks: Boolean =
+ PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
+ private val showLanguageTag: Boolean = PreferenceUtils.showLanguageTag(context)
+ private val showConfidence: Boolean = PreferenceUtils.shouldShowTextConfidence(context)
- override fun stop() {
- super.stop()
- textRecognizer.close()
- }
-
- override fun detectInImage(image: InputImage): Task {
- return textRecognizer.process(image)
- }
-
-
-
-
- override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
- Log.d(TAG, "On-device Text detection successful")
- logExtrasForTesting(text)
- graphicOverlay.add(
- TextGraphic(
- graphicOverlay,
- text,
- shouldGroupRecognizedTextInBlocks,
- showLanguageTag,
- showConfidence
- )
- )
- }
-
- override fun onFailure(e: Exception) {
- Log.w(TAG, "Text detection failed.$e")
- }
-
- companion object {
- private const val TAG = "TextRecProcessor"
- private fun logExtrasForTesting(text: Text?) {
- if (text != null) {
- Log.v(MANUAL_TESTING_LOG, "text context is : " + text.text)
- Log.v(MANUAL_TESTING_LOG, "Detected text has : " + text.textBlocks.size + " blocks")
- for (i in text.textBlocks.indices) {
- val lines = text.textBlocks[i].lines
- Log.v(
- MANUAL_TESTING_LOG,
- String.format("Detected text block %d has %d lines", i, lines.size)
- )
- for (j in lines.indices) {
- val elements = lines[j].elements
- Log.v(
- MANUAL_TESTING_LOG,
- String.format("Detected text line %d has %d elements", j, elements.size)
- )
- for (k in elements.indices) {
- val element = elements[k]
- Log.v(
- MANUAL_TESTING_LOG,
- String.format("Detected text element %d says: %s", k, element.text)
- )
- Log.v(
- MANUAL_TESTING_LOG,
- String.format(
- "Detected text element %d has a bounding box: %s",
- k,
- element.boundingBox!!.flattenToString()
- )
- )
- Log.v(
- MANUAL_TESTING_LOG,
- String.format(
- "Expected corner point size is 4, get %d",
- element.cornerPoints!!.size
- )
- )
- for (point in element.cornerPoints!!) {
- Log.v(
- MANUAL_TESTING_LOG,
- String.format(
- "Corner point for element %d is located at: x - %d, y = %d",
- k,
- point.x,
- point.y
- )
- )
- }
- }
- }
- }
- }
+ override fun stop() {
+ super.stop()
+ textRecognizer.close()
+ }
+
+ override fun detectInImage(image: InputImage): Task {
+ return textRecognizer.process(image)
+ }
+
+
+ override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
+ PreferenceLanguageUtils.putString("language_source", getMostFrequentLanguage(text))
+
+ Log.d(TAG, "On-device Text detection successful")
+ logExtrasForTesting(text)
+ graphicOverlay.add(
+ TextGraphic(
+ graphicOverlay,
+ text,
+ shouldGroupRecognizedTextInBlocks,
+ showLanguageTag,
+ showConfidence
+ )
+ )
+ }
+
+ override fun onFailure(e: Exception) {
+ Log.w(TAG, "Text detection failed.$e")
+ }
+
+
+ // 推测最可能的语言
+ private fun getMostFrequentLanguage(text: Text): String {
+ val languageCount = mutableMapOf()
+ for (textBlock in text.textBlocks) {
+ for (line in textBlock.lines) {
+ for (element in line.elements) {
+ val language = element.recognizedLanguage
+ if (language != "und-Latn") {
+ if (languageCount.containsKey(language)) {
+ languageCount[language] = languageCount[language]!! + 1
+ } else {
+ languageCount[language] = 1
+ }
+ }
+ }
+ }
+ }
+
+ val maxCode = languageCount.maxByOrNull { it.value }?.key ?: "zh_CN"
+
+ val lanByCode = LanguagesConstants.getInstance()
+ .getLanguageByLanguageCode(maxCode, MyApp.applicationContext())
+
+
+ return if (lanByCode != null) {
+ lanByCode.language
+ } else {
+ "English"
+ }
+ }
+
+ companion object {
+ private const val TAG = "TextRecProcessor"
+ private fun logExtrasForTesting(text: Text?) {
+ if (text != null) {
+ Log.v(MANUAL_TESTING_LOG, "text context is : " + text.text)
+ Log.v(MANUAL_TESTING_LOG, "Detected text has : " + text.textBlocks.size + " blocks")
+ for (i in text.textBlocks.indices) {
+ val lines = text.textBlocks[i].lines
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format("Detected text block %d has %d lines", i, lines.size)
+ )
+ for (j in lines.indices) {
+ val elements = lines[j].elements
+
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format("Detected text line %d has %d elements", j, elements.size)
+ )
+ for (k in elements.indices) {
+ val element = elements[k]
+ Log.v("推测是什么语言", element.recognizedLanguage)
+
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format("Detected text element %d says: %s", k, element.text)
+ )
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format(
+ "Detected text element %d has a bounding box: %s",
+ k,
+ element.boundingBox!!.flattenToString()
+ )
+ )
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format(
+ "Expected corner point size is 4, get %d",
+ element.cornerPoints!!.size
+ )
+ )
+ for (point in element.cornerPoints!!) {
+ Log.v(
+ MANUAL_TESTING_LOG,
+ String.format(
+ "Corner point for element %d is located at: x - %d, y = %d",
+ k,
+ point.x,
+ point.y
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
}
- }
}
diff --git a/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt b/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt
index 10ba01c..39383db 100644
--- a/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt
+++ b/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt
@@ -14,7 +14,7 @@ class SusView : View.OnClickListener {
override fun onClick(v: View?) {
- TODO("Not yet implemented")
+ // TODO("Not yet implemented")
}
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.kt
index 52d2358..fa94139 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.kt
@@ -1,172 +1,166 @@
-package com.assimilate.alltrans.viewui;
+package com.assimilate.alltrans.viewui
-import android.content.Context;
-import android.os.Bundle;
-import android.speech.tts.TextToSpeech;
-import android.view.View;
+import android.content.Context
+import android.os.Bundle
+import android.speech.tts.TextToSpeech
+import android.view.View
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.assimilate.alltrans.R
+import com.assimilate.alltrans.adapters.TranslationAdapter
+import com.assimilate.alltrans.adapters.TranslationAdapter.TranslationItemCallback
+import com.assimilate.alltrans.common.Widget
+import com.assimilate.alltrans.databinding.ActivityHistoryBinding
+import com.assimilate.alltrans.mydb.DbTranslation
+import com.assimilate.alltrans.mydb.Translations
+import java.util.Collections
+import java.util.Locale
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.graphics.Insets;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.WindowInsetsCompat;
-import androidx.recyclerview.widget.LinearLayoutManager;
+class HistoryActivity : AppCompatActivity() {
+ private var tts: TextToSpeech? = null
+ private var binding: ActivityHistoryBinding? = null
+ private var ids: HashSet? = null // 通过id 删除数据库文件
+ private var items: HashSet? = null // 通许index 删除界面上面的数据
+ private var operationCollection = false
-import com.assimilate.alltrans.R;
-import com.assimilate.alltrans.adapters.TranslationAdapter;
-import com.assimilate.alltrans.common.Widget;
-import com.assimilate.alltrans.databinding.ActivityHistoryBinding;
-import com.assimilate.alltrans.mydb.DbTranslation;
-import com.assimilate.alltrans.mydb.Translations;
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ binding = ActivityHistoryBinding.inflate(layoutInflater)
+ setContentView(binding!!.root)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(26, systemBars.top, 26, systemBars.bottom)
+ insets
+ }
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Locale;
+ ids = HashSet()
+ items = HashSet()
-public class HistoryActivity extends AppCompatActivity {
- public final static String COMMAND = "remove";
- public final static String COMMAND_COLLECTION = "remove-collection";
- public final static String COMMAND_HISTORY = "remove-history";
- private TextToSpeech tts;
- private ActivityHistoryBinding binding;
- private HashSet ids; // 通过id 删除数据库文件
- private HashSet items; // 通许index 删除界面上面的数据
- private boolean operationCollection = false;
+ val extra = intent.getStringExtra(COMMAND)
+ operationCollection = COMMAND_COLLECTION == extra
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- binding = ActivityHistoryBinding.inflate(getLayoutInflater());
- setContentView(binding.getRoot());
- ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
- Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(26, systemBars.top, 26, systemBars.bottom);
- return insets;
- });
+ tts = TextToSpeech(this) { status ->
+ if (null != tts && TextToSpeech.SUCCESS == status) tts!!.setLanguage(
+ Locale.getDefault()
+ )
+ }
- ids = new HashSet<>();
- items = new HashSet<>();
-
- String extra = getIntent().getStringExtra(COMMAND);
- operationCollection = COMMAND_COLLECTION.equals(extra);
-
- tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
- @Override
- public void onInit(int status) {
- if (null != tts && TextToSpeech.SUCCESS == status)
- tts.setLanguage(Locale.getDefault());
- }
- });
-
- ArrayList translations = new ArrayList<>();
- if (operationCollection) {
+ val translations = ArrayList()
+ if (!operationCollection) {
// 查出收藏的翻译记录
- binding.tvFuncTrans.setText("Collect");
- binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
- ArrayList list = new DbTranslation(this).getTranslations(true);
+ binding!!.tvFuncTrans.text = getString(R.string.favor_title)
+ // binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
+ val list = DbTranslation(this).getTranslations(true)
if (null != list && !list.isEmpty()) {
- translations.addAll(list);
+ translations.addAll(list)
}
} else {
// 查出所有的翻译记录
- binding.tvFuncTrans.setText("History");
- binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
- ArrayList list = new DbTranslation(this).getTranslations(false);
- if (null != list && !list.isEmpty()) {
- translations.addAll(list);
+ binding!!.tvFuncTrans.text = getString(R.string.his_title)
+ // binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
+ val list = DbTranslation(this).getTranslations(false)
+ if (null != list && list.isNotEmpty()) {
+ translations.addAll(list)
}
}
- final TranslationAdapter adapter = new TranslationAdapter(translations, new TranslationAdapter.TranslationItemCallback() {
- @Override
- public void updateList(TranslationAdapter.Operation operation, long id, int position) {
+ val adapter = TranslationAdapter(translations, object : TranslationItemCallback {
+ override fun updateList(
+ operation: TranslationAdapter.Operation,
+ id: Long,
+ position: Int
+ ) {
if (TranslationAdapter.Operation.ADD == operation) {
- add(id, position);
+ add(id, position)
} else if (TranslationAdapter.Operation.REMOVE == operation) {
- remove(id, position);
+ remove(id, position)
}
- updateBtn();
+ updateBtn()
}
- @Override
- public void speech(String value) {
+ override fun speech(value: String) {
if (null != tts
- && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) {
- tts.speak(value, 0, null, null);
+ && TextToSpeech.LANG_NOT_SUPPORTED != tts!!.isLanguageAvailable(Locale.getDefault())
+ ) {
+ tts!!.speak(value, 0, null, null)
}
}
- private void add(long id, int index) {
- ids.add(id);
- items.add(index);
+ private fun add(id: Long, index: Int) {
+ ids!!.add(id)
+ items!!.add(index)
}
- private void remove(long id, int index) {
- ids.remove(id);
- items.remove(index);
+ private fun remove(id: Long, index: Int) {
+ ids!!.remove(id)
+ items!!.remove(index)
}
- private void updateBtn() {
- if (ids.isEmpty()) {
- binding.remove.setVisibility(View.INVISIBLE);
+ private fun updateBtn() {
+ if (ids!!.isEmpty()) {
+ binding!!.remove.visibility = View.INVISIBLE
} else {
- binding.remove.setVisibility(View.VISIBLE);
+ binding!!.remove.visibility = View.VISIBLE
}
}
- });
- final LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
+ })
+ val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
- binding.remove.setOnClickListener(new View.OnClickListener() {
- private DbTranslation dbTranslation;
+ binding!!.remove.setOnClickListener(object : View.OnClickListener {
+ private var dbTranslation: DbTranslation? = null
- @Override
- public void onClick(View v) {
- if (ids.isEmpty()) {
- Widget.makeToast(HistoryActivity.this, "Noting to remove.");
- return;
+ override fun onClick(v: View) {
+ if (ids!!.isEmpty()) {
+ Widget.makeToast(this@HistoryActivity, "Noting to remove.")
+ return
}
- if (!ids.isEmpty()) {
- ArrayList longArrayList = new ArrayList<>(ids);
+ if (!ids!!.isEmpty()) {
+ val longArrayList = ArrayList(ids)
if (operationCollection) {
- getDbTranslation(HistoryActivity.this).removeCollectTranslations(longArrayList);
+ getDbTranslation(this@HistoryActivity).removeCollectTranslations(
+ longArrayList
+ )
} else {
- getDbTranslation(HistoryActivity.this).removeTranslations(longArrayList);
+ getDbTranslation(this@HistoryActivity).removeTranslations(longArrayList)
}
- ids.clear();
+ ids!!.clear()
}
- if (!items.isEmpty()) {
- ArrayList integerArrayList = new ArrayList<>(items);
- Collections.sort(integerArrayList, new Comparator() {
- @Override
- public int compare(Integer o1, Integer o2) {
- return o2.compareTo(o1);
- }
- });
- adapter.updateSet(integerArrayList);
- items.clear();
+ if (!items!!.isEmpty()) {
+ val integerArrayList = ArrayList(items)
+ Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
+ adapter.updateSet(integerArrayList)
+ items!!.clear()
}
- binding.remove.setVisibility(View.VISIBLE);
+ binding!!.remove.visibility = View.VISIBLE
}
- private DbTranslation getDbTranslation(Context context) {
+ private fun getDbTranslation(context: Context): DbTranslation {
if (null == dbTranslation) {
- dbTranslation = new DbTranslation(context);
+ dbTranslation = DbTranslation(context)
}
- return dbTranslation;
+ return dbTranslation!!
}
- });
+ })
- binding.histories.setLayoutManager(layoutManager);
- binding.histories.setAdapter(adapter);
+ binding!!.histories.layoutManager = layoutManager
+ binding!!.histories.adapter = adapter
}
- @Override
- public void onBackPressed() {
- super.onBackPressed();
+ override fun onBackPressed() {
+ super.onBackPressed()
}
- public void clickBack(View view) {
- onBackPressed();
+ fun clickBack(view: View?) {
+ onBackPressed()
+ }
+
+ companion object {
+ const val COMMAND: String = "remove"
+ const val COMMAND_COLLECTION: String = "remove_collection"
+ const val COMMAND_HISTORY: String = "remove_history"
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt
index e497cac..81c6524 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt
@@ -31,10 +31,26 @@ class LanguageChangeActivity : AppCompatActivity() {
}
initView()
+ initSearch() // 添加这一行
initList()
initClick()
}
+ private fun initSearch() {
+ binding.chSearch.setOnQueryTextListener(object :
+ androidx.appcompat.widget.SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String?): Boolean {
+ (binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText)
+ return true
+ }
+ })
+ }
+
+
private fun initView() {
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
@@ -57,32 +73,38 @@ class LanguageChangeActivity : AppCompatActivity() {
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
- binding.listLanCommon5.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ binding.listLanCommon5.layoutManager =
+ LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanCommon5.adapter = recentAdapter
}
}
private fun initClick() {
+ binding.ivChBack.setOnClickListener { onBackPressed() }
binding.tvChangeSource.setOnClickListener {
lastTranslateLanguage = false
+ binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff0e8ce8))
+ binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff1f1724))
}
binding.tvChangeTarget.setOnClickListener {
lastTranslateLanguage = true
+ binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff1f1724))
+ binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff0e8ce8))
}
- binding.tvExchange.setOnClickListener {
- binding.tvExchange.setOnClickListener {
- val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
- val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
- // 交换源语言和目标语言
- PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
- PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
- // 更新界面显示
- binding.tvChangeSource.text = currentTargetLanguage
- binding.tvChangeTarget.text = currentSourceLanguage
- onBackPressed()
- }
+ binding.tvExchange.setOnClickListener {
+ val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
+ val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
+ // 交换源语言和目标语言
+ PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
+ PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
+ // 更新界面显示
+ binding.tvChangeSource.text = currentTargetLanguage
+ binding.tvChangeTarget.text = currentSourceLanguage
+ onBackPressed()
}
+
+
}
private fun initList() {
@@ -101,8 +123,10 @@ class LanguageChangeActivity : AppCompatActivity() {
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
- binding.listLanguages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ binding.listLanguages.layoutManager =
+ LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanguages.adapter = adapter
}
}
+
}
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt
index 3f42926..1975bcd 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt
@@ -7,8 +7,10 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.media.projection.MediaProjectionManager
+import android.net.Uri
import android.os.Build
import android.os.Bundle
+import android.provider.Settings
import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextUtils
@@ -23,7 +25,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.allservice.SusService
import com.assimilate.alltrans.common.LanguagesConstants
@@ -33,16 +34,16 @@ import com.assimilate.alltrans.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
-
private var launcher: ActivityResultLauncher? = null
-
private lateinit var binding: ActivityMainBinding
private lateinit var lcm: LocalBroadcastManager
private lateinit var mediaProjectionManager: MediaProjectionManager
- private val REQUEST_CODE_SCREEN_CAPTURE = 1001
+ private val REQUEST_CODE = 1000
+ private val mpResultCode = "mpResultCode"
+ private val mpData = "mpData"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -59,7 +60,18 @@ class MainActivity : AppCompatActivity() {
initClick()
registerResult()
+ // 初始化
+ mediaProjectionManager =
+ getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ // 检查并请求悬浮窗权限
+ if (!Settings.canDrawOverlays(this)) {
+ val intent = Intent(
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:$packageName")
+ )
+ startActivityForResult(intent, REQUEST_CODE)
+ }
}
private fun registerResult() {
@@ -77,32 +89,25 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun startScreenCapture() {
- val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
- startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE)
- }
-
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == Activity.RESULT_OK) {
- startSusService(resultCode, data)
+ if (requestCode == REQUEST_CODE) {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ val serviceIntent = Intent(this, SusService::class.java).apply {
+ putExtra(mpResultCode, resultCode)
+ putExtra(mpData, data)
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(serviceIntent)
+ } else {
+ startService(serviceIntent)
+ }
+ } else {
+ Log.e("MainActivity", "Screen capture permission denied")
+ }
}
}
- private fun startSusService(resultCode: Int, data: Intent?) {
- val serviceIntent = Intent(this, SusService::class.java).apply {
- putExtra("resultCode", resultCode)
- putExtra("data", data)
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(serviceIntent)
- } else {
- startService(serviceIntent)
- }
- }
-
-
private fun initView() {
binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
binding.chTargetLanguage.text = PreferenceLanguageUtils.getString("language_target")
@@ -120,6 +125,14 @@ class MainActivity : AppCompatActivity() {
}
override fun afterTextChanged(s: Editable?) {
+ // 统计字符数并显示在 TextView 中
+ val charCount = s?.length ?: 0
+ // 截断逻辑
+ if (charCount > 1800) {
+ val truncatedText = s?.subSequence(0, 1800)
+ binding.etText.setText(truncatedText)
+ binding.etText.setSelection(1800) // 设置光标位置到文本末尾
+ }
// 根据EditText的内容显示或隐藏粘贴按钮
if (s.isNullOrEmpty()) {
binding.tvMainTrans.visibility = View.GONE
@@ -127,6 +140,10 @@ class MainActivity : AppCompatActivity() {
} else {
binding.tvMainTrans.visibility = View.VISIBLE
}
+
+ // 更新字符计数显示
+ val displayCharCount = if (charCount > 1800) 1800 else charCount
+ binding.tvMainLimitText.text = getString(R.string.main_limit_num, displayCharCount)
}
})
@@ -167,8 +184,10 @@ class MainActivity : AppCompatActivity() {
)
}
binding.ivMainHistory.setOnClickListener {
+ intent = Intent(this, HistoryActivity::class.java)
+ intent.putExtra("remove", "remove_collection")
startActivity(
- Intent(this, HistoryActivity::class.java)
+ intent
)
}
binding.llQuickSet.setOnClickListener {
@@ -177,17 +196,12 @@ class MainActivity : AppCompatActivity() {
)
}
binding.ivQuickStart.setOnClickListener {
+ // 启动截图
+ startActivityForResult(
+ mediaProjectionManager.createScreenCaptureIntent(),
+ REQUEST_CODE
+ )
- mediaProjectionManager =
- getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
- startScreenCapture()
-
- val intent = Intent(this, SusService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(intent)
- } else {
- startService(intent)
- }
}
binding.ivMainExChange.setOnClickListener {
@@ -203,9 +217,6 @@ class MainActivity : AppCompatActivity() {
binding.chSourceLanguage.text = currentTargetLanguage
binding.chTargetLanguage.text = currentSourceLanguage
- // 打印日志,验证交换后的语言设置
- Log.d("fdhash_su", PreferenceLanguageUtils.getString("language_source"))
- Log.d("fdhash_ta", PreferenceLanguageUtils.getString("language_target"))
}
@@ -216,7 +227,6 @@ class MainActivity : AppCompatActivity() {
return
}
val intent = Intent(this, TextResultActivity::class.java)
- // 将字符串数据添加到Intent中
intent.putExtra("source_text", binding.etText.text.toString())
startActivity(intent)
binding.etText.text = null
@@ -228,11 +238,11 @@ class MainActivity : AppCompatActivity() {
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
speechIntent.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
- 5000
+ 4000
) // 设置5秒的静默时间
speechIntent.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
- 5000
+ 4000
) // 设置5秒的可能完全静默时间
@@ -254,7 +264,7 @@ class MainActivity : AppCompatActivity() {
try {
launcher?.launch(speechIntent)
} catch (ea: ActivityNotFoundException) {
- Widget.makeToast(this, "Something went wrong.")
+ Widget.makeToast(this, getString(R.string.main_voice_to_text))
}
}
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/SettingsActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/SettingsActivity.kt
index 161ae97..b84f96c 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/SettingsActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/SettingsActivity.kt
@@ -1,5 +1,6 @@
package com.assimilate.alltrans.viewui
+import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
@@ -44,5 +45,11 @@ class SettingsActivity
bottomSheetDialog.show()
}
+ binding.llFavorite.setOnClickListener {
+ val intent = Intent(this, HistoryActivity::class.java)
+ intent.putExtra("remove", "remove_history")
+
+ startActivity(intent)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt
index 0b4aa45..c6a4b90 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt
@@ -9,19 +9,23 @@ import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.util.Pair
+import android.view.MenuItem
import android.view.View
import android.view.ViewTreeObserver
import android.widget.AdapterView
-import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.ImageView
+import android.widget.PopupMenu
+import android.widget.SearchView
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
+import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
@@ -31,14 +35,24 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import com.assimilate.alltrans.R
+import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.BitmapUtils
+import com.assimilate.alltrans.common.Language
+import com.assimilate.alltrans.common.LanguagesConstants
+import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.curview.GraphicOverlay
import com.google.android.gms.common.annotation.KeepName
+import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.common.util.concurrent.ListenableFuture
+import com.google.mlkit.common.model.LocalModel
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
@@ -47,9 +61,12 @@ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
+import java.util.ArrayList
import java.util.Locale
+import kotlin.math.max
+import kotlin.math.min
-/** 演示使用相机拍摄静态图像进行不同图像检测功能的活动。 */
+/** Activity demonstrating different image detector features with a still image from camera. */
@KeepName
class StillImageActivity : AppCompatActivity() {
private var preview: ImageView? = null
@@ -67,56 +84,120 @@ class StillImageActivity : AppCompatActivity() {
private var isFlashOn = false
private val REQUEST_CAMERA_PERMISSION = 100
+ private lateinit var bottomSheetDialog: BottomSheetDialog
+ private var chooseLanguage :Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
setContentView(R.layout.activity_still_image)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom)
+ insets
+ }
- // 检查并请求相机权限
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
- != PackageManager.PERMISSION_GRANTED
+ Widget.makeSnackbar(this, "Photographing text for translation")
+
+ preview = findViewById(R.id.preview)
+ graphicOverlay = findViewById(R.id.graphic_overlay)
+
+ isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ savedInstanceState?.let {
+ imageUri = it.getParcelable(KEY_IMAGE_URI)
+ imageMaxWidth = it.getInt(KEY_IMAGE_MAX_WIDTH)
+ imageMaxHeight = it.getInt(KEY_IMAGE_MAX_HEIGHT)
+ selectedSize = it.getString(KEY_SELECTED_SIZE)
+ }
+
+ val rootView = findViewById(R.id.root)
+ rootView.viewTreeObserver.addOnGlobalLayoutListener(object :
+ ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ imageMaxWidth = rootView.width
+ imageMaxHeight = rootView.height - findViewById(R.id.control).height
+ if (SIZE_SCREEN == selectedSize) {
+ tryReloadAndDetectInImage()
+ }
+ }
+ })
+
+ outputDirectory = getOutputDirectory()
+ // 检查并启动相机
+ checkCameraPermission()
+ initView()
+ initClick()
+ }
+
+ private fun checkCameraPermission() {
+ if (ContextCompat.checkSelfPermission(
+ this, Manifest.permission.CAMERA
+ ) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION
)
+ } else {
+ startCamera()
}
- Widget.makeSnackbar(this, "Photographing text for translation")
-
- preview = findViewById(R.id.preview)
- graphicOverlay = findViewById(R.id.graphic_overlay)
-
- populateFeatureSelector()
-
- isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- if (savedInstanceState != null) {
- imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI)
- imageMaxWidth = savedInstanceState.getInt(KEY_IMAGE_MAX_WIDTH)
- imageMaxHeight = savedInstanceState.getInt(KEY_IMAGE_MAX_HEIGHT)
- selectedSize = savedInstanceState.getString(KEY_SELECTED_SIZE)
- }
-
- val rootView = findViewById(R.id.root)
- rootView.viewTreeObserver.addOnGlobalLayoutListener(
- object : ViewTreeObserver.OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
- imageMaxWidth = rootView.width
- imageMaxHeight = rootView.height - findViewById(R.id.control).height
- if (SIZE_SCREEN == selectedSize) {
- tryReloadAndDetectInImage()
- }
- }
- }
- )
-
- // 初始化相机
- startCamera()
- outputDirectory = getOutputDirectory()
- initClick()
}
+ private fun initView() {
+ findViewById(R.id.still_source_language).text =
+ PreferenceLanguageUtils.getString("language_source")
+ findViewById(R.id.still_target_language).text =
+ PreferenceLanguageUtils.getString("language_target")
+
+ bottomSheetDialog = BottomSheetDialog(this)
+ bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan)
+ bottomSheetDialog.dismissWithAnimation = true
+ bottomSheetDialog.findViewById(R.id.ph_search)
+ ?.setOnQueryTextListener(object :
+ androidx.appcompat.widget.SearchView.OnQueryTextListener {
+ override fun onQueryTextSubmit(query: String?): Boolean {
+ return false
+ }
+
+ override fun onQueryTextChange(newText: String?): Boolean {
+ (bottomSheetDialog.findViewById(R.id.list_languages)?.adapter as LanguageAdapter).filter.filter(
+ newText
+ )
+
+ return true
+ }
+ })
+
+ initList()
+ }
+
+ private fun initList() {
+ val languages: ArrayList = LanguagesConstants.getInstance().getList(this)
+ if (languages.isNotEmpty()) {
+ val adapter = LanguageAdapter(this, languages) { _, language ->
+ if (!chooseLanguage) {
+ PreferenceLanguageUtils.putString("language_source", language.language)
+ bottomSheetDialog.dismiss()
+ } else {
+ PreferenceLanguageUtils.putString("language_target", language.language)
+ bottomSheetDialog.dismiss()
+ }
+
+ findViewById(R.id.still_source_language).text =
+ PreferenceLanguageUtils.getString("language_source")
+ findViewById(R.id.still_target_language).text =
+ PreferenceLanguageUtils.getString("language_target")
+ }
+ bottomSheetDialog.findViewById(R.id.list_languages)?.layoutManager =
+ LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ bottomSheetDialog.findViewById(R.id.list_languages)?.adapter = adapter
+ }
+ }
+
+
+
private fun initClick() {
findViewById(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
findViewById(R.id.iv_still_take).setOnClickListener { takePhoto() }
@@ -125,46 +206,40 @@ class StillImageActivity : AppCompatActivity() {
toggleFlash()
updateFlashButtonUI()
}
- findViewById(R.id.still_source_language).setOnClickListener { }
- findViewById(R.id.still_target_language).setOnClickListener { }
- findViewById(R.id.still_exChange).setOnClickListener { }
+ findViewById(R.id.still_source_language).setOnClickListener {
+ chooseLanguage = false
+ bottomSheetDialog.show()
+ }
+ findViewById(R.id.still_target_language).setOnClickListener {
+ chooseLanguage = true
+ bottomSheetDialog.show()
+ }
+ findViewById(R.id.still_exChange).setOnClickListener {
+ }
}
private fun toggleFlash() {
- if (isFlashOn) {
- imageCapture.flashMode = ImageCapture.FLASH_MODE_OFF
- } else {
- imageCapture.flashMode = ImageCapture.FLASH_MODE_ON
- }
+ imageCapture.flashMode =
+ if (isFlashOn) ImageCapture.FLASH_MODE_OFF else ImageCapture.FLASH_MODE_ON
isFlashOn = !isFlashOn
}
private fun updateFlashButtonUI() {
- if (isFlashOn) {
- findViewById(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_bulibuli)
- } else {
- findViewById(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_notbuli)
- }
+ findViewById(R.id.iv_still_buling).setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
}
-
private fun startCamera() {
val previewView = findViewById(R.id.photo_preview)
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
- val preview = Preview.Builder()
- .build()
- .also {
- it.setSurfaceProvider(previewView.surfaceProvider)
- }
-
- imageCapture = ImageCapture.Builder()
- .setFlashMode(ImageCapture.FLASH_MODE_OFF) // 默认关闭闪光灯
- .build()
+ val preview = Preview.Builder().build().also {
+ it.setSurfaceProvider(previewView.surfaceProvider)
+ }
+ imageCapture = ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_OFF).build()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
@@ -177,14 +252,10 @@ class StillImageActivity : AppCompatActivity() {
}
private fun takePhoto() {
-
- val imageCapture = imageCapture
-
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
)
-
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
@@ -196,25 +267,20 @@ class StillImageActivity : AppCompatActivity() {
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
- val savedUri = Uri.fromFile(photoFile)
- imageUri = savedUri
- val msg = "Photo capture succeeded: $savedUri"
- Log.d(TAG, msg)
+ imageUri = Uri.fromFile(photoFile)
+ Log.d(TAG, "Photo capture succeeded: $imageUri")
tryReloadAndDetectInImage()
}
- }
- )
+ })
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
- return if (mediaDir != null && mediaDir.exists())
- mediaDir else filesDir
+ return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir
}
-
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
@@ -224,80 +290,43 @@ class StillImageActivity : AppCompatActivity() {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
- // Permission is granted, proceed with camera
- startCameraIntentForResult()
+ startCamera()
} else {
- // Permission denied, show a message to the user
Toast.makeText(
this,
"Camera permission is required to use the camera",
Toast.LENGTH_SHORT
).show()
}
- return
}
}
}
-
- public override fun onResume() {
+ override fun onResume() {
super.onResume()
Log.d(TAG, "onResume")
+
+
createImageProcessor()
tryReloadAndDetectInImage()
+
+
}
- public override fun onPause() {
+ override fun onPause() {
super.onPause()
- imageProcessor?.run { this.stop() }
+ imageProcessor?.stop()
}
- public override fun onDestroy() {
+ override fun onDestroy() {
super.onDestroy()
-
- imageProcessor?.run { this.stop() }
- // 释放相机资源
+ imageProcessor?.stop()
if (::cameraProviderFuture.isInitialized) {
cameraProviderFuture.get().unbindAll()
}
}
- private fun populateFeatureSelector() {
- val featureSpinner = findViewById(R.id.feature_selector)
- val options: MutableList = ArrayList()
- options.add(TEXT_RECOGNITION_CHINESE); // 识别中文文本
- options.add(TEXT_RECOGNITION_LATIN); // 识别拉丁文本
- options.add(TEXT_RECOGNITION_DEVANAGARI); // 识别梵文文本
- options.add(TEXT_RECOGNITION_JAPANESE); // 识别日文文本
- options.add(TEXT_RECOGNITION_KOREAN); // 识别韩文文本
-
-
- // Creating adapter for featureSpinner
- val dataAdapter = ArrayAdapter(this, R.layout.spinner_style, options)
- // Drop down layout style - list view with radio button
- dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
- // attaching data adapter to spinner
- featureSpinner.adapter = dataAdapter
- featureSpinner.onItemSelectedListener =
- object : OnItemSelectedListener {
- override fun onItemSelected(
- parentView: AdapterView<*>,
- selectedItemView: View?,
- pos: Int,
- id: Long
- ) {
- if (pos >= 0) {
- selectedMode = parentView.getItemAtPosition(pos).toString()
- createImageProcessor()
- tryReloadAndDetectInImage()
- }
- }
-
- override fun onNothingSelected(arg0: AdapterView<*>?) {}
- }
- }
-
- public override fun onSaveInstanceState(outState: Bundle) {
+ override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_IMAGE_URI, imageUri)
outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth)
@@ -305,104 +334,71 @@ class StillImageActivity : AppCompatActivity() {
outState.putString(KEY_SELECTED_SIZE, selectedSize)
}
- private fun startCameraIntentForResult() {
+ private fun createImageProcessor() {
+ imageProcessor?.stop()
+ try {
+ imageProcessor = when (selectedMode) {
+ TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
+ this,
+ ChineseTextRecognizerOptions.Builder().build()
+ )
- // Ensure permission is still granted before starting camera intent
- if (ContextCompat.checkSelfPermission(
- this,
- Manifest.permission.CAMERA
- ) != PackageManager.PERMISSION_GRANTED
- ) {
+ "Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
+ this,
+ DevanagariTextRecognizerOptions.Builder().build()
+ )
+
+ TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
+ this,
+ JapaneseTextRecognizerOptions.Builder().build()
+ )
+
+ TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
+ this,
+ KoreanTextRecognizerOptions.Builder().build()
+ )
+
+ else -> TextRecognitionProcessor(
+ this,
+ TextRecognizerOptions.Builder().build()
+ )
+
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Can not create image processor: $selectedMode", e)
Toast.makeText(
- this,
- "Camera permission is required to use the camera",
- Toast.LENGTH_SHORT
+ applicationContext,
+ "Can not create image processor: " + e.message,
+ Toast.LENGTH_LONG
).show()
- return
- }
-
- imageUri = null
- preview!!.setImageBitmap(null)
- val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
- if (takePictureIntent.resolveActivity(packageManager) != null) {
- val values = ContentValues()
- values.put(MediaStore.Images.Media.TITLE, "New Picture")
- values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera")
- imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
- takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
- startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
- }
- }
-
- private fun startChooseImageIntentForResult() {
- val intent = Intent()
- intent.type = "image/*"
- intent.action = Intent.ACTION_GET_CONTENT
- startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
- tryReloadAndDetectInImage()
- } else if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK) {
- // In this case, imageUri is returned by the chooser, save it.
- imageUri = data!!.data
- tryReloadAndDetectInImage()
- } else {
- super.onActivityResult(requestCode, resultCode, data)
}
}
private fun tryReloadAndDetectInImage() {
- Log.d(TAG, "Try reload and detect image")
try {
- if (imageUri == null) {
- return
- }
- if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) {
- // UI layout has not finished yet, will reload once it's ready.
- return
- }
+ if (imageUri == null) return
+ if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) return
+
val imageBitmap =
BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return
- // Clear the overlay first
- graphicOverlay!!.clear()
- val resizedBitmap: Bitmap
- resizedBitmap =
- if (selectedSize == SIZE_ORIGINAL) {
- imageBitmap
- } else {
- // Get the dimensions of the image view
- val targetedSize: Pair = targetedWidthHeight
-
- // Determine how much to scale down the image
- val scaleFactor =
- Math.max(
- imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
- imageBitmap.height.toFloat() / targetedSize.second.toFloat()
- )
- Bitmap.createScaledBitmap(
- imageBitmap,
- (imageBitmap.width / scaleFactor).toInt(),
- (imageBitmap.height / scaleFactor).toInt(),
- true
- )
- }
-
- preview!!.setImageBitmap(resizedBitmap)
- if (imageProcessor != null) {
- graphicOverlay!!.setImageSourceInfo(
- resizedBitmap.width,
- resizedBitmap.height,
- /* isFlipped= */ false
+ if (selectedSize == SIZE_SCREEN) {
+ val targetedSize = targetedWidthHeight
+ val scaleFactor = max(
+ imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
+ imageBitmap.height.toFloat() / targetedSize.second.toFloat()
)
- imageProcessor!!.processBitmap(resizedBitmap, graphicOverlay)
+ val resizedBitmap = Bitmap.createScaledBitmap(
+ imageBitmap,
+ (imageBitmap.width / scaleFactor).toInt(),
+ (imageBitmap.height / scaleFactor).toInt(),
+ true
+ )
+ preview?.setImageBitmap(resizedBitmap)
+ processImage(resizedBitmap)
} else {
- Log.e(
- TAG,
- "Null imageProcessor, please check adb logs for imageProcessor creation error"
- )
+ preview?.setImageBitmap(imageBitmap)
+ processImage(imageBitmap)
}
} catch (e: IOException) {
Log.e(TAG, "Error retrieving saved image")
@@ -412,95 +408,52 @@ class StillImageActivity : AppCompatActivity() {
private val targetedWidthHeight: Pair
get() {
- val targetWidth: Int
- val targetHeight: Int
- when (selectedSize) {
- SIZE_SCREEN -> {
- targetWidth = imageMaxWidth
- targetHeight = imageMaxHeight
- }
-
- SIZE_640_480 -> {
- targetWidth = if (isLandScape) 640 else 480
- targetHeight = if (isLandScape) 480 else 640
- }
-
- SIZE_1024_768 -> {
- targetWidth = if (isLandScape) 1024 else 768
- targetHeight = if (isLandScape) 768 else 1024
- }
-
- else -> throw IllegalStateException("Unknown size")
- }
+ val targetWidth = if (isLandScape) max(imageMaxWidth, imageMaxHeight) else min(
+ imageMaxWidth,
+ imageMaxHeight
+ )
+ val targetHeight = if (isLandScape) min(imageMaxWidth, imageMaxHeight) else max(
+ imageMaxWidth,
+ imageMaxHeight
+ )
return Pair(targetWidth, targetHeight)
}
- private fun createImageProcessor() {
- try {
- when (selectedMode) {
- TEXT_RECOGNITION_LATIN ->
- imageProcessor =
- TextRecognitionProcessor(this, TextRecognizerOptions.Builder().build())
+ private fun processImage(bitmap: Bitmap) {
+ graphicOverlay?.clear()
+ imageProcessor?.processBitmap(bitmap, graphicOverlay)
+ }
- TEXT_RECOGNITION_CHINESE ->
- imageProcessor =
- TextRecognitionProcessor(
- this,
- ChineseTextRecognizerOptions.Builder().build()
- )
+ private fun startChooseImageIntentForResult() {
+ val intent = Intent().apply {
+ type = "image/*"
+ action = Intent.ACTION_GET_CONTENT
+ }
+ startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
+ }
- TEXT_RECOGNITION_DEVANAGARI ->
- imageProcessor =
- TextRecognitionProcessor(
- this,
- DevanagariTextRecognizerOptions.Builder().build()
- )
-
- TEXT_RECOGNITION_JAPANESE ->
- imageProcessor =
- TextRecognitionProcessor(
- this,
- JapaneseTextRecognizerOptions.Builder().build()
- )
-
- TEXT_RECOGNITION_KOREAN ->
- imageProcessor =
- TextRecognitionProcessor(
- this,
- KoreanTextRecognizerOptions.Builder().build()
- )
-
- else -> Log.e(TAG, "Unknown selectedMode: $selectedMode")
- }
- } catch (e: Exception) {
- Log.e(TAG, "Can not create image processor: $selectedMode", e)
- Toast.makeText(
- applicationContext,
- "Can not create image processor: " + e.message,
- Toast.LENGTH_LONG
- )
- .show()
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
+ imageUri = data.data
+ tryReloadAndDetectInImage()
}
}
companion object {
private const val TAG = "StillImageActivity"
- private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin"
- private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese"
- private const val TEXT_RECOGNITION_DEVANAGARI = "Text Recognition Devanagari"
- private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese"
- private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean"
-
- private const val SIZE_SCREEN = "w:screen" // Match screen width
- private const val SIZE_1024_768 = "w:1024" // ~1024*768 in a normal ratio
- private const val SIZE_640_480 = "w:640" // ~640*480 in a normal ratio
- private const val SIZE_ORIGINAL = "w:original" // Original image size
- private const val KEY_IMAGE_URI = "com.google.mlkit.vision.demo.KEY_IMAGE_URI"
- private const val KEY_IMAGE_MAX_WIDTH = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_WIDTH"
- private const val KEY_IMAGE_MAX_HEIGHT = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_HEIGHT"
- private const val KEY_SELECTED_SIZE = "com.google.mlkit.vision.demo.KEY_SELECTED_SIZE"
- private const val REQUEST_IMAGE_CAPTURE = 1001
- private const val REQUEST_CHOOSE_IMAGE = 1002
+ private const val KEY_IMAGE_URI = "com.google.mlkit.demo.stillImage.KEY_IMAGE_URI"
+ private const val KEY_IMAGE_MAX_WIDTH =
+ "com.google.mlkit.demo.stillImage.KEY_IMAGE_MAX_WIDTH"
+ private const val KEY_IMAGE_MAX_HEIGHT =
+ "com.google.mlkit.demo.stillImage.KEY_IMAGE_MAX_HEIGHT"
+ private const val KEY_SELECTED_SIZE = "com.google.mlkit.demo.stillImage.KEY_SELECTED_SIZE"
+ private const val SIZE_SCREEN = "w:screen"
+ private const val TEXT_RECOGNITION_CHINESE = "Chinese, Simplified"
+ private const val TEXT_RECOGNITION_JAPANESE = "Japanese"
+ private const val TEXT_RECOGNITION_KOREAN = "Korean"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
+ private const val REQUEST_CHOOSE_IMAGE = 1001
}
}
+
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
deleted file mode 100644
index 08e87cc..0000000
--- a/app/src/main/res/drawable/ic_close.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_history.xml b/app/src/main/res/layout/activity_history.xml
index 6f610d8..04fcaae 100644
--- a/app/src/main/res/layout/activity_history.xml
+++ b/app/src/main/res/layout/activity_history.xml
@@ -1,5 +1,5 @@
-
+
+ android:padding="16dp"
+ android:src="@drawable/ic_back"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
+
+
+
+
+ android:layout_height="0dp"
+ android:layout_marginTop="13dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
-
+
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_language_change.xml b/app/src/main/res/layout/activity_language_change.xml
index a50a52c..b2cd7c1 100644
--- a/app/src/main/res/layout/activity_language_change.xml
+++ b/app/src/main/res/layout/activity_language_change.xml
@@ -9,19 +9,42 @@
android:paddingTop="16dp"
tools:context=".viewui.LanguageChangeActivity">
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/iv_ch_back">
@@ -85,6 +106,7 @@
app:layout_constraintTop_toBottomOf="@id/change_language">
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 6ed939e..70beaed 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -228,7 +228,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
- android:text="1.11"
+ android:text="1.0.1"
android:textColor="@color/main_text_ff1f1724"
android:textSize="14sp"
app:drawableEndCompat="@drawable/ic_next" />
diff --git a/app/src/main/res/layout/activity_still_image.xml b/app/src/main/res/layout/activity_still_image.xml
index 3790b1f..14f07b6 100755
--- a/app/src/main/res/layout/activity_still_image.xml
+++ b/app/src/main/res/layout/activity_still_image.xml
@@ -4,24 +4,25 @@
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@color/bg_53514c"
android:keepScreenOn="true">
+ app:layout_constraintTop_toTopOf="parent" />
@@ -30,6 +31,7 @@
android:id="@+id/graphic_overlay"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:background="@color/bg_40_000000"
app:layout_constraintBottom_toBottomOf="@id/preview"
app:layout_constraintLeft_toLeftOf="@id/preview"
app:layout_constraintRight_toRightOf="@id/preview"
@@ -46,7 +48,7 @@
diff --git a/app/src/main/res/layout/activity_text_result.xml b/app/src/main/res/layout/activity_text_result.xml
index e30f17c..b3acf7e 100644
--- a/app/src/main/res/layout/activity_text_result.xml
+++ b/app/src/main/res/layout/activity_text_result.xml
@@ -19,6 +19,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
+
+
+ android:padding="12dp"
+ android:src="@drawable/ic_tr_close" />
+
+
+
- />
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/sus_control_view.xml b/app/src/main/res/layout/sus_control_view.xml
index 06bbe68..5aaf019 100644
--- a/app/src/main/res/layout/sus_control_view.xml
+++ b/app/src/main/res/layout/sus_control_view.xml
@@ -1,14 +1,14 @@
-
-
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index b576e5b..958195e 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,6 +3,8 @@
#FF000000
#FFFFFFFF
+
+ #66000000
#FFF9F9F9
#FFE2EFFF
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e15a553..4a1fd72 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,19 +13,25 @@
translate
Chinese
English
- enter text
+ Enter text
Paste
+ Your device may not support speech-to-text.
Translate
Quick Translate
settings
Photo Translation
Dictionary
- 0/5000
+ %1$d/1800
+
+
+ Languages
+
New Translate
Common
other
Speech in this language is temporarily not supported.
+ Translator
Settings
Languages
@@ -38,6 +44,7 @@
透明度
悬浮球显示时的透明度
自动折叠时间
+ Favorite
Global Translation
Copy Text
@@ -45,6 +52,8 @@
District Translation
Delete
+ History record
+
\ No newline at end of file