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>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<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>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<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" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MyApp"
|
android:name=".MyApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -27,6 +27,7 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
android:largeHeap="true"
|
||||||
android:theme="@style/Theme.Alltrans"
|
android:theme="@style/Theme.Alltrans"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<service
|
<service
|
||||||
|
|||||||
@ -9,18 +9,16 @@ import com.assimilate.alltrans.common.Language
|
|||||||
import com.assimilate.alltrans.common.LanguagesConstants
|
import com.assimilate.alltrans.common.LanguagesConstants
|
||||||
import com.assimilate.alltrans.common.Logger
|
import com.assimilate.alltrans.common.Logger
|
||||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||||
|
import com.assimilate.alltrans.common.ScreenUtils
|
||||||
|
|
||||||
class MyApp : Application() {
|
class MyApp : Application() {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
instance = this
|
instance = this
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSystemLanguage() {
|
private fun setSystemLanguage() {
|
||||||
|
|
||||||
|
|
||||||
// 检查是否是第一次进入应用
|
// 检查是否是第一次进入应用
|
||||||
if (PreferenceLanguageUtils.isFirstTime()) {
|
if (PreferenceLanguageUtils.isFirstTime()) {
|
||||||
// 第一次进入应用的逻辑
|
// 第一次进入应用的逻辑
|
||||||
@ -52,6 +50,7 @@ class MyApp : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
instance = this
|
instance = this
|
||||||
|
// ScreenUtils.init(this)
|
||||||
setSystemLanguage()
|
setSystemLanguage()
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import android.text.TextUtils;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Filter;
|
||||||
|
import android.widget.Filterable;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -17,24 +19,21 @@ import com.bumptech.glide.Glide;
|
|||||||
import com.bumptech.glide.request.RequestOptions;
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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 Activity mActivity;
|
||||||
private final ArrayList<Language> languages;
|
private final ArrayList<Language> languages;
|
||||||
|
private final ArrayList<Language> filteredLanguages;
|
||||||
private final OnClickListener listener;
|
private final OnClickListener listener;
|
||||||
private final RequestOptions options;
|
private final RequestOptions options;
|
||||||
|
|
||||||
private boolean sorting = false;
|
|
||||||
|
|
||||||
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
|
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
|
||||||
this.mActivity = activity;
|
this.mActivity = activity;
|
||||||
this.languages = new ArrayList<Language>();
|
this.languages = new ArrayList<>(languageList);
|
||||||
|
this.filteredLanguages = new ArrayList<>(languageList);
|
||||||
this.listener = onClickListener;
|
this.listener = onClickListener;
|
||||||
|
|
||||||
if (!languageList.isEmpty()) {
|
|
||||||
languages.addAll(languageList);
|
|
||||||
}
|
|
||||||
|
|
||||||
options = new RequestOptions()
|
options = new RequestOptions()
|
||||||
.placeholder(R.mipmap.ic_launcher)
|
.placeholder(R.mipmap.ic_launcher)
|
||||||
.error(R.mipmap.ic_launcher)
|
.error(R.mipmap.ic_launcher)
|
||||||
@ -49,9 +48,9 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull LanguageHolder holder, int position) {
|
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);
|
drawImage(currentLanguage.mFlagUrl, holder.mBinding.languageFlag);
|
||||||
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
|
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
|
||||||
holder.mBinding.language.setText(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() {
|
holder.mBinding.getRoot().setOnClickListener(view -> {
|
||||||
@Override
|
final int pos = holder.getAdapterPosition();
|
||||||
public void onClick(View view) {
|
if (listener != null)
|
||||||
final int position = holder.getAdapterPosition();
|
listener.click(pos, currentLanguage);
|
||||||
if (null != listener)
|
|
||||||
listener.click(position, currentLanguage);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return languages.size();
|
return filteredLanguages.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
|
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 static class LanguageHolder extends RecyclerView.ViewHolder {
|
||||||
public LanguageItemLayoutBinding mBinding;
|
public LanguageItemLayoutBinding mBinding;
|
||||||
|
|
||||||
|
|||||||
@ -1,34 +1,47 @@
|
|||||||
package com.assimilate.alltrans.allservice
|
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.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.PixelFormat
|
import android.graphics.PixelFormat
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
import android.hardware.display.VirtualDisplay
|
import android.hardware.display.VirtualDisplay
|
||||||
|
import android.media.Image
|
||||||
import android.media.ImageReader
|
import android.media.ImageReader
|
||||||
import android.media.projection.MediaProjection
|
import android.media.projection.MediaProjection
|
||||||
import android.media.projection.MediaProjectionManager
|
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.DisplayMetrics
|
||||||
import android.util.Log
|
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 androidx.core.app.NotificationCompat
|
||||||
import com.assimilate.alltrans.R
|
import com.assimilate.alltrans.R
|
||||||
import com.assimilate.alltrans.common.TextRecognitionProcessor
|
import com.assimilate.alltrans.common.TextRecognitionProcessor
|
||||||
import com.assimilate.alltrans.common.VisionImageProcessor
|
import com.assimilate.alltrans.common.VisionImageProcessor
|
||||||
import com.assimilate.alltrans.databinding.SusControlViewBinding
|
|
||||||
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
|
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
|
||||||
|
import com.assimilate.alltrans.databinding.SusControlViewBinding
|
||||||
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
|
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class SusService : Service() {
|
class SusService : Service() {
|
||||||
private var imageProcessor: VisionImageProcessor? = null
|
private var imageProcessor: VisionImageProcessor? = null
|
||||||
private lateinit var mediaProjectionManager: MediaProjectionManager
|
private lateinit var mediaProjectionManager: MediaProjectionManager
|
||||||
private lateinit var mediaProjection: MediaProjection
|
private var mediaProjection: MediaProjection? = null
|
||||||
private lateinit var virtualDisplay: VirtualDisplay
|
private var virtualDisplay: VirtualDisplay? = null
|
||||||
private lateinit var displayMetrics: DisplayMetrics
|
private lateinit var displayMetrics: DisplayMetrics
|
||||||
|
private lateinit var imageReader: ImageReader
|
||||||
|
|
||||||
private lateinit var windowManager: WindowManager
|
private lateinit var windowManager: WindowManager
|
||||||
private lateinit var floatingView: View
|
private lateinit var floatingView: View
|
||||||
@ -37,34 +50,79 @@ class SusService : Service() {
|
|||||||
private lateinit var bindingSusControl: SusControlViewBinding
|
private lateinit var bindingSusControl: SusControlViewBinding
|
||||||
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
|
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? {
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
return null
|
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 {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
val resultCode = intent?.getIntExtra("resultCode", Activity.RESULT_OK)
|
if (intent == null) {
|
||||||
val data: Intent? = intent?.getParcelableExtra("data")
|
|
||||||
if (resultCode != null && data != null) {
|
|
||||||
startScreenshot(resultCode, data)
|
|
||||||
}
|
|
||||||
return START_NOT_STICKY
|
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
|
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))
|
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this))
|
||||||
floatingView = bindingSusControl.root
|
floatingView = bindingSusControl.root
|
||||||
|
|
||||||
@ -85,31 +143,7 @@ class SusService : Service() {
|
|||||||
|
|
||||||
windowManager.addView(floatingView, layoutParams)
|
windowManager.addView(floatingView, layoutParams)
|
||||||
|
|
||||||
// 设置点击事件
|
initControlClick()
|
||||||
bindingSusControl.tvSusGlobal.setOnClickListener {
|
|
||||||
// 处理全局翻译点击事件
|
|
||||||
addGlobalView()
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.tvSusCopy.setOnClickListener {
|
|
||||||
// 处理复制文本点击事件
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.tvSusPhoto.setOnClickListener {
|
|
||||||
// 处理照片翻译点击事件
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.tvSusDistrict.setOnClickListener {
|
|
||||||
// 处理地区翻译点击事件
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.ivSusHome.setOnClickListener {
|
|
||||||
// 处理返回主页点击事件
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.ivSusMove.setOnClickListener {
|
|
||||||
// 处理移动窗口点击事件
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener {
|
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener {
|
||||||
private var startX = 0f
|
private var startX = 0f
|
||||||
@ -122,9 +156,9 @@ class SusService : Service() {
|
|||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
startX = layoutParams.x.toFloat()
|
startX = layoutParams.x.toFloat()
|
||||||
startY = layoutParams.y.toFloat()
|
startY = layoutParams.y.toFloat()
|
||||||
|
|
||||||
touchX = event.rawX
|
touchX = event.rawX
|
||||||
touchY = event.rawY
|
touchY = event.rawY
|
||||||
|
handler.removeCallbacks(hideRunnable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,26 +169,78 @@ class SusService : Service() {
|
|||||||
return true
|
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
|
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() {
|
private fun addGlobalView() {
|
||||||
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
|
||||||
|
|
||||||
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
|
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
|
||||||
globalView = bindingSubGlobal.root
|
globalView = bindingSubGlobal.root
|
||||||
imageProcessor = TextRecognitionProcessor(
|
|
||||||
this,
|
|
||||||
ChineseTextRecognizerOptions.Builder().build()
|
|
||||||
)
|
|
||||||
|
|
||||||
val layoutParams = WindowManager.LayoutParams(
|
val layoutParams = WindowManager.LayoutParams(
|
||||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
WindowManager.LayoutParams.MATCH_PARENT,
|
||||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
WindowManager.LayoutParams.MATCH_PARENT,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||||
} else {
|
} else {
|
||||||
@ -165,22 +251,113 @@ class SusService : Service() {
|
|||||||
)
|
)
|
||||||
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
|
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
|
||||||
layoutParams.x = 0
|
layoutParams.x = 0
|
||||||
layoutParams.y = 100
|
layoutParams.y = 0
|
||||||
|
|
||||||
windowManager.addView(globalView, layoutParams)
|
windowManager.addView(globalView, layoutParams)
|
||||||
|
|
||||||
|
initGlobalClick()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
private fun initGlobalClick() {
|
||||||
super.onDestroy()
|
bindingSubGlobal.susGlobalClose.setOnClickListener {
|
||||||
if (floatingView.isAttachedToWindow) {
|
|
||||||
windowManager.removeView(floatingView)
|
|
||||||
}
|
|
||||||
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
|
|
||||||
windowManager.removeView(globalView)
|
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 {
|
private fun createNotification(): Notification {
|
||||||
|
Log.d("SusService", "Creating notification")
|
||||||
val notificationChannelId = "FOREGROUND_SERVICE_CHANNEL"
|
val notificationChannelId = "FOREGROUND_SERVICE_CHANNEL"
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
@ -206,7 +383,6 @@ class SusService : Service() {
|
|||||||
|
|
||||||
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
|
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
|
||||||
try {
|
try {
|
||||||
// Clear the overlay first
|
|
||||||
bindingSubGlobal.susGraphicOverlay.clear()
|
bindingSubGlobal.susGraphicOverlay.clear()
|
||||||
|
|
||||||
if (imageProcessor != null) {
|
if (imageProcessor != null) {
|
||||||
@ -223,53 +399,20 @@ class SusService : Service() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e("SusService", "Error retrieving saved image")
|
Log.e("SusService", "Error retrieving saved image", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startScreenshot(resultCode: Int, data: Intent?) {
|
override fun onDestroy() {
|
||||||
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!)
|
super.onDestroy()
|
||||||
val imageReader = ImageReader.newInstance(
|
Log.d("SusService", "Service onDestroy")
|
||||||
displayMetrics.widthPixels,
|
if (floatingView.isAttachedToWindow) {
|
||||||
displayMetrics.heightPixels,
|
windowManager.removeView(floatingView)
|
||||||
PixelFormat.RGBA_8888,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
virtualDisplay = mediaProjection.createVirtualDisplay(
|
|
||||||
"Screenshot",
|
|
||||||
displayMetrics.widthPixels,
|
|
||||||
displayMetrics.heightPixels,
|
|
||||||
displayMetrics.densityDpi,
|
|
||||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
|
||||||
imageReader.surface,
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
|
|
||||||
// 初始化 globalView
|
|
||||||
addGlobalView()
|
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
|
||||||
val image = imageReader.acquireLatestImage()
|
|
||||||
if (image != null) {
|
|
||||||
val planes = image.planes
|
|
||||||
val buffer = planes[0].buffer
|
|
||||||
val pixelStride = planes[0].pixelStride
|
|
||||||
val rowStride = planes[0].rowStride
|
|
||||||
val rowPadding = rowStride - pixelStride * displayMetrics.widthPixels
|
|
||||||
|
|
||||||
val bitmap = Bitmap.createBitmap(
|
|
||||||
displayMetrics.widthPixels + rowPadding / pixelStride,
|
|
||||||
displayMetrics.heightPixels,
|
|
||||||
Bitmap.Config.ARGB_8888
|
|
||||||
)
|
|
||||||
bitmap.copyPixelsFromBuffer(buffer)
|
|
||||||
image.close()
|
|
||||||
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
|
|
||||||
tryReloadAndDetectInImage(bitmap)
|
|
||||||
}
|
}
|
||||||
stopSelf()
|
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
|
||||||
}, 1000)
|
windowManager.removeView(globalView)
|
||||||
}
|
}
|
||||||
|
stopProjection()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,6 +62,11 @@ public class LanguagesConstants {
|
|||||||
// 根据语言代码获取 Language 对象
|
// 根据语言代码获取 Language 对象
|
||||||
public Language getLanguageByLanguageCode(@NonNull String languageCode, @NonNull Context context) {
|
public Language getLanguageByLanguageCode(@NonNull String languageCode, @NonNull Context context) {
|
||||||
ArrayList<Language> languages = getList(context);
|
ArrayList<Language> languages = getList(context);
|
||||||
|
|
||||||
|
if (languageCode.equals("zh")) {
|
||||||
|
languageCode = "zh_CN";
|
||||||
|
}
|
||||||
|
|
||||||
for (Language lang : languages) {
|
for (Language lang : languages) {
|
||||||
if (lang.getLanguageCode().equalsIgnoreCase(languageCode)) {
|
if (lang.getLanguageCode().equalsIgnoreCase(languageCode)) {
|
||||||
return lang;
|
return lang;
|
||||||
|
|||||||
@ -4,8 +4,11 @@ import android.graphics.Canvas
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.graphics.Typeface
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.text.Layout
|
||||||
|
import android.text.StaticLayout
|
||||||
import android.text.TextPaint
|
import android.text.TextPaint
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.assimilate.alltrans.MyApp
|
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.GoogleTranslator
|
||||||
import com.assimilate.alltrans.http.Translator
|
import com.assimilate.alltrans.http.Translator
|
||||||
import com.google.mlkit.vision.text.Text
|
import com.google.mlkit.vision.text.Text
|
||||||
import java.util.Arrays
|
import java.util.concurrent.Executors
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
@ -27,8 +30,10 @@ class TextGraphic(
|
|||||||
|
|
||||||
private val rectPaint: Paint = Paint()
|
private val rectPaint: Paint = Paint()
|
||||||
private val textPaint: TextPaint
|
private val textPaint: TextPaint
|
||||||
|
|
||||||
private val labelPaint: Paint
|
private val labelPaint: Paint
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
prepareTranslation()
|
prepareTranslation()
|
||||||
@ -37,6 +42,9 @@ class TextGraphic(
|
|||||||
rectPaint.strokeWidth = STROKE_WIDTH
|
rectPaint.strokeWidth = STROKE_WIDTH
|
||||||
textPaint = TextPaint()
|
textPaint = TextPaint()
|
||||||
textPaint.color = TEXT_COLOR
|
textPaint.color = TEXT_COLOR
|
||||||
|
textPaint.textSize = TEXT_SIZE
|
||||||
|
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
||||||
|
textPaint.isFakeBoldText = true
|
||||||
labelPaint = Paint()
|
labelPaint = Paint()
|
||||||
labelPaint.color = MARKER_COLOR
|
labelPaint.color = MARKER_COLOR
|
||||||
labelPaint.style = Paint.Style.FILL
|
labelPaint.style = Paint.Style.FILL
|
||||||
@ -48,7 +56,10 @@ class TextGraphic(
|
|||||||
|
|
||||||
// Method to prepare translation before drawing
|
// Method to prepare translation before drawing
|
||||||
private fun prepareTranslation() {
|
private fun prepareTranslation() {
|
||||||
Thread {
|
|
||||||
|
|
||||||
|
executor.execute {
|
||||||
|
|
||||||
val textToTranslate = StringBuilder()
|
val textToTranslate = StringBuilder()
|
||||||
|
|
||||||
// Collect all text to be translated and append delimiter
|
// Collect all text to be translated and append delimiter
|
||||||
@ -64,13 +75,22 @@ class TextGraphic(
|
|||||||
PreferenceLanguageUtils.getString("language_target"),
|
PreferenceLanguageUtils.getString("language_target"),
|
||||||
MyApp.applicationContext()
|
MyApp.applicationContext()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (lanTargetCode == null || lanSourceCode == null || textToTranslate.toString()
|
||||||
|
.isEmpty()
|
||||||
|
) {
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
// Define translation parameters
|
// Define translation parameters
|
||||||
val param = HashMap<String, String>().apply {
|
val param = HashMap<String, String>().apply {
|
||||||
put("sourceLanguage", lanSourceCode)
|
put("sourceLanguage", lanSourceCode)
|
||||||
put("translationLanguage", lanTargetCode)
|
put("translationLanguage", lanTargetCode)
|
||||||
put("text", textToTranslate.toString())
|
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> =
|
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
|
||||||
GoogleTranslator()
|
GoogleTranslator()
|
||||||
|
|
||||||
@ -78,27 +98,23 @@ class TextGraphic(
|
|||||||
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
|
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
|
||||||
// Split translated text by delimiter
|
// Split translated text by delimiter
|
||||||
translatedTextBlocks =
|
translatedTextBlocks =
|
||||||
translatedText.split(DELIMITER.toRegex()).dropLastWhile { it.isEmpty() }
|
translatedText.split(DELIMITER.toRegex()).filter { it.isNotEmpty() }
|
||||||
|
|
||||||
// Update UI thread
|
// Update UI thread
|
||||||
handler.post {
|
handler.post {
|
||||||
postInvalidate() // Notify to redraw
|
postInvalidate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}.start()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun draw(canvas: Canvas) {
|
override fun draw(canvas: Canvas) {
|
||||||
Log.d(TAG, "Text is: " + text.text)
|
|
||||||
|
|
||||||
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
|
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
|
||||||
val translatedBlockText =
|
val translatedBlockText =
|
||||||
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text
|
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) {
|
if (shouldGroupTextInBlocks) {
|
||||||
drawText(
|
drawText(
|
||||||
getFormattedText(
|
getFormattedText(
|
||||||
@ -107,53 +123,16 @@ class TextGraphic(
|
|||||||
confidence = null
|
confidence = null
|
||||||
),
|
),
|
||||||
RectF(textBlock.boundingBox),
|
RectF(textBlock.boundingBox),
|
||||||
height1 - 3 * STROKE_WIDTH,
|
|
||||||
canvas
|
canvas
|
||||||
)
|
)
|
||||||
} else {
|
Log.d(
|
||||||
for (line in textBlock.lines) {
|
"fdgfsdfsdfas", getFormattedText(
|
||||||
Log.d(TAG, "Line text is: " + line.text)
|
|
||||||
Log.d(TAG, "Line boundingbox is: " + line.boundingBox)
|
|
||||||
Log.d(TAG, "Line cornerpoint is: " + Arrays.toString(line.cornerPoints))
|
|
||||||
Log.d(TAG, "Line confidence is: " + line.confidence)
|
|
||||||
Log.d(TAG, "Line angle is: " + line.angle)
|
|
||||||
// Draw the bounding box around the TextBlock.
|
|
||||||
val rect = RectF(line.boundingBox)
|
|
||||||
drawText(
|
|
||||||
getFormattedText(
|
|
||||||
translatedBlockText,
|
translatedBlockText,
|
||||||
line.recognizedLanguage,
|
textBlock.recognizedLanguage,
|
||||||
line.confidence
|
confidence = null
|
||||||
),
|
|
||||||
rect,
|
|
||||||
((line.boundingBox?.bottom?.toFloat()
|
|
||||||
?: 20f) - (line.boundingBox?.top?.toFloat()
|
|
||||||
?: 0f)) - 2 * STROKE_WIDTH,
|
|
||||||
canvas
|
|
||||||
)
|
)
|
||||||
for (element in line.elements) {
|
|
||||||
Log.d(TAG, "Element text is: " + element.text)
|
|
||||||
Log.d(TAG, "Element boundingbox is: " + element.boundingBox)
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"Element cornerpoint is: " + Arrays.toString(element.cornerPoints)
|
|
||||||
)
|
)
|
||||||
Log.d(TAG, "Element language is: " + element.recognizedLanguage)
|
} // 不需要单行(删除)
|
||||||
Log.d(TAG, "Element confidence is: " + element.confidence)
|
|
||||||
Log.d(TAG, "Element angle is: " + element.angle)
|
|
||||||
for (symbol in element.symbols) {
|
|
||||||
Log.d(TAG, "Symbol text is: " + symbol.text)
|
|
||||||
Log.d(TAG, "Symbol boundingbox is: " + symbol.boundingBox)
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"Symbol cornerpoint is: " + Arrays.toString(symbol.cornerPoints)
|
|
||||||
)
|
|
||||||
Log.d(TAG, "Symbol confidence is: " + symbol.confidence)
|
|
||||||
Log.d(TAG, "Symbol angle is: " + symbol.angle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,78 +147,60 @@ class TextGraphic(
|
|||||||
else res
|
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 x0 = translateX(rect.left)
|
||||||
val x1 = translateX(rect.right)
|
val x1 = translateX(rect.right)
|
||||||
rect.left = min(x0, x1)
|
rect.left = min(x0, x1)
|
||||||
rect.right = max(x0, x1)
|
rect.right = max(x0, x1)
|
||||||
rect.top = translateY(rect.top)
|
rect.top = translateY(rect.top)
|
||||||
rect.bottom = translateY(rect.bottom)
|
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
|
if (textLayout.height <= availableHeight) {
|
||||||
val totalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
|
break
|
||||||
val totalTextHeightWithSpacing = totalTextHeight * lines.size
|
}
|
||||||
|
textSize -= 1
|
||||||
// Adjust the text size if the total height is greater than the rectangle height
|
|
||||||
if (totalTextHeightWithSpacing > rect.height()) {
|
|
||||||
textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
|
|
||||||
lines = wrapText(text.trim(), rect.width())
|
|
||||||
} else if (totalTextHeightWithSpacing < rect.height()) {
|
|
||||||
// If the total text height is less than the rectangle height, increase text size
|
|
||||||
textPaint.textSize *= rect.height() / totalTextHeightWithSpacing
|
|
||||||
lines = wrapText(text.trim(), rect.width())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate new total height with adjusted text size
|
// 使用 StaticLayout 绘制文本
|
||||||
val finalTextHeight = textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
|
textLayout =
|
||||||
val finalTotalTextHeightWithSpacing = finalTextHeight * lines.size
|
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
|
canvas.save()
|
||||||
var textY =
|
canvas.translate(rect.left, rect.top)
|
||||||
rect.top + ((rect.height() - finalTotalTextHeightWithSpacing) / 2) - textPaint.fontMetrics.ascent
|
textLayout.draw(canvas)
|
||||||
|
canvas.restore()
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val DELIMITER = "`0_.._0`"
|
private const val DELIMITER = "0`~`0"
|
||||||
private const val TAG = "TextGraphic"
|
private const val TAG = "TextGraphic"
|
||||||
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
|
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
|
||||||
private val TEXT_COLOR = Color.parseColor("#FF474747")
|
private val TEXT_COLOR = Color.parseColor("#FF474747")
|
||||||
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
|
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
|
||||||
private const val STROKE_WIDTH = 2.0f
|
private const val STROKE_WIDTH = 2.0f
|
||||||
|
private const val TEXT_SIZE = 44.0f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
|
|
||||||
package com.assimilate.alltrans.common
|
package com.assimilate.alltrans.common
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.assimilate.alltrans.MyApp
|
import com.assimilate.alltrans.MyApp
|
||||||
import com.assimilate.alltrans.curview.GraphicOverlay
|
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.android.gms.tasks.Task
|
||||||
import com.google.mlkit.vision.common.InputImage
|
import com.google.mlkit.vision.common.InputImage
|
||||||
import com.google.mlkit.vision.text.Text
|
import com.google.mlkit.vision.text.Text
|
||||||
@ -36,9 +32,9 @@ class TextRecognitionProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
|
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
|
||||||
|
PreferenceLanguageUtils.putString("language_source", getMostFrequentLanguage(text))
|
||||||
|
|
||||||
Log.d(TAG, "On-device Text detection successful")
|
Log.d(TAG, "On-device Text detection successful")
|
||||||
logExtrasForTesting(text)
|
logExtrasForTesting(text)
|
||||||
graphicOverlay.add(
|
graphicOverlay.add(
|
||||||
@ -56,6 +52,38 @@ class TextRecognitionProcessor(
|
|||||||
Log.w(TAG, "Text detection failed.$e")
|
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 {
|
companion object {
|
||||||
private const val TAG = "TextRecProcessor"
|
private const val TAG = "TextRecProcessor"
|
||||||
private fun logExtrasForTesting(text: Text?) {
|
private fun logExtrasForTesting(text: Text?) {
|
||||||
@ -70,12 +98,15 @@ class TextRecognitionProcessor(
|
|||||||
)
|
)
|
||||||
for (j in lines.indices) {
|
for (j in lines.indices) {
|
||||||
val elements = lines[j].elements
|
val elements = lines[j].elements
|
||||||
|
|
||||||
Log.v(
|
Log.v(
|
||||||
MANUAL_TESTING_LOG,
|
MANUAL_TESTING_LOG,
|
||||||
String.format("Detected text line %d has %d elements", j, elements.size)
|
String.format("Detected text line %d has %d elements", j, elements.size)
|
||||||
)
|
)
|
||||||
for (k in elements.indices) {
|
for (k in elements.indices) {
|
||||||
val element = elements[k]
|
val element = elements[k]
|
||||||
|
Log.v("推测是什么语言", element.recognizedLanguage)
|
||||||
|
|
||||||
Log.v(
|
Log.v(
|
||||||
MANUAL_TESTING_LOG,
|
MANUAL_TESTING_LOG,
|
||||||
String.format("Detected text element %d says: %s", k, element.text)
|
String.format("Detected text element %d says: %s", k, element.text)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class SusView : View.OnClickListener {
|
|||||||
|
|
||||||
|
|
||||||
override fun onClick(v: View?) {
|
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.content.Context
|
||||||
import android.os.Bundle;
|
import android.os.Bundle
|
||||||
import android.speech.tts.TextToSpeech;
|
import android.speech.tts.TextToSpeech
|
||||||
import android.view.View;
|
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;
|
class HistoryActivity : AppCompatActivity() {
|
||||||
import androidx.core.graphics.Insets;
|
private var tts: TextToSpeech? = null
|
||||||
import androidx.core.view.ViewCompat;
|
private var binding: ActivityHistoryBinding? = null
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
private var ids: HashSet<Long>? = null // 通过id 删除数据库文件
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
private var items: HashSet<Int>? = null // 通许index 删除界面上面的数据
|
||||||
|
private var operationCollection = false
|
||||||
|
|
||||||
import com.assimilate.alltrans.R;
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
import com.assimilate.alltrans.adapters.TranslationAdapter;
|
super.onCreate(savedInstanceState)
|
||||||
import com.assimilate.alltrans.common.Widget;
|
enableEdgeToEdge()
|
||||||
import com.assimilate.alltrans.databinding.ActivityHistoryBinding;
|
binding = ActivityHistoryBinding.inflate(layoutInflater)
|
||||||
import com.assimilate.alltrans.mydb.DbTranslation;
|
setContentView(binding!!.root)
|
||||||
import com.assimilate.alltrans.mydb.Translations;
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->
|
||||||
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
import java.util.ArrayList;
|
v.setPadding(26, systemBars.top, 26, systemBars.bottom)
|
||||||
import java.util.Collections;
|
insets
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
ArrayList<Translations> translations = new ArrayList<>();
|
ids = HashSet()
|
||||||
if (operationCollection) {
|
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!!.tvFuncTrans.text = getString(R.string.favor_title)
|
||||||
binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
||||||
ArrayList<Translations> list = new DbTranslation(this).getTranslations(true);
|
val list = DbTranslation(this).getTranslations(true)
|
||||||
if (null != list && !list.isEmpty()) {
|
if (null != list && !list.isEmpty()) {
|
||||||
translations.addAll(list);
|
translations.addAll(list)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 查出所有的翻译记录
|
// 查出所有的翻译记录
|
||||||
binding.tvFuncTrans.setText("History");
|
binding!!.tvFuncTrans.text = getString(R.string.his_title)
|
||||||
binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
||||||
ArrayList<Translations> list = new DbTranslation(this).getTranslations(false);
|
val list = DbTranslation(this).getTranslations(false)
|
||||||
if (null != list && !list.isEmpty()) {
|
if (null != list && list.isNotEmpty()) {
|
||||||
translations.addAll(list);
|
translations.addAll(list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final TranslationAdapter adapter = new TranslationAdapter(translations, new TranslationAdapter.TranslationItemCallback() {
|
val adapter = TranslationAdapter(translations, object : TranslationItemCallback {
|
||||||
@Override
|
override fun updateList(
|
||||||
public void updateList(TranslationAdapter.Operation operation, long id, int position) {
|
operation: TranslationAdapter.Operation,
|
||||||
|
id: Long,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
if (TranslationAdapter.Operation.ADD == operation) {
|
if (TranslationAdapter.Operation.ADD == operation) {
|
||||||
add(id, position);
|
add(id, position)
|
||||||
} else if (TranslationAdapter.Operation.REMOVE == operation) {
|
} else if (TranslationAdapter.Operation.REMOVE == operation) {
|
||||||
remove(id, position);
|
remove(id, position)
|
||||||
}
|
}
|
||||||
updateBtn();
|
updateBtn()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun speech(value: String) {
|
||||||
public void speech(String value) {
|
|
||||||
if (null != tts
|
if (null != tts
|
||||||
&& TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) {
|
&& TextToSpeech.LANG_NOT_SUPPORTED != tts!!.isLanguageAvailable(Locale.getDefault())
|
||||||
tts.speak(value, 0, null, null);
|
) {
|
||||||
|
tts!!.speak(value, 0, null, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(long id, int index) {
|
private fun add(id: Long, index: Int) {
|
||||||
ids.add(id);
|
ids!!.add(id)
|
||||||
items.add(index);
|
items!!.add(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void remove(long id, int index) {
|
private fun remove(id: Long, index: Int) {
|
||||||
ids.remove(id);
|
ids!!.remove(id)
|
||||||
items.remove(index);
|
items!!.remove(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBtn() {
|
private fun updateBtn() {
|
||||||
if (ids.isEmpty()) {
|
if (ids!!.isEmpty()) {
|
||||||
binding.remove.setVisibility(View.INVISIBLE);
|
binding!!.remove.visibility = View.INVISIBLE
|
||||||
} else {
|
} 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() {
|
binding!!.remove.setOnClickListener(object : View.OnClickListener {
|
||||||
private DbTranslation dbTranslation;
|
private var dbTranslation: DbTranslation? = null
|
||||||
|
|
||||||
@Override
|
override fun onClick(v: View) {
|
||||||
public void onClick(View v) {
|
if (ids!!.isEmpty()) {
|
||||||
if (ids.isEmpty()) {
|
Widget.makeToast(this@HistoryActivity, "Noting to remove.")
|
||||||
Widget.makeToast(HistoryActivity.this, "Noting to remove.");
|
return
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ids.isEmpty()) {
|
if (!ids!!.isEmpty()) {
|
||||||
ArrayList<Long> longArrayList = new ArrayList<>(ids);
|
val longArrayList = ArrayList(ids)
|
||||||
if (operationCollection) {
|
if (operationCollection) {
|
||||||
getDbTranslation(HistoryActivity.this).removeCollectTranslations(longArrayList);
|
getDbTranslation(this@HistoryActivity).removeCollectTranslations(
|
||||||
|
longArrayList
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
getDbTranslation(HistoryActivity.this).removeTranslations(longArrayList);
|
getDbTranslation(this@HistoryActivity).removeTranslations(longArrayList)
|
||||||
}
|
}
|
||||||
ids.clear();
|
ids!!.clear()
|
||||||
}
|
}
|
||||||
if (!items.isEmpty()) {
|
if (!items!!.isEmpty()) {
|
||||||
ArrayList<Integer> integerArrayList = new ArrayList<>(items);
|
val integerArrayList = ArrayList(items)
|
||||||
Collections.sort(integerArrayList, new Comparator<Integer>() {
|
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
|
||||||
@Override
|
adapter.updateSet(integerArrayList)
|
||||||
public int compare(Integer o1, Integer o2) {
|
items!!.clear()
|
||||||
return o2.compareTo(o1);
|
|
||||||
}
|
}
|
||||||
});
|
binding!!.remove.visibility = View.VISIBLE
|
||||||
adapter.updateSet(integerArrayList);
|
|
||||||
items.clear();
|
|
||||||
}
|
|
||||||
binding.remove.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DbTranslation getDbTranslation(Context context) {
|
private fun getDbTranslation(context: Context): DbTranslation {
|
||||||
if (null == dbTranslation) {
|
if (null == dbTranslation) {
|
||||||
dbTranslation = new DbTranslation(context);
|
dbTranslation = DbTranslation(context)
|
||||||
}
|
}
|
||||||
return dbTranslation;
|
return dbTranslation!!
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
binding.histories.setLayoutManager(layoutManager);
|
binding!!.histories.layoutManager = layoutManager
|
||||||
binding.histories.setAdapter(adapter);
|
binding!!.histories.adapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
override fun onBackPressed() {
|
||||||
public void onBackPressed() {
|
super.onBackPressed()
|
||||||
super.onBackPressed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clickBack(View view) {
|
fun clickBack(view: View?) {
|
||||||
onBackPressed();
|
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()
|
initView()
|
||||||
|
initSearch() // 添加这一行
|
||||||
initList()
|
initList()
|
||||||
initClick()
|
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() {
|
private fun initView() {
|
||||||
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
||||||
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
||||||
@ -57,19 +73,25 @@ class LanguageChangeActivity : AppCompatActivity() {
|
|||||||
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
||||||
updateRecentLanguages()
|
updateRecentLanguages()
|
||||||
}
|
}
|
||||||
binding.listLanCommon5.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
binding.listLanCommon5.layoutManager =
|
||||||
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
binding.listLanCommon5.adapter = recentAdapter
|
binding.listLanCommon5.adapter = recentAdapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initClick() {
|
private fun initClick() {
|
||||||
|
binding.ivChBack.setOnClickListener { onBackPressed() }
|
||||||
binding.tvChangeSource.setOnClickListener {
|
binding.tvChangeSource.setOnClickListener {
|
||||||
lastTranslateLanguage = false
|
lastTranslateLanguage = false
|
||||||
|
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff0e8ce8))
|
||||||
|
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff1f1724))
|
||||||
}
|
}
|
||||||
binding.tvChangeTarget.setOnClickListener {
|
binding.tvChangeTarget.setOnClickListener {
|
||||||
lastTranslateLanguage = true
|
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 {
|
binding.tvExchange.setOnClickListener {
|
||||||
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
|
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
|
||||||
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
|
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
|
||||||
@ -82,7 +104,7 @@ class LanguageChangeActivity : AppCompatActivity() {
|
|||||||
onBackPressed()
|
onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initList() {
|
private fun initList() {
|
||||||
@ -101,8 +123,10 @@ class LanguageChangeActivity : AppCompatActivity() {
|
|||||||
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
||||||
updateRecentLanguages()
|
updateRecentLanguages()
|
||||||
}
|
}
|
||||||
binding.listLanguages.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
binding.listLanguages.layoutManager =
|
||||||
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
binding.listLanguages.adapter = adapter
|
binding.listLanguages.adapter = adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,10 @@ import android.content.ClipboardManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.projection.MediaProjectionManager
|
import android.media.projection.MediaProjectionManager
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import android.speech.RecognizerIntent
|
import android.speech.RecognizerIntent
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
@ -23,7 +25,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import com.assimilate.alltrans.MyApp
|
|
||||||
import com.assimilate.alltrans.R
|
import com.assimilate.alltrans.R
|
||||||
import com.assimilate.alltrans.allservice.SusService
|
import com.assimilate.alltrans.allservice.SusService
|
||||||
import com.assimilate.alltrans.common.LanguagesConstants
|
import com.assimilate.alltrans.common.LanguagesConstants
|
||||||
@ -33,16 +34,16 @@ import com.assimilate.alltrans.databinding.ActivityMainBinding
|
|||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
|
||||||
private var launcher: ActivityResultLauncher<Intent>? = null
|
private var launcher: ActivityResultLauncher<Intent>? = null
|
||||||
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var lcm: LocalBroadcastManager
|
private lateinit var lcm: LocalBroadcastManager
|
||||||
|
|
||||||
private lateinit var mediaProjectionManager: MediaProjectionManager
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -59,7 +60,18 @@ class MainActivity : AppCompatActivity() {
|
|||||||
initClick()
|
initClick()
|
||||||
|
|
||||||
registerResult()
|
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() {
|
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?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == Activity.RESULT_OK) {
|
if (requestCode == REQUEST_CODE) {
|
||||||
startSusService(resultCode, data)
|
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startSusService(resultCode: Int, data: Intent?) {
|
|
||||||
val serviceIntent = Intent(this, SusService::class.java).apply {
|
val serviceIntent = Intent(this, SusService::class.java).apply {
|
||||||
putExtra("resultCode", resultCode)
|
putExtra(mpResultCode, resultCode)
|
||||||
putExtra("data", data)
|
putExtra(mpData, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
startForegroundService(serviceIntent)
|
startForegroundService(serviceIntent)
|
||||||
} else {
|
} else {
|
||||||
startService(serviceIntent)
|
startService(serviceIntent)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("MainActivity", "Screen capture permission denied")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
|
binding.chSourceLanguage.text = PreferenceLanguageUtils.getString("language_source")
|
||||||
@ -120,6 +125,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun afterTextChanged(s: Editable?) {
|
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的内容显示或隐藏粘贴按钮
|
// 根据EditText的内容显示或隐藏粘贴按钮
|
||||||
if (s.isNullOrEmpty()) {
|
if (s.isNullOrEmpty()) {
|
||||||
binding.tvMainTrans.visibility = View.GONE
|
binding.tvMainTrans.visibility = View.GONE
|
||||||
@ -127,6 +140,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
} else {
|
} else {
|
||||||
binding.tvMainTrans.visibility = View.VISIBLE
|
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 {
|
binding.ivMainHistory.setOnClickListener {
|
||||||
|
intent = Intent(this, HistoryActivity::class.java)
|
||||||
|
intent.putExtra("remove", "remove_collection")
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(this, HistoryActivity::class.java)
|
intent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
binding.llQuickSet.setOnClickListener {
|
binding.llQuickSet.setOnClickListener {
|
||||||
@ -177,17 +196,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
binding.ivQuickStart.setOnClickListener {
|
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 {
|
binding.ivMainExChange.setOnClickListener {
|
||||||
@ -203,9 +217,6 @@ class MainActivity : AppCompatActivity() {
|
|||||||
binding.chSourceLanguage.text = currentTargetLanguage
|
binding.chSourceLanguage.text = currentTargetLanguage
|
||||||
binding.chTargetLanguage.text = currentSourceLanguage
|
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
|
return
|
||||||
}
|
}
|
||||||
val intent = Intent(this, TextResultActivity::class.java)
|
val intent = Intent(this, TextResultActivity::class.java)
|
||||||
// 将字符串数据添加到Intent中
|
|
||||||
intent.putExtra("source_text", binding.etText.text.toString())
|
intent.putExtra("source_text", binding.etText.text.toString())
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
binding.etText.text = null
|
binding.etText.text = null
|
||||||
@ -228,11 +238,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
||||||
speechIntent.putExtra(
|
speechIntent.putExtra(
|
||||||
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
|
RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
|
||||||
5000
|
4000
|
||||||
) // 设置5秒的静默时间
|
) // 设置5秒的静默时间
|
||||||
speechIntent.putExtra(
|
speechIntent.putExtra(
|
||||||
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
|
RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
|
||||||
5000
|
4000
|
||||||
) // 设置5秒的可能完全静默时间
|
) // 设置5秒的可能完全静默时间
|
||||||
|
|
||||||
|
|
||||||
@ -254,7 +264,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
launcher?.launch(speechIntent)
|
launcher?.launch(speechIntent)
|
||||||
} catch (ea: ActivityNotFoundException) {
|
} 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
|
package com.assimilate.alltrans.viewui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
@ -44,5 +45,11 @@ class SettingsActivity
|
|||||||
bottomSheetDialog.show()
|
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.content.res.Configuration
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.AdapterView.OnItemSelectedListener
|
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
import android.widget.PopupMenu
|
||||||
|
import android.widget.SearchView
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import androidx.camera.core.ImageCapture
|
import androidx.camera.core.ImageCapture
|
||||||
@ -31,14 +35,24 @@ import androidx.camera.lifecycle.ProcessCameraProvider
|
|||||||
import androidx.camera.view.PreviewView
|
import androidx.camera.view.PreviewView
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
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.R
|
||||||
|
import com.assimilate.alltrans.adapters.LanguageAdapter
|
||||||
import com.assimilate.alltrans.common.BitmapUtils
|
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.TextRecognitionProcessor
|
||||||
import com.assimilate.alltrans.common.VisionImageProcessor
|
import com.assimilate.alltrans.common.VisionImageProcessor
|
||||||
import com.assimilate.alltrans.common.Widget
|
import com.assimilate.alltrans.common.Widget
|
||||||
import com.assimilate.alltrans.curview.GraphicOverlay
|
import com.assimilate.alltrans.curview.GraphicOverlay
|
||||||
import com.google.android.gms.common.annotation.KeepName
|
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.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.chinese.ChineseTextRecognizerOptions
|
||||||
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
|
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
|
||||||
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
|
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.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.ArrayList
|
||||||
import java.util.Locale
|
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
|
@KeepName
|
||||||
class StillImageActivity : AppCompatActivity() {
|
class StillImageActivity : AppCompatActivity() {
|
||||||
private var preview: ImageView? = null
|
private var preview: ImageView? = null
|
||||||
@ -67,39 +84,35 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
private var isFlashOn = false
|
private var isFlashOn = false
|
||||||
|
|
||||||
private val REQUEST_CAMERA_PERMISSION = 100
|
private val REQUEST_CAMERA_PERMISSION = 100
|
||||||
|
private lateinit var bottomSheetDialog: BottomSheetDialog
|
||||||
|
private var chooseLanguage :Boolean = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
setContentView(R.layout.activity_still_image)
|
setContentView(R.layout.activity_still_image)
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
|
||||||
// 检查并请求相机权限
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom)
|
||||||
!= PackageManager.PERMISSION_GRANTED
|
insets
|
||||||
) {
|
|
||||||
ActivityCompat.requestPermissions(
|
|
||||||
this,
|
|
||||||
arrayOf(Manifest.permission.CAMERA),
|
|
||||||
REQUEST_CAMERA_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget.makeSnackbar(this, "Photographing text for translation")
|
Widget.makeSnackbar(this, "Photographing text for translation")
|
||||||
|
|
||||||
preview = findViewById(R.id.preview)
|
preview = findViewById(R.id.preview)
|
||||||
graphicOverlay = findViewById(R.id.graphic_overlay)
|
graphicOverlay = findViewById(R.id.graphic_overlay)
|
||||||
|
|
||||||
populateFeatureSelector()
|
|
||||||
|
|
||||||
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||||
if (savedInstanceState != null) {
|
savedInstanceState?.let {
|
||||||
imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI)
|
imageUri = it.getParcelable(KEY_IMAGE_URI)
|
||||||
imageMaxWidth = savedInstanceState.getInt(KEY_IMAGE_MAX_WIDTH)
|
imageMaxWidth = it.getInt(KEY_IMAGE_MAX_WIDTH)
|
||||||
imageMaxHeight = savedInstanceState.getInt(KEY_IMAGE_MAX_HEIGHT)
|
imageMaxHeight = it.getInt(KEY_IMAGE_MAX_HEIGHT)
|
||||||
selectedSize = savedInstanceState.getString(KEY_SELECTED_SIZE)
|
selectedSize = it.getString(KEY_SELECTED_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootView = findViewById<View>(R.id.root)
|
val rootView = findViewById<View>(R.id.root)
|
||||||
rootView.viewTreeObserver.addOnGlobalLayoutListener(
|
rootView.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||||
object : ViewTreeObserver.OnGlobalLayoutListener {
|
ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
rootView.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||||
imageMaxWidth = rootView.width
|
imageMaxWidth = rootView.width
|
||||||
@ -108,15 +121,83 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
tryReloadAndDetectInImage()
|
tryReloadAndDetectInImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// 初始化相机
|
|
||||||
startCamera()
|
|
||||||
outputDirectory = getOutputDirectory()
|
outputDirectory = getOutputDirectory()
|
||||||
|
// 检查并启动相机
|
||||||
|
checkCameraPermission()
|
||||||
|
initView()
|
||||||
initClick()
|
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() {
|
private fun initClick() {
|
||||||
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
|
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
|
||||||
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
|
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
|
||||||
@ -125,29 +206,28 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
toggleFlash()
|
toggleFlash()
|
||||||
updateFlashButtonUI()
|
updateFlashButtonUI()
|
||||||
}
|
}
|
||||||
findViewById<TextView>(R.id.still_source_language).setOnClickListener { }
|
findViewById<TextView>(R.id.still_source_language).setOnClickListener {
|
||||||
findViewById<TextView>(R.id.still_target_language).setOnClickListener { }
|
chooseLanguage = false
|
||||||
findViewById<TextView>(R.id.still_exChange).setOnClickListener { }
|
bottomSheetDialog.show()
|
||||||
|
}
|
||||||
|
findViewById<TextView>(R.id.still_target_language).setOnClickListener {
|
||||||
|
chooseLanguage = true
|
||||||
|
bottomSheetDialog.show()
|
||||||
|
}
|
||||||
|
findViewById<ImageView>(R.id.still_exChange).setOnClickListener {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun toggleFlash() {
|
private fun toggleFlash() {
|
||||||
if (isFlashOn) {
|
imageCapture.flashMode =
|
||||||
imageCapture.flashMode = ImageCapture.FLASH_MODE_OFF
|
if (isFlashOn) ImageCapture.FLASH_MODE_OFF else ImageCapture.FLASH_MODE_ON
|
||||||
} else {
|
|
||||||
imageCapture.flashMode = ImageCapture.FLASH_MODE_ON
|
|
||||||
}
|
|
||||||
isFlashOn = !isFlashOn
|
isFlashOn = !isFlashOn
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFlashButtonUI() {
|
private fun updateFlashButtonUI() {
|
||||||
if (isFlashOn) {
|
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
|
||||||
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_bulibuli)
|
|
||||||
} else {
|
|
||||||
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(R.drawable.ic_still_notbuli)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun startCamera() {
|
private fun startCamera() {
|
||||||
val previewView = findViewById<PreviewView>(R.id.photo_preview)
|
val previewView = findViewById<PreviewView>(R.id.photo_preview)
|
||||||
@ -155,16 +235,11 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
cameraProviderFuture.addListener(Runnable {
|
cameraProviderFuture.addListener(Runnable {
|
||||||
val cameraProvider = cameraProviderFuture.get()
|
val cameraProvider = cameraProviderFuture.get()
|
||||||
val preview = Preview.Builder()
|
val preview = Preview.Builder().build().also {
|
||||||
.build()
|
|
||||||
.also {
|
|
||||||
it.setSurfaceProvider(previewView.surfaceProvider)
|
it.setSurfaceProvider(previewView.surfaceProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
imageCapture = ImageCapture.Builder()
|
imageCapture = ImageCapture.Builder().setFlashMode(ImageCapture.FLASH_MODE_OFF).build()
|
||||||
.setFlashMode(ImageCapture.FLASH_MODE_OFF) // 默认关闭闪光灯
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -177,14 +252,10 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun takePhoto() {
|
private fun takePhoto() {
|
||||||
|
|
||||||
val imageCapture = imageCapture
|
|
||||||
|
|
||||||
val photoFile = File(
|
val photoFile = File(
|
||||||
outputDirectory,
|
outputDirectory,
|
||||||
SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
|
SimpleDateFormat(FILENAME_FORMAT, Locale.US).format(System.currentTimeMillis()) + ".jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||||
|
|
||||||
imageCapture.takePicture(
|
imageCapture.takePicture(
|
||||||
@ -196,25 +267,20 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
||||||
val savedUri = Uri.fromFile(photoFile)
|
imageUri = Uri.fromFile(photoFile)
|
||||||
imageUri = savedUri
|
Log.d(TAG, "Photo capture succeeded: $imageUri")
|
||||||
val msg = "Photo capture succeeded: $savedUri"
|
|
||||||
Log.d(TAG, msg)
|
|
||||||
tryReloadAndDetectInImage()
|
tryReloadAndDetectInImage()
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOutputDirectory(): File {
|
private fun getOutputDirectory(): File {
|
||||||
val mediaDir = externalMediaDirs.firstOrNull()?.let {
|
val mediaDir = externalMediaDirs.firstOrNull()?.let {
|
||||||
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
|
File(it, resources.getString(R.string.app_name)).apply { mkdirs() }
|
||||||
}
|
}
|
||||||
return if (mediaDir != null && mediaDir.exists())
|
return if (mediaDir != null && mediaDir.exists()) mediaDir else filesDir
|
||||||
mediaDir else filesDir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
permissions: Array<String>,
|
permissions: Array<String>,
|
||||||
@ -224,80 +290,43 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
REQUEST_CAMERA_PERMISSION -> {
|
REQUEST_CAMERA_PERMISSION -> {
|
||||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||||
// Permission is granted, proceed with camera
|
startCamera()
|
||||||
startCameraIntentForResult()
|
|
||||||
} else {
|
} else {
|
||||||
// Permission denied, show a message to the user
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
this,
|
||||||
"Camera permission is required to use the camera",
|
"Camera permission is required to use the camera",
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
public override fun onResume() {
|
|
||||||
super.onResume()
|
super.onResume()
|
||||||
Log.d(TAG, "onResume")
|
Log.d(TAG, "onResume")
|
||||||
|
|
||||||
|
|
||||||
createImageProcessor()
|
createImageProcessor()
|
||||||
tryReloadAndDetectInImage()
|
tryReloadAndDetectInImage()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
imageProcessor?.run { this.stop() }
|
imageProcessor?.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
imageProcessor?.stop()
|
||||||
imageProcessor?.run { this.stop() }
|
|
||||||
// 释放相机资源
|
|
||||||
if (::cameraProviderFuture.isInitialized) {
|
if (::cameraProviderFuture.isInitialized) {
|
||||||
cameraProviderFuture.get().unbindAll()
|
cameraProviderFuture.get().unbindAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateFeatureSelector() {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
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) {
|
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putParcelable(KEY_IMAGE_URI, imageUri)
|
outState.putParcelable(KEY_IMAGE_URI, imageUri)
|
||||||
outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth)
|
outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth)
|
||||||
@ -305,104 +334,71 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
outState.putString(KEY_SELECTED_SIZE, selectedSize)
|
outState.putString(KEY_SELECTED_SIZE, selectedSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCameraIntentForResult() {
|
private fun createImageProcessor() {
|
||||||
|
imageProcessor?.stop()
|
||||||
// Ensure permission is still granted before starting camera intent
|
try {
|
||||||
if (ContextCompat.checkSelfPermission(
|
imageProcessor = when (selectedMode) {
|
||||||
|
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
|
||||||
this,
|
this,
|
||||||
Manifest.permission.CAMERA
|
ChineseTextRecognizerOptions.Builder().build()
|
||||||
) != PackageManager.PERMISSION_GRANTED
|
)
|
||||||
) {
|
|
||||||
|
"Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
|
||||||
|
this,
|
||||||
|
DevanagariTextRecognizerOptions.Builder().build()
|
||||||
|
)
|
||||||
|
|
||||||
|
TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
|
||||||
|
this,
|
||||||
|
JapaneseTextRecognizerOptions.Builder().build()
|
||||||
|
)
|
||||||
|
|
||||||
|
TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
|
||||||
|
this,
|
||||||
|
KoreanTextRecognizerOptions.Builder().build()
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> TextRecognitionProcessor(
|
||||||
|
this,
|
||||||
|
TextRecognizerOptions.Builder().build()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Can not create image processor: $selectedMode", e)
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this,
|
applicationContext,
|
||||||
"Camera permission is required to use the camera",
|
"Can not create image processor: " + e.message,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).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() {
|
private fun tryReloadAndDetectInImage() {
|
||||||
Log.d(TAG, "Try reload and detect image")
|
|
||||||
try {
|
try {
|
||||||
if (imageUri == null) {
|
if (imageUri == null) return
|
||||||
return
|
if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) return
|
||||||
}
|
|
||||||
if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) {
|
|
||||||
// UI layout has not finished yet, will reload once it's ready.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val imageBitmap =
|
val imageBitmap =
|
||||||
BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return
|
BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return
|
||||||
// Clear the overlay first
|
|
||||||
graphicOverlay!!.clear()
|
|
||||||
|
|
||||||
val resizedBitmap: Bitmap
|
if (selectedSize == SIZE_SCREEN) {
|
||||||
resizedBitmap =
|
val targetedSize = targetedWidthHeight
|
||||||
if (selectedSize == SIZE_ORIGINAL) {
|
val scaleFactor = max(
|
||||||
imageBitmap
|
|
||||||
} else {
|
|
||||||
// Get the dimensions of the image view
|
|
||||||
val targetedSize: Pair<Int, Int> = targetedWidthHeight
|
|
||||||
|
|
||||||
// Determine how much to scale down the image
|
|
||||||
val scaleFactor =
|
|
||||||
Math.max(
|
|
||||||
imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
|
imageBitmap.width.toFloat() / targetedSize.first.toFloat(),
|
||||||
imageBitmap.height.toFloat() / targetedSize.second.toFloat()
|
imageBitmap.height.toFloat() / targetedSize.second.toFloat()
|
||||||
)
|
)
|
||||||
Bitmap.createScaledBitmap(
|
val resizedBitmap = Bitmap.createScaledBitmap(
|
||||||
imageBitmap,
|
imageBitmap,
|
||||||
(imageBitmap.width / scaleFactor).toInt(),
|
(imageBitmap.width / scaleFactor).toInt(),
|
||||||
(imageBitmap.height / scaleFactor).toInt(),
|
(imageBitmap.height / scaleFactor).toInt(),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
preview?.setImageBitmap(resizedBitmap)
|
||||||
|
processImage(resizedBitmap)
|
||||||
preview!!.setImageBitmap(resizedBitmap)
|
|
||||||
if (imageProcessor != null) {
|
|
||||||
graphicOverlay!!.setImageSourceInfo(
|
|
||||||
resizedBitmap.width,
|
|
||||||
resizedBitmap.height,
|
|
||||||
/* isFlipped= */ false
|
|
||||||
)
|
|
||||||
imageProcessor!!.processBitmap(resizedBitmap, graphicOverlay)
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(
|
preview?.setImageBitmap(imageBitmap)
|
||||||
TAG,
|
processImage(imageBitmap)
|
||||||
"Null imageProcessor, please check adb logs for imageProcessor creation error"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Error retrieving saved image")
|
Log.e(TAG, "Error retrieving saved image")
|
||||||
@ -412,95 +408,52 @@ class StillImageActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private val targetedWidthHeight: Pair<Int, Int>
|
private val targetedWidthHeight: Pair<Int, Int>
|
||||||
get() {
|
get() {
|
||||||
val targetWidth: Int
|
val targetWidth = if (isLandScape) max(imageMaxWidth, imageMaxHeight) else min(
|
||||||
val targetHeight: Int
|
imageMaxWidth,
|
||||||
when (selectedSize) {
|
imageMaxHeight
|
||||||
SIZE_SCREEN -> {
|
)
|
||||||
targetWidth = imageMaxWidth
|
val targetHeight = if (isLandScape) min(imageMaxWidth, imageMaxHeight) else max(
|
||||||
targetHeight = imageMaxHeight
|
imageMaxWidth,
|
||||||
}
|
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")
|
|
||||||
}
|
|
||||||
return Pair(targetWidth, targetHeight)
|
return Pair(targetWidth, targetHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createImageProcessor() {
|
private fun processImage(bitmap: Bitmap) {
|
||||||
try {
|
graphicOverlay?.clear()
|
||||||
when (selectedMode) {
|
imageProcessor?.processBitmap(bitmap, graphicOverlay)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Can not create image processor: $selectedMode", e)
|
private fun startChooseImageIntentForResult() {
|
||||||
Toast.makeText(
|
val intent = Intent().apply {
|
||||||
applicationContext,
|
type = "image/*"
|
||||||
"Can not create image processor: " + e.message,
|
action = Intent.ACTION_GET_CONTENT
|
||||||
Toast.LENGTH_LONG
|
}
|
||||||
)
|
startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE)
|
||||||
.show()
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
|
||||||
|
imageUri = data.data
|
||||||
|
tryReloadAndDetectInImage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "StillImageActivity"
|
private const val TAG = "StillImageActivity"
|
||||||
private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin"
|
private const val KEY_IMAGE_URI = "com.google.mlkit.demo.stillImage.KEY_IMAGE_URI"
|
||||||
private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese"
|
private const val KEY_IMAGE_MAX_WIDTH =
|
||||||
private const val TEXT_RECOGNITION_DEVANAGARI = "Text Recognition Devanagari"
|
"com.google.mlkit.demo.stillImage.KEY_IMAGE_MAX_WIDTH"
|
||||||
private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese"
|
private const val KEY_IMAGE_MAX_HEIGHT =
|
||||||
private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean"
|
"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" // Match screen width
|
private const val SIZE_SCREEN = "w:screen"
|
||||||
private const val SIZE_1024_768 = "w:1024" // ~1024*768 in a normal ratio
|
private const val TEXT_RECOGNITION_CHINESE = "Chinese, Simplified"
|
||||||
private const val SIZE_640_480 = "w:640" // ~640*480 in a normal ratio
|
private const val TEXT_RECOGNITION_JAPANESE = "Japanese"
|
||||||
private const val SIZE_ORIGINAL = "w:original" // Original image size
|
private const val TEXT_RECOGNITION_KOREAN = "Korean"
|
||||||
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 FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
|
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"?>
|
<?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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/main"
|
android:id="@+id/main"
|
||||||
@ -8,18 +8,15 @@
|
|||||||
android:background="@color/main_text_ffffffff"
|
android:background="@color/main_text_ffffffff"
|
||||||
tools:context=".viewui.HistoryActivity">
|
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
|
<ImageView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent">
|
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
|
<ImageView
|
||||||
android:id="@+id/iv_func_trans"
|
android:id="@+id/iv_func_trans"
|
||||||
@ -27,7 +24,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:layout_marginEnd="76dp"
|
android:layout_marginEnd="76dp"
|
||||||
android:src="@drawable/ic_close"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
@ -35,20 +31,23 @@
|
|||||||
android:id="@+id/tv_func_trans"
|
android:id="@+id/tv_func_trans"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="2dp"
|
android:layout_marginTop="19dp"
|
||||||
android:text="@string/main_dictionary"
|
android:text="@string/his_title"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/main_text_ff1f1724"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/iv_func_trans"
|
android:textSize="18sp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/iv_func_trans"
|
android:textStyle="bold"
|
||||||
app:layout_constraintTop_toTopOf="@id/iv_func_trans" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/histories"
|
android:id="@+id/histories"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_marginTop="13dp"
|
||||||
app:layout_constraintHeight_percent="0.9"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/remove"
|
android:id="@+id/remove"
|
||||||
@ -58,11 +57,11 @@
|
|||||||
android:drawableStart="@drawable/ic_remove"
|
android:drawableStart="@drawable/ic_remove"
|
||||||
android:drawablePadding="6dp"
|
android:drawablePadding="6dp"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:textColor="#FFFF5F5F"
|
|
||||||
android:text="@string/his_delete"
|
android:text="@string/his_delete"
|
||||||
|
android:textColor="#FFFF5F5F"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</FrameLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@ -9,19 +9,42 @@
|
|||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
tools:context=".viewui.LanguageChangeActivity">
|
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
|
<LinearLayout
|
||||||
android:id="@+id/change_language"
|
android:id="@+id/change_language"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="23dp"
|
android:layout_marginTop="13dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:elevation="2dp"
|
android:elevation="2dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toBottomOf="@id/iv_ch_back">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_change_source"
|
android:id="@+id/tv_change_source"
|
||||||
@ -31,11 +54,9 @@
|
|||||||
android:background="@drawable/button_r20_white_bg"
|
android:background="@drawable/button_r20_white_bg"
|
||||||
android:drawablePadding="25dp"
|
android:drawablePadding="25dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
|
android:textColor="@color/main_text_ff0e8ce8"
|
||||||
android:textColor="@color/main_text_ff1f1724"
|
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:drawableEndCompat="@drawable/ic_down_choose" />
|
app:drawableEndCompat="@drawable/ic_down_choose" />
|
||||||
@ -85,6 +106,7 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/change_language">
|
app:layout_constraintTop_toBottomOf="@id/change_language">
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
<androidx.appcompat.widget.SearchView
|
||||||
|
android:id="@+id/ch_search"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
|||||||
@ -146,7 +146,7 @@
|
|||||||
android:id="@+id/tv_main_limit_text"
|
android:id="@+id/tv_main_limit_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/main_limit_num"
|
android:text="0/1800"
|
||||||
android:textColor="@color/main_text_ff0e8ce8"
|
android:textColor="@color/main_text_ff0e8ce8"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
|||||||
@ -228,7 +228,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:drawablePadding="12dp"
|
android:drawablePadding="12dp"
|
||||||
android:text="1.11"
|
android:text="1.0.1"
|
||||||
android:textColor="@color/main_text_ff1f1724"
|
android:textColor="@color/main_text_ff1f1724"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:drawableEndCompat="@drawable/ic_next" />
|
app:drawableEndCompat="@drawable/ic_next" />
|
||||||
|
|||||||
@ -4,24 +4,25 @@
|
|||||||
android:id="@+id/root"
|
android:id="@+id/root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/bg_53514c"
|
||||||
android:keepScreenOn="true">
|
android:keepScreenOn="true">
|
||||||
|
|
||||||
<androidx.camera.view.PreviewView
|
<androidx.camera.view.PreviewView
|
||||||
android:id="@+id/photo_preview"
|
android:id="@+id/photo_preview"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/control"
|
app:layout_constraintBottom_toTopOf="@id/control"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/preview"
|
android:id="@+id/preview"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
app:layout_constraintBaseline_toTopOf="@id/control"
|
app:layout_constraintBottom_toTopOf="@id/control"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -30,6 +31,7 @@
|
|||||||
android:id="@+id/graphic_overlay"
|
android:id="@+id/graphic_overlay"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
android:background="@color/bg_40_000000"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/preview"
|
app:layout_constraintBottom_toBottomOf="@id/preview"
|
||||||
app:layout_constraintLeft_toLeftOf="@id/preview"
|
app:layout_constraintLeft_toLeftOf="@id/preview"
|
||||||
app:layout_constraintRight_toRightOf="@id/preview"
|
app:layout_constraintRight_toRightOf="@id/preview"
|
||||||
@ -46,7 +48,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="19dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -115,16 +117,14 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/button_r20_black_bg"
|
android:background="@drawable/button_r20_99000000_bg"
|
||||||
android:drawablePadding="25dp"
|
android:drawablePadding="25dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/text_source_language"
|
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
|
||||||
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
@ -134,7 +134,7 @@
|
|||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:background="@drawable/button_r20_black_bg"
|
android:background="@drawable/button_r20_99000000_bg"
|
||||||
android:paddingStart="12dp"
|
android:paddingStart="12dp"
|
||||||
android:paddingTop="6dp"
|
android:paddingTop="6dp"
|
||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
@ -146,16 +146,14 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:background="@drawable/button_r20_black_bg"
|
android:background="@drawable/button_r20_99000000_bg"
|
||||||
android:drawablePadding="25dp"
|
android:drawablePadding="25dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/text_target_language"
|
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
|
||||||
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
app:drawableEndCompat="@drawable/ic_down_choose_white" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,18 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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
|
<LinearLayout
|
||||||
android:id="@+id/ll_main_enter_text"
|
android:id="@+id/ll_main_enter_text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -59,8 +71,8 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:layout_marginStart="12dp"
|
android:padding="12dp"
|
||||||
android:src="@drawable/ic_close" />
|
android:src="@drawable/ic_tr_close" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|||||||
@ -4,23 +4,29 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@android:color/transparent">
|
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
|
<com.assimilate.alltrans.curview.GraphicOverlay
|
||||||
android:id="@+id/sus_graphic_overlay"
|
android:id="@+id/sus_graphic_overlay"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="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
|
<ImageView
|
||||||
|
android:id="@+id/sus_global_close"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:padding="20dp"
|
android:paddingStart="1dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
android:src="@drawable/ic_close" />
|
android:src="@drawable/ic_close" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
@ -1,14 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<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_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/button_r20_black_bg"
|
android:background="@drawable/button_r20_black_bg"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<!-- 第一行 -->
|
<!-- 第一行 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/sus_ll1"
|
||||||
android:layout_width="180dp"
|
android:layout_width="180dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
@ -46,6 +46,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<!-- 第二行 -->
|
<!-- 第二行 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/sus_ll2"
|
||||||
android:layout_width="180dp"
|
android:layout_width="180dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
@ -84,6 +85,7 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
<!-- 分隔线 -->
|
<!-- 分隔线 -->
|
||||||
<View
|
<View
|
||||||
|
android:id="@+id/sus_view1"
|
||||||
android:layout_width="110dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="5dp"
|
android:layout_height="5dp"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@ -103,7 +105,6 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/iv_sus_home"
|
android:id="@+id/iv_sus_home"
|
||||||
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@ -121,5 +122,4 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:src="@drawable/sus_trans_move" />
|
android:src="@drawable/sus_trans_move" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
|
||||||
|
<!-- text_bg-->
|
||||||
|
<color name="bg_40_000000">#66000000</color>
|
||||||
<!--main_page-->
|
<!--main_page-->
|
||||||
<color name="main_bg_fff9f9f9">#FFF9F9F9</color>
|
<color name="main_bg_fff9f9f9">#FFF9F9F9</color>
|
||||||
<color name="main_bg_ffe2efff">#FFE2EFFF</color>
|
<color name="main_bg_ffe2efff">#FFE2EFFF</color>
|
||||||
|
|||||||
@ -13,19 +13,25 @@
|
|||||||
<string name="text_main_title">translate</string>
|
<string name="text_main_title">translate</string>
|
||||||
<string name="text_source_language">Chinese</string>
|
<string name="text_source_language">Chinese</string>
|
||||||
<string name="text_target_language">English</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_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_try_text">Translate</string>
|
||||||
<string name="main_quick_text">Quick Translate</string>
|
<string name="main_quick_text">Quick Translate</string>
|
||||||
<string name="main_quick_set">settings</string>
|
<string name="main_quick_set">settings</string>
|
||||||
<string name="main_photo_trans">Photo Translation</string>
|
<string name="main_photo_trans">Photo Translation</string>
|
||||||
<string name="main_dictionary">Dictionary</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-->
|
<!--text_re_page-->
|
||||||
<string name="tr_add_new">New Translate</string>
|
<string name="tr_add_new">New Translate</string>
|
||||||
<string name="tr_common">Common</string>
|
<string name="tr_common">Common</string>
|
||||||
<string name="tr_other">other</string>
|
<string name="tr_other">other</string>
|
||||||
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
|
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
|
||||||
|
<string name="tr_title">Translator</string>
|
||||||
<!--settings_page-->
|
<!--settings_page-->
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="languages">Languages</string>
|
<string name="languages">Languages</string>
|
||||||
@ -38,6 +44,7 @@
|
|||||||
<string name="quick_set_touming">透明度</string>
|
<string name="quick_set_touming">透明度</string>
|
||||||
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
|
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
|
||||||
<string name="quick_set_zd_time">自动折叠时间</string>
|
<string name="quick_set_zd_time">自动折叠时间</string>
|
||||||
|
<string name="favor_title">Favorite</string>
|
||||||
<!-- sus_view-->
|
<!-- sus_view-->
|
||||||
<string name="global_translation">Global Translation</string>
|
<string name="global_translation">Global Translation</string>
|
||||||
<string name="copy_text">Copy Text</string>
|
<string name="copy_text">Copy Text</string>
|
||||||
@ -45,6 +52,8 @@
|
|||||||
<string name="district_translation">District Translation</string>
|
<string name="district_translation">District Translation</string>
|
||||||
<!-- his_page-->
|
<!-- his_page-->
|
||||||
<string name="his_delete">Delete</string>
|
<string name="his_delete">Delete</string>
|
||||||
|
<string name="his_title">History record</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Loading…
Reference in New Issue
Block a user