update history+photo trans
This commit is contained in:
parent
67c17356d9
commit
c4c3998f65
8
.idea/deploymentTargetSelector.xml
generated
8
.idea/deploymentTargetSelector.xml
generated
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
private fun initSusView() {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d("SusService", "Service onDestroy")
|
||||
if (floatingView.isAttachedToWindow) {
|
||||
windowManager.removeView(floatingView)
|
||||
}
|
||||
stopSelf()
|
||||
}, 1000)
|
||||
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
|
||||
windowManager.removeView(globalView)
|
||||
}
|
||||
stopProjection()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(
|
||||
Log.d(
|
||||
"fdgfsdfsdfas", getFormattedText(
|
||||
translatedBlockText,
|
||||
line.recognizedLanguage,
|
||||
line.confidence
|
||||
),
|
||||
rect,
|
||||
((line.boundingBox?.bottom?.toFloat()
|
||||
?: 20f) - (line.boundingBox?.top?.toFloat()
|
||||
?: 0f)) - 2 * STROKE_WIDTH,
|
||||
canvas
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -36,9 +32,9 @@ class TextRecognitionProcessor(
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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(
|
||||
@ -56,6 +52,38 @@ class TextRecognitionProcessor(
|
||||
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?) {
|
||||
@ -70,12 +98,15 @@ class TextRecognitionProcessor(
|
||||
)
|
||||
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)
|
||||
|
||||
@ -14,7 +14,7 @@ class SusView : View.OnClickListener {
|
||||
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
TODO("Not yet implemented")
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
|
||||
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;
|
||||
|
||||
@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;
|
||||
});
|
||||
|
||||
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());
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
ArrayList<Translations> translations = new ArrayList<>();
|
||||
if (operationCollection) {
|
||||
ids = HashSet()
|
||||
items = HashSet()
|
||||
|
||||
val extra = intent.getStringExtra(COMMAND)
|
||||
operationCollection = COMMAND_COLLECTION == extra
|
||||
|
||||
tts = TextToSpeech(this) { status ->
|
||||
if (null != tts && TextToSpeech.SUCCESS == status) tts!!.setLanguage(
|
||||
Locale.getDefault()
|
||||
)
|
||||
}
|
||||
|
||||
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);
|
||||
if (!items!!.isEmpty()) {
|
||||
val integerArrayList = ArrayList(items)
|
||||
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
|
||||
adapter.updateSet(integerArrayList)
|
||||
items!!.clear()
|
||||
}
|
||||
});
|
||||
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"
|
||||
}
|
||||
}
|
||||
@ -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,19 +73,25 @@ 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")
|
||||
@ -82,7 +104,7 @@ class LanguageChangeActivity : AppCompatActivity() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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,31 +89,24 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startSusService(resultCode: Int, data: Intent?) {
|
||||
if (requestCode == REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
val serviceIntent = Intent(this, SusService::class.java).apply {
|
||||
putExtra("resultCode", resultCode)
|
||||
putExtra("data", data)
|
||||
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 initView() {
|
||||
binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,39 +84,35 @@ 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)
|
||||
|
||||
// 检查并请求相机权限
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ActivityCompat.requestPermissions(
|
||||
this,
|
||||
arrayOf(Manifest.permission.CAMERA),
|
||||
REQUEST_CAMERA_PERMISSION
|
||||
)
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
rootView.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
imageMaxWidth = rootView.width
|
||||
@ -108,15 +121,83 @@ class StillImageActivity : AppCompatActivity() {
|
||||
tryReloadAndDetectInImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// 初始化相机
|
||||
startCamera()
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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,29 +206,28 @@ 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)
|
||||
@ -155,16 +235,11 @@ class StillImageActivity : AppCompatActivity() {
|
||||
|
||||
cameraProviderFuture.addListener(Runnable {
|
||||
val cameraProvider = cameraProviderFuture.get()
|
||||
val preview = Preview.Builder()
|
||||
.build()
|
||||
.also {
|
||||
val preview = Preview.Builder().build().also {
|
||||
it.setSurfaceProvider(previewView.surfaceProvider)
|
||||
}
|
||||
|
||||
imageCapture = ImageCapture.Builder()
|
||||
.setFlashMode(ImageCapture.FLASH_MODE_OFF) // 默认关闭闪光灯
|
||||
.build()
|
||||
|
||||
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() {
|
||||
|
||||
// Ensure permission is still granted before starting camera intent
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
private fun createImageProcessor() {
|
||||
imageProcessor?.stop()
|
||||
try {
|
||||
imageProcessor = when (selectedMode) {
|
||||
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
|
||||
this,
|
||||
Manifest.permission.CAMERA
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
ChineseTextRecognizerOptions.Builder().build()
|
||||
)
|
||||
|
||||
"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(
|
||||
if (selectedSize == SIZE_SCREEN) {
|
||||
val targetedSize = targetedWidthHeight
|
||||
val scaleFactor = max(
|
||||
imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
|
||||
imageBitmap.height.toFloat() / targetedSize.second.toFloat()
|
||||
)
|
||||
Bitmap.createScaledBitmap(
|
||||
val resizedBitmap = 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
|
||||
)
|
||||
imageProcessor!!.processBitmap(resizedBitmap, graphicOverlay)
|
||||
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())
|
||||
|
||||
TEXT_RECOGNITION_CHINESE ->
|
||||
imageProcessor =
|
||||
TextRecognitionProcessor(
|
||||
this,
|
||||
ChineseTextRecognizerOptions.Builder().build()
|
||||
)
|
||||
|
||||
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")
|
||||
private fun processImage(bitmap: Bitmap) {
|
||||
graphicOverlay?.clear()
|
||||
imageProcessor?.processBitmap(bitmap, graphicOverlay)
|
||||
}
|
||||
} 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()
|
||||
|
||||
private fun startChooseImageIntentForResult() {
|
||||
val intent = Intent().apply {
|
||||
type = "image/*"
|
||||
action = Intent.ACTION_GET_CONTENT
|
||||
}
|
||||
startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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,18 +8,15 @@
|
||||
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:onClick="clickBack"
|
||||
android:padding="12dp"
|
||||
android:src="@drawable/ic_back" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="clickBack"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_back"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_func_trans"
|
||||
@ -27,7 +24,6 @@
|
||||
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" />
|
||||
|
||||
@ -35,20 +31,23 @@
|
||||
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" />
|
||||
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="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHeight_percent="0.9"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
android:layout_marginTop="13dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/remove"
|
||||
@ -58,11 +57,11 @@
|
||||
android:drawableStart="@drawable/ic_remove"
|
||||
android:drawablePadding="6dp"
|
||||
android:padding="12dp"
|
||||
android:textColor="#FFFF5F5F"
|
||||
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" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
@ -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"
|
||||
|
||||
@ -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" />
|
||||
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
Loading…
Reference in New Issue
Block a user