diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 91db46c..9008dda 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -56,11 +56,20 @@ dependencies {
// To recognize Korean script
implementation("com.google.mlkit:text-recognition-korean:16.0.0")
- // CameraX
+ // CameraX core library
+
+ implementation(libs.androidx.camera.core)
+
+ // CameraX Camera2 extensions
implementation(libs.androidx.camera.camera2)
+
+ // CameraX Lifecycle library
implementation(libs.androidx.camera.lifecycle)
+
+ // CameraX View class
implementation(libs.androidx.camera.view)
+
// 文本识别
// To recognize Latin script
// implementation(libs.play.services.mlkit.text.recognition)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6d35de4..2479661 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
= Build.VERSION_CODES.P) {
val processName = getUniqueProcessName()
if (processName != null && processName != packageName) {
@@ -81,76 +76,4 @@ class MyApp : Application() {
object Config {
const val openSusViewMode: String = "open_sus_view"
}
-
- private fun initLanguage() {
- // 拿到最近一次的翻译情况,分别设置最近一次的,并赋值setSourceLanguage|setTargetLanguage
-
- // 以下是默认情况
-
- val languages: ArrayList = LanguagesConstants.getInstance().getList(applicationContext)
- if (languages.isNotEmpty()) {
- for (language in languages) {
- if ("Afrikaans" == language.language) {
- tl = language
- break
- }
- }
- for (language in languages) {
- if ("English" == language.language) {
- sl = language
- break
- }
- }
- }
- }
-
- private fun getSourceLanguageCode(): String {
- if (sl == null) {
- return "en"
- }
- return sl!!.languageCode
- }
-
- private fun getSourceLanguage(): String {
- if (sl == null) {
- return "English"
- }
- return sl!!.language
- }
-
- private fun getSourceSpeechCode(): String {
- if (sl == null) {
- return "en-GB"
- }
- return sl!!.speechCode
- }
-
- private fun getTargetLanguageCode(): String {
- if (tl == null) {
- return "en"
- }
- return tl!!.languageCode
- }
-
- private fun getTargetLanguage(): String {
- if (tl == null) {
- return "English"
- }
- return tl!!.language
- }
-
- private fun getTargetSpeechCode(): String {
- if (tl == null) {
- return "en-GB"
- }
- return tl!!.speechCode
- }
-
- private fun setSourceLanguage(language: Language) {
- sl = language
- }
-
- private fun setTargetLanguage(language: Language) {
- tl = language
- }
}
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 3eb84f1..ada6964 100644
--- a/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt
+++ b/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt
@@ -266,7 +266,7 @@ class SusService : Service() {
)
bitmap.copyPixelsFromBuffer(buffer)
image.close()
- bindingSubGlobal.preview.setImageBitmap(bitmap)
+ bindingSubGlobal.susPreview.setImageBitmap(bitmap)
tryReloadAndDetectInImage(bitmap)
}
stopSelf()
diff --git a/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java b/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java
index 703162c..e097e68 100755
--- a/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java
+++ b/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java
@@ -52,26 +52,26 @@ public class InferenceInfoGraphic extends GraphicOverlay.Graphic {
float x = TEXT_SIZE * 0.5f;
float y = TEXT_SIZE * 1.5f;
- canvas.drawText(
- "InputImage size: " + overlay.getImageHeight() + "x" + overlay.getImageWidth(),
- x,
- y,
- textPaint);
-
- if (!showLatencyInfo) {
- return;
- }
- // Draw FPS (if valid) and inference latency
- if (framesPerSecond != null) {
- canvas.drawText(
- "FPS: " + framesPerSecond + ", Frame latency: " + frameLatency + " ms",
- x,
- y + TEXT_SIZE,
- textPaint);
- } else {
- canvas.drawText("Frame latency: " + frameLatency + " ms", x, y + TEXT_SIZE, textPaint);
- }
- canvas.drawText(
- "Detector latency: " + detectorLatency + " ms", x, y + TEXT_SIZE * 2, textPaint);
+// canvas.drawText(
+// "InputImage size: " + overlay.getImageHeight() + "x" + overlay.getImageWidth(),
+// x,
+// y,
+// textPaint);
+//
+// if (!showLatencyInfo) {
+// return;
+// }
+// // Draw FPS (if valid) and inference latency
+// if (framesPerSecond != null) {
+// canvas.drawText(
+// "FPS: " + framesPerSecond + ", Frame latency: " + frameLatency + " ms",
+// x,
+// y + TEXT_SIZE,
+// textPaint);
+// } else {
+// canvas.drawText("Frame latency: " + frameLatency + " ms", x, y + TEXT_SIZE, textPaint);
+// }
+// canvas.drawText(
+// "Detector latency: " + detectorLatency + " ms", x, y + TEXT_SIZE * 2, textPaint);
}
}
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 e77523c..458e699 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java
+++ b/app/src/main/java/com/assimilate/alltrans/common/LanguagesConstants.java
@@ -22,6 +22,7 @@ import java.util.Objects;
public class LanguagesConstants {
private static boolean instanced = false;
private volatile static LanguagesConstants languagesConstant;
+ private final Gson gson = new Gson();
private final ArrayList languages;
@@ -30,11 +31,11 @@ public class LanguagesConstants {
if (instanced) {
throw new RuntimeException("Instance multiple LanguagesConstants.");
} else {
- languages = new ArrayList();
+ languages = new ArrayList<>();
instanced = true;
}
- if(languagesConstant != null) {
+ if (languagesConstant != null) {
throw new RuntimeException("looper error(LanguagesConstants instanced).");
}
}
@@ -58,6 +59,40 @@ public class LanguagesConstants {
return languages;
}
+ // 根据语言代码获取 Language 对象
+ public Language getLanguageByLanguageCode(@NonNull String languageCode, @NonNull Context context) {
+ ArrayList languages = getList(context);
+ for (Language lang : languages) {
+ if (lang.getLanguageCode().equalsIgnoreCase(languageCode)) {
+ return lang;
+ }
+ }
+ return null;
+ }
+
+ // 获取 languageCode
+ public String getLanguageCodeByLanguage(@NonNull String language, @NonNull Context context) {
+ Language lang = getLanguageObjectByLanguage(language, context);
+ return lang != null ? lang.getLanguageCode() : "en";
+ }
+
+ // 获取 speechCode
+ public String getSpeechCodeByLanguage(@NonNull String language, @NonNull Context context) {
+ Language lang = getLanguageObjectByLanguage(language, context);
+ return lang != null ? lang.getSpeechCode() : "en";
+ }
+
+ // 获取 Language 对象
+ private Language getLanguageObjectByLanguage(@NonNull String language, @NonNull Context context) {
+ ArrayList languages = getList(context);
+ for (Language lang : languages) {
+ if (lang.getLanguage().equalsIgnoreCase(language)) {
+ return lang;
+ }
+ }
+ return null;
+ }
+
private void getLanguages(final Context context) {
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
@@ -70,7 +105,7 @@ public class LanguagesConstants {
// step2. 读取json文件信息
final StringBuilder builder = new StringBuilder();
- String line = null;
+ String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
@@ -78,8 +113,8 @@ public class LanguagesConstants {
// step3. 将json文本转换成Java对象
final String result = builder.toString();
if (!TextUtils.isEmpty(result)) {
- Gson gson = new Gson();
- Type listType = new TypeToken>(){}.getType();
+ Type listType = new TypeToken>() {
+ }.getType();
List temp = gson.fromJson(result, listType);
if (null != temp && !temp.isEmpty()) {
diff --git a/app/src/main/java/com/assimilate/alltrans/common/PreferenceLanguageUtils.kt b/app/src/main/java/com/assimilate/alltrans/common/PreferenceLanguageUtils.kt
new file mode 100644
index 0000000..eff9973
--- /dev/null
+++ b/app/src/main/java/com/assimilate/alltrans/common/PreferenceLanguageUtils.kt
@@ -0,0 +1,87 @@
+package com.assimilate.alltrans.common
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.assimilate.alltrans.MyApp
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+
+object PreferenceLanguageUtils {
+
+ private const val SP_NAME = "sp_language"
+ private const val KEY_RECENT_LANGUAGES = "recent_languages"
+ private const val MAX_RECENT_LANGUAGES = 5
+ private const val PREF_KEY_FIRST_TIME = "first_time"
+
+
+ @Volatile
+ private var sharedPreferences: SharedPreferences? = null
+
+ private fun getSharedPreferences(): SharedPreferences {
+ return sharedPreferences ?: synchronized(this) {
+ val instance = MyApp.applicationContext()
+ .getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
+ sharedPreferences = instance
+ instance
+ }
+ }
+
+
+ fun putString(key: String, value: String) {
+ getSharedPreferences().edit().putString(key, value).apply()
+ }
+
+ fun getString(key: String, defValue: String = "english"): String {
+ val value = getSharedPreferences().getString(key, defValue) ?: defValue
+ return value
+ }
+
+ fun clear() {
+ getSharedPreferences().edit().clear().apply()
+ }
+
+ fun clearExceptReferrerUrl() {
+ val sharedPreferences = getSharedPreferences()
+ val editor = sharedPreferences.edit()
+ sharedPreferences.all.keys.forEach { key ->
+ if (key != "refe_url") {
+ editor.remove(key)
+ }
+ }
+ editor.apply()
+ }
+
+ fun addRecentLanguage(language: Language) {
+ val recentLanguages = getRecentLanguages().toMutableList()
+ recentLanguages.remove(language) // Remove if already exists
+ recentLanguages.add(0, language) // Add to the beginning
+ if (recentLanguages.size > MAX_RECENT_LANGUAGES) {
+ recentLanguages.removeAt(recentLanguages.size - 1) // Remove the oldest
+ }
+ saveRecentLanguages(recentLanguages)
+ }
+
+ fun getRecentLanguages(): List {
+ val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null)
+ return if (json != null) {
+ val type = object : TypeToken>() {}.type
+ Gson().fromJson(json, type)
+ } else {
+ emptyList()
+ }
+ }
+
+ private fun saveRecentLanguages(languages: List) {
+ val json = Gson().toJson(languages)
+ getSharedPreferences().edit().putString(KEY_RECENT_LANGUAGES, json).apply()
+ }
+
+ // 检查是否是第一次进入应用
+ fun isFirstTime(): Boolean {
+ return getSharedPreferences().getBoolean(PREF_KEY_FIRST_TIME, true)
+ }
+ // 设置已经不是第一次进入应用了
+ fun setNotFirstTime() {
+ getSharedPreferences().edit().putBoolean(PREF_KEY_FIRST_TIME, false).apply()
+ }
+}
diff --git a/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java b/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java
index 9e1c6e6..4af22b6 100755
--- a/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java
+++ b/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java
@@ -24,7 +24,7 @@ public class PreferenceUtils {
public static boolean shouldGroupRecognizedTextInBlocks(Context context) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
String prefKey = context.getString(R.string.pref_key_group_recognized_text_in_blocks);
- return sharedPreferences.getBoolean(prefKey, false);
+ return sharedPreferences.getBoolean(prefKey, true);
}
public static boolean showLanguageTag(Context context) {
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 df70440..8433f2b 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt
+++ b/app/src/main/java/com/assimilate/alltrans/common/TextGraphic.kt
@@ -1,23 +1,23 @@
package com.assimilate.alltrans.common
+
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
+import android.os.Handler
+import android.os.Looper
import android.text.TextPaint
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.mlkit.vision.text.Text
import java.util.Arrays
import kotlin.math.max
import kotlin.math.min
-/**
- * Graphic instance for rendering TextBlock position, size, and ID within an associated graphic
- * overlay view.
- */
-class TextGraphic
-constructor(
+class TextGraphic(
overlay: GraphicOverlay?,
private val text: Text,
private val shouldGroupTextInBlocks: Boolean,
@@ -28,14 +28,15 @@ constructor(
private val rectPaint: Paint = Paint()
private val textPaint: TextPaint
private val labelPaint: Paint
+ private val handler = Handler(Looper.getMainLooper())
init {
+ prepareTranslation()
rectPaint.color = MARKER_COLOR
rectPaint.style = Paint.Style.STROKE
rectPaint.strokeWidth = STROKE_WIDTH
textPaint = TextPaint()
textPaint.color = TEXT_COLOR
- textPaint.textSize = TEXT_SIZE
labelPaint = Paint()
labelPaint.color = MARKER_COLOR
labelPaint.style = Paint.Style.FILL
@@ -43,25 +44,70 @@ constructor(
postInvalidate()
}
- /** Draws the text block annotations for position, size, and raw value on the supplied canvas. */
+ private var translatedTextBlocks: List = listOf()
+
+ // Method to prepare translation before drawing
+ private fun prepareTranslation() {
+ Thread {
+ val textToTranslate = StringBuilder()
+
+ // Collect all text to be translated and append delimiter
+ for (textBlock in text.textBlocks) {
+ val textWithDelimiter = textBlock.text + DELIMITER
+ textToTranslate.append(textWithDelimiter)
+ }
+ val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
+ PreferenceLanguageUtils.getString("language_source"),
+ MyApp.applicationContext()
+ )
+ val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
+ PreferenceLanguageUtils.getString("language_target"),
+ MyApp.applicationContext()
+ )
+ // Define translation parameters
+ val param = HashMap().apply {
+ put("sourceLanguage", lanSourceCode)
+ put("translationLanguage", lanTargetCode)
+ put("text", textToTranslate.toString())
+ }
+
+ val translator: Translator =
+ GoogleTranslator()
+
+ // Perform translation
+ translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
+ // Split translated text by delimiter
+ translatedTextBlocks =
+ translatedText.split(DELIMITER.toRegex()).dropLastWhile { it.isEmpty() }
+
+ // Update UI thread
+ handler.post {
+ postInvalidate() // Notify to redraw
+ }
+ })
+ }.start()
+ }
+
override fun draw(canvas: Canvas) {
+ Log.d(TAG, "Text is: " + text.text)
- for (textBlock in text.textBlocks) { // Renders the text at the bottom of the box.
- Log.d(TAG, "TextBlock text is: " + textBlock.text)
- Log.d(TAG, "TextBlock recognizedLanguage is: " + textBlock.recognizedLanguage)
- Log.d(TAG, "TextBlock boundingbox is: " + textBlock.boundingBox)
- Log.d(TAG, "TextBlock cornerpoint is: " + Arrays.toString(textBlock.cornerPoints))
+ 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(
- textBlock.text,
+ translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
),
RectF(textBlock.boundingBox),
- TEXT_SIZE * textBlock.lines.size + 2 * STROKE_WIDTH,
+ height1 - 3 * STROKE_WIDTH,
canvas
)
} else {
@@ -71,12 +117,18 @@ constructor(
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)
- // Draws the bounding box around the TextBlock.
+ // Draw the bounding box around the TextBlock.
val rect = RectF(line.boundingBox)
drawText(
- getFormattedText(line.text, line.recognizedLanguage, line.confidence),
+ getFormattedText(
+ translatedBlockText,
+ line.recognizedLanguage,
+ line.confidence
+ ),
rect,
- TEXT_SIZE + 2 * STROKE_WIDTH,
+ ((line.boundingBox?.bottom?.toFloat()
+ ?: 20f) - (line.boundingBox?.top?.toFloat()
+ ?: 0f)) - 2 * STROKE_WIDTH,
canvas
)
for (element in line.elements) {
@@ -103,79 +155,6 @@ constructor(
}
}
}
-
-
- val traString =
- "aaaaaaaaaaa(`_`))bbbbbbbbbbb((`_`)ccccc(`_`)dhsihs(`_`)dhksskjh(`_`)dhskjfhsdkjfj(`_`)"
- val delimiter = "(`_`)"
- val parts = traString.split(delimiter)
-
- for (part in parts) {
- Log.d(TAG, "TextBlock aaaaa is: $part")
-
- }
-
- // 遍历每个TextBlock并处理分割后的部分
- for ((index, textBlock) in text.textBlocks.withIndex()) { // 使用withIndex()获取索引
- Log.d(TAG, "TextBlock text is: " + textBlock.text)
- Log.d(TAG, "TextBlock recognizedLanguage is: " + textBlock.recognizedLanguage)
- Log.d(TAG, "TextBlock boundingbox is: " + textBlock.boundingBox)
- Log.d(TAG, "TextBlock cornerpoint is: " + Arrays.toString(textBlock.cornerPoints))
-
- if (shouldGroupTextInBlocks) {
- // 获取当前索引对应的part
- val part = if (index < parts.size) parts[index] else ""
-
- drawText(
- getFormattedText(
- part,
- textBlock.recognizedLanguage,
- confidence = null
- ),
- RectF(textBlock.boundingBox),
- TEXT_SIZE * textBlock.lines.size + 2 * 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)
- // Draws the bounding box around the TextBlock.
- val rect = RectF(line.boundingBox)
- drawText(
- getFormattedText(line.text, line.recognizedLanguage, line.confidence),
- rect,
- TEXT_SIZE + 2 * STROKE_WIDTH,
- canvas
- )
- 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)
- }
- }
- }
- }
- }
-
}
private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
@@ -189,35 +168,78 @@ constructor(
else res
}
- private fun drawText(text: String, rect: RectF, textHeight: Float, canvas: Canvas) {
- // If the image is flipped, the left will be translated to right, and the right to left.
+ private fun drawText(text: String, rect: RectF, textSize: Float, 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, rectPaint)
- val textWidth = textPaint.measureText(text)
+
+ // Set initial text size
+ textPaint.textSize = textSize
+
+ // Break the text into multiple lines if necessary
+ var lines = wrapText(text.trim(), rect.width())
+
+ // 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())
+ }
+
+ // Calculate new total height with adjusted text size
+ val finalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
+ val finalTotalTextHeightWithSpacing = finalTextHeight * lines.size
+
+ // 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 - textHeight,
- rect.left + textWidth + 2 * STROKE_WIDTH,
- rect.top,
+ rect.top - STROKE_WIDTH,
+ rect.right + STROKE_WIDTH,
+ rect.bottom + STROKE_WIDTH,
labelPaint
)
- // Renders the text at the bottom of the box.
- canvas.drawText(text, rect.left, rect.top - STROKE_WIDTH, textPaint)
+
+ // 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
}
companion object {
+ private const val DELIMITER = "`0_.._0`"
private const val TAG = "TextGraphic"
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
- private const val TEXT_COLOR = Color.BLACK
- private const val MARKER_COLOR = Color.GREEN
- private const val TEXT_SIZE = 54.0f
- private const val STROKE_WIDTH = 4.0f
+ private val TEXT_COLOR = Color.parseColor("#FF474747")
+ private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
+ private const val STROKE_WIDTH = 2.0f
}
-
-
-}
\ No newline at end of file
+}
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 fe9151a..ac1e6f4 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt
+++ b/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt
@@ -2,8 +2,12 @@
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
@@ -31,6 +35,9 @@ class TextRecognitionProcessor(
return textRecognizer.process(image)
}
+
+
+
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
Log.d(TAG, "On-device Text detection successful")
logExtrasForTesting(text)
diff --git a/app/src/main/java/com/assimilate/alltrans/common/Widget.java b/app/src/main/java/com/assimilate/alltrans/common/Widget.java
index 954e562..c84a47e 100644
--- a/app/src/main/java/com/assimilate/alltrans/common/Widget.java
+++ b/app/src/main/java/com/assimilate/alltrans/common/Widget.java
@@ -1,10 +1,16 @@
package com.assimilate.alltrans.common;
import android.app.Activity;
+import android.graphics.Color;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import com.google.android.material.snackbar.Snackbar;
+
public class Widget {
private static volatile Toast toast;
@@ -15,4 +21,18 @@ public class Widget {
toast = Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT);
toast.show();
}
+
+ public static void makeSnackbar(@NonNull final Activity mActivity, @NonNull final String msg) {
+ View view = mActivity.findViewById(android.R.id.content);
+ Snackbar snackbar = Snackbar.make(view, msg, Snackbar.LENGTH_LONG);
+ View snackbarView = snackbar.getView();
+ snackbarView.setBackgroundColor(Color.parseColor("#66000000"));
+
+ TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
+ textView.setTextColor(Color.WHITE);
+ textView.setTextSize(16f);
+ textView.setGravity(Gravity.CENTER);
+
+ snackbar.show();
+ }
}
diff --git a/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java b/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java
index 2b49a0e..53ae5c1 100755
--- a/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java
+++ b/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java
@@ -9,6 +9,7 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.google.common.base.Preconditions;
@@ -37,266 +38,294 @@ import java.util.List;
*
*/
public class GraphicOverlay extends View {
- private final Object lock = new Object();
- private final List graphics = new ArrayList<>();
- // Matrix for transforming from image coordinates to overlay view coordinates.
- private final Matrix transformationMatrix = new Matrix();
+ private final Object lock = new Object();
+ private final List graphics = new ArrayList<>();
+ // Matrix for transforming from image coordinates to overlay view coordinates.
+ private final Matrix transformationMatrix = new Matrix();
- private int imageWidth;
- private int imageHeight;
- // The factor of overlay View size to image size. Anything in the image coordinates need to be
- // scaled by this amount to fit with the area of overlay View.
- private float scaleFactor = 1.0f;
- // The number of horizontal pixels needed to be cropped on each side to fit the image with the
- // area of overlay View after scaling.
- private float postScaleWidthOffset;
- // The number of vertical pixels needed to be cropped on each side to fit the image with the
- // area of overlay View after scaling.
- private float postScaleHeightOffset;
- private boolean isImageFlipped;
- private boolean needUpdateTransformation = true;
+ private int imageWidth;
+ private int imageHeight;
+ // The factor of overlay View size to image size. Anything in the image coordinates need to be
+ // scaled by this amount to fit with the area of overlay View.
+ private float scaleFactor = 1.0f;
+ // The number of horizontal pixels needed to be cropped on each side to fit the image with the
+ // area of overlay View after scaling.
+ private float postScaleWidthOffset;
+ // The number of vertical pixels needed to be cropped on each side to fit the image with the
+ // area of overlay View after scaling.
+ private float postScaleHeightOffset;
+ private boolean isImageFlipped;
+ private boolean needUpdateTransformation = true;
- /**
- * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass
- * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add
- * instances to the overlay using {@link GraphicOverlay#add(Graphic)}.
- */
- public abstract static class Graphic {
- private GraphicOverlay overlay;
+ /**
+ * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass
+ * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add
+ * instances to the overlay using {@link GraphicOverlay#add(Graphic)}.
+ */
+ public abstract static class Graphic {
+ private GraphicOverlay overlay;
- public Graphic(GraphicOverlay overlay) {
- this.overlay = overlay;
+ public Graphic(GraphicOverlay overlay) {
+ this.overlay = overlay;
+ }
+
+ /**
+ * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
+ * to view coordinates for the graphics that are drawn:
+ *
+ *
+ *
{@link Graphic#scale(float)} adjusts the size of the supplied value from the image
+ * scale to the view scale.
+ *
{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
+ * coordinate from the image's coordinate system to the view coordinate system.
+ *
+ *
+ * @param canvas drawing canvas
+ */
+ public abstract void draw(Canvas canvas);
+
+ protected void drawRect(
+ Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
+ canvas.drawRect(left, top, right, bottom, paint);
+ }
+
+ protected void drawText(Canvas canvas, String text, float x, float y, Paint paint) {
+ canvas.drawText(text, x, y, paint);
+ }
+
+ /**
+ * Adjusts the supplied value from the image scale to the view scale.
+ */
+ public float scale(float imagePixel) {
+ return imagePixel * overlay.scaleFactor;
+ }
+
+ /**
+ * Returns the application context of the app.
+ */
+ public Context getApplicationContext() {
+ return overlay.getContext().getApplicationContext();
+ }
+
+ public boolean isImageFlipped() {
+ return overlay.isImageFlipped;
+ }
+
+ /**
+ * Adjusts the x coordinate from the image's coordinate system to the view coordinate system.
+ */
+ public float translateX(float x) {
+ if (overlay.isImageFlipped) {
+ return overlay.getWidth() - (scale(x) - overlay.postScaleWidthOffset);
+ } else {
+ return scale(x) - overlay.postScaleWidthOffset;
+ }
+ }
+
+ /**
+ * Adjusts the y coordinate from the image's coordinate system to the view coordinate system.
+ */
+ public float translateY(float y) {
+ return scale(y) - overlay.postScaleHeightOffset;
+ }
+
+ /**
+ * Returns a {@link Matrix} for transforming from image coordinates to overlay view coordinates.
+ */
+ public Matrix getTransformationMatrix() {
+ return overlay.transformationMatrix;
+ }
+
+ public void postInvalidate() {
+ overlay.postInvalidate();
+ }
+
+ /**
+ * Given the {@code zInImagePixel}, update the color for the passed in {@code paint}. The color will be
+ * more red if the {@code zInImagePixel} is smaller, or more blue ish vice versa. This is
+ * useful to visualize the z value of landmarks via color for features like Pose and Face Mesh.
+ *
+ * @param paint the paint to update color with
+ * @param canvas the canvas used to draw with paint
+ * @param visualizeZ if true, paint color will be changed.
+ * @param rescaleZForVisualization if true, re-scale the z value with zMin and zMax to make
+ * color more distinguishable
+ * @param zInImagePixel the z value used to update the paint color
+ * @param zMin min value of all z values going to be passed in
+ * @param zMax max value of all z values going to be passed in
+ */
+ public void updatePaintColorByZValue(
+ Paint paint,
+ Canvas canvas,
+ boolean visualizeZ,
+ boolean rescaleZForVisualization,
+ float zInImagePixel,
+ float zMin,
+ float zMax) {
+ if (!visualizeZ) {
+ return;
+ }
+
+ // When visualizeZ is true, sets up the paint to different colors based on z values.
+ // Gets the range of z value.
+ float zLowerBoundInScreenPixel;
+ float zUpperBoundInScreenPixel;
+
+ if (rescaleZForVisualization) {
+ zLowerBoundInScreenPixel = min(-0.001f, scale(zMin));
+ zUpperBoundInScreenPixel = max(0.001f, scale(zMax));
+ } else {
+ // By default, assume the range of z value in screen pixel is [-canvasWidth, canvasWidth].
+ float defaultRangeFactor = 1f;
+ zLowerBoundInScreenPixel = -defaultRangeFactor * canvas.getWidth();
+ zUpperBoundInScreenPixel = defaultRangeFactor * canvas.getWidth();
+ }
+
+ float zInScreenPixel = scale(zInImagePixel);
+
+ if (zInScreenPixel < 0) {
+ // Sets up the paint to be red if the item is in front of the z origin.
+ // Maps values within [zLowerBoundInScreenPixel, 0) to [255, 0) and use it to control the
+ // color. The larger the value is, the more red it will be.
+ int v = (int) (zInScreenPixel / zLowerBoundInScreenPixel * 255);
+ v = Ints.constrainToRange(v, 0, 255);
+ paint.setARGB(255, 255, 255 - v, 255 - v);
+ } else {
+ // Sets up the paint to be blue if the item is behind the z origin.
+ // Maps values within [0, zUpperBoundInScreenPixel] to [0, 255] and use it to control the
+ // color. The larger the value is, the more blue it will be.
+ int v = (int) (zInScreenPixel / zUpperBoundInScreenPixel * 255);
+ v = Ints.constrainToRange(v, 0, 255);
+ paint.setARGB(255, 255 - v, 255 - v, 255);
+ }
+ }
+ }
+
+ public GraphicOverlay(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ addOnLayoutChangeListener(
+ (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+ needUpdateTransformation = true);
}
/**
- * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
- * to view coordinates for the graphics that are drawn:
+ * Removes all graphics from the overlay.
+ */
+ public void clear() {
+ synchronized (lock) {
+ graphics.clear();
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Adds a graphic to the overlay.
+ */
+ public void add(Graphic graphic) {
+ synchronized (lock) {
+ graphics.add(graphic);
+ }
+ }
+
+ /**
+ * Removes a graphic from the overlay.
+ */
+ public void remove(Graphic graphic) {
+ synchronized (lock) {
+ graphics.remove(graphic);
+ }
+ postInvalidate();
+ }
+
+ /**
+ * Sets the source information of the image being processed by detectors, including size and
+ * whether it is flipped, which informs how to transform image coordinates later.
*
- *
- *
{@link Graphic#scale(float)} adjusts the size of the supplied value from the image
- * scale to the view scale.
- *
{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
- * coordinate from the image's coordinate system to the view coordinate system.
- *
- *
- * @param canvas drawing canvas
+ * @param imageWidth the width of the image sent to ML Kit detectors
+ * @param imageHeight the height of the image sent to ML Kit detectors
+ * @param isFlipped whether the image is flipped. Should set it to true when the image is from the
+ * front camera.
*/
- public abstract void draw(Canvas canvas);
-
- protected void drawRect(
- Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
- canvas.drawRect(left, top, right, bottom, paint);
+ public void setImageSourceInfo(int imageWidth, int imageHeight, boolean isFlipped) {
+ Preconditions.checkState(imageWidth > 0, "image width must be positive");
+ Preconditions.checkState(imageHeight > 0, "image height must be positive");
+ synchronized (lock) {
+ this.imageWidth = imageWidth;
+ this.imageHeight = imageHeight;
+ this.isImageFlipped = isFlipped;
+ needUpdateTransformation = true;
+ }
+ postInvalidate();
}
- protected void drawText(Canvas canvas, String text, float x, float y, Paint paint) {
- canvas.drawText(text, x, y, paint);
+ public int getImageWidth() {
+ return imageWidth;
}
- /** Adjusts the supplied value from the image scale to the view scale. */
- public float scale(float imagePixel) {
- return imagePixel * overlay.scaleFactor;
+ public int getImageHeight() {
+ return imageHeight;
}
- /** Returns the application context of the app. */
- public Context getApplicationContext() {
- return overlay.getContext().getApplicationContext();
- }
+ private void updateTransformationIfNeeded() {
+ if (!needUpdateTransformation || imageWidth <= 0 || imageHeight <= 0) {
+ return;
+ }
+ float viewAspectRatio = (float) getWidth() / getHeight();
+ float imageAspectRatio = (float) imageWidth / imageHeight;
+ postScaleWidthOffset = 0;
+ postScaleHeightOffset = 0;
+ if (viewAspectRatio > imageAspectRatio) {
+ // The image needs to be vertically cropped to be displayed in this view.
+ scaleFactor = (float) getWidth() / imageWidth;
+ postScaleHeightOffset = ((float) getWidth() / imageAspectRatio - getHeight()) / 2;
+ } else {
+ // The image needs to be horizontally cropped to be displayed in this view.
+ scaleFactor = (float) getHeight() / imageHeight;
+ postScaleWidthOffset = ((float) getHeight() * imageAspectRatio - getWidth()) / 2;
+ }
- public boolean isImageFlipped() {
- return overlay.isImageFlipped;
+ transformationMatrix.reset();
+ transformationMatrix.setScale(scaleFactor, scaleFactor);
+ transformationMatrix.postTranslate(-postScaleWidthOffset, -postScaleHeightOffset);
+
+ if (isImageFlipped) {
+ transformationMatrix.postScale(-1f, 1f, getWidth() / 2f, getHeight() / 2f);
+ }
+
+ needUpdateTransformation = false;
}
/**
- * Adjusts the x coordinate from the image's coordinate system to the view coordinate system.
+ * Draws the overlay with its associated graphic objects.
*/
- public float translateX(float x) {
- if (overlay.isImageFlipped) {
- return overlay.getWidth() - (scale(x) - overlay.postScaleWidthOffset);
- } else {
- return scale(x) - overlay.postScaleWidthOffset;
- }
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ synchronized (lock) {
+ updateTransformationIfNeeded();
+
+ for (Graphic graphic : graphics) {
+ graphic.draw(canvas);
+ }
+ }
}
- /**
- * Adjusts the y coordinate from the image's coordinate system to the view coordinate system.
- */
- public float translateY(float y) {
- return scale(y) - overlay.postScaleHeightOffset;
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+
+ setVisibility(View.INVISIBLE);
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+
+ setVisibility(View.VISIBLE);
+ return true;
+ }
+ return super.onTouchEvent(event);
}
- /**
- * Returns a {@link Matrix} for transforming from image coordinates to overlay view coordinates.
- */
- public Matrix getTransformationMatrix() {
- return overlay.transformationMatrix;
- }
-
- public void postInvalidate() {
- overlay.postInvalidate();
- }
-
- /**
- * Given the {@code zInImagePixel}, update the color for the passed in {@code paint}. The color will be
- * more red if the {@code zInImagePixel} is smaller, or more blue ish vice versa. This is
- * useful to visualize the z value of landmarks via color for features like Pose and Face Mesh.
- *
- * @param paint the paint to update color with
- * @param canvas the canvas used to draw with paint
- * @param visualizeZ if true, paint color will be changed.
- * @param rescaleZForVisualization if true, re-scale the z value with zMin and zMax to make
- * color more distinguishable
- * @param zInImagePixel the z value used to update the paint color
- * @param zMin min value of all z values going to be passed in
- * @param zMax max value of all z values going to be passed in
- */
- public void updatePaintColorByZValue(
- Paint paint,
- Canvas canvas,
- boolean visualizeZ,
- boolean rescaleZForVisualization,
- float zInImagePixel,
- float zMin,
- float zMax) {
- if (!visualizeZ) {
- return;
- }
-
- // When visualizeZ is true, sets up the paint to different colors based on z values.
- // Gets the range of z value.
- float zLowerBoundInScreenPixel;
- float zUpperBoundInScreenPixel;
-
- if (rescaleZForVisualization) {
- zLowerBoundInScreenPixel = min(-0.001f, scale(zMin));
- zUpperBoundInScreenPixel = max(0.001f, scale(zMax));
- } else {
- // By default, assume the range of z value in screen pixel is [-canvasWidth, canvasWidth].
- float defaultRangeFactor = 1f;
- zLowerBoundInScreenPixel = -defaultRangeFactor * canvas.getWidth();
- zUpperBoundInScreenPixel = defaultRangeFactor * canvas.getWidth();
- }
-
- float zInScreenPixel = scale(zInImagePixel);
-
- if (zInScreenPixel < 0) {
- // Sets up the paint to be red if the item is in front of the z origin.
- // Maps values within [zLowerBoundInScreenPixel, 0) to [255, 0) and use it to control the
- // color. The larger the value is, the more red it will be.
- int v = (int) (zInScreenPixel / zLowerBoundInScreenPixel * 255);
- v = Ints.constrainToRange(v, 0, 255);
- paint.setARGB(255, 255, 255 - v, 255 - v);
- } else {
- // Sets up the paint to be blue if the item is behind the z origin.
- // Maps values within [0, zUpperBoundInScreenPixel] to [0, 255] and use it to control the
- // color. The larger the value is, the more blue it will be.
- int v = (int) (zInScreenPixel / zUpperBoundInScreenPixel * 255);
- v = Ints.constrainToRange(v, 0, 255);
- paint.setARGB(255, 255 - v, 255 - v, 255);
- }
- }
- }
-
- public GraphicOverlay(Context context, AttributeSet attrs) {
- super(context, attrs);
- addOnLayoutChangeListener(
- (view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
- needUpdateTransformation = true);
- }
-
- /** Removes all graphics from the overlay. */
- public void clear() {
- synchronized (lock) {
- graphics.clear();
- }
- postInvalidate();
- }
-
- /** Adds a graphic to the overlay. */
- public void add(Graphic graphic) {
- synchronized (lock) {
- graphics.add(graphic);
- }
- }
-
- /** Removes a graphic from the overlay. */
- public void remove(Graphic graphic) {
- synchronized (lock) {
- graphics.remove(graphic);
- }
- postInvalidate();
- }
-
- /**
- * Sets the source information of the image being processed by detectors, including size and
- * whether it is flipped, which informs how to transform image coordinates later.
- *
- * @param imageWidth the width of the image sent to ML Kit detectors
- * @param imageHeight the height of the image sent to ML Kit detectors
- * @param isFlipped whether the image is flipped. Should set it to true when the image is from the
- * front camera.
- */
- public void setImageSourceInfo(int imageWidth, int imageHeight, boolean isFlipped) {
- Preconditions.checkState(imageWidth > 0, "image width must be positive");
- Preconditions.checkState(imageHeight > 0, "image height must be positive");
- synchronized (lock) {
- this.imageWidth = imageWidth;
- this.imageHeight = imageHeight;
- this.isImageFlipped = isFlipped;
- needUpdateTransformation = true;
- }
- postInvalidate();
- }
-
- public int getImageWidth() {
- return imageWidth;
- }
-
- public int getImageHeight() {
- return imageHeight;
- }
-
- private void updateTransformationIfNeeded() {
- if (!needUpdateTransformation || imageWidth <= 0 || imageHeight <= 0) {
- return;
- }
- float viewAspectRatio = (float) getWidth() / getHeight();
- float imageAspectRatio = (float) imageWidth / imageHeight;
- postScaleWidthOffset = 0;
- postScaleHeightOffset = 0;
- if (viewAspectRatio > imageAspectRatio) {
- // The image needs to be vertically cropped to be displayed in this view.
- scaleFactor = (float) getWidth() / imageWidth;
- postScaleHeightOffset = ((float) getWidth() / imageAspectRatio - getHeight()) / 2;
- } else {
- // The image needs to be horizontally cropped to be displayed in this view.
- scaleFactor = (float) getHeight() / imageHeight;
- postScaleWidthOffset = ((float) getHeight() * imageAspectRatio - getWidth()) / 2;
- }
-
- transformationMatrix.reset();
- transformationMatrix.setScale(scaleFactor, scaleFactor);
- transformationMatrix.postTranslate(-postScaleWidthOffset, -postScaleHeightOffset);
-
- if (isImageFlipped) {
- transformationMatrix.postScale(-1f, 1f, getWidth() / 2f, getHeight() / 2f);
- }
-
- needUpdateTransformation = false;
- }
-
- /** Draws the overlay with its associated graphic objects. */
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- synchronized (lock) {
- updateTransformationIfNeeded();
-
- for (Graphic graphic : graphics) {
- graphic.draw(canvas);
- }
- }
- }
-
}
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java
index 13c3a92..52d2358 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java
@@ -122,7 +122,7 @@ public class HistoryActivity extends AppCompatActivity {
@Override
public void onClick(View v) {
if (ids.isEmpty()) {
- Widget.makeToast(HistoryActivity.this, "Noting to remove.");
+ Widget.makeToast(HistoryActivity.this, "Noting to remove.");
return;
}
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 8f77b68..e497cac 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt
@@ -7,64 +7,102 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
-import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
+import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding
class LanguageChangeActivity : AppCompatActivity() {
private lateinit var binding: ActivityLanguageChangeBinding
private var lastTranslateLanguage = false
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLanguageChangeBinding.inflate(layoutInflater)
enableEdgeToEdge()
setContentView(binding.root)
+
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
+ initView()
initList()
initClick()
}
+ private fun initView() {
+ binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
+ binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
+ updateRecentLanguages()
+ }
+
+ private fun updateRecentLanguages() {
+ val recentLanguages = PreferenceLanguageUtils.getRecentLanguages()
+ if (recentLanguages.isNotEmpty()) {
+ val recentAdapter = LanguageAdapter(this, ArrayList(recentLanguages)) { _, language ->
+ Log.d("LanguageChange", language.language)
+ if (lastTranslateLanguage) {
+ PreferenceLanguageUtils.putString("language_target", language.language)
+ onBackPressed()
+ } else {
+ PreferenceLanguageUtils.putString("language_source", language.language)
+ }
+ PreferenceLanguageUtils.addRecentLanguage(language)
+ binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
+ binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
+ updateRecentLanguages()
+ }
+ binding.listLanCommon5.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ binding.listLanCommon5.adapter = recentAdapter
+ }
+ }
+
private fun initClick() {
binding.tvChangeSource.setOnClickListener {
lastTranslateLanguage = false
}
binding.tvChangeTarget.setOnClickListener {
lastTranslateLanguage = true
+ }
+ 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()
+ }
}
}
private fun initList() {
- val languages: ArrayList = LanguagesConstants.getInstance().getList(
- this
- )
+ val languages: ArrayList = LanguagesConstants.getInstance().getList(this)
if (languages.isNotEmpty()) {
- val adapter =
- LanguageAdapter(
- this, languages
- ) { _, language ->
-
- Log.d("fsdafsdfd", language.language)
- if (lastTranslateLanguage) {
- MyApp.setTargetLanguage(language)
- onBackPressed()
- } else {
- MyApp.setSourceLanguage(language)
- }
- binding.tvChangeSource.text = MyApp.getSourceLanguage()
- binding.tvChangeTarget.text = MyApp.getTargetLanguage()
+ val adapter = LanguageAdapter(this, languages) { _, language ->
+ Log.d("LanguageChange", language.language)
+ if (lastTranslateLanguage) {
+ PreferenceLanguageUtils.putString("language_target", language.language)
+ onBackPressed()
+ } else {
+ PreferenceLanguageUtils.putString("language_source", language.language)
}
- val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
- binding.listLanguages.setLayoutManager(layoutManager)
- binding.listLanguages.setAdapter(adapter)
+ PreferenceLanguageUtils.addRecentLanguage(language)
+ binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
+ binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
+ updateRecentLanguages()
+ }
+ binding.listLanguages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
+ binding.listLanguages.adapter = adapter
}
}
-}
\ No newline at end of file
+}
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 b95fd5d..3f42926 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt
@@ -13,6 +13,7 @@ import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
+import android.util.Log
import android.view.View
import android.widget.EditText
import androidx.activity.enableEdgeToEdge
@@ -25,6 +26,8 @@ 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
+import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityMainBinding
@@ -101,8 +104,8 @@ class MainActivity : AppCompatActivity() {
private fun initView() {
- binding.chSourceLanguage.text = MyApp.getSourceLanguage()
- binding.chTargetLanguage.text = MyApp.getTargetLanguage()
+ binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
+ binding.chTargetLanguage.text = PreferenceLanguageUtils.getString("language_target")
}
private fun initSet() {
@@ -187,6 +190,25 @@ class MainActivity : AppCompatActivity() {
}
}
+ binding.ivMainExChange.setOnClickListener {
+ // 读取当前的源语言和目标语言
+ val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
+ val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
+
+ // 交换源语言和目标语言
+ PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
+ PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
+
+ // 更新界面显示
+ binding.chSourceLanguage.text = currentTargetLanguage
+ binding.chTargetLanguage.text = currentSourceLanguage
+
+ // 打印日志,验证交换后的语言设置
+ Log.d("fdhash_su", PreferenceLanguageUtils.getString("language_source"))
+ Log.d("fdhash_ta", PreferenceLanguageUtils.getString("language_target"))
+ }
+
+
}
private fun toTextTransResult() {
@@ -200,6 +222,7 @@ class MainActivity : AppCompatActivity() {
binding.etText.text = null
}
+
// 语音转文本
private fun voiceToText() {
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
@@ -212,18 +235,22 @@ class MainActivity : AppCompatActivity() {
5000
) // 设置5秒的可能完全静默时间
- speechIntent.putExtra(
- "android.speech.extra.LANGUAGE_MODEL",
- MyApp.getSourceLanguage()
- )
+
+// speechIntent.putExtra(
+// "android.speech.extra.LANGUAGE_MODEL",
+// MyApp.getSourceLanguage()
+// )
+ val languageCode = LanguagesConstants.getInstance()
+ .getLanguageCodeByLanguage(PreferenceLanguageUtils.getString("language_source"), this)
speechIntent.putExtra(
"android.speech.extra.LANGUAGE",
- MyApp.getSourceLanguageCode()
- )
- speechIntent.putExtra(
- "android.speech.extra.LANGUAGE_PREFERENCE",
- MyApp.getSourceLanguage()
+ languageCode
+
)
+// speechIntent.putExtra(
+// "android.speech.extra.LANGUAGE_PREFERENCE",
+//
+// )
try {
launcher?.launch(speechIntent)
} catch (ea: ActivityNotFoundException) {
@@ -250,7 +277,6 @@ class MainActivity : AppCompatActivity() {
}
}
-
override fun onResume() {
super.onResume()
initView()
@@ -264,5 +290,4 @@ class MainActivity : AppCompatActivity() {
}
}
-
}
\ 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 834be83..0b4aa45 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt
@@ -13,34 +13,43 @@ 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.Spinner
+import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.Preview
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.BitmapUtils
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.common.util.concurrent.ListenableFuture
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions
import com.google.mlkit.vision.text.latin.TextRecognizerOptions
+import java.io.File
import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.Locale
-/** Activity demonstrating different image detector features with a still image from camera. */
+/** 演示使用相机拍摄静态图像进行不同图像检测功能的活动。 */
@KeepName
class StillImageActivity : AppCompatActivity() {
private var preview: ImageView? = null
@@ -49,17 +58,16 @@ class StillImageActivity : AppCompatActivity() {
private var selectedSize: String? = SIZE_SCREEN
private var isLandScape = false
private var imageUri: Uri? = null
-
- // Max width (portrait mode)
private var imageMaxWidth = 0
-
- // Max height (portrait mode)
private var imageMaxHeight = 0
private var imageProcessor: VisionImageProcessor? = null
+ private lateinit var imageCapture: ImageCapture
+ private lateinit var outputDirectory: File
+ private lateinit var cameraProviderFuture: ListenableFuture
+ private var isFlashOn = false
private val REQUEST_CAMERA_PERMISSION = 100
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_still_image)
@@ -74,31 +82,13 @@ class StillImageActivity : AppCompatActivity() {
REQUEST_CAMERA_PERMISSION
)
}
+ Widget.makeSnackbar(this, "Photographing text for translation")
-
- findViewById(R.id.select_image_button).setOnClickListener { view: View ->
- // Menu for selecting either: a) take new photo b) select from existing
- val popup = PopupMenu(this@StillImageActivity, view)
- popup.setOnMenuItemClickListener { menuItem: MenuItem ->
- val itemId = menuItem.itemId
- if (itemId == R.id.select_images_from_local) {
- startChooseImageIntentForResult()
- return@setOnMenuItemClickListener true
- } else if (itemId == R.id.take_photo_using_camera) {
- startCameraIntentForResult()
- return@setOnMenuItemClickListener true
- }
- false
- }
- val inflater = popup.menuInflater
- inflater.inflate(R.menu.camera_button_menu, popup.menu)
- popup.show()
- }
preview = findViewById(R.id.preview)
graphicOverlay = findViewById(R.id.graphic_overlay)
populateFeatureSelector()
- populateSizeSelector()
+
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
if (savedInstanceState != null) {
imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI)
@@ -121,12 +111,107 @@ class StillImageActivity : AppCompatActivity() {
}
)
- val settingsButton = findViewById(R.id.settings_button)
- settingsButton.setOnClickListener {
-// val intent = Intent(applicationContext, SettingsActivity::class.java)
-// intent.putExtra(SettingsActivity.EXTRA_LAUNCH_SOURCE, LaunchSource.STILL_IMAGE)
-// startActivity(intent)
+ // 初始化相机
+ startCamera()
+ outputDirectory = getOutputDirectory()
+ initClick()
+ }
+
+ private fun initClick() {
+ findViewById(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
+ findViewById(R.id.iv_still_take).setOnClickListener { takePhoto() }
+ findViewById(R.id.iv_still_back).setOnClickListener { onBackPressed() }
+ findViewById(R.id.iv_still_buling).setOnClickListener {
+ toggleFlash()
+ updateFlashButtonUI()
}
+ findViewById(R.id.still_source_language).setOnClickListener { }
+ findViewById(R.id.still_target_language).setOnClickListener { }
+ findViewById(R.id.still_exChange).setOnClickListener { }
+
+ }
+
+ private fun toggleFlash() {
+ if (isFlashOn) {
+ imageCapture.flashMode = ImageCapture.FLASH_MODE_OFF
+ } else {
+ imageCapture.flashMode = 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)
+ }
+ }
+
+
+ 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 cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+
+ try {
+ cameraProvider.unbindAll()
+ cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
+ } catch (exc: Exception) {
+ Log.e(TAG, "Use case binding failed", exc)
+ }
+ }, ContextCompat.getMainExecutor(this))
+ }
+
+ 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(
+ outputOptions,
+ ContextCompat.getMainExecutor(this),
+ object : ImageCapture.OnImageSavedCallback {
+ override fun onError(exc: ImageCaptureException) {
+ Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
+ }
+
+ override fun onImageSaved(output: ImageCapture.OutputFileResults) {
+ val savedUri = Uri.fromFile(photoFile)
+ imageUri = savedUri
+ val msg = "Photo capture succeeded: $savedUri"
+ Log.d(TAG, msg)
+ 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
}
@@ -169,7 +254,12 @@ class StillImageActivity : AppCompatActivity() {
public override fun onDestroy() {
super.onDestroy()
+
imageProcessor?.run { this.stop() }
+ // 释放相机资源
+ if (::cameraProviderFuture.isInitialized) {
+ cameraProviderFuture.get().unbindAll()
+ }
}
private fun populateFeatureSelector() {
@@ -207,37 +297,6 @@ class StillImageActivity : AppCompatActivity() {
}
}
- private fun populateSizeSelector() {
- val sizeSpinner = findViewById(R.id.size_selector)
- val options: MutableList = ArrayList()
- options.add(SIZE_SCREEN)
- options.add(SIZE_1024_768)
- options.add(SIZE_640_480)
- options.add(SIZE_ORIGINAL)
- // 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
- sizeSpinner.adapter = dataAdapter
- sizeSpinner.onItemSelectedListener =
- object : OnItemSelectedListener {
- override fun onItemSelected(
- parentView: AdapterView<*>,
- selectedItemView: View?,
- pos: Int,
- id: Long
- ) {
- if (pos >= 0) {
- selectedSize = parentView.getItemAtPosition(pos).toString()
- tryReloadAndDetectInImage()
- }
- }
-
- override fun onNothingSelected(arg0: AdapterView<*>?) {}
- }
- }
-
public override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_IMAGE_URI, imageUri)
@@ -247,6 +306,7 @@ class StillImageActivity : AppCompatActivity() {
}
private fun startCameraIntentForResult() {
+
// Ensure permission is still granted before starting camera intent
if (ContextCompat.checkSelfPermission(
this,
@@ -299,12 +359,10 @@ class StillImageActivity : AppCompatActivity() {
if (imageUri == null) {
return
}
-
if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) {
// UI layout has not finished yet, will reload once it's ready.
return
}
-
val imageBitmap =
BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return
// Clear the overlay first
@@ -443,5 +501,6 @@ class StillImageActivity : AppCompatActivity() {
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 FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
}
}
diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt
index 27a3496..b861274 100644
--- a/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt
+++ b/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt
@@ -7,13 +7,16 @@ import android.content.Intent
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.text.TextUtils
+import android.util.Log
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
+import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.Logger
+import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityTextResultBinding
import com.assimilate.alltrans.http.GoogleTranslator
@@ -67,8 +70,20 @@ class TextResultActivity : AppCompatActivity() {
}
binding.ivTrCopy.setOnClickListener { copyToClipboard() }
binding.ivSourceClear.setOnClickListener { onBackPressed() }
- binding.ivSourceTts.setOnClickListener { readText(binding.tvTrSource.text.toString()) }
- binding.ivTargetTts.setOnClickListener { readText(binding.tvTrTarget.text.toString()) }
+ binding.ivSourceTts.setOnClickListener {
+ readText(
+ binding.tvTrSource.text.toString(),
+ PreferenceLanguageUtils.getString("language_source"),
+ this
+ )
+ }
+ binding.ivTargetTts.setOnClickListener {
+ readText(
+ binding.tvTrTarget.text.toString(),
+ PreferenceLanguageUtils.getString("language_target"),
+ this
+ )
+ }
binding.ivTrTargetShare.setOnClickListener { shareText(binding.tvTrTarget.text.toString()) }
binding.ivTrCollect.setOnClickListener { addCollect() }
@@ -81,9 +96,19 @@ class TextResultActivity : AppCompatActivity() {
}
translating = true
+
+ 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().apply {
- put("sourceLanguage", MyApp.getSourceLanguageCode())
- put("translationLanguage", MyApp.getTargetLanguageCode())
+ put("sourceLanguage", lanSourceCode)
+ put("translationLanguage", lanTargetCode)
put("text", text)
}
@@ -104,9 +129,9 @@ class TextResultActivity : AppCompatActivity() {
private fun addHistory(transResult: String) {
val dbTranslation = DbTranslation(this)
val translations = Translations(
- MyApp.getSourceLanguage(),
+ PreferenceLanguageUtils.getString("language_source"),
binding.tvTrSource.text.toString(),
- MyApp.getTargetLanguage(),
+ PreferenceLanguageUtils.getString("language_target"),
transResult
)
@@ -131,17 +156,29 @@ class TextResultActivity : AppCompatActivity() {
}
}
- private fun readText(text: String) {
+ // readText 方法,整合获取语言代码和朗读文本功能
+ private fun readText(text: String, targetLanguage: String, context: Context) {
val speech: String = text.trim()
if (!TextUtils.isEmpty(speech)) {
- if (TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())
- ) {
- tts.speak(speech, 0, null, null)
+ // 获取目标语言的语言代码
+ val languageCode =
+ LanguagesConstants.getInstance().getLanguageCodeByLanguage(targetLanguage, context)
+ Log.d("LanguageCode", "Language Code for $targetLanguage: $languageCode")
+
+ // 创建语言 Locale 对象
+ val locale = Locale(languageCode)
+
+ // 判断语言是否支持
+ if (TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(locale)) {
+ tts.language = locale
+ tts.speak(speech, TextToSpeech.QUEUE_FLUSH, null, null)
+ } else {
+ Widget.makeToast(this, getString(R.string.tr_tts_error))
}
}
-
}
+
// 复制到粘贴板
private fun copyToClipboard() {
val tip = "Copied to clipboard!"
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_still_bulibuli.webp b/app/src/main/res/drawable-xxxhdpi/ic_still_bulibuli.webp
new file mode 100644
index 0000000..7ad4412
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_still_bulibuli.webp differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_still_notbuli.webp b/app/src/main/res/drawable-xxxhdpi/ic_still_notbuli.webp
new file mode 100644
index 0000000..a4dd01b
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_still_notbuli.webp differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_still_pic.webp b/app/src/main/res/drawable-xxxhdpi/ic_still_pic.webp
new file mode 100644
index 0000000..383c39f
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_still_pic.webp differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_still_take.webp b/app/src/main/res/drawable-xxxhdpi/ic_still_take.webp
new file mode 100644
index 0000000..3f9d5b1
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_still_take.webp differ
diff --git a/app/src/main/res/drawable/button_r20_black_bg.xml b/app/src/main/res/drawable/button_r20_black_bg.xml
index 44a9fea..e51716e 100644
--- a/app/src/main/res/drawable/button_r20_black_bg.xml
+++ b/app/src/main/res/drawable/button_r20_black_bg.xml
@@ -1,6 +1,6 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_back_white.xml b/app/src/main/res/drawable/ic_back_white.xml
new file mode 100644
index 0000000..efe8e48
--- /dev/null
+++ b/app/src/main/res/drawable/ic_back_white.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_down_choose_white.xml b/app/src/main/res/drawable/ic_down_choose_white.xml
new file mode 100644
index 0000000..ae39bf1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_down_choose_white.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_language_change.xml b/app/src/main/res/layout/activity_language_change.xml
index c0f9663..a50a52c 100644
--- a/app/src/main/res/layout/activity_language_change.xml
+++ b/app/src/main/res/layout/activity_language_change.xml
@@ -31,15 +31,17 @@
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:gravity="center"
+
android:paddingStart="16dp"
android:paddingEnd="12dp"
- android:text="@string/text_source_language"
+
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" />
+
+
@@ -26,20 +36,48 @@
app:layout_constraintTop_toTopOf="@id/preview" />
-
+ android:layout_marginTop="16dp"
+ android:orientation="horizontal">
+
+
+
+
+
+
+
+
-
-
+ android:layout_gravity="center" />
-
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_text_result.xml b/app/src/main/res/layout/activity_text_result.xml
index 16a6922..e30f17c 100644
--- a/app/src/main/res/layout/activity_text_result.xml
+++ b/app/src/main/res/layout/activity_text_result.xml
@@ -153,15 +153,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:src="@drawable/tr_photo" />
-
-
diff --git a/app/src/main/res/raw/languages.json b/app/src/main/res/raw/languages.json
index 91feb66..66ee67d 100644
--- a/app/src/main/res/raw/languages.json
+++ b/app/src/main/res/raw/languages.json
@@ -71,8 +71,8 @@
},
{
"language": "Chinese, Simplified",
- "languageCode": "zh",
- "speechCode": "zh"
+ "languageCode": "zh_CN",
+ "speechCode": "zh_CN"
},
{
"language": "Chinese, Traditional",
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 8538da9..b576e5b 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -18,8 +18,10 @@
#FF2F2F2F#FF605C62
-
+
#FFA5A5A5
+
+ #FF53514C
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e407a48..e15a553 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -25,6 +25,7 @@
New TranslateCommonother
+ Speech in this language is temporarily not supported.SettingsLanguages
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6561f35..5fa21a4 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,6 +1,7 @@
[versions]
agp = "8.4.1"
cameraCamera2 = "1.3.4"
+cameraCore = "1.3.4"
converterGson = "2.9.0"
glide = "4.16.0"
gson = "2.10.1"
@@ -19,9 +20,10 @@ playServicesMlkitTextRecognition = "19.0.0"
playServicesMlkitTextRecognitionChinese = "16.0.0"
[libraries]
-androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCamera2" }
-androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraCamera2" }
-androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraCamera2" }
+androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" }
+androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }
+androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraCore" }
+androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "cameraCore" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }