update history+photo trans

This commit is contained in:
Simon 2024-07-19 18:39:46 +08:00
parent 67c17356d9
commit c4c3998f65
25 changed files with 1102 additions and 917 deletions

View File

@ -4,6 +4,14 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-07-19T10:26:54.102874Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=bd95a93d" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>

View File

@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -18,6 +17,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application
android:name=".MyApp"
android:allowBackup="true"
@ -27,6 +27,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:largeHeap="true"
android:theme="@style/Theme.Alltrans"
tools:targetApi="31">
<service

View File

@ -9,18 +9,16 @@ import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.ScreenUtils
class MyApp : Application() {
init {
instance = this
}
private fun setSystemLanguage() {
// 检查是否是第一次进入应用
if (PreferenceLanguageUtils.isFirstTime()) {
// 第一次进入应用的逻辑
@ -52,6 +50,7 @@ class MyApp : Application() {
override fun onCreate() {
super.onCreate()
instance = this
// ScreenUtils.init(this)
setSystemLanguage()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {

View File

@ -5,6 +5,8 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import androidx.annotation.NonNull;
@ -17,24 +19,21 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import java.util.ArrayList;
import java.util.List;
public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> {
public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> implements Filterable {
private final Activity mActivity;
private final ArrayList<Language> languages;
private final ArrayList<Language> filteredLanguages;
private final OnClickListener listener;
private final RequestOptions options;
private boolean sorting = false;
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
this.mActivity = activity;
this.languages = new ArrayList<Language>();
this.languages = new ArrayList<>(languageList);
this.filteredLanguages = new ArrayList<>(languageList);
this.listener = onClickListener;
if (!languageList.isEmpty()) {
languages.addAll(languageList);
}
options = new RequestOptions()
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
@ -49,9 +48,9 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
@Override
public void onBindViewHolder(@NonNull LanguageHolder holder, int position) {
final Language currentLanguage = languages.get(position);
final Language currentLanguage = filteredLanguages.get(position);
if (null != currentLanguage) {
if (currentLanguage != null) {
drawImage(currentLanguage.mFlagUrl, holder.mBinding.languageFlag);
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
holder.mBinding.language.setText(currentLanguage.getLanguage());
@ -60,19 +59,16 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
}
}
holder.mBinding.getRoot().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final int position = holder.getAdapterPosition();
if (null != listener)
listener.click(position, currentLanguage);
}
holder.mBinding.getRoot().setOnClickListener(view -> {
final int pos = holder.getAdapterPosition();
if (listener != null)
listener.click(pos, currentLanguage);
});
}
@Override
public int getItemCount() {
return languages.size();
return filteredLanguages.size();
}
private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
@ -90,6 +86,37 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
}
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<Language> filteredList = new ArrayList<>();
if (TextUtils.isEmpty(constraint)) {
filteredList.addAll(languages);
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for (Language language : languages) {
if (language.getLanguage().toLowerCase().contains(filterPattern)) {
filteredList.add(language);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
@Override
@SuppressWarnings("unchecked")
protected void publishResults(CharSequence constraint, FilterResults results) {
filteredLanguages.clear();
filteredLanguages.addAll((List<Language>) results.values);
notifyDataSetChanged();
}
};
}
public static class LanguageHolder extends RecyclerView.ViewHolder {
public LanguageItemLayoutBinding mBinding;

View File

@ -1,34 +1,47 @@
package com.assimilate.alltrans.allservice
import android.app.*
import android.app.Activity
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.Image
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.*
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import androidx.core.app.NotificationCompat
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
class SusService : Service() {
private var imageProcessor: VisionImageProcessor? = null
private lateinit var mediaProjectionManager: MediaProjectionManager
private lateinit var mediaProjection: MediaProjection
private lateinit var virtualDisplay: VirtualDisplay
private var mediaProjection: MediaProjection? = null
private var virtualDisplay: VirtualDisplay? = null
private lateinit var displayMetrics: DisplayMetrics
private lateinit var imageReader: ImageReader
private lateinit var windowManager: WindowManager
private lateinit var floatingView: View
@ -37,34 +50,79 @@ class SusService : Service() {
private lateinit var bindingSusControl: SusControlViewBinding
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
private var mResultCode = 0
private var mResultData: Intent? = null
private val mpResultCode = "mpResultCode"
private val mpData = "mpData"
private val ball_time = 5000L
private val handler = Handler(Looper.getMainLooper())
private val hideRunnable = Runnable {
// floatingView.visibility = View.GONE
// val layoutParams = floatingView.layoutParams as WindowManager.LayoutParams
// val iconLayoutParams = WindowManager.LayoutParams(
// 100, 100, // Set the size of the icon as needed
// layoutParams.type,
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
// PixelFormat.TRANSLUCENT
// )
// iconLayoutParams.gravity = layoutParams.gravity
// iconLayoutParams.x = layoutParams.x
// iconLayoutParams.y = layoutParams.y
//
// val iconView = ImageView(this).apply {
// setImageResource(R.drawable.ic_like_yes) // Set your icon drawable here
// setOnClickListener {
// floatingView.visibility = View.VISIBLE
// windowManager.removeView(this)
// windowManager.addView(floatingView, layoutParams)
// startHideTimer()
// }
// }
// windowManager.addView(iconView, iconLayoutParams)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
displayMetrics = DisplayMetrics()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay?.getMetrics(displayMetrics)
initSusView()
startForeground(1, createNotification())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val resultCode = intent?.getIntExtra("resultCode", Activity.RESULT_OK)
val data: Intent? = intent?.getParcelableExtra("data")
if (resultCode != null && data != null) {
startScreenshot(resultCode, data)
if (intent == null) {
return START_NOT_STICKY
}
val resultCode = intent.getIntExtra(mpResultCode, Activity.RESULT_CANCELED)
val data = intent.getParcelableExtra<Intent>(mpData)
if (resultCode == Activity.RESULT_OK && data != null) {
mResultCode = resultCode
mResultData = data
startProjection()
} else {
Log.e("SusService", "Intent or data is null")
}
return START_NOT_STICKY
}
private fun initSusView() {
override fun onCreate() {
super.onCreate()
displayMetrics = DisplayMetrics()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
initControlView()
imageProcessor = TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
startForeground(1, createNotification())
}
private fun initControlView() {
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this))
floatingView = bindingSusControl.root
@ -85,31 +143,7 @@ class SusService : Service() {
windowManager.addView(floatingView, layoutParams)
// 设置点击事件
bindingSusControl.tvSusGlobal.setOnClickListener {
// 处理全局翻译点击事件
addGlobalView()
}
bindingSusControl.tvSusCopy.setOnClickListener {
// 处理复制文本点击事件
}
bindingSusControl.tvSusPhoto.setOnClickListener {
// 处理照片翻译点击事件
}
bindingSusControl.tvSusDistrict.setOnClickListener {
// 处理地区翻译点击事件
}
bindingSusControl.ivSusHome.setOnClickListener {
// 处理返回主页点击事件
}
bindingSusControl.ivSusMove.setOnClickListener {
// 处理移动窗口点击事件
}
initControlClick()
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener {
private var startX = 0f
@ -122,9 +156,9 @@ class SusService : Service() {
MotionEvent.ACTION_DOWN -> {
startX = layoutParams.x.toFloat()
startY = layoutParams.y.toFloat()
touchX = event.rawX
touchY = event.rawY
handler.removeCallbacks(hideRunnable)
return true
}
@ -135,26 +169,78 @@ class SusService : Service() {
return true
}
MotionEvent.ACTION_UP -> return true
MotionEvent.ACTION_UP -> {
val screenWidth = resources.displayMetrics.widthPixels
if (layoutParams.x + floatingView.width / 2 <= screenWidth / 2) {
layoutParams.x = 0
} else {
layoutParams.x = screenWidth - floatingView.width
}
v.performClick() // 手动触发点击事件
windowManager.updateViewLayout(floatingView, layoutParams)
startHideTimer()
return true
}
}
return false
}
})
startHideTimer() // Start the timer when the view is initialized
}
private fun startHideTimer() {
handler.removeCallbacks(hideRunnable)
handler.postDelayed(hideRunnable, ball_time)
}
private fun initControlClick() {
bindingSusControl.ivSusMove.setOnClickListener {
bindingSusControl.ivSusMove.setImageResource(R.drawable.ic_little_ball)
bindingSusControl.susLl1.visibility = View.GONE
bindingSusControl.susLl2.visibility = View.GONE
bindingSusControl.ivSusHome.visibility = View.GONE
}
bindingSusControl.tvSusGlobal.setOnClickListener {
bindingSusControl.susControlRoot.visibility = View.INVISIBLE
Handler(Looper.getMainLooper()).postDelayed({
addGlobalView()
captureScreenshot()
}, 555)
}
bindingSusControl.tvSusCopy.setOnClickListener {
Log.d("SusService", "Copy text clicked")
}
bindingSusControl.tvSusPhoto.setOnClickListener {
Log.d("SusService", "Photo translation clicked")
}
bindingSusControl.tvSusDistrict.setOnClickListener {
Log.d("SusService", "District translation clicked")
}
bindingSusControl.ivSusHome.setOnClickListener {
Log.d("SusService", "Home clicked")
}
}
private fun addGlobalView() {
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
globalView = bindingSubGlobal.root
imageProcessor = TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
@ -165,22 +251,113 @@ class SusService : Service() {
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = 0
layoutParams.y = 100
layoutParams.y = 0
windowManager.addView(globalView, layoutParams)
initGlobalClick()
}
override fun onDestroy() {
super.onDestroy()
if (floatingView.isAttachedToWindow) {
windowManager.removeView(floatingView)
}
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
private fun initGlobalClick() {
bindingSubGlobal.susGlobalClose.setOnClickListener {
windowManager.removeView(globalView)
bindingSusControl.susControlRoot.visibility = View.VISIBLE
}
}
private fun startProjection() {
if (mResultData == null) {
Log.e("SusService", "mResultData is null, cannot start projection")
return
}
if (mediaProjection == null) {
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
mediaProjection =
mediaProjectionManager.getMediaProjection(mResultCode, mResultData!!)
mediaProjection?.registerCallback(object : MediaProjection.Callback() {}, null)
Handler(Looper.getMainLooper()).postDelayed({
try {
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val density = displayMetrics.densityDpi
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)
virtualDisplay = mediaProjection?.createVirtualDisplay(
"ScreenCapture",
width,
height,
density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.surface,
null,
null
)
} catch (e: Exception) {
Log.e("SusService", "Error starting projection", e)
}
}, 1234)
}
}
private fun stopProjection() {
// Release the virtual display
try {
virtualDisplay?.release()
virtualDisplay = null
} catch (e: Exception) {
Log.e("SusService", "Error releasing virtual display", e)
}
// Stop the media projection
try {
mediaProjection?.stop()
mediaProjection = null
} catch (e: Exception) {
Log.e("SusService", "Error stopping media projection", e)
}
}
private fun captureScreenshot() {
val image: Image? = imageReader.acquireLatestImage()
image?.let {
val bitmap = imageToBitmap(it)
image.close()
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
tryReloadAndDetectInImage(bitmap)
}
}
private fun imageToBitmap(image: Image): Bitmap {
val planes = image.planes
val buffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * image.width
val bitmap = Bitmap.createBitmap(
image.width + rowPadding / pixelStride,
image.height,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
return Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
}
private fun createNotification(): Notification {
Log.d("SusService", "Creating notification")
val notificationChannelId = "FOREGROUND_SERVICE_CHANNEL"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -206,7 +383,6 @@ class SusService : Service() {
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
// Clear the overlay first
bindingSubGlobal.susGraphicOverlay.clear()
if (imageProcessor != null) {
@ -223,53 +399,20 @@ class SusService : Service() {
)
}
} catch (e: IOException) {
Log.e("SusService", "Error retrieving saved image")
Log.e("SusService", "Error retrieving saved image", e)
}
}
fun startScreenshot(resultCode: Int, data: Intent?) {
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
val imageReader = ImageReader.newInstance(
displayMetrics.widthPixels,
displayMetrics.heightPixels,
PixelFormat.RGBA_8888,
2
)
virtualDisplay = mediaProjection.createVirtualDisplay(
"Screenshot",
displayMetrics.widthPixels,
displayMetrics.heightPixels,
displayMetrics.densityDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.surface,
null,
null
)
// 初始化 globalView
addGlobalView()
Handler(Looper.getMainLooper()).postDelayed({
val image = imageReader.acquireLatestImage()
if (image != null) {
val planes = image.planes
val buffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * displayMetrics.widthPixels
val bitmap = Bitmap.createBitmap(
displayMetrics.widthPixels + rowPadding / pixelStride,
displayMetrics.heightPixels,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
image.close()
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
tryReloadAndDetectInImage(bitmap)
}
stopSelf()
}, 1000)
override fun onDestroy() {
super.onDestroy()
Log.d("SusService", "Service onDestroy")
if (floatingView.isAttachedToWindow) {
windowManager.removeView(floatingView)
}
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
windowManager.removeView(globalView)
}
stopProjection()
}
}

View File

@ -62,6 +62,11 @@ public class LanguagesConstants {
// 根据语言代码获取 Language 对象
public Language getLanguageByLanguageCode(@NonNull String languageCode, @NonNull Context context) {
ArrayList<Language> languages = getList(context);
if (languageCode.equals("zh")) {
languageCode = "zh_CN";
}
for (Language lang : languages) {
if (lang.getLanguageCode().equalsIgnoreCase(languageCode)) {
return lang;

View File

@ -4,8 +4,11 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.util.Log
import com.assimilate.alltrans.MyApp
@ -13,7 +16,7 @@ import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.google.mlkit.vision.text.Text
import java.util.Arrays
import java.util.concurrent.Executors
import kotlin.math.max
import kotlin.math.min
@ -27,8 +30,10 @@ class TextGraphic(
private val rectPaint: Paint = Paint()
private val textPaint: TextPaint
private val labelPaint: Paint
private val handler = Handler(Looper.getMainLooper())
private val executor = Executors.newSingleThreadExecutor()
init {
prepareTranslation()
@ -37,6 +42,9 @@ class TextGraphic(
rectPaint.strokeWidth = STROKE_WIDTH
textPaint = TextPaint()
textPaint.color = TEXT_COLOR
textPaint.textSize = TEXT_SIZE
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
textPaint.isFakeBoldText = true
labelPaint = Paint()
labelPaint.color = MARKER_COLOR
labelPaint.style = Paint.Style.FILL
@ -48,7 +56,10 @@ class TextGraphic(
// Method to prepare translation before drawing
private fun prepareTranslation() {
Thread {
executor.execute {
val textToTranslate = StringBuilder()
// Collect all text to be translated and append delimiter
@ -64,13 +75,22 @@ class TextGraphic(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
if (lanTargetCode == null || lanSourceCode == null || textToTranslate.toString()
.isEmpty()
) {
return@execute
}
// Define translation parameters
val param = HashMap<String, String>().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", textToTranslate.toString())
}
Log.d("fdasfas_sou", lanSourceCode)
Log.d("fdasfas_tar", lanTargetCode)
Log.d("fdasfas_tex", textToTranslate.toString())
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
GoogleTranslator()
@ -78,27 +98,23 @@ class TextGraphic(
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
// Split translated text by delimiter
translatedTextBlocks =
translatedText.split(DELIMITER.toRegex()).dropLastWhile { it.isEmpty() }
translatedText.split(DELIMITER.toRegex()).filter { it.isNotEmpty() }
// Update UI thread
handler.post {
postInvalidate() // Notify to redraw
postInvalidate()
}
})
}.start()
}
}
override fun draw(canvas: Canvas) {
Log.d(TAG, "Text is: " + text.text)
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
val translatedBlockText =
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text
val height1 = ((textBlock.boundingBox?.bottom?.toFloat()
?: 30f) - (textBlock.boundingBox?.top?.toFloat()
?: 0f)) / textBlock.lines.size
if (shouldGroupTextInBlocks) {
drawText(
getFormattedText(
@ -107,53 +123,16 @@ class TextGraphic(
confidence = null
),
RectF(textBlock.boundingBox),
height1 - 3 * STROKE_WIDTH,
canvas
)
} else {
for (line in textBlock.lines) {
Log.d(TAG, "Line text is: " + line.text)
Log.d(TAG, "Line boundingbox is: " + line.boundingBox)
Log.d(TAG, "Line cornerpoint is: " + Arrays.toString(line.cornerPoints))
Log.d(TAG, "Line confidence is: " + line.confidence)
Log.d(TAG, "Line angle is: " + line.angle)
// Draw the bounding box around the TextBlock.
val rect = RectF(line.boundingBox)
drawText(
getFormattedText(
translatedBlockText,
line.recognizedLanguage,
line.confidence
),
rect,
((line.boundingBox?.bottom?.toFloat()
?: 20f) - (line.boundingBox?.top?.toFloat()
?: 0f)) - 2 * STROKE_WIDTH,
canvas
Log.d(
"fdgfsdfsdfas", getFormattedText(
translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
)
for (element in line.elements) {
Log.d(TAG, "Element text is: " + element.text)
Log.d(TAG, "Element boundingbox is: " + element.boundingBox)
Log.d(
TAG,
"Element cornerpoint is: " + Arrays.toString(element.cornerPoints)
)
Log.d(TAG, "Element language is: " + element.recognizedLanguage)
Log.d(TAG, "Element confidence is: " + element.confidence)
Log.d(TAG, "Element angle is: " + element.angle)
for (symbol in element.symbols) {
Log.d(TAG, "Symbol text is: " + symbol.text)
Log.d(TAG, "Symbol boundingbox is: " + symbol.boundingBox)
Log.d(
TAG,
"Symbol cornerpoint is: " + Arrays.toString(symbol.cornerPoints)
)
Log.d(TAG, "Symbol confidence is: " + symbol.confidence)
Log.d(TAG, "Symbol angle is: " + symbol.angle)
}
}
}
}
)
} // 不需要单行(删除)
}
}
@ -168,78 +147,60 @@ class TextGraphic(
else res
}
private fun drawText(text: String, rect: RectF, textSize: Float, canvas: Canvas) {
private fun drawText(text: String, rect: RectF, canvas: Canvas) {
// 如果图像是翻转的,将左边翻译到右边,右边翻译到左边。
val x0 = translateX(rect.left)
val x1 = translateX(rect.right)
rect.left = min(x0, x1)
rect.right = max(x0, x1)
rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom)
canvas.drawRect(rect, labelPaint)
// Set initial text size
textPaint.textSize = textSize
// 设置文本大小以适应矩形
val textPaintCopy = TextPaint(textPaint)
val availableWidth = rect.width().toInt()
val availableHeight = rect.height().toInt()
var textSize = textPaintCopy.textSize
// Break the text into multiple lines if necessary
var lines = wrapText(text.trim(), rect.width())
// 调整文本大小以适应矩形
var textLayout: StaticLayout
while (textSize > 0) {
textPaintCopy.textSize = textSize
textLayout =
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
.build()
// Calculate total height of the text
val totalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
val totalTextHeightWithSpacing = totalTextHeight * lines.size
// Adjust the text size if the total height is greater than the rectangle height
if (totalTextHeightWithSpacing > rect.height()) {
textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
lines = wrapText(text.trim(), rect.width())
} else if (totalTextHeightWithSpacing < rect.height()) {
// If the total text height is less than the rectangle height, increase text size
textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
lines = wrapText(text.trim(), rect.width())
if (textLayout.height <= availableHeight) {
break
}
textSize -= 1
}
// Calculate new total height with adjusted text size
val finalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
val finalTotalTextHeightWithSpacing = finalTextHeight * lines.size
// 使用 StaticLayout 绘制文本
textLayout =
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
.build()
// Calculate starting Y coordinate to center the text vertically
var textY =
rect.top + ((rect.height() - finalTotalTextHeightWithSpacing) / 2) - textPaint.fontMetrics.ascent
// Draw the background rectangle
canvas.drawRect(
rect.left - STROKE_WIDTH,
rect.top - STROKE_WIDTH,
rect.right + STROKE_WIDTH,
rect.bottom + STROKE_WIDTH,
labelPaint
)
// Draw each line of text
for (line in lines) {
canvas.drawText(line, rect.left, textY, textPaint)
textY += finalTextHeight
}
}
private fun wrapText(text: String, maxWidth: Float): List<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
canvas.save()
canvas.translate(rect.left, rect.top)
textLayout.draw(canvas)
canvas.restore()
}
companion object {
private const val DELIMITER = "`0_.._0`"
private const val DELIMITER = "0`~`0"
private const val TAG = "TextGraphic"
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
private val TEXT_COLOR = Color.parseColor("#FF474747")
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
private const val STROKE_WIDTH = 2.0f
private const val TEXT_SIZE = 44.0f
}
}

View File

@ -1,13 +1,9 @@
package com.assimilate.alltrans.common
import android.content.Context
import android.text.TextUtils
import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.google.android.gms.tasks.Task
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.Text
@ -17,99 +13,134 @@ import com.google.mlkit.vision.text.TextRecognizerOptionsInterface
/** Processor for the text detector demo. */
class TextRecognitionProcessor(
private val context: Context,
textRecognizerOptions: TextRecognizerOptionsInterface
private val context: Context,
textRecognizerOptions: TextRecognizerOptionsInterface
) : VisionProcessorBase<Text>(context) {
private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
private val shouldGroupRecognizedTextInBlocks: Boolean =
PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
private val showLanguageTag: Boolean = PreferenceUtils.showLanguageTag(context)
private val showConfidence: Boolean = PreferenceUtils.shouldShowTextConfidence(context)
private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
private val shouldGroupRecognizedTextInBlocks: Boolean =
PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
private val showLanguageTag: Boolean = PreferenceUtils.showLanguageTag(context)
private val showConfidence: Boolean = PreferenceUtils.shouldShowTextConfidence(context)
override fun stop() {
super.stop()
textRecognizer.close()
}
override fun detectInImage(image: InputImage): Task<Text> {
return textRecognizer.process(image)
}
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
Log.d(TAG, "On-device Text detection successful")
logExtrasForTesting(text)
graphicOverlay.add(
TextGraphic(
graphicOverlay,
text,
shouldGroupRecognizedTextInBlocks,
showLanguageTag,
showConfidence
)
)
}
override fun onFailure(e: Exception) {
Log.w(TAG, "Text detection failed.$e")
}
companion object {
private const val TAG = "TextRecProcessor"
private fun logExtrasForTesting(text: Text?) {
if (text != null) {
Log.v(MANUAL_TESTING_LOG, "text context is : " + text.text)
Log.v(MANUAL_TESTING_LOG, "Detected text has : " + text.textBlocks.size + " blocks")
for (i in text.textBlocks.indices) {
val lines = text.textBlocks[i].lines
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text block %d has %d lines", i, lines.size)
)
for (j in lines.indices) {
val elements = lines[j].elements
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text line %d has %d elements", j, elements.size)
)
for (k in elements.indices) {
val element = elements[k]
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text element %d says: %s", k, element.text)
)
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Detected text element %d has a bounding box: %s",
k,
element.boundingBox!!.flattenToString()
)
)
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Expected corner point size is 4, get %d",
element.cornerPoints!!.size
)
)
for (point in element.cornerPoints!!) {
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Corner point for element %d is located at: x - %d, y = %d",
k,
point.x,
point.y
)
)
}
}
}
}
}
override fun stop() {
super.stop()
textRecognizer.close()
}
override fun detectInImage(image: InputImage): Task<Text> {
return textRecognizer.process(image)
}
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
PreferenceLanguageUtils.putString("language_source", getMostFrequentLanguage(text))
Log.d(TAG, "On-device Text detection successful")
logExtrasForTesting(text)
graphicOverlay.add(
TextGraphic(
graphicOverlay,
text,
shouldGroupRecognizedTextInBlocks,
showLanguageTag,
showConfidence
)
)
}
override fun onFailure(e: Exception) {
Log.w(TAG, "Text detection failed.$e")
}
// 推测最可能的语言
private fun getMostFrequentLanguage(text: Text): String {
val languageCount = mutableMapOf<String, Int>()
for (textBlock in text.textBlocks) {
for (line in textBlock.lines) {
for (element in line.elements) {
val language = element.recognizedLanguage
if (language != "und-Latn") {
if (languageCount.containsKey(language)) {
languageCount[language] = languageCount[language]!! + 1
} else {
languageCount[language] = 1
}
}
}
}
}
val maxCode = languageCount.maxByOrNull { it.value }?.key ?: "zh_CN"
val lanByCode = LanguagesConstants.getInstance()
.getLanguageByLanguageCode(maxCode, MyApp.applicationContext())
return if (lanByCode != null) {
lanByCode.language
} else {
"English"
}
}
companion object {
private const val TAG = "TextRecProcessor"
private fun logExtrasForTesting(text: Text?) {
if (text != null) {
Log.v(MANUAL_TESTING_LOG, "text context is : " + text.text)
Log.v(MANUAL_TESTING_LOG, "Detected text has : " + text.textBlocks.size + " blocks")
for (i in text.textBlocks.indices) {
val lines = text.textBlocks[i].lines
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text block %d has %d lines", i, lines.size)
)
for (j in lines.indices) {
val elements = lines[j].elements
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text line %d has %d elements", j, elements.size)
)
for (k in elements.indices) {
val element = elements[k]
Log.v("推测是什么语言", element.recognizedLanguage)
Log.v(
MANUAL_TESTING_LOG,
String.format("Detected text element %d says: %s", k, element.text)
)
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Detected text element %d has a bounding box: %s",
k,
element.boundingBox!!.flattenToString()
)
)
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Expected corner point size is 4, get %d",
element.cornerPoints!!.size
)
)
for (point in element.cornerPoints!!) {
Log.v(
MANUAL_TESTING_LOG,
String.format(
"Corner point for element %d is located at: x - %d, y = %d",
k,
point.x,
point.y
)
)
}
}
}
}
}
}
}
}
}

View File

@ -14,7 +14,7 @@ class SusView : View.OnClickListener {
override fun onClick(v: View?) {
TODO("Not yet implemented")
// TODO("Not yet implemented")
}

View File

@ -1,172 +1,166 @@
package com.assimilate.alltrans.viewui;
package com.assimilate.alltrans.viewui
import android.content.Context;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.View;
import android.content.Context
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.view.View
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.TranslationAdapter
import com.assimilate.alltrans.adapters.TranslationAdapter.TranslationItemCallback
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityHistoryBinding
import com.assimilate.alltrans.mydb.DbTranslation
import com.assimilate.alltrans.mydb.Translations
import java.util.Collections
import java.util.Locale
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
class HistoryActivity : AppCompatActivity() {
private var tts: TextToSpeech? = null
private var binding: ActivityHistoryBinding? = null
private var ids: HashSet<Long>? = null // 通过id 删除数据库文件
private var items: HashSet<Int>? = null // 通许index 删除界面上面的数据
private var operationCollection = false
import com.assimilate.alltrans.R;
import com.assimilate.alltrans.adapters.TranslationAdapter;
import com.assimilate.alltrans.common.Widget;
import com.assimilate.alltrans.databinding.ActivityHistoryBinding;
import com.assimilate.alltrans.mydb.DbTranslation;
import com.assimilate.alltrans.mydb.Translations;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityHistoryBinding.inflate(layoutInflater)
setContentView(binding!!.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(26, systemBars.top, 26, systemBars.bottom)
insets
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Locale;
ids = HashSet()
items = HashSet()
public class HistoryActivity extends AppCompatActivity {
public final static String COMMAND = "remove";
public final static String COMMAND_COLLECTION = "remove-collection";
public final static String COMMAND_HISTORY = "remove-history";
private TextToSpeech tts;
private ActivityHistoryBinding binding;
private HashSet<Long> ids; // 通过id 删除数据库文件
private HashSet<Integer> items; // 通许index 删除界面上面的数据
private boolean operationCollection = false;
val extra = intent.getStringExtra(COMMAND)
operationCollection = COMMAND_COLLECTION == extra
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityHistoryBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(26, systemBars.top, 26, systemBars.bottom);
return insets;
});
tts = TextToSpeech(this) { status ->
if (null != tts && TextToSpeech.SUCCESS == status) tts!!.setLanguage(
Locale.getDefault()
)
}
ids = new HashSet<>();
items = new HashSet<>();
String extra = getIntent().getStringExtra(COMMAND);
operationCollection = COMMAND_COLLECTION.equals(extra);
tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (null != tts && TextToSpeech.SUCCESS == status)
tts.setLanguage(Locale.getDefault());
}
});
ArrayList<Translations> translations = new ArrayList<>();
if (operationCollection) {
val translations = ArrayList<Translations>()
if (!operationCollection) {
// 查出收藏的翻译记录
binding.tvFuncTrans.setText("Collect");
binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
ArrayList<Translations> list = new DbTranslation(this).getTranslations(true);
binding!!.tvFuncTrans.text = getString(R.string.favor_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(true)
if (null != list && !list.isEmpty()) {
translations.addAll(list);
translations.addAll(list)
}
} else {
// 查出所有的翻译记录
binding.tvFuncTrans.setText("History");
binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
ArrayList<Translations> list = new DbTranslation(this).getTranslations(false);
if (null != list && !list.isEmpty()) {
translations.addAll(list);
binding!!.tvFuncTrans.text = getString(R.string.his_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(false)
if (null != list && list.isNotEmpty()) {
translations.addAll(list)
}
}
final TranslationAdapter adapter = new TranslationAdapter(translations, new TranslationAdapter.TranslationItemCallback() {
@Override
public void updateList(TranslationAdapter.Operation operation, long id, int position) {
val adapter = TranslationAdapter(translations, object : TranslationItemCallback {
override fun updateList(
operation: TranslationAdapter.Operation,
id: Long,
position: Int
) {
if (TranslationAdapter.Operation.ADD == operation) {
add(id, position);
add(id, position)
} else if (TranslationAdapter.Operation.REMOVE == operation) {
remove(id, position);
remove(id, position)
}
updateBtn();
updateBtn()
}
@Override
public void speech(String value) {
override fun speech(value: String) {
if (null != tts
&& TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) {
tts.speak(value, 0, null, null);
&& TextToSpeech.LANG_NOT_SUPPORTED != tts!!.isLanguageAvailable(Locale.getDefault())
) {
tts!!.speak(value, 0, null, null)
}
}
private void add(long id, int index) {
ids.add(id);
items.add(index);
private fun add(id: Long, index: Int) {
ids!!.add(id)
items!!.add(index)
}
private void remove(long id, int index) {
ids.remove(id);
items.remove(index);
private fun remove(id: Long, index: Int) {
ids!!.remove(id)
items!!.remove(index)
}
private void updateBtn() {
if (ids.isEmpty()) {
binding.remove.setVisibility(View.INVISIBLE);
private fun updateBtn() {
if (ids!!.isEmpty()) {
binding!!.remove.visibility = View.INVISIBLE
} else {
binding.remove.setVisibility(View.VISIBLE);
binding!!.remove.visibility = View.VISIBLE
}
}
});
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
})
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.remove.setOnClickListener(new View.OnClickListener() {
private DbTranslation dbTranslation;
binding!!.remove.setOnClickListener(object : View.OnClickListener {
private var dbTranslation: DbTranslation? = null
@Override
public void onClick(View v) {
if (ids.isEmpty()) {
Widget.makeToast(HistoryActivity.this, "Noting to remove.");
return;
override fun onClick(v: View) {
if (ids!!.isEmpty()) {
Widget.makeToast(this@HistoryActivity, "Noting to remove.")
return
}
if (!ids.isEmpty()) {
ArrayList<Long> longArrayList = new ArrayList<>(ids);
if (!ids!!.isEmpty()) {
val longArrayList = ArrayList(ids)
if (operationCollection) {
getDbTranslation(HistoryActivity.this).removeCollectTranslations(longArrayList);
getDbTranslation(this@HistoryActivity).removeCollectTranslations(
longArrayList
)
} else {
getDbTranslation(HistoryActivity.this).removeTranslations(longArrayList);
getDbTranslation(this@HistoryActivity).removeTranslations(longArrayList)
}
ids.clear();
ids!!.clear()
}
if (!items.isEmpty()) {
ArrayList<Integer> integerArrayList = new ArrayList<>(items);
Collections.sort(integerArrayList, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
adapter.updateSet(integerArrayList);
items.clear();
if (!items!!.isEmpty()) {
val integerArrayList = ArrayList(items)
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
adapter.updateSet(integerArrayList)
items!!.clear()
}
binding.remove.setVisibility(View.VISIBLE);
binding!!.remove.visibility = View.VISIBLE
}
private DbTranslation getDbTranslation(Context context) {
private fun getDbTranslation(context: Context): DbTranslation {
if (null == dbTranslation) {
dbTranslation = new DbTranslation(context);
dbTranslation = DbTranslation(context)
}
return dbTranslation;
return dbTranslation!!
}
});
})
binding.histories.setLayoutManager(layoutManager);
binding.histories.setAdapter(adapter);
binding!!.histories.layoutManager = layoutManager
binding!!.histories.adapter = adapter
}
@Override
public void onBackPressed() {
super.onBackPressed();
override fun onBackPressed() {
super.onBackPressed()
}
public void clickBack(View view) {
onBackPressed();
fun clickBack(view: View?) {
onBackPressed()
}
companion object {
const val COMMAND: String = "remove"
const val COMMAND_COLLECTION: String = "remove_collection"
const val COMMAND_HISTORY: String = "remove_history"
}
}

View File

@ -31,10 +31,26 @@ class LanguageChangeActivity : AppCompatActivity() {
}
initView()
initSearch() // 添加这一行
initList()
initClick()
}
private fun initSearch() {
binding.chSearch.setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
(binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText)
return true
}
})
}
private fun initView() {
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
@ -57,32 +73,38 @@ class LanguageChangeActivity : AppCompatActivity() {
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
binding.listLanCommon5.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanCommon5.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanCommon5.adapter = recentAdapter
}
}
private fun initClick() {
binding.ivChBack.setOnClickListener { onBackPressed() }
binding.tvChangeSource.setOnClickListener {
lastTranslateLanguage = false
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff0e8ce8))
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff1f1724))
}
binding.tvChangeTarget.setOnClickListener {
lastTranslateLanguage = true
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff1f1724))
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff0e8ce8))
}
binding.tvExchange.setOnClickListener {
binding.tvExchange.setOnClickListener {
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding.tvChangeSource.text = currentTargetLanguage
binding.tvChangeTarget.text = currentSourceLanguage
onBackPressed()
}
binding.tvExchange.setOnClickListener {
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding.tvChangeSource.text = currentTargetLanguage
binding.tvChangeTarget.text = currentSourceLanguage
onBackPressed()
}
}
private fun initList() {
@ -101,8 +123,10 @@ class LanguageChangeActivity : AppCompatActivity() {
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
binding.listLanguages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanguages.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
binding.listLanguages.adapter = adapter
}
}
}

View File

@ -7,8 +7,10 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextUtils
@ -23,7 +25,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.allservice.SusService
import com.assimilate.alltrans.common.LanguagesConstants
@ -33,16 +34,16 @@ import com.assimilate.alltrans.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private var launcher: ActivityResultLauncher<Intent>? = null
private lateinit var binding: ActivityMainBinding
private lateinit var lcm: LocalBroadcastManager
private lateinit var mediaProjectionManager: MediaProjectionManager
private val REQUEST_CODE_SCREEN_CAPTURE = 1001
private val REQUEST_CODE = 1000
private val mpResultCode = "mpResultCode"
private val mpData = "mpData"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -59,7 +60,18 @@ class MainActivity : AppCompatActivity() {
initClick()
registerResult()
// 初始化
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
// 检查并请求悬浮窗权限
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, REQUEST_CODE)
}
}
private fun registerResult() {
@ -77,32 +89,25 @@ class MainActivity : AppCompatActivity() {
}
}
private fun startScreenCapture() {
val captureIntent = mediaProjectionManager.createScreenCaptureIntent()
startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == Activity.RESULT_OK) {
startSusService(resultCode, data)
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
val serviceIntent = Intent(this, SusService::class.java).apply {
putExtra(mpResultCode, resultCode)
putExtra(mpData, data)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
} else {
Log.e("MainActivity", "Screen capture permission denied")
}
}
}
private fun startSusService(resultCode: Int, data: Intent?) {
val serviceIntent = Intent(this, SusService::class.java).apply {
putExtra("resultCode", resultCode)
putExtra("data", data)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
}
private fun initView() {
binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
binding.chTargetLanguage.text = PreferenceLanguageUtils.getString("language_target")
@ -120,6 +125,14 @@ class MainActivity : AppCompatActivity() {
}
override fun afterTextChanged(s: Editable?) {
// 统计字符数并显示在 TextView 中
val charCount = s?.length ?: 0
// 截断逻辑
if (charCount > 1800) {
val truncatedText = s?.subSequence(0, 1800)
binding.etText.setText(truncatedText)
binding.etText.setSelection(1800) // 设置光标位置到文本末尾
}
// 根据EditText的内容显示或隐藏粘贴按钮
if (s.isNullOrEmpty()) {
binding.tvMainTrans.visibility = View.GONE
@ -127,6 +140,10 @@ class MainActivity : AppCompatActivity() {
} else {
binding.tvMainTrans.visibility = View.VISIBLE
}
// 更新字符计数显示
val displayCharCount = if (charCount > 1800) 1800 else charCount
binding.tvMainLimitText.text = getString(R.string.main_limit_num, displayCharCount)
}
})
@ -167,8 +184,10 @@ class MainActivity : AppCompatActivity() {
)
}
binding.ivMainHistory.setOnClickListener {
intent = Intent(this, HistoryActivity::class.java)
intent.putExtra("remove", "remove_collection")
startActivity(
Intent(this, HistoryActivity::class.java)
intent
)
}
binding.llQuickSet.setOnClickListener {
@ -177,17 +196,12 @@ class MainActivity : AppCompatActivity() {
)
}
binding.ivQuickStart.setOnClickListener {
// 启动截图
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
REQUEST_CODE
)
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
startScreenCapture()
val intent = Intent(this, SusService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
}
binding.ivMainExChange.setOnClickListener {
@ -203,9 +217,6 @@ class MainActivity : AppCompatActivity() {
binding.chSourceLanguage.text = currentTargetLanguage
binding.chTargetLanguage.text = currentSourceLanguage
// 打印日志,验证交换后的语言设置
Log.d("fdhash_su", PreferenceLanguageUtils.getString("language_source"))
Log.d("fdhash_ta", PreferenceLanguageUtils.getString("language_target"))
}
@ -216,7 +227,6 @@ class MainActivity : AppCompatActivity() {
return
}
val intent = Intent(this, TextResultActivity::class.java)
// 将字符串数据添加到Intent中
intent.putExtra("source_text", binding.etText.text.toString())
startActivity(intent)
binding.etText.text = null
@ -228,11 +238,11 @@ class MainActivity : AppCompatActivity() {
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
speechIntent.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
5000
4000
) // 设置5秒的静默时间
speechIntent.putExtra(
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
5000
4000
) // 设置5秒的可能完全静默时间
@ -254,7 +264,7 @@ class MainActivity : AppCompatActivity() {
try {
launcher?.launch(speechIntent)
} catch (ea: ActivityNotFoundException) {
Widget.makeToast(this, "Something went wrong.")
Widget.makeToast(this, getString(R.string.main_voice_to_text))
}
}

View File

@ -1,5 +1,6 @@
package com.assimilate.alltrans.viewui
import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
@ -44,5 +45,11 @@ class SettingsActivity
bottomSheetDialog.show()
}
binding.llFavorite.setOnClickListener {
val intent = Intent(this, HistoryActivity::class.java)
intent.putExtra("remove", "remove_history")
startActivity(intent)
}
}
}

View File

@ -9,19 +9,23 @@ import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.util.Pair
import android.view.MenuItem
import android.view.View
import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.SearchView
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
@ -31,14 +35,24 @@ import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.BitmapUtils
import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.curview.GraphicOverlay
import com.google.android.gms.common.annotation.KeepName
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.common.util.concurrent.ListenableFuture
import com.google.mlkit.common.model.LocalModel
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
@ -47,9 +61,12 @@ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
/** 演示使用相机拍摄静态图像进行不同图像检测功能的活动。 */
/** Activity demonstrating different image detector features with a still image from camera. */
@KeepName
class StillImageActivity : AppCompatActivity() {
private var preview: ImageView? = null
@ -67,56 +84,120 @@ class StillImageActivity : AppCompatActivity() {
private var isFlashOn = false
private val REQUEST_CAMERA_PERMISSION = 100
private lateinit var bottomSheetDialog: BottomSheetDialog
private var chooseLanguage :Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_still_image)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom)
insets
}
// 检查并请求相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED
Widget.makeSnackbar(this, "Photographing text for translation")
preview = findViewById(R.id.preview)
graphicOverlay = findViewById(R.id.graphic_overlay)
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
savedInstanceState?.let {
imageUri = it.getParcelable(KEY_IMAGE_URI)
imageMaxWidth = it.getInt(KEY_IMAGE_MAX_WIDTH)
imageMaxHeight = it.getInt(KEY_IMAGE_MAX_HEIGHT)
selectedSize = it.getString(KEY_SELECTED_SIZE)
}
val rootView = findViewById<View>(R.id.root)
rootView.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
imageMaxWidth = rootView.width
imageMaxHeight = rootView.height - findViewById<View>(R.id.control).height
if (SIZE_SCREEN == selectedSize) {
tryReloadAndDetectInImage()
}
}
})
outputDirectory = getOutputDirectory()
// 检查并启动相机
checkCameraPermission()
initView()
initClick()
}
private fun checkCameraPermission() {
if (ContextCompat.checkSelfPermission(
this, Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION
)
} else {
startCamera()
}
Widget.makeSnackbar(this, "Photographing text for translation")
preview = findViewById(R.id.preview)
graphicOverlay = findViewById(R.id.graphic_overlay)
populateFeatureSelector()
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
if (savedInstanceState != null) {
imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI)
imageMaxWidth = savedInstanceState.getInt(KEY_IMAGE_MAX_WIDTH)
imageMaxHeight = savedInstanceState.getInt(KEY_IMAGE_MAX_HEIGHT)
selectedSize = savedInstanceState.getString(KEY_SELECTED_SIZE)
}
val rootView = findViewById<View>(R.id.root)
rootView.viewTreeObserver.addOnGlobalLayoutListener(
object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
imageMaxWidth = rootView.width
imageMaxHeight = rootView.height - findViewById<View>(R.id.control).height
if (SIZE_SCREEN == selectedSize) {
tryReloadAndDetectInImage()
}
}
}
)
// 初始化相机
startCamera()
outputDirectory = getOutputDirectory()
initClick()
}
private fun initView() {
findViewById<TextView>(R.id.still_source_language).text =
PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text =
PreferenceLanguageUtils.getString("language_target")
bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan)
bottomSheetDialog.dismissWithAnimation = true
bottomSheetDialog.findViewById<androidx.appcompat.widget.SearchView>(R.id.ph_search)
?.setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
(bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.adapter as LanguageAdapter).filter.filter(
newText
)
return true
}
})
initList()
}
private fun initList() {
val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(this)
if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(this, languages) { _, language ->
if (!chooseLanguage) {
PreferenceLanguageUtils.putString("language_source", language.language)
bottomSheetDialog.dismiss()
} else {
PreferenceLanguageUtils.putString("language_target", language.language)
bottomSheetDialog.dismiss()
}
findViewById<TextView>(R.id.still_source_language).text =
PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text =
PreferenceLanguageUtils.getString("language_target")
}
bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.adapter = adapter
}
}
private fun initClick() {
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
@ -125,46 +206,40 @@ class StillImageActivity : AppCompatActivity() {
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 { }
findViewById<TextView>(R.id.still_source_language).setOnClickListener {
chooseLanguage = false
bottomSheetDialog.show()
}
findViewById<TextView>(R.id.still_target_language).setOnClickListener {
chooseLanguage = true
bottomSheetDialog.show()
}
findViewById<ImageView>(R.id.still_exChange).setOnClickListener {
}
}
private fun toggleFlash() {
if (isFlashOn) {
imageCapture.flashMode = ImageCapture.FLASH_MODE_OFF
} else {
imageCapture.flashMode = ImageCapture.FLASH_MODE_ON
}
imageCapture.flashMode =
if (isFlashOn) ImageCapture.FLASH_MODE_OFF else ImageCapture.FLASH_MODE_ON
isFlashOn = !isFlashOn
}
private fun updateFlashButtonUI() {
if (isFlashOn) {
findViewById<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)
}
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
}
private fun startCamera() {
val previewView = findViewById<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 preview = Preview.Builder().build().also {
it.setSurfaceProvider(previewView.surfaceProvider)
}
imageCapture = ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_OFF).build()
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
@ -177,14 +252,10 @@ class StillImageActivity : AppCompatActivity() {
}
private fun takePhoto() {
val imageCapture = imageCapture
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
@ -196,25 +267,20 @@ class StillImageActivity : AppCompatActivity() {
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
imageUri = savedUri
val msg = "Photo capture succeeded: $savedUri"
Log.d(TAG, msg)
imageUri = Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $imageUri")
tryReloadAndDetectInImage()
}
}
)
})
}
private fun getOutputDirectory(): File {
val mediaDir = externalMediaDirs.firstOrNull()?.let {
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
}
return if (mediaDir != null && mediaDir.exists())
mediaDir else filesDir
return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
@ -224,80 +290,43 @@ class StillImageActivity : AppCompatActivity() {
when (requestCode) {
REQUEST_CAMERA_PERMISSION -> {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
// Permission is granted, proceed with camera
startCameraIntentForResult()
startCamera()
} else {
// Permission denied, show a message to the user
Toast.makeText(
this,
"Camera permission is required to use the camera",
Toast.LENGTH_SHORT
).show()
}
return
}
}
}
public override fun onResume() {
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume")
createImageProcessor()
tryReloadAndDetectInImage()
}
public override fun onPause() {
override fun onPause() {
super.onPause()
imageProcessor?.run { this.stop() }
imageProcessor?.stop()
}
public override fun onDestroy() {
override fun onDestroy() {
super.onDestroy()
imageProcessor?.run { this.stop() }
// 释放相机资源
imageProcessor?.stop()
if (::cameraProviderFuture.isInitialized) {
cameraProviderFuture.get().unbindAll()
}
}
private fun populateFeatureSelector() {
val featureSpinner = findViewById<Spinner>(R.id.feature_selector)
val options: MutableList<String> = ArrayList()
options.add(TEXT_RECOGNITION_CHINESE); // 识别中文文本
options.add(TEXT_RECOGNITION_LATIN); // 识别拉丁文本
options.add(TEXT_RECOGNITION_DEVANAGARI); // 识别梵文文本
options.add(TEXT_RECOGNITION_JAPANESE); // 识别日文文本
options.add(TEXT_RECOGNITION_KOREAN); // 识别韩文文本
// Creating adapter for featureSpinner
val dataAdapter = ArrayAdapter(this, R.layout.spinner_style, options)
// Drop down layout style - list view with radio button
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// attaching data adapter to spinner
featureSpinner.adapter = dataAdapter
featureSpinner.onItemSelectedListener =
object : OnItemSelectedListener {
override fun onItemSelected(
parentView: AdapterView<*>,
selectedItemView: View?,
pos: Int,
id: Long
) {
if (pos >= 0) {
selectedMode = parentView.getItemAtPosition(pos).toString()
createImageProcessor()
tryReloadAndDetectInImage()
}
}
override fun onNothingSelected(arg0: AdapterView<*>?) {}
}
}
public override fun onSaveInstanceState(outState: Bundle) {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_IMAGE_URI, imageUri)
outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth)
@ -305,104 +334,71 @@ class StillImageActivity : AppCompatActivity() {
outState.putString(KEY_SELECTED_SIZE, selectedSize)
}
private fun startCameraIntentForResult() {
private fun createImageProcessor() {
imageProcessor?.stop()
try {
imageProcessor = when (selectedMode) {
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
// Ensure permission is still granted before starting camera intent
if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
) != PackageManager.PERMISSION_GRANTED
) {
"Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
this,
DevanagariTextRecognizerOptions.Builder().build()
)
TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
this,
JapaneseTextRecognizerOptions.Builder().build()
)
TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
this,
KoreanTextRecognizerOptions.Builder().build()
)
else -> TextRecognitionProcessor(
this,
TextRecognizerOptions.Builder().build()
)
}
} catch (e: Exception) {
Log.e(TAG, "Can not create image processor: $selectedMode", e)
Toast.makeText(
this,
"Camera permission is required to use the camera",
Toast.LENGTH_SHORT
applicationContext,
"Can not create image processor: " + e.message,
Toast.LENGTH_LONG
).show()
return
}
imageUri = null
preview!!.setImageBitmap(null)
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(packageManager) != null) {
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, "New Picture")
values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera")
imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
private fun startChooseImageIntentForResult() {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) {
tryReloadAndDetectInImage()
} else if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK) {
// In this case, imageUri is returned by the chooser, save it.
imageUri = data!!.data
tryReloadAndDetectInImage()
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun tryReloadAndDetectInImage() {
Log.d(TAG, "Try reload and detect image")
try {
if (imageUri == null) {
return
}
if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) {
// UI layout has not finished yet, will reload once it's ready.
return
}
if (imageUri == null) return
if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) return
val imageBitmap =
BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return
// Clear the overlay first
graphicOverlay!!.clear()
val resizedBitmap: Bitmap
resizedBitmap =
if (selectedSize == SIZE_ORIGINAL) {
imageBitmap
} else {
// Get the dimensions of the image view
val targetedSize: Pair<Int, Int> = targetedWidthHeight
// Determine how much to scale down the image
val scaleFactor =
Math.max(
imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
imageBitmap.height.toFloat() / targetedSize.second.toFloat()
)
Bitmap.createScaledBitmap(
imageBitmap,
(imageBitmap.width / scaleFactor).toInt(),
(imageBitmap.height / scaleFactor).toInt(),
true
)
}
preview!!.setImageBitmap(resizedBitmap)
if (imageProcessor != null) {
graphicOverlay!!.setImageSourceInfo(
resizedBitmap.width,
resizedBitmap.height,
/* isFlipped= */ false
if (selectedSize == SIZE_SCREEN) {
val targetedSize = targetedWidthHeight
val scaleFactor = max(
imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
imageBitmap.height.toFloat() / targetedSize.second.toFloat()
)
imageProcessor!!.processBitmap(resizedBitmap, graphicOverlay)
val resizedBitmap = Bitmap.createScaledBitmap(
imageBitmap,
(imageBitmap.width / scaleFactor).toInt(),
(imageBitmap.height / scaleFactor).toInt(),
true
)
preview?.setImageBitmap(resizedBitmap)
processImage(resizedBitmap)
} else {
Log.e(
TAG,
"Null imageProcessor, please check adb logs for imageProcessor creation error"
)
preview?.setImageBitmap(imageBitmap)
processImage(imageBitmap)
}
} catch (e: IOException) {
Log.e(TAG, "Error retrieving saved image")
@ -412,95 +408,52 @@ class StillImageActivity : AppCompatActivity() {
private val targetedWidthHeight: Pair<Int, Int>
get() {
val targetWidth: Int
val targetHeight: Int
when (selectedSize) {
SIZE_SCREEN -> {
targetWidth = imageMaxWidth
targetHeight = imageMaxHeight
}
SIZE_640_480 -> {
targetWidth = if (isLandScape) 640 else 480
targetHeight = if (isLandScape) 480 else 640
}
SIZE_1024_768 -> {
targetWidth = if (isLandScape) 1024 else 768
targetHeight = if (isLandScape) 768 else 1024
}
else -> throw IllegalStateException("Unknown size")
}
val targetWidth = if (isLandScape) max(imageMaxWidth, imageMaxHeight) else min(
imageMaxWidth,
imageMaxHeight
)
val targetHeight = if (isLandScape) min(imageMaxWidth, imageMaxHeight) else max(
imageMaxWidth,
imageMaxHeight
)
return Pair(targetWidth, targetHeight)
}
private fun createImageProcessor() {
try {
when (selectedMode) {
TEXT_RECOGNITION_LATIN ->
imageProcessor =
TextRecognitionProcessor(this, TextRecognizerOptions.Builder().build())
private fun processImage(bitmap: Bitmap) {
graphicOverlay?.clear()
imageProcessor?.processBitmap(bitmap, graphicOverlay)
}
TEXT_RECOGNITION_CHINESE ->
imageProcessor =
TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
private fun startChooseImageIntentForResult() {
val intent = Intent().apply {
type = "image/*"
action = Intent.ACTION_GET_CONTENT
}
startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
}
TEXT_RECOGNITION_DEVANAGARI ->
imageProcessor =
TextRecognitionProcessor(
this,
DevanagariTextRecognizerOptions.Builder().build()
)
TEXT_RECOGNITION_JAPANESE ->
imageProcessor =
TextRecognitionProcessor(
this,
JapaneseTextRecognizerOptions.Builder().build()
)
TEXT_RECOGNITION_KOREAN ->
imageProcessor =
TextRecognitionProcessor(
this,
KoreanTextRecognizerOptions.Builder().build()
)
else -> Log.e(TAG, "Unknown selectedMode: $selectedMode")
}
} catch (e: Exception) {
Log.e(TAG, "Can not create image processor: $selectedMode", e)
Toast.makeText(
applicationContext,
"Can not create image processor: " + e.message,
Toast.LENGTH_LONG
)
.show()
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
imageUri = data.data
tryReloadAndDetectInImage()
}
}
companion object {
private const val TAG = "StillImageActivity"
private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin"
private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese"
private const val TEXT_RECOGNITION_DEVANAGARI = "Text Recognition Devanagari"
private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese"
private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean"
private const val SIZE_SCREEN = "w:screen" // Match screen width
private const val SIZE_1024_768 = "w:1024" // ~1024*768 in a normal ratio
private const val SIZE_640_480 = "w:640" // ~640*480 in a normal ratio
private const val SIZE_ORIGINAL = "w:original" // Original image size
private const val KEY_IMAGE_URI = "com.google.mlkit.vision.demo.KEY_IMAGE_URI"
private const val KEY_IMAGE_MAX_WIDTH = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_WIDTH"
private const val KEY_IMAGE_MAX_HEIGHT = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_HEIGHT"
private const val KEY_SELECTED_SIZE = "com.google.mlkit.vision.demo.KEY_SELECTED_SIZE"
private const val REQUEST_IMAGE_CAPTURE = 1001
private const val REQUEST_CHOOSE_IMAGE = 1002
private const val KEY_IMAGE_URI = "com.google.mlkit.demo.stillImage.KEY_IMAGE_URI"
private const val KEY_IMAGE_MAX_WIDTH =
"com.google.mlkit.demo.stillImage.KEY_IMAGE_MAX_WIDTH"
private const val KEY_IMAGE_MAX_HEIGHT =
"com.google.mlkit.demo.stillImage.KEY_IMAGE_MAX_HEIGHT"
private const val KEY_SELECTED_SIZE = "com.google.mlkit.demo.stillImage.KEY_SELECTED_SIZE"
private const val SIZE_SCREEN = "w:screen"
private const val TEXT_RECOGNITION_CHINESE = "Chinese, Simplified"
private const val TEXT_RECOGNITION_JAPANESE = "Japanese"
private const val TEXT_RECOGNITION_KOREAN = "Korean"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CHOOSE_IMAGE = 1001
}
}

View File

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="#C6C6CB"
android:pathData="M9.99999 18.3333C14.6024 18.3333 18.3333 14.6024 18.3333 10C18.3333 5.39762 14.6024 1.66667 9.99999 1.66667C5.39761 1.66667 1.66666 5.39762 1.66666 10C1.66666 14.6024 5.39761 18.3333 9.99999 18.3333Z"
android:strokeWidth="1.66667"
android:strokeColor="#C6C6CB"
android:strokeLineJoin="round" />
<path
android:pathData="M12.357 7.643L7.64291 12.357"
android:strokeWidth="1.66667"
android:strokeColor="@color/white"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:pathData="M7.64307 7.643L12.3571 12.357"
android:strokeWidth="1.66667"
android:strokeColor="@color/white"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
@ -8,61 +8,60 @@
android:background="@color/main_text_ffffffff"
tools:context=".viewui.HistoryActivity">
<ImageView
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginStart="12dp"
android:layout_marginTop="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickBack"
android:padding="12dp"
android:src="@drawable/ic_back" />
android:padding="16dp"
android:src="@drawable/ic_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
<ImageView
android:id="@+id/iv_func_trans"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginEnd="76dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_func_trans"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="19dp"
android:text="@string/his_title"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/histories"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="0dp"
android:layout_marginTop="13dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
<ImageView
android:id="@+id/iv_func_trans"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginEnd="76dp"
android:src="@drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:drawableStart="@drawable/ic_remove"
android:drawablePadding="6dp"
android:padding="12dp"
android:text="@string/his_delete"
android:textColor="#FFFF5F5F"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_func_trans"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="2dp"
android:text="@string/main_dictionary"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="@id/iv_func_trans"
app:layout_constraintEnd_toStartOf="@id/iv_func_trans"
app:layout_constraintTop_toTopOf="@id/iv_func_trans" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/histories"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_percent="0.9"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/remove"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="18dp"
android:drawableStart="@drawable/ic_remove"
android:drawablePadding="6dp"
android:padding="12dp"
android:textColor="#FFFF5F5F"
android:text="@string/his_delete"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -9,19 +9,42 @@
android:paddingTop="16dp"
tools:context=".viewui.LanguageChangeActivity">
<ImageView
android:id="@+id/iv_ch_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:src="@drawable/ic_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ch_title"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/iv_ch_back"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_ch_back" />
<LinearLayout
android:id="@+id/change_language"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="16dp"
android:layout_marginTop="23dp"
android:layout_marginTop="13dp"
android:layout_marginEnd="16dp"
android:elevation="2dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
app:layout_constraintTop_toBottomOf="@id/iv_ch_back">
<TextView
android:id="@+id/tv_change_source"
@ -31,11 +54,9 @@
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:gravity="center"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:textColor="@color/main_text_ff1f1724"
android:textColor="@color/main_text_ff0e8ce8"
android:textSize="16sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" />
@ -85,6 +106,7 @@
app:layout_constraintTop_toBottomOf="@id/change_language">
<androidx.appcompat.widget.SearchView
android:id="@+id/ch_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"

View File

@ -146,7 +146,7 @@
android:id="@+id/tv_main_limit_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/main_limit_num"
android:text="0/1800"
android:textColor="@color/main_text_ff0e8ce8"
android:textSize="12sp" />

View File

@ -228,7 +228,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:text="1.11"
android:text="1.0.1"
android:textColor="@color/main_text_ff1f1724"
android:textSize="14sp"
app:drawableEndCompat="@drawable/ic_next" />

View File

@ -4,24 +4,25 @@
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_53514c"
android:keepScreenOn="true">
<androidx.camera.view.PreviewView
android:id="@+id/photo_preview"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/control"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintBaseline_toTopOf="@id/control"
app:layout_constraintBottom_toTopOf="@id/control"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@ -30,6 +31,7 @@
android:id="@+id/graphic_overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/bg_40_000000"
app:layout_constraintBottom_toBottomOf="@id/preview"
app:layout_constraintLeft_toLeftOf="@id/preview"
app:layout_constraintRight_toRightOf="@id/preview"
@ -46,7 +48,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginTop="19dp"
android:orientation="horizontal">
<ImageView
@ -115,16 +117,14 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_black_bg"
android:background="@drawable/button_r20_99000000_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
@ -134,7 +134,7 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/button_r20_black_bg"
android:background="@drawable/button_r20_99000000_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
@ -146,16 +146,14 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_black_bg"
android:background="@drawable/button_r20_99000000_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>

View File

@ -19,6 +19,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tr_title"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/iv_tr_back"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/iv_tr_back" />
<LinearLayout
android:id="@+id/ll_main_enter_text"
android:layout_width="match_parent"
@ -59,8 +71,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="12dp"
android:src="@drawable/ic_close" />
android:padding="12dp"
android:src="@drawable/ic_tr_close" />
</LinearLayout>
<ImageView

View File

@ -4,23 +4,29 @@
android:layout_height="match_parent"
android:background="@android:color/transparent">
<ImageView
android:id="@+id/sus_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:adjustViewBounds="true" />
<com.assimilate.alltrans.curview.GraphicOverlay
android:id="@+id/sus_graphic_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:background="@color/bg_40_000000" />
/>
<ImageView
android:id="@+id/sus_preview"
android:layout_width="150dp"
android:layout_height="150dp"
android:adjustViewBounds="true" />
<ImageView
android:id="@+id/sus_global_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:padding="20dp"
android:paddingStart="1dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:src="@drawable/ic_close" />
</FrameLayout>

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sus_control_root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/button_r20_black_bg"
android:orientation="vertical">
<!-- 第一行 -->
<LinearLayout
android:id="@+id/sus_ll1"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@ -46,6 +46,7 @@
</LinearLayout>
<!-- 第二行 -->
<LinearLayout
android:id="@+id/sus_ll2"
android:layout_width="180dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@ -84,6 +85,7 @@
</LinearLayout>
<!-- 分隔线 -->
<View
android:id="@+id/sus_view1"
android:layout_width="110dp"
android:layout_height="5dp"
android:layout_gravity="center"
@ -103,7 +105,6 @@
<ImageView
android:id="@+id/iv_sus_home"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -121,5 +122,4 @@
android:gravity="center"
android:src="@drawable/sus_trans_move" />
</LinearLayout>
</LinearLayout>

View File

@ -3,6 +3,8 @@
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<!-- text_bg-->
<color name="bg_40_000000">#66000000</color>
<!--main_page-->
<color name="main_bg_fff9f9f9">#FFF9F9F9</color>
<color name="main_bg_ffe2efff">#FFE2EFFF</color>

View File

@ -13,19 +13,25 @@
<string name="text_main_title">translate</string>
<string name="text_source_language">Chinese</string>
<string name="text_target_language">English</string>
<string name="main_text_enter">enter text</string>
<string name="main_text_enter">Enter text</string>
<string name="main_paste_text">Paste</string>
<string name="main_voice_to_text">Your device may not support speech-to-text.</string>
<string name="main_try_text">Translate</string>
<string name="main_quick_text">Quick Translate</string>
<string name="main_quick_set">settings</string>
<string name="main_photo_trans">Photo Translation</string>
<string name="main_dictionary">Dictionary</string>
<string name="main_limit_num">0/5000</string>
<string name="main_limit_num">%1$d/1800</string>
<!-- change_page-->
<string name="ch_title">Languages</string>
<!--text_re_page-->
<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>
<string name="tr_title">Translator</string>
<!--settings_page-->
<string name="settings">Settings</string>
<string name="languages">Languages</string>
@ -38,6 +44,7 @@
<string name="quick_set_touming">透明度</string>
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
<string name="quick_set_zd_time">自动折叠时间</string>
<string name="favor_title">Favorite</string>
<!-- sus_view-->
<string name="global_translation">Global Translation</string>
<string name="copy_text">Copy Text</string>
@ -45,6 +52,8 @@
<string name="district_translation">District Translation</string>
<!-- his_page-->
<string name="his_delete">Delete</string>
<string name="his_title">History record</string>
</resources>