text,photo,exchange end
This commit is contained in:
parent
4e66e7f84d
commit
ea4a023519
@ -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)
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
|
||||
@ -7,58 +7,53 @@ import android.os.Build
|
||||
import android.webkit.WebView
|
||||
import com.assimilate.alltrans.common.Language
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.Logger
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
|
||||
class MyApp : Application() {
|
||||
|
||||
init {
|
||||
instance = this
|
||||
|
||||
|
||||
}
|
||||
|
||||
private var sl: Language? = null
|
||||
private var tl: Language? = null
|
||||
private fun setSystemLanguage() {
|
||||
|
||||
|
||||
// 检查是否是第一次进入应用
|
||||
if (PreferenceLanguageUtils.isFirstTime()) {
|
||||
// 第一次进入应用的逻辑
|
||||
val config = applicationContext().resources.configuration
|
||||
val locale = config.locale.toString() // 获取完整的语言环境信息,如 "en_US" 或 "zh_CN"
|
||||
Logger.d("dslocaleds", locale)
|
||||
val languages = LanguagesConstants.getInstance().getLanguageByLanguageCode(locale, this)
|
||||
Logger.d("dslocaledsa", languages.language)
|
||||
|
||||
PreferenceLanguageUtils.putString(
|
||||
"language_source",
|
||||
languages.language.toString().trim()
|
||||
)
|
||||
|
||||
// 设置已经不是第一次进入应用了
|
||||
PreferenceLanguageUtils.setNotFirstTime()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var instance: MyApp? = null
|
||||
|
||||
fun applicationContext(): Context {
|
||||
return instance!!.applicationContext
|
||||
}
|
||||
|
||||
fun getSourceLanguageCode(): String {
|
||||
return instance?.getSourceLanguageCode() ?: "zh"
|
||||
}
|
||||
|
||||
fun getSourceLanguage(): String {
|
||||
return instance?.getSourceLanguage() ?: "English"
|
||||
}
|
||||
|
||||
fun getSourceSpeechCode(): String {
|
||||
return instance?.getSourceSpeechCode() ?: "en-GB"
|
||||
}
|
||||
|
||||
fun getTargetLanguageCode(): String {
|
||||
return instance?.getTargetLanguageCode() ?: "en"
|
||||
}
|
||||
|
||||
fun getTargetLanguage(): String {
|
||||
return instance?.getTargetLanguage() ?: "English"
|
||||
}
|
||||
|
||||
fun getTargetSpeechCode(): String {
|
||||
return instance?.getTargetSpeechCode() ?: "en-GB"
|
||||
}
|
||||
|
||||
fun setSourceLanguage(language: Language) {
|
||||
instance?.setSourceLanguage(language)
|
||||
}
|
||||
|
||||
fun setTargetLanguage(language: Language) {
|
||||
instance?.setTargetLanguage(language)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
initLanguage()
|
||||
instance = this
|
||||
setSystemLanguage()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 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<Language> = 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,7 +266,7 @@ class SusService : Service() {
|
||||
)
|
||||
bitmap.copyPixelsFromBuffer(buffer)
|
||||
image.close()
|
||||
bindingSubGlobal.preview.setImageBitmap(bitmap)
|
||||
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
|
||||
tryReloadAndDetectInImage(bitmap)
|
||||
}
|
||||
stopSelf()
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<Language> languages;
|
||||
|
||||
@ -30,11 +31,11 @@ public class LanguagesConstants {
|
||||
if (instanced) {
|
||||
throw new RuntimeException("Instance multiple LanguagesConstants.");
|
||||
} else {
|
||||
languages = new ArrayList<Language>();
|
||||
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<Language> 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<Language> 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<List<Language>>(){}.getType();
|
||||
Type listType = new TypeToken<List<Language>>() {
|
||||
}.getType();
|
||||
|
||||
List<Language> temp = gson.fromJson(result, listType);
|
||||
if (null != temp && !temp.isEmpty()) {
|
||||
|
||||
@ -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<Language> {
|
||||
val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null)
|
||||
return if (json != null) {
|
||||
val type = object : TypeToken<List<Language>>() {}.type
|
||||
Gson().fromJson(json, type)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRecentLanguages(languages: List<Language>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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<String> = 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<String, String>().apply {
|
||||
put("sourceLanguage", lanSourceCode)
|
||||
put("translationLanguage", lanTargetCode)
|
||||
put("text", textToTranslate.toString())
|
||||
}
|
||||
|
||||
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
|
||||
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<String> {
|
||||
val lines = mutableListOf<String>()
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -92,12 +93,16 @@ public class GraphicOverlay extends View {
|
||||
canvas.drawText(text, x, y, paint);
|
||||
}
|
||||
|
||||
/** Adjusts the supplied value from the image scale to the view scale. */
|
||||
/**
|
||||
* 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. */
|
||||
/**
|
||||
* Returns the application context of the app.
|
||||
*/
|
||||
public Context getApplicationContext() {
|
||||
return overlay.getContext().getApplicationContext();
|
||||
}
|
||||
@ -203,7 +208,9 @@ public class GraphicOverlay extends View {
|
||||
needUpdateTransformation = true);
|
||||
}
|
||||
|
||||
/** Removes all graphics from the overlay. */
|
||||
/**
|
||||
* Removes all graphics from the overlay.
|
||||
*/
|
||||
public void clear() {
|
||||
synchronized (lock) {
|
||||
graphics.clear();
|
||||
@ -211,14 +218,18 @@ public class GraphicOverlay extends View {
|
||||
postInvalidate();
|
||||
}
|
||||
|
||||
/** Adds a graphic to the overlay. */
|
||||
/**
|
||||
* Adds a graphic to the overlay.
|
||||
*/
|
||||
public void add(Graphic graphic) {
|
||||
synchronized (lock) {
|
||||
graphics.add(graphic);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a graphic from the overlay. */
|
||||
/**
|
||||
* Removes a graphic from the overlay.
|
||||
*/
|
||||
public void remove(Graphic graphic) {
|
||||
synchronized (lock) {
|
||||
graphics.remove(graphic);
|
||||
@ -284,7 +295,9 @@ public class GraphicOverlay extends View {
|
||||
needUpdateTransformation = false;
|
||||
}
|
||||
|
||||
/** Draws the overlay with its associated graphic objects. */
|
||||
/**
|
||||
* Draws the overlay with its associated graphic objects.
|
||||
*/
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
@ -298,5 +311,21 @@ public class GraphicOverlay extends View {
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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<Language> = LanguagesConstants.getInstance().getList(
|
||||
this
|
||||
)
|
||||
val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(this)
|
||||
if (languages.isNotEmpty()) {
|
||||
val adapter =
|
||||
LanguageAdapter(
|
||||
this, languages
|
||||
) { _, language ->
|
||||
|
||||
Log.d("fsdafsdfd", language.language)
|
||||
val adapter = LanguageAdapter(this, languages) { _, language ->
|
||||
Log.d("LanguageChange", language.language)
|
||||
if (lastTranslateLanguage) {
|
||||
MyApp.setTargetLanguage(language)
|
||||
PreferenceLanguageUtils.putString("language_target", language.language)
|
||||
onBackPressed()
|
||||
} else {
|
||||
MyApp.setSourceLanguage(language)
|
||||
PreferenceLanguageUtils.putString("language_source", language.language)
|
||||
}
|
||||
binding.tvChangeSource.text = MyApp.getSourceLanguage()
|
||||
binding.tvChangeTarget.text = MyApp.getTargetLanguage()
|
||||
PreferenceLanguageUtils.addRecentLanguage(language)
|
||||
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
||||
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
||||
updateRecentLanguages()
|
||||
}
|
||||
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding.listLanguages.setLayoutManager(layoutManager)
|
||||
binding.listLanguages.setAdapter(adapter)
|
||||
binding.listLanguages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
binding.listLanguages.adapter = adapter
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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<ProcessCameraProvider>
|
||||
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<View>(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<ImageView>(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<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
|
||||
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
|
||||
findViewById<ImageView>(R.id.iv_still_back).setOnClickListener { onBackPressed() }
|
||||
findViewById<ImageView>(R.id.iv_still_buling).setOnClickListener {
|
||||
toggleFlash()
|
||||
updateFlashButtonUI()
|
||||
}
|
||||
findViewById<TextView>(R.id.still_source_language).setOnClickListener { }
|
||||
findViewById<TextView>(R.id.still_target_language).setOnClickListener { }
|
||||
findViewById<TextView>(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<ImageView>(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_bulibuli)
|
||||
} else {
|
||||
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_notbuli)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun startCamera() {
|
||||
val previewView = findViewById<PreviewView>(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<Spinner>(R.id.size_selector)
|
||||
val options: MutableList<String> = 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"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<String, String>().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,16 +156,28 @@ 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() {
|
||||
|
||||
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_bulibuli.webp
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_bulibuli.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_notbuli.webp
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_notbuli.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_pic.webp
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_pic.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_take.webp
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_still_take.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="20dp"/>
|
||||
<solid android:color="#FF2F2F2F"/>
|
||||
<corners android:radius="20dp" />
|
||||
<solid android:color="#FF2F2F2F" />
|
||||
</shape>
|
||||
19
app/src/main/res/drawable/ic_back_white.xml
Normal file
19
app/src/main/res/drawable/ic_back_white.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.89941 12H20.8994"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"/>
|
||||
<path
|
||||
android:pathData="M8.8994 18L2.89941 12L8.8994 6"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeWidth="2"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_down_choose_white.xml
Normal file
10
app/src/main/res/drawable/ic_down_choose_white.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M9.99991 10.9763L14.1247 6.85156L15.3032 8.03007L9.99991 13.3334L4.69666 8.03007L5.87516 6.85156L9.99991 10.9763Z" />
|
||||
</vector>
|
||||
@ -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" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/tv_exchange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
@ -62,7 +64,7 @@
|
||||
android:gravity="center"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/text_target_language"
|
||||
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
@ -103,6 +105,7 @@
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_lan_common5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
|
||||
@ -83,6 +83,7 @@
|
||||
app:drawableEndCompat="@drawable/ic_down_choose" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_main_exChange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
|
||||
@ -6,12 +6,22 @@
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true">
|
||||
|
||||
<androidx.camera.view.PreviewView
|
||||
android:id="@+id/photo_preview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/control"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/control"
|
||||
app:layout_constraintBaseline_toTopOf="@id/control"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@ -26,20 +36,48 @@
|
||||
app:layout_constraintTop_toTopOf="@id/preview" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@id/control"
|
||||
android:id="@+id/control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#000"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/select_image_button"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_still_pic"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:layout_weight="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:src="@drawable/ic_still_pic" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_still_take"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="12dp"
|
||||
android:text="@string/select_image" />
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/ic_still_take" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_still_buling"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:layout_weight="1"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:src="@drawable/ic_still_notbuli" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/control2"
|
||||
@ -49,28 +87,85 @@
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/size_selector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/feature_selector"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1" />
|
||||
android:layout_gravity="center" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/settings_button"
|
||||
layout="@layout/settings_style"
|
||||
<LinearLayout
|
||||
android:id="@+id/change_language"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="23dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:elevation="2dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/control"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/still_source_language"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_black_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/text_source_language"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/still_exChange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintRight_toRightOf="@id/root"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/button_r20_black_bg"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:src="@drawable/ic_exchage" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/still_target_language"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_black_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/text_target_language"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_still_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_back_white"
|
||||
app:layout_constraintStart_toStartOf="@id/root"
|
||||
app:layout_constraintTop_toTopOf="@id/root" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@ -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" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/tr_voice" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/preview"
|
||||
android:id="@+id/sus_preview"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:adjustViewBounds="true" />
|
||||
|
||||
@ -71,8 +71,8 @@
|
||||
},
|
||||
{
|
||||
"language": "Chinese, Simplified",
|
||||
"languageCode": "zh",
|
||||
"speechCode": "zh"
|
||||
"languageCode": "zh_CN",
|
||||
"speechCode": "zh_CN"
|
||||
},
|
||||
{
|
||||
"language": "Chinese, Traditional",
|
||||
|
||||
@ -18,8 +18,10 @@
|
||||
<color name="bg_ff2f2f2f">#FF2F2F2F</color>
|
||||
<!-- change_language-->
|
||||
<color name="bg_ff605c62">#FF605C62</color>
|
||||
<!-- his_page-->
|
||||
<!-- his_page-->
|
||||
<color name="text_ffa5a5a5">#FFA5A5A5</color>
|
||||
<!-- still-page-->
|
||||
<color name="bg_53514c">#FF53514C</color>
|
||||
|
||||
|
||||
</resources>
|
||||
@ -25,6 +25,7 @@
|
||||
<string name="tr_add_new">New Translate</string>
|
||||
<string name="tr_common">Common</string>
|
||||
<string name="tr_other">other</string>
|
||||
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
|
||||
<!--settings_page-->
|
||||
<string name="settings">Settings</string>
|
||||
<string name="languages">Languages</string>
|
||||
|
||||
@ -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" }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user