version 1.0.2 -Improve and optimize

This commit is contained in:
Simon 2024-08-02 18:46:46 +08:00
parent c4c3998f65
commit 3825eca205
89 changed files with 4234 additions and 1131 deletions

View File

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

View File

@ -1,6 +1,13 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
// Make sure that you have the Google services Gradle plugin
id("com.google.gms.google-services")
// Add the Crashlytics Gradle plugin
id("com.google.firebase.crashlytics")
}
android {
@ -11,8 +18,8 @@ android {
applicationId = "com.assimilate.alltrans"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0"
versionCode = 2
versionName = "1.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@ -24,16 +31,37 @@ android {
buildTypes {
debug { }
debug {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
release {
isMinifyEnabled = false
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
// applicationVariants.all {
// outputs.all {
// val appName = "Translark" // 替换为你的应用名称
// val versionName = versionName
// val date = SimpleDateFormat("yyyyMMdd-HHmm").format(Date())
// val newApkName = "${appName}-${date}-${versionName}.apk"
// (this as com.android.build.gradle.internal.api.BaseVariantOutputImpl).outputFileName = newApkName
// }
// }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
@ -45,6 +73,15 @@ android {
dependencies {
// Import the BoM for the Firebase platform
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
// Add the dependencies for the Crashlytics and Analytics libraries
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.firebase:firebase-analytics")
// To recognize Latin script
implementation("com.google.mlkit:text-recognition:16.0.0")
// To recognize Chinese script

29
app/google-services.json Executable file
View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "567922999559",
"project_id": "translark-3b0ac",
"storage_bucket": "translark-3b0ac.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:567922999559:android:b84454b1dc33599afcc5a2",
"android_client_info": {
"package_name": "com.assimilate.alltrans"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAQxYgnToybS5ZXFfa45iBWeJZAfnK-WuE"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -16,6 +16,29 @@
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# 保留com.assimilate.alltrans.keepmodel包及其子包的所有类
-keep class com.assimilate.alltrans.keepmodel.** { *; }
# 保留com.assimilate.alltrans.http包及其子包的所有类
-keep class com.assimilate.alltrans.http.** { *; }
# 保留Gson相关的混淆规则
-keep class com.google.gson.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
-keep class **$Gson$Types { *; }
-keep class **$Gson$Preconditions { *; }
# 保留使用的注解
-keepattributes *Annotation*
# 保留Retrofit类和方法
-keep class retrofit2.** { *; }
-dontwarn retrofit2.**
-keep class okhttp3.** { *; }
-dontwarn okhttp3.**
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,37 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.assimilate.alltrans",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 2,
"versionName": "1.0.2",
"outputFile": "app-release.apk"
}
],
"elementType": "File",
"baselineProfiles": [
{
"minApi": 28,
"maxApi": 30,
"baselineProfiles": [
"baselineProfiles/1/app-release.dm"
]
},
{
"minApi": 31,
"maxApi": 2147483647,
"baselineProfiles": [
"baselineProfiles/0/app-release.dm"
]
}
],
"minSdkVersionForDexing": 23
}

Binary file not shown.

View File

@ -7,7 +7,6 @@
android:required="false" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -17,26 +16,29 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application
android:name=".MyApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/app_icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:largeHeap="true"
android:roundIcon="@mipmap/app_icon_round"
android:supportsRtl="true"
android:theme="@style/Theme.Alltrans"
tools:targetApi="31">
<activity
android:name=".viewui.MainActivity"
android:exported="false"
android:launchMode="singleTask" />
<service
android:name=".allservice.SusService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="mediaProjection" />
<activity
android:name=".viewui.QuickSetActivity"
android:exported="false" />
@ -45,7 +47,8 @@
android:exported="false" />
<activity
android:name=".viewui.LanguageChangeActivity"
android:exported="false" />
android:exported="false"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
<activity
android:name=".viewui.TextResultActivity"
android:exported="false" />
@ -54,10 +57,10 @@
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="ocr" /> <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
<activity
android:name=".viewui.StillImageActivity"
android:name=".viewui.PhotoImageActivity"
android:exported="false" />
<activity
android:name=".viewui.MainActivity"
android:name=".viewui.WelActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -66,6 +69,7 @@
</intent-filter>
</activity>
<activity android:name=".viewui.HistoryActivity" />
<activity android:name=".viewui.DicActivity" />
</application>
</manifest>

File diff suppressed because one or more lines are too long

View File

@ -5,34 +5,26 @@ import android.app.Application
import android.content.Context
import android.os.Build
import android.webkit.WebView
import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.ScreenUtils
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
class MyApp : Application() {
init {
instance = this
}
private fun setSystemLanguage() {
// 检查是否是第一次进入应用
if (PreferenceLanguageUtils.isFirstTime()) {
// 第一次进入应用的逻辑
val config = applicationContext().resources.configuration
val locale = config.locale.toString() // 获取完整的语言环境信息,如 "en_US" 或 "zh_CN"
Logger.d("dslocaleds", locale)
val languages = LanguagesConstants.getInstance().getLanguageByLanguageCode(locale, this)
Logger.d("dslocaledsa", languages.language)
PreferenceLanguageUtils.putString(
"language_source",
"language_target",
languages.language.toString().trim()
)
// 设置已经不是第一次进入应用了
PreferenceLanguageUtils.setNotFirstTime()
}
@ -51,6 +43,7 @@ class MyApp : Application() {
super.onCreate()
instance = this
// ScreenUtils.init(this)
setSystemLanguage()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@ -74,5 +67,46 @@ class MyApp : Application() {
object Config {
const val openSusViewMode: String = "open_sus_view"
// firebase_event_key
const val launchPv = "launch_pv"
const val homePv = "home_pv"
const val languageChoose = "language_choose"
const val languageFrom = "language_from"
const val textTransSwitch = "text_trans_switch"
const val textInput = "text_input"
const val textTransClick = "text_trans_click"
const val textTransResult = "text_trans_result"
const val textTransPaste = "text_trans_paste"
const val textVoiceClick = "text_voice_click"
const val textVoiceResult = "text_voice_result"
const val textTransPlay = "text_trans_play"
const val textTransCopy = "text_trans_copy"
const val textTransShare = "text_trans_share"
const val textTransLike = "text_trans_like"
const val hoverButtonClick = "hover_button_click"
const val hoverButtonCancel = "hover_button_cancel"
const val hover_button_agree = "hover_button_agree"
const val hover_screen_click = "hover_screen_click"
const val hover_screen_imp = "hover_screen_imp"
const val hover_screen_global = "hover_screen_global"
const val hover_global_result = "hover_global_result"
const val hover_screen_district = "hover_screen_district"
const val hover_district_result = "hover_district_result"
const val image_click = "image_click"
const val image_trans_camera = "image_trans_camera"
const val image_camera_result = "image_camera_result"
const val image_trans_photo = "image_trans_photo"
const val image_photo_result = "image_photo_result"
const val history_click = "history_click"
const val history_delete = "history_delete"
}
}

View File

@ -1,9 +1,8 @@
package com.assimilate.alltrans.adapters;
import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
@ -13,7 +12,7 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.assimilate.alltrans.R;
import com.assimilate.alltrans.common.Language;
import com.assimilate.alltrans.keepmodel.Language;
import com.assimilate.alltrans.databinding.LanguageItemLayoutBinding;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
@ -22,14 +21,15 @@ import java.util.ArrayList;
import java.util.List;
public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> implements Filterable {
private final Activity mActivity;
private final Context mContext;
private final ArrayList<Language> languages;
private final ArrayList<Language> filteredLanguages;
private final OnClickListener listener;
private final RequestOptions options;
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
this.mActivity = activity;
public LanguageAdapter(@NonNull final Context mContext, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
this.mContext = mContext;
this.languages = new ArrayList<>(languageList);
this.filteredLanguages = new ArrayList<>(languageList);
this.listener = onClickListener;
@ -55,7 +55,7 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
holder.mBinding.language.setText(currentLanguage.getLanguage());
} else {
holder.mBinding.language.setText(mActivity.getString(R.string.app_name));
holder.mBinding.language.setText(mContext.getString(R.string.app_name));
}
}
@ -72,14 +72,13 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
}
private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
if (mActivity.isFinishing() || mActivity.isDestroyed()) return;
if (TextUtils.isEmpty(url)) {
Glide.with(mActivity)
Glide.with(mContext)
.applyDefaultRequestOptions(options)
.load(R.mipmap.ic_launcher)
.into(view);
} else {
Glide.with(mActivity)
Glide.with(mContext)
.applyDefaultRequestOptions(options)
.load(url)
.into(view);

View File

@ -1,10 +1,10 @@
package com.assimilate.alltrans.adapters;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@ -52,17 +52,24 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
}
}
});
holder.mBinding.checkboxCurrent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// 为checkboxCurrent设置点击事件
holder.mBinding.checkboxCurrent.setOnClickListener(v -> {
boolean isChecked = holder.mBinding.checkboxCurrent.isChecked();
item.currentChecked = isChecked;
Log.d("fadsfdas", "Checkbox clicked");
if (isChecked) {
callback.updateList(Operation.ADD, item.getId(), holder.getAdapterPosition());
} else {
callback.updateList(Operation.REMOVE, item.getId(), holder.getAdapterPosition());
}
}
});
// 为整个item设置点击事件
holder.itemView.setOnClickListener(v -> {
holder.mBinding.checkboxCurrent.performClick();
});
final String itemSourceTxt = item.getSourceTxt();
@ -116,6 +123,7 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
public interface TranslationItemCallback {
void updateList(Operation operation, long id, int position);
void speech(String value);
}

View File

@ -7,80 +7,34 @@ import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.Image
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import androidx.core.app.NotificationCompat
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
import com.assimilate.alltrans.common.ScreenCaptureManager
import com.assimilate.alltrans.curview.ControlView
import com.assimilate.alltrans.curview.CopyTextView
import com.assimilate.alltrans.curview.FloatingView
import com.assimilate.alltrans.curview.GlobalView
import com.assimilate.alltrans.curview.LanguageSelectorView
import com.assimilate.alltrans.curview.SelectionView
class SusService : Service() {
private var imageProcessor: VisionImageProcessor? = null
private lateinit var mediaProjectionManager: MediaProjectionManager
private var mediaProjection: MediaProjection? = null
private var virtualDisplay: VirtualDisplay? = null
private lateinit var displayMetrics: DisplayMetrics
private lateinit var imageReader: ImageReader
private lateinit var windowManager: WindowManager
private lateinit var floatingView: View
private lateinit var globalView: View
private lateinit var bindingSusControl: SusControlViewBinding
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
private var mResultCode = 0
private var mResultData: Intent? = null
private val mpResultCode = "mpResultCode"
private val mpData = "mpData"
private val ball_time = 5000L
private val handler = Handler(Looper.getMainLooper())
private val hideRunnable = Runnable {
// floatingView.visibility = View.GONE
// val layoutParams = floatingView.layoutParams as WindowManager.LayoutParams
// val iconLayoutParams = WindowManager.LayoutParams(
// 100, 100, // Set the size of the icon as needed
// layoutParams.type,
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
// PixelFormat.TRANSLUCENT
// )
// iconLayoutParams.gravity = layoutParams.gravity
// iconLayoutParams.x = layoutParams.x
// iconLayoutParams.y = layoutParams.y
//
// val iconView = ImageView(this).apply {
// setImageResource(R.drawable.ic_like_yes) // Set your icon drawable here
// setOnClickListener {
// floatingView.visibility = View.VISIBLE
// windowManager.removeView(this)
// windowManager.addView(floatingView, layoutParams)
// startHideTimer()
// }
// }
// windowManager.addView(iconView, iconLayoutParams)
}
private lateinit var languageSelectorView: LanguageSelectorView
private lateinit var screenCaptureManager: ScreenCaptureManager
private lateinit var floatingView: FloatingView
private lateinit var controlView: ControlView
private lateinit var globalView: GlobalView
private lateinit var selectionView: SelectionView
private lateinit var copyTextView: CopyTextView
override fun onBind(intent: Intent?): IBinder? {
return null
@ -95,9 +49,11 @@ class SusService : Service() {
val data = intent.getParcelableExtra<Intent>(mpData)
if (resultCode == Activity.RESULT_OK && data != null) {
mResultCode = resultCode
mResultData = data
startProjection()
screenCaptureManager = ScreenCaptureManager(this)
screenCaptureManager.setProjectionParams(resultCode, data)
screenCaptureManager.startProjection()
addFloatingView()
} else {
Log.e("SusService", "Intent or data is null")
}
@ -107,253 +63,54 @@ class SusService : Service() {
override fun onCreate() {
super.onCreate()
displayMetrics = DisplayMetrics()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
initControlView()
imageProcessor = TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
startForeground(1, createNotification())
}
private fun initControlView() {
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this))
floatingView = bindingSusControl.root
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = 0
layoutParams.y = 100
windowManager.addView(floatingView, layoutParams)
initControlClick()
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener {
private var startX = 0f
private var startY = 0f
private var touchX = 0f
private var touchY = 0f
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = layoutParams.x.toFloat()
startY = layoutParams.y.toFloat()
touchX = event.rawX
touchY = event.rawY
handler.removeCallbacks(hideRunnable)
return true
private fun addFloatingView() {
floatingView = FloatingView(this)
floatingView.attachToWindow()
floatingView.setOnMovePointListener(object : FloatingView.OnMovePointListener {
override fun onMovePoint() {
Log.d("SusService_f", "FloatingView moved")
floatingView.setImageReader(screenCaptureManager.getImageReader())
}
})
floatingView.setOnClickListener(object : FloatingView.OnClickListener {
override fun onClick() {
Log.d("SusService_f", "FloatingView clicked")
addControlView()
}
})
}
MotionEvent.ACTION_MOVE -> {
layoutParams.x = (startX + (event.rawX - touchX)).toInt()
layoutParams.y = (startY + (event.rawY - touchY)).toInt()
windowManager.updateViewLayout(floatingView, layoutParams)
return true
private fun addControlView() {
controlView = ControlView(this)
controlView.setControlViewListener(object : ControlView.ControlViewListener {
override fun onGlobalClick() {
globalView = GlobalView(this@SusService)
Handler(Looper.getMainLooper()).postDelayed({
globalView.addGlobalView(screenCaptureManager.getImageReader())
}, 333)
}
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
override fun onCopyClick() {
copyTextView = CopyTextView(this@SusService)
copyTextView.addView(screenCaptureManager.getImageReader())
}
v.performClick() // 手动触发点击事件
windowManager.updateViewLayout(floatingView, layoutParams)
startHideTimer()
return true
override fun onDistrictClick() {
selectionView = SelectionView(this@SusService)
selectionView.addSelectionView(screenCaptureManager.getImageReader())
}
}
return false
override fun onChangeLan() {
languageSelectorView = LanguageSelectorView(this@SusService)
languageSelectorView.createFloatingView()
}
})
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() {
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
globalView = bindingSubGlobal.root
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = 0
layoutParams.y = 0
windowManager.addView(globalView, layoutParams)
initGlobalClick()
}
private fun initGlobalClick() {
bindingSubGlobal.susGlobalClose.setOnClickListener {
windowManager.removeView(globalView)
bindingSusControl.susControlRoot.visibility = View.VISIBLE
}
}
private fun startProjection() {
if (mResultData == null) {
Log.e("SusService", "mResultData is null, cannot start projection")
return
}
if (mediaProjection == null) {
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
mediaProjection =
mediaProjectionManager.getMediaProjection(mResultCode, mResultData!!)
mediaProjection?.registerCallback(object : MediaProjection.Callback() {}, null)
Handler(Looper.getMainLooper()).postDelayed({
try {
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val density = displayMetrics.densityDpi
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)
virtualDisplay = mediaProjection?.createVirtualDisplay(
"ScreenCapture",
width,
height,
density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.surface,
null,
null
)
} catch (e: Exception) {
Log.e("SusService", "Error starting projection", e)
}
}, 1234)
}
}
private fun stopProjection() {
// Release the virtual display
try {
virtualDisplay?.release()
virtualDisplay = null
} catch (e: Exception) {
Log.e("SusService", "Error releasing virtual display", e)
}
// Stop the media projection
try {
mediaProjection?.stop()
mediaProjection = null
} catch (e: Exception) {
Log.e("SusService", "Error stopping media projection", e)
}
}
private fun captureScreenshot() {
val image: Image? = imageReader.acquireLatestImage()
image?.let {
val bitmap = imageToBitmap(it)
image.close()
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
tryReloadAndDetectInImage(bitmap)
}
}
private fun imageToBitmap(image: Image): Bitmap {
val planes = image.planes
val buffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * image.width
val bitmap = Bitmap.createBitmap(
image.width + rowPadding / pixelStride,
image.height,
Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
return Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
controlView.addControlView()
}
private fun createNotification(): Notification {
@ -372,8 +129,8 @@ class SusService : Service() {
}
val builder = NotificationCompat.Builder(this, notificationChannelId)
.setContentTitle("悬浮窗服务")
.setContentText("悬浮窗服务正在运行")
.setContentTitle("Screen Translation Service")
.setContentText("Screen translation is running...")
.setSmallIcon(R.drawable.ic_close)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(Notification.CATEGORY_SERVICE)
@ -381,38 +138,27 @@ class SusService : Service() {
return builder.build()
}
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
bindingSubGlobal.susGraphicOverlay.clear()
if (imageProcessor != null) {
bindingSubGlobal.susGraphicOverlay.setImageSourceInfo(
bitmap.width,
bitmap.height,
false
)
imageProcessor!!.processBitmap(bitmap, bindingSubGlobal.susGraphicOverlay)
} else {
Log.e(
"SusService",
"Null imageProcessor, please check adb logs for imageProcessor creation error"
)
}
} catch (e: IOException) {
Log.e("SusService", "Error retrieving saved image", e)
}
}
override fun onDestroy() {
super.onDestroy()
Log.d("SusService", "Service onDestroy")
if (floatingView.isAttachedToWindow) {
windowManager.removeView(floatingView)
}
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
windowManager.removeView(globalView)
}
stopProjection()
if (::controlView.isInitialized) {
controlView.removeControlView()
}
if (::floatingView.isInitialized) {
floatingView.detachFromWindow()
}
if (::selectionView.isInitialized) {
selectionView.removeSelectionView()
}
if (::screenCaptureManager.isInitialized) {
screenCaptureManager.stopProjection()
}
if (::copyTextView.isInitialized) {
copyTextView.removeView()
}
}
}

View File

@ -6,7 +6,9 @@ import android.graphics.Canvas;
import com.assimilate.alltrans.curview.GraphicOverlay;
/** Draw camera image to background. */
/**
* Draw camera image to background.
*/
public class CameraImageGraphic extends GraphicOverlay.Graphic {
private final Bitmap bitmap;
@ -20,4 +22,9 @@ public class CameraImageGraphic extends GraphicOverlay.Graphic {
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, getTransformationMatrix(), null);
}
@Override
public boolean contains(float x, float y) {
return false;
}
}

View File

@ -0,0 +1,122 @@
package com.assimilate.alltrans.common
import android.os.Bundle
import com.assimilate.alltrans.MyApp
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
object FirebaseAnalyticsHelper {
private val firebaseAnalytics: FirebaseAnalytics by lazy {
Firebase.analytics
}
private fun logCusEvent(eventName: String, params: Map<String, Any?>? = null) {
val bundle = Bundle().apply {
params?.forEach { (key, value) ->
when (value) {
is String -> putString(key, value)
is Long -> putLong(key, value)
is Double -> putDouble(key, value)
is Int -> putInt(key, value)
else -> throw IllegalArgumentException("Unsupported bundle parameter type")
}
}
}
// firebaseAnalytics.logEvent(eventName, bundle)
}
fun launchPvEvent() {
logCusEvent(MyApp.Config.launchPv, null)
}
fun homePvEvent(from: String) {
val params = when (from) {
"cap" -> mapOf("cap" to 1)
"create" -> mapOf("cap" to 1)
else -> {
null
}
}
logCusEvent(MyApp.Config.homePv, params)
}
fun textTransSwitchEvent(from: String) {
val params = when (from) {
"cap" -> mapOf("cap" to 1)
"create" -> mapOf("cap" to 1)
else -> {
null
}
}
}
fun languageChooseEvent(from: String) {
val params = mapOf(from to 1)
logCusEvent(MyApp.Config.languageChoose, params)
}
fun textTransClickEvent() {
logCusEvent(MyApp.Config.textTransClick, null)
}
fun textTransResultEvent(result: String) {
val params = mapOf(result to 1)
logCusEvent(MyApp.Config.textTransResult, params)
}
fun textInputEvent() {
logCusEvent(MyApp.Config.textInput, null)
}
fun textTransPasteEvent() {
logCusEvent(MyApp.Config.textTransPaste, null)
}
fun textVoiceClickEvent() {
logCusEvent(MyApp.Config.textVoiceClick, null)
}
fun textVoiceResultEvent(failReason: String) {
val params = mapOf(failReason to 1)
logCusEvent(MyApp.Config.textVoiceResult, params)
}
fun textTransPlayEvent() {
logCusEvent(MyApp.Config.textTransPlay, null)
}
fun textTransCopyEvent() {
logCusEvent(MyApp.Config.textTransCopy, null)
}
fun textTransShareEvent() {
logCusEvent(MyApp.Config.textTransShare, null)
}
fun textTransLikeEvent() {
logCusEvent(MyApp.Config.textTransLike, null)
}
fun hoverButtonClickEvent() {
logCusEvent(MyApp.Config.hoverButtonClick, null)
}
fun hoverButtonCancelEvent() {
logCusEvent(MyApp.Config.hoverButtonCancel, null)
}
}

View File

@ -1,77 +0,0 @@
package com.assimilate.alltrans.common;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import androidx.annotation.Nullable;
import com.assimilate.alltrans.curview.GraphicOverlay;
/** Graphic instance for rendering inference info (latency, FPS, resolution) in an overlay view. */
public class InferenceInfoGraphic extends GraphicOverlay.Graphic {
private static final int TEXT_COLOR = Color.WHITE;
private static final float TEXT_SIZE = 60.0f;
private final Paint textPaint;
private final GraphicOverlay overlay;
private final long frameLatency;
private final long detectorLatency;
// Only valid when a stream of input images is being processed. Null for single image mode.
@Nullable private final Integer framesPerSecond;
private boolean showLatencyInfo = true;
public InferenceInfoGraphic(
GraphicOverlay overlay,
long frameLatency,
long detectorLatency,
@Nullable Integer framesPerSecond) {
super(overlay);
this.overlay = overlay;
this.frameLatency = frameLatency;
this.detectorLatency = detectorLatency;
this.framesPerSecond = framesPerSecond;
textPaint = new Paint();
textPaint.setColor(TEXT_COLOR);
textPaint.setTextSize(TEXT_SIZE);
textPaint.setShadowLayer(5.0f, 0f, 0f, Color.BLACK);
postInvalidate();
}
/** Creates an {@link InferenceInfoGraphic} to only display image size. */
public InferenceInfoGraphic(GraphicOverlay overlay) {
this(overlay, 0, 0, null);
showLatencyInfo = false;
}
@Override
public synchronized void draw(Canvas canvas) {
float x = TEXT_SIZE * 0.5f;
float y = TEXT_SIZE * 1.5f;
// canvas.drawText(
// "InputImage size: " + overlay.getImageHeight() + "x" + overlay.getImageWidth(),
// x,
// y,
// textPaint);
//
// if (!showLatencyInfo) {
// return;
// }
// // Draw FPS (if valid) and inference latency
// if (framesPerSecond != null) {
// canvas.drawText(
// "FPS: " + framesPerSecond + ", Frame latency: " + frameLatency + " ms",
// x,
// y + TEXT_SIZE,
// textPaint);
// } else {
// canvas.drawText("Frame latency: " + frameLatency + " ms", x, y + TEXT_SIZE, textPaint);
// }
// canvas.drawText(
// "Detector latency: " + detectorLatency + " ms", x, y + TEXT_SIZE * 2, textPaint);
}
}

View File

@ -0,0 +1,94 @@
package com.assimilate.alltrans.common
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Handler
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.WindowManager
class ScreenCaptureManager(private val context: Context) {
private lateinit var mediaProjectionManager: MediaProjectionManager
private var mediaProjection: MediaProjection? = null
private lateinit var imageReader: ImageReader
private var virtualDisplay: VirtualDisplay? = null
private var displayMetrics: DisplayMetrics = DisplayMetrics()
private var mResultCode = 0
private var mResultData: Intent? = null
init {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowManager.defaultDisplay.getMetrics(displayMetrics)
}
fun setProjectionParams(resultCode: Int, resultData: Intent) {
mResultCode = resultCode
mResultData = resultData
}
fun startProjection() {
if (mResultData == null) {
Log.e("ScreenCaptureManager", "mResultData is null, cannot start projection")
return
}
if (mediaProjection == null) {
mediaProjectionManager =
context.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
mediaProjection =
mediaProjectionManager.getMediaProjection(mResultCode, mResultData!!)
mediaProjection?.registerCallback(object : MediaProjection.Callback() {}, null)
Handler(Looper.getMainLooper()).postDelayed({
try {
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("ScreenCaptureManager", "Error starting projection", e)
}
}, 666)
}
}
fun stopProjection() {
try {
virtualDisplay?.release()
virtualDisplay = null
} catch (e: Exception) {
Log.e("ScreenCaptureManager", "Error releasing virtual display", e)
}
try {
mediaProjection?.stop()
mediaProjection = null
} catch (e: Exception) {
Log.e("ScreenCaptureManager", "Error stopping media projection", e)
}
}
fun getImageReader(): ImageReader {
return imageReader
}
}

View File

@ -5,18 +5,12 @@ import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.google.mlkit.vision.text.Text
import java.util.concurrent.Executors
import kotlin.math.max
import kotlin.math.min
@ -25,150 +19,143 @@ class TextGraphic(
private val text: Text,
private val shouldGroupTextInBlocks: Boolean,
private val showLanguageTag: Boolean,
private val showConfidence: Boolean
private val showConfidence: Boolean,
private val textShow: Boolean,
private val needTrans: Boolean
) : GraphicOverlay.Graphic(overlay) {
private val rectPaint: Paint = Paint()
private val textPaint: TextPaint
private val labelPaint: Paint
private val handler = Handler(Looper.getMainLooper())
private val executor = Executors.newSingleThreadExecutor()
private val textPaint: TextPaint = TextPaint().apply {
color = TEXT_COLOR
textSize = TEXT_SIZE
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
isFakeBoldText = true
}
private val labelPaint: Paint = Paint().apply {
color = MARKER_COLOR
style = Paint.Style.FILL
}
private var hiddenTextBlocks: MutableList<Boolean> = MutableList(text.textBlocks.size) { !textShow }
private var isVisible: Boolean
init {
prepareTranslation()
rectPaint.color = MARKER_COLOR
rectPaint.style = Paint.Style.STROKE
rectPaint.strokeWidth = STROKE_WIDTH
textPaint = TextPaint()
textPaint.color = TEXT_COLOR
textPaint.textSize = TEXT_SIZE
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
textPaint.isFakeBoldText = true
labelPaint = Paint()
labelPaint.color = MARKER_COLOR
labelPaint.style = Paint.Style.FILL
isVisible = textShow
if (needTrans) {
TranslationManager(text) { translatedTextPairs ->
translatedTextBlocks = translatedTextPairs.map { it.first }
// 可以同时打印原Text和翻译后的结果
translatedTextPairs.forEach { (translated, original) ->
Log.d("Translation", "Original: $original -> Translated: $translated")
}
postInvalidate()
}
}
// Redraw the overlay, as this graphic has been added.
postInvalidate()
}
private var translatedTextBlocks: List<String> = listOf()
// Method to prepare translation before drawing
private fun prepareTranslation() {
executor.execute {
val textToTranslate = StringBuilder()
// Collect all text to be translated and append delimiter
for (textBlock in text.textBlocks) {
val textWithDelimiter = textBlock.text + DELIMITER
textToTranslate.append(textWithDelimiter)
}
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_source"),
MyApp.applicationContext()
)
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
if (lanTargetCode == null || lanSourceCode == null || textToTranslate.toString()
.isEmpty()
) {
return@execute
}
// Define translation parameters
val param = HashMap<String, String>().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", textToTranslate.toString())
}
Log.d("fdasfas_sou", lanSourceCode)
Log.d("fdasfas_tar", lanTargetCode)
Log.d("fdasfas_tex", textToTranslate.toString())
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
GoogleTranslator()
// Perform translation
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
// Split translated text by delimiter
translatedTextBlocks =
translatedText.split(DELIMITER.toRegex()).filter { it.isNotEmpty() }
// Update UI thread
handler.post {
postInvalidate()
}
})
}
}
override fun draw(canvas: Canvas) {
if (!isVisible) return
for ((index, textBlock) in text.textBlocks.withIndex()) {
if (hiddenTextBlocks[index]) continue
drawTextBlock(textBlock, canvas, index)
}
}
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
val translatedBlockText =
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text
private fun drawTextBlock(textBlock: Text.TextBlock, canvas: Canvas, index: Int) {
val translatedBlockText = translatedTextBlocks.getOrNull(index) ?: textBlock.text
if (shouldGroupTextInBlocks) {
val rect = RectF(textBlock.boundingBox)
drawText(
getFormattedText(
translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
),
RectF(textBlock.boundingBox),
canvas
getFormattedText(translatedBlockText, textBlock.recognizedLanguage, null),
rect,
canvas,
textPaint
)
Log.d(
"fdgfsdfsdfas", getFormattedText(
translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
)
)
} // 不需要单行(删除)
}
}
private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
val res =
if (showLanguageTag) String.format(
TEXT_WITH_LANGUAGE_TAG_FORMAT,
languageTag,
text
) else text
return if (showConfidence && confidence != null) String.format("%s (%.2f)", res, confidence)
else res
}
private fun drawText(text: String, rect: RectF, canvas: Canvas) {
// 如果图像是翻转的,将左边翻译到右边,右边翻译到左边。
override fun contains(x: Float, y: Float): Boolean {
for (textBlock in text.textBlocks) {
val rect = RectF(textBlock.boundingBox)
val x0 = translateX(rect.left)
val x1 = translateX(rect.right)
rect.left = min(x0, x1)
rect.right = max(x0, x1)
rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom)
if (rect.contains(x, y)) {
return true
}
}
return false
}
fun showTextBlockAt(x: Float, y: Float) {
isVisible = true
for ((index, textBlock) in text.textBlocks.withIndex()) {
val rect = RectF(textBlock.boundingBox)
val x0 = translateX(rect.left)
val x1 = translateX(rect.right)
rect.left = min(x0, x1)
rect.right = max(x0, x1)
rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom)
if (rect.contains(x, y)) {
hiddenTextBlocks[index] = false
postInvalidate()
break
}
}
}
fun hideTextBlock() {
isVisible = false
for (i in hiddenTextBlocks.indices) {
hiddenTextBlocks[i] = true
}
postInvalidate()
}
private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
val res = if (showLanguageTag) String.format(
TEXT_WITH_LANGUAGE_TAG_FORMAT,
languageTag,
text
) else text
return if (showConfidence && confidence != null) String.format(
"%s (%.2f)",
res,
confidence
) else res
}
private fun drawText(text: String, rect: RectF, canvas: Canvas, paint: TextPaint) {
val x0 = translateX(rect.left)
val x1 = translateX(rect.right)
rect.left = min(x0, x1)
rect.right = max(x0, x1)
rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom)
// 绘制背景矩形
canvas.drawRect(rect, labelPaint)
// 设置文本大小以适应矩形
val textPaintCopy = TextPaint(textPaint)
// 准备文本绘制
val textPaintCopy = TextPaint(paint)
val availableWidth = rect.width().toInt()
val availableHeight = rect.height().toInt()
var textSize = textPaintCopy.textSize
// 调整文本大小以适应矩形
var textLayout: StaticLayout
while (textSize > 0) {
textPaintCopy.textSize = textSize
textLayout =
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
@ -180,9 +167,7 @@ class TextGraphic(
textSize -= 1
}
// 使用 StaticLayout 绘制文本
textLayout =
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
@ -195,12 +180,10 @@ class TextGraphic(
}
companion object {
private const val DELIMITER = "0`~`0"
private const val TAG = "TextGraphic"
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
private val TEXT_COLOR = Color.parseColor("#FF474747")
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
private const val STROKE_WIDTH = 2.0f
private const val TEXT_SIZE = 44.0f
}
}

View File

@ -4,6 +4,8 @@ import android.content.Context
import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.google.android.gms.tasks.Task
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.Text
@ -14,8 +16,16 @@ import com.google.mlkit.vision.text.TextRecognizerOptionsInterface
/** Processor for the text detector demo. */
class TextRecognitionProcessor(
private val context: Context,
textRecognizerOptions: TextRecognizerOptionsInterface
textRecognizerOptions: TextRecognizerOptionsInterface,
private val textShow:Boolean,
private val needTrans: Boolean,
private val callback: TextRecognitionCallback? = null // 添加回调
) : VisionProcessorBase<Text>(context) {
interface TextRecognitionCallback {
fun onTextRecognized(text: String)
}
private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
private val shouldGroupRecognizedTextInBlocks: Boolean =
PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
@ -31,7 +41,6 @@ class TextRecognitionProcessor(
return textRecognizer.process(image)
}
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
PreferenceLanguageUtils.putString("language_source", getMostFrequentLanguage(text))
@ -43,16 +52,20 @@ class TextRecognitionProcessor(
text,
shouldGroupRecognizedTextInBlocks,
showLanguageTag,
showConfidence
showConfidence,
textShow,
needTrans
)
)
// 调用回调
callback?.onTextRecognized(text.text)
}
override fun onFailure(e: Exception) {
Log.w(TAG, "Text detection failed.$e")
}
// 推测最可能的语言
private fun getMostFrequentLanguage(text: Text): String {
val languageCount = mutableMapOf<String, Int>()

View File

@ -0,0 +1,90 @@
package com.assimilate.alltrans.common
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.google.mlkit.vision.text.Text
import java.util.concurrent.Executors
class TranslationManager(
private val text: Text,
private val callback: (List<Pair<String, String>>) -> Unit // 修改callback的参数类型
) {
private val handler = Handler(Looper.getMainLooper())
private val executor = Executors.newSingleThreadExecutor()
private var translatedTextBlocks: MutableList<Pair<Int, String>> = mutableListOf()
init {
prepareTranslation()
}
private fun prepareTranslation() {
executor.execute {
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_source"),
MyApp.applicationContext()
)
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
if (lanTargetCode == null || lanSourceCode == null) {
println("Language source or target code is null")
return@execute
}
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
GoogleTranslator()
for ((index, textBlock) in text.textBlocks.withIndex()) {
// 如果源语言和目标语言相同,直接添加原始文本到 translatedTextBlocks
if (lanSourceCode == lanTargetCode) {
translatedTextBlocks.add(Pair(index, textBlock.text))
if (translatedTextBlocks.size == text.textBlocks.size) {
handler.post {
callback(translatedTextBlocks.sortedBy { it.first }.map { it.second to text.textBlocks[it.first].text })
}
}
} else {
val param = HashMap<String, String>().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", textBlock.text)
}
translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
override fun onResponse(result: String?, errorMessage: String?) {
if (result != null) {
translatedTextBlocks.add(Pair(index, result))
} else {
translatedTextBlocks.add(Pair(index, ""))
}
if (translatedTextBlocks.size == text.textBlocks.size) {
handler.post {
callback(translatedTextBlocks.sortedBy { it.first }.map { it.second to text.textBlocks[it.first].text })
}
}
}
override fun onFailure(errorMessage: String?) {
translatedTextBlocks.add(Pair(index, ""))
if (translatedTextBlocks.size == text.textBlocks.size) {
handler.post {
callback(translatedTextBlocks.sortedBy { it.first }.map { it.second to text.textBlocks[it.first].text })
}
}
}
})
}
}
}
}
}

View File

@ -321,16 +321,7 @@ abstract class VisionProcessorBase<T>(context: Context) : VisionImageProcessor {
graphicOverlay.add(CameraImageGraphic(graphicOverlay, originalCameraImage))
}
this@VisionProcessorBase.onSuccess(results, graphicOverlay)
if (!PreferenceUtils.shouldHideDetectionInfo(graphicOverlay.context)) {
graphicOverlay.add(
InferenceInfoGraphic(
graphicOverlay,
currentFrameLatencyMs,
currentDetectorLatencyMs,
if (shouldShowFps) framesPerSecond else null
)
)
}
graphicOverlay.postInvalidate()
}
)

View File

@ -4,11 +4,18 @@ import android.app.Activity;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import com.assimilate.alltrans.MyApp;
import com.assimilate.alltrans.R;
import com.google.android.material.snackbar.Snackbar;
public class Widget {
@ -23,16 +30,51 @@ public class Widget {
}
public static void makeSnackbar(@NonNull final Activity mActivity, @NonNull final String msg) {
View view = mActivity.findViewById(android.R.id.content);
Snackbar snackbar = Snackbar.make(view, msg, Snackbar.LENGTH_LONG);
View snackbarView = snackbar.getView();
snackbarView.setBackgroundColor(Color.parseColor("#66000000"));
Snackbar snackbar = Snackbar.make(mActivity.findViewById(android.R.id.content), msg, Snackbar.LENGTH_SHORT);
View view = snackbar.getView();
view.setBackground(AppCompatResources.getDrawable(mActivity,R.drawable.button_r20_black_bg));
TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
textView.setTextColor(Color.WHITE);
textView.setTextSize(16f);
textView.setGravity(Gravity.CENTER);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
params.gravity = Gravity.CENTER;
view.setLayoutParams(params);
// 获取Snackbar的TextView并设置文字居中
TextView textView = view.findViewById(com.google.android.material.R.id.snackbar_text);
if (textView != null) {
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
}
snackbar.show();
}
// 封装放大动画的方法
public static void startScaleAnimation(final ImageView imageView) {
final ScaleAnimation scaleAnimation = new ScaleAnimation(
1.0f, 1.2f, // 从1倍放大到1.2倍
1.0f, 1.2f, // 从1倍放大到1.2倍
Animation.RELATIVE_TO_SELF, 0.5f, // 以自身中心X轴为缩放中心
Animation.RELATIVE_TO_SELF, 0.5f // 以自身中心Y轴为缩放中心
);
scaleAnimation.setDuration(700); // 动画持续时间单位为毫秒
scaleAnimation.setFillAfter(true); // 动画结束后保持放大状态
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
private int repeatCount = 2;
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
if (repeatCount > 1) {
repeatCount--;
imageView.startAnimation(scaleAnimation);
}
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
// 开始动画
imageView.startAnimation(scaleAnimation);
}
}

View File

@ -0,0 +1,154 @@
package com.assimilate.alltrans.curview
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.databinding.SusControlViewBinding
import com.assimilate.alltrans.viewui.PhotoImageActivity
import com.assimilate.alltrans.viewui.WelActivity
class ControlView(private val context: Context) {
interface ControlViewListener {
fun onGlobalClick()
fun onCopyClick()
fun onDistrictClick()
fun onChangeLan()
}
private lateinit var binding: SusControlViewBinding
private lateinit var controlView: View
private var isAdded: Boolean = false
private lateinit var fullScreenView: View
private var listener: ControlViewListener? = null
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
fun setControlViewListener(listener: ControlViewListener) {
this.listener = listener
}
fun addControlView() {
if (isAdded) return
binding = SusControlViewBinding.inflate(LayoutInflater.from(context))
controlView = binding.root
// 添加全屏透明视图,点击区域外关闭
fullScreenView = View(context)
val fullScreenLayoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
fullScreenLayoutParams.gravity = Gravity.TOP or Gravity.LEFT
windowManager.addView(fullScreenView, fullScreenLayoutParams)
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = 0
layoutParams.y = 100
windowManager.addView(controlView, layoutParams)
isAdded = true
initControlClick()
// 设置触摸监听器
fullScreenView.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
removeControlView()
true
} else {
false
}
}
}
private fun initControlClick() {
binding.ivSusChange.setOnClickListener {
binding.susControlRoot.visibility = View.GONE
listener?.onChangeLan()
}
binding.tvSusGlobal.setOnClickListener {
binding.susControlRoot.visibility = View.GONE
listener?.onGlobalClick()
}
binding.tvSusCopy.setOnClickListener {
Log.d("ControlViewManager", "Copy text clicked")
binding.susControlRoot.visibility = View.GONE
listener?.onCopyClick()
}
binding.tvSusPhoto.setOnClickListener {
val intent = Intent(context, PhotoImageActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
pendingIntent.send()
removeControlView()
}
binding.tvSusDistrict.setOnClickListener {
binding.susControlRoot.visibility = View.GONE
Log.d("ControlViewManager", "District translation clicked")
listener?.onDistrictClick()
}
binding.ivSusHome.setOnClickListener {
Log.d("ControlViewManager", "Home clicked")
val intent = Intent(context, WelActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
pendingIntent.send()
}
}
fun removeControlView() {
if (isAdded) {
windowManager.removeView(controlView)
windowManager.removeView(fullScreenView)
isAdded = false
}
}
}

View File

@ -0,0 +1,162 @@
package com.assimilate.alltrans.curview
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.media.Image
import android.media.ImageReader
import android.os.Build
import android.text.TextUtils
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.core.content.ContextCompat.getSystemService
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.LayoutSusCopyBinding
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
class CopyTextView(private val context: Context) :
TextRecognitionProcessor.TextRecognitionCallback {
private var imageProcessor: VisionImageProcessor? = null
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private var binding: LayoutSusCopyBinding? = null
private var rootView: View? = null
private var recognizedText: String? = null
fun addView(imageReader: ImageReader) {
if (rootView != null) return
// 这里还需要调整
imageProcessor = TextRecognitionProcessor(
context,
ChineseTextRecognizerOptions.Builder().build(), true, false, this
)
val inflater = LayoutInflater.from(context)
binding = LayoutSusCopyBinding.inflate(inflater, null, false)
rootView = binding?.root
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
windowManager.addView(rootView, params)
captureScreenshot(imageReader)
binding?.susGlobalClose?.setOnClickListener {
removeView()
}
binding?.btSusCopyAll?.setOnClickListener {
Log.d("gdsfsfsadf",recognizedText.toString())
copyToClipboard(recognizedText.toString())
removeView()
}
}
fun removeView() {
if (rootView != null) {
windowManager.removeView(rootView)
rootView = null
binding = null
}
}
private fun captureScreenshot(imageReader: ImageReader) {
val image: Image? = imageReader.acquireLatestImage()
image?.let {
val bitmap = imageToBitmap(it)
image.close()
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 tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
binding?.susGraphicOverlay?.clear()
if (imageProcessor != null) {
binding?.susGraphicOverlay?.setImageSourceInfo(
bitmap.width,
bitmap.height,
false
)
imageProcessor!!.processBitmap(bitmap, binding?.susGraphicOverlay)
} else {
Log.e(
"SusService",
"Null imageProcessor, please check adb logs for imageProcessor creation error"
)
}
} catch (e: IOException) {
Log.e("SusService", "Error retrieving saved image", e)
}
}
override fun onTextRecognized(text: String) {
recognizedText = text
}
// 复制到粘贴板
private fun copyToClipboard(text: String?) {
val tip = "Copied to clipboard!"
val tipNull = "Text is null!"
if (!TextUtils.isEmpty(text)) {
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText("targetValue", text)
clipboardManager.setPrimaryClip(clipData)
Toast.makeText(context, tip, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, tipNull, Toast.LENGTH_SHORT).show()
}
}
}

View File

@ -0,0 +1,235 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.graphics.Rect
import android.media.ImageReader
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.databinding.LayoutSusDistrictBinding
import com.assimilate.alltrans.http.GoogleTranslator
import com.assimilate.alltrans.http.Translator
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.nio.ByteBuffer
class DistrictView(
private val context: Context
) {
private lateinit var bindingSusDistrict: LayoutSusDistrictBinding
private lateinit var districtView: View
private lateinit var overlayView: View
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
fun showDistrictView(imageReader: ImageReader, startX: Int, startY: Int, endX: Int, endY: Int) {
// 添加一个全屏的透明背景视图
overlayView = View(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setBackgroundColor(0x00000000) // 全透明
setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
removeDistrictView()
}
true
}
}
val overlayParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT
)
windowManager.addView(overlayView, overlayParams)
// 初始化 DistrictView
bindingSusDistrict = LayoutSusDistrictBinding.inflate(LayoutInflater.from(context))
districtView = bindingSusDistrict.root
val displayMetrics = context.resources.displayMetrics
val screenWidth = displayMetrics.widthPixels
val density = displayMetrics.density
val marginDp = 16
val marginPx = (marginDp * density).toInt()
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
layoutParams.x = marginPx
layoutParams.y = 100
layoutParams.width = screenWidth - 2 * marginPx
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT
windowManager.addView(districtView, layoutParams)
initDistrictClick()
// 获取最新的图像并处理
val image = imageReader.acquireLatestImage()
image?.let {
val planes = it.planes
if (planes.isNotEmpty()) {
val buffer: ByteBuffer = planes[0].buffer
val pixelStride: Int = planes[0].pixelStride
val rowStride: Int = planes[0].rowStride
val rowPadding: Int = rowStride - pixelStride * image.width
// 从缓冲区创建位图
val bitmap = Bitmap.createBitmap(
image.width + rowPadding / pixelStride, image.height, Bitmap.Config.ARGB_8888
)
bitmap.copyPixelsFromBuffer(buffer)
// 根据用户框选区域裁剪位图
val rect = Rect(startX, startY, endX, endY)
takeDistrictScreenshot(bitmap, rect)
}
image.close()
}
}
private fun initDistrictClick() {
bindingSusDistrict.ivTrCollect.setOnClickListener { addCollect() }
bindingSusDistrict.ivSourceClear.setOnClickListener {
bindingSusDistrict.tvTrSource.text = ""
bindingSusDistrict.tvTrTarget.text = ""
}
}
fun removeDistrictView() {
windowManager.removeView(districtView)
windowManager.removeView(overlayView)
}
private fun takeDistrictScreenshot(image: Bitmap, rect: Rect) {
if (rect.width() > 0 && rect.height() > 0) {
val croppedBitmap = Bitmap.createBitmap(
image,
rect.left,
rect.top,
rect.width(),
rect.height()
)
val recognizer =
TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
val imageNew = InputImage.fromBitmap(croppedBitmap, 0)
recognizer.process(imageNew)
.addOnSuccessListener { visionText ->
bindingSusDistrict.tvTrSource.text = visionText.text
// Add translation functionality here if needed
translate(visionText.text)
}
.addOnFailureListener { e ->
Log.e("DistrictView", "Text recognition failed", e)
}
} else {
Log.e("DistrictView", "Rect width or height is 0")
}
}
private fun addCollect() {
// Add collect functionality here
bindingSusDistrict.ivTrCollect.setImageResource(R.drawable.ic_like_yes)
}
private fun translate(text: String) {
if (text.isEmpty()) {
Logger.d("log", "translating(not post data)...")
return
}
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_source"),
MyApp.applicationContext()
)
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
PreferenceLanguageUtils.getString("language_target"),
MyApp.applicationContext()
)
val param = HashMap<String, String>().apply {
put("sourceLanguage", lanSourceCode)
put("translationLanguage", lanTargetCode)
put("text", text)
}
Handler(Looper.getMainLooper()).post {
bindingSusDistrict.tvTrTarget.text = "translating..."
}
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator()
translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
override fun onResponse(result: String?, errorMessage: String?) {
if (!TextUtils.isEmpty(result)) {
bindingSusDistrict.tvTrTarget.text
} else {
// 处理错误信息
if (!TextUtils.isEmpty(errorMessage)) {
// 显示错误信息或记录日志
if (errorMessage != null) {
Log.e("TranslationError", errorMessage)
}
}
bindingSusDistrict.tvTrTarget.text = "Translation failed: $errorMessage"
}
}
override fun onFailure(errorMessage: String?) {
// 显示失败信息或记录日志
if (!TextUtils.isEmpty(errorMessage)) {
if (errorMessage != null) {
Log.e("TranslationFailure", errorMessage)
}
bindingSusDistrict.tvTrTarget.text = "Translation failed: $errorMessage"
} else {
bindingSusDistrict.tvTrTarget.text = "Translation failed: Unknown error"
}
}
})
}
}

View File

@ -0,0 +1,75 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.view.MotionEvent
import android.view.View
import com.assimilate.alltrans.listeners.OnRegionSelectedListener
class DrawChooseView(context: Context) : View(context) {
private var paintTransparent: Paint = Paint()
private var paintSemiTransparent: Paint = Paint()
private var rect: Rect? = null
private var startX: Float = 0f
private var startY: Float = 0f
init {
paintTransparent.color = Color.TRANSPARENT
paintTransparent.style = Paint.Style.FILL
paintSemiTransparent.color = Color.parseColor("#88000000") // 半透明黑色
paintSemiTransparent.style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
rect?.let {
val left = Math.min(it.left, it.right)
val right = Math.max(it.left, it.right)
val top = Math.min(it.top, it.bottom)
val bottom = Math.max(it.top, it.bottom)
// 绘制框选外的半透明区域
canvas.drawRect(0f, 0f, width.toFloat(), top.toFloat(), paintSemiTransparent)
canvas.drawRect(0f, top.toFloat(), left.toFloat(), bottom.toFloat(), paintSemiTransparent)
canvas.drawRect(right.toFloat(), top.toFloat(), width.toFloat(), bottom.toFloat(), paintSemiTransparent)
canvas.drawRect(0f, bottom.toFloat(), width.toFloat(), height.toFloat(), paintSemiTransparent)
// 绘制框选内的透明区域
canvas.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), paintTransparent)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
rect = Rect(startX.toInt(), startY.toInt(), startX.toInt(), startY.toInt())
invalidate()
return true
}
MotionEvent.ACTION_MOVE -> {
rect?.apply {
right = event.x.toInt()
bottom = event.y.toInt()
}
invalidate()
return true
}
MotionEvent.ACTION_UP -> {
rect?.let {
(context as? OnRegionSelectedListener)?.onRegionSelected(it)
}
return true
}
}
return super.onTouchEvent(event)
}
}

View File

@ -0,0 +1,354 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.graphics.PixelFormat
import android.media.Image
import android.media.ImageReader
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.widget.ImageView
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.TextGraphic
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
class FloatingView(
private val context: Context
) {
private var isAttachedToWindow = false
private var imageProcessor: VisionImageProcessor? = null
private var graphicOverlay: GraphicOverlay? = null
private var imageReader: ImageReader? = null
private var onMovePointListener: OnMovePointListener? = null
private var onClickListener: OnClickListener? = null
private val handler = Handler(Looper.getMainLooper())
private val fadeRunnable = Runnable { fadeAndStickToEdge() }
private val originalImageResId = R.drawable.ic_sus_little_ball
private val edgeImageResId = R.drawable.ic_sus_little_ball_tie
private val moveImageResId = R.drawable.ic_sus_little_search
private var imageView: ImageView = ImageView(context).apply {
setImageResource(originalImageResId)
}
private var lastX = 0
private var lastY = 0
private var hasCapturedScreenshot = false
init {
setupTouchListener()
handler.postDelayed(fadeRunnable, 2333)
// 初始化语言识别
imageProcessor = TextRecognitionProcessor(
context,
ChineseTextRecognizerOptions.Builder().build(), false, true
)
graphicOverlay = GraphicOverlay(context, null)
}
interface OnMovePointListener {
fun onMovePoint()
}
interface OnClickListener {
fun onClick()
}
fun setOnMovePointListener(listener: OnMovePointListener) {
this.onMovePointListener = listener
}
fun setOnClickListener(listener: OnClickListener) {
this.onClickListener = listener
}
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val screenWidth: Int = context.resources.displayMetrics.widthPixels
private val originalImageWidth: Int =
BitmapFactory.decodeResource(context.resources, R.drawable.ic_sus_little_ball).width
private val fullScreenParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
private val params: WindowManager.LayoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
).apply {
gravity = Gravity.TOP or Gravity.START
x = screenWidth - originalImageWidth
y = 500
}
private fun setupTouchListener() {
imageView.setOnTouchListener(object : View.OnTouchListener {
private var initialX = 0
private var initialY = 0
private var initialTouchX = 0f
private var initialTouchY = 0f
private var isClick = true
override fun onTouch(v: View?, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isClick = true
initialX = params.x
initialY = params.y
initialTouchX = event.rawX
initialTouchY = event.rawY
handler.removeCallbacks(fadeRunnable)
imageView.alpha = 1.0f
imageView.setImageResource(originalImageResId)
return true
}
MotionEvent.ACTION_UP -> {
handler.postDelayed(fadeRunnable, 3000)
imageView.setImageResource(originalImageResId)
stickToEdge()
if (isClick) {
onClickListener?.onClick()
} else {
try {
windowManager.removeView(graphicOverlay)
hasCapturedScreenshot = false
windowManager.updateViewLayout(imageView, params)
} catch (e: IllegalArgumentException) {
Log.e("FloatingView", "View not attached to window manager", e)
}
}
return true
}
MotionEvent.ACTION_MOVE -> {
val dx = event.rawX - initialTouchX
val dy = event.rawY - initialTouchY
if (dx * dx + dy * dy > imageView.width * imageView.width) {
isClick = false
}
if (!isClick) {
// 将图片的位置设置为手指触摸点的上方
params.x = initialX + dx.toInt()
params.y = initialY + dy.toInt() - imageView.height / 2 // 调整y坐标使图片位于手指的上方
onMovePointListener?.onMovePoint()
imageView.setImageResource(moveImageResId)
lastX = params.x + imageView.width / 2
lastY = params.y + imageView.height / 2
updateGraphicOverlay(lastX, lastY)
try {
windowManager.updateViewLayout(imageView, params)
} catch (e: IllegalArgumentException) {
Log.e("FloatingView", "View not attached to window manager", e)
}
}
return true
}
}
return false
}
})
}
private fun updateGraphicOverlay(x: Int, y: Int) {
graphicOverlay?.let {
synchronized(it.lock) {
val graphics = it.graphics
for (graphic in graphics) {
if (graphic.contains(x.toFloat(), y.toFloat())) {
if (graphic is TextGraphic) {
graphic.showTextBlockAt(x.toFloat(), y.toFloat())
}
Log.d("GraphicOverlay", "Graphic on move!")
break
}
}
}
}
}
private fun captureAndProcessScreenshot(reader: ImageReader?) {
reader?.let { it ->
val image = it.acquireLatestImage()
image?.let {
val bitmap = imageToBitmap(it)
image.close()
tryReloadAndDetectInImage(bitmap)
}
}
}
private fun stickToEdge() {
val screenWidth = context.resources.displayMetrics.widthPixels
val midPoint = screenWidth / 2
params.x = if (params.x < midPoint) {
0
} else {
screenWidth - originalImageWidth
}
if (isAttachedToWindow) {
try {
windowManager.updateViewLayout(imageView, params)
} catch (e: IllegalArgumentException) {
Log.e("FloatingView", "View not attached to window manager", e)
}
}
}
private fun fadeAndStickToEdge() {
imageView.animate().alpha(0.5f).setDuration(500).withEndAction {
val screenWidth = context.resources.displayMetrics.widthPixels
val originalImageWidth =
BitmapFactory.decodeResource(context.resources, originalImageResId).width
val edgeImageWidth =
BitmapFactory.decodeResource(context.resources, edgeImageResId).width
val isLeftEdge = params.x + originalImageWidth / 2 < screenWidth / 2
params.x = if (isLeftEdge) {
0
} else {
screenWidth - edgeImageWidth
}
if (isAttachedToWindow) {
try {
windowManager.updateViewLayout(imageView, params)
} catch (e: IllegalArgumentException) {
Log.e("FloatingView", "View not attached to window manager", e)
}
}
updateImageForEdge(imageView, isLeftEdge)
}.start()
}
private fun updateImageForEdge(imageView: ImageView, isLeftEdge: Boolean) {
val bitmap = BitmapFactory.decodeResource(context.resources, edgeImageResId)
if (isLeftEdge) {
imageView.setImageBitmap(flipBitmapHorizontally(bitmap))
imageView.setPadding(0, 20, 30, 20)
} else {
imageView.setImageBitmap(bitmap)
imageView.setPadding(30, 20, 0, 20)
}
}
private fun flipBitmapHorizontally(src: Bitmap): Bitmap {
val matrix = Matrix()
matrix.setScale(-1f, 1f)
return Bitmap.createBitmap(src, 0, 0, src.width, src.height, matrix, true)
}
fun attachToWindow() {
if (!isAttachedToWindow) {
windowManager.addView(imageView, params)
isAttachedToWindow = true
}
}
fun detachFromWindow() {
Log.e("FloatingView", "View not attached to window manager")
if (isAttachedToWindow) {
try {
windowManager.removeView(imageView)
isAttachedToWindow = false
} catch (e: IllegalArgumentException) {
Log.e("FloatingView", "View not attached to window manager", e)
}
}
}
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 tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
graphicOverlay?.clear()
if (imageProcessor != null) {
graphicOverlay?.setImageSourceInfo(
bitmap.width,
bitmap.height,
false
)
imageProcessor!!.processBitmap(bitmap, graphicOverlay)
} else {
Log.e(
"FloatingView",
"Null imageProcessor, please check adb logs for imageProcessor creation error"
)
}
} catch (e: IOException) {
Log.e("FloatingView", "Error retrieving saved image", e)
}
}
fun setImageReader(reader: ImageReader) {
this.imageReader = reader
if (!hasCapturedScreenshot) {
hasCapturedScreenshot = true
captureAndProcessScreenshot(imageReader)
windowManager.addView(graphicOverlay, fullScreenParams)
}
}
}

View File

@ -0,0 +1,128 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.media.Image
import android.media.ImageReader
import android.os.Build
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import java.io.IOException
class GlobalView(private val context: Context) {
private var imageProcessor: VisionImageProcessor? = null
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
private lateinit var globalView: View
private var globalViewIsAdd: Boolean = false
fun addGlobalView(imageReader: ImageReader) {
// 这里还需要调整
imageProcessor = TextRecognitionProcessor(
context,
ChineseTextRecognizerOptions.Builder().build(),true,true
)
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(context))
globalView = bindingSubGlobal.root
val layoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
PixelFormat.TRANSLUCENT
)
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
captureScreenshot(imageReader)
windowManager.addView(globalView, layoutParams)
globalViewIsAdd = true
initGlobalClick()
}
private fun initGlobalClick() {
bindingSubGlobal.susGlobalClose.setOnClickListener {
removeGlobalView()
}
}
private fun removeGlobalView() {
if (globalViewIsAdd) {
windowManager.removeView(globalView)
globalViewIsAdd = false
}
}
private fun captureScreenshot(imageReader: ImageReader) {
val image: Image? = imageReader.acquireLatestImage()
image?.let {
val bitmap = imageToBitmap(it)
image.close()
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 tryReloadAndDetectInImage(bitmap: Bitmap) {
try {
bindingSubGlobal.susGraphicOverlay.clear()
if (imageProcessor != null) {
bindingSubGlobal.susGraphicOverlay.setImageSourceInfo(
bitmap.width,
bitmap.height,
false
)
imageProcessor!!.processBitmap(bitmap, bindingSubGlobal.susGraphicOverlay)
} else {
Log.e(
"SusService",
"Null imageProcessor, please check adb logs for imageProcessor creation error"
)
}
} catch (e: IOException) {
Log.e("SusService", "Error retrieving saved image", e)
}
}
}

View File

@ -9,36 +9,29 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.assimilate.alltrans.common.TextGraphic;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
import java.util.ArrayList;
import java.util.List;
/**
* A view which renders a series of custom graphics to be overlayed on top of an associated preview
* (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove
* them, triggering the appropriate drawing and invalidation within the view.
*
* <p>Supports scaling and mirroring of the graphics relative the camera's preview properties. The
* idea is that detection items are expressed in terms of an image size, but need to be scaled up to
* the full view size, and also mirrored in the case of the front-facing camera.
*
* <p>Associated {@link Graphic} items should use the following methods to convert to view
* coordinates for the graphics that are drawn:
*
* <ol>
* <li>{@link Graphic#scale(float)} adjusts the size of the supplied value from the image scale to
* the view scale.
* <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
* coordinate from the image's coordinate system to the view coordinate system.
* </ol>
*/
public class GraphicOverlay extends View {
private final Object lock = new Object();
public List<Graphic> getGraphics() {
return graphics;
}
public Object getLock() {
return lock;
}
private final List<Graphic> graphics = new ArrayList<>();
// Matrix for transforming from image coordinates to overlay view coordinates.
private final Matrix transformationMatrix = new Matrix();
@ -64,11 +57,23 @@ public class GraphicOverlay extends View {
*/
public abstract static class Graphic {
private GraphicOverlay overlay;
private boolean textVisible = true; // 添加可见性
public Graphic(GraphicOverlay overlay) {
this.overlay = overlay;
}
public boolean isVisible() {
return textVisible;
}
public void setVisible(boolean visible) {
this.textVisible = visible;
overlay.postInvalidate();
}
/**
* Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
* to view coordinates for the graphics that are drawn:
@ -84,6 +89,9 @@ public class GraphicOverlay extends View {
*/
public abstract void draw(Canvas canvas);
public abstract boolean contains(float x, float y);
protected void drawRect(
Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
canvas.drawRect(left, top, right, bottom, paint);
@ -140,6 +148,7 @@ public class GraphicOverlay extends View {
overlay.postInvalidate();
}
/**
* Given the {@code zInImagePixel}, update the color for the passed in {@code paint}. The color will be
* more red if the {@code zInImagePixel} is smaller, or more blue ish vice versa. This is
@ -208,6 +217,7 @@ public class GraphicOverlay extends View {
needUpdateTransformation = true);
}
/**
* Removes all graphics from the overlay.
*/
@ -311,21 +321,56 @@ public class GraphicOverlay extends View {
}
}
private TextGraphic currentlyVisibleTextGraphic = null;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setVisibility(View.INVISIBLE);
synchronized (lock) {
currentlyVisibleTextGraphic = null;
return true;
}
case MotionEvent.ACTION_MOVE:
synchronized (lock) {
boolean foundGraphic = false;
for (Graphic graphic : graphics) {
if (graphic instanceof TextGraphic) {
TextGraphic textGraphic = (TextGraphic) graphic;
if (textGraphic.contains(x, y)) {
if (currentlyVisibleTextGraphic != textGraphic) {
if (currentlyVisibleTextGraphic != null) {
currentlyVisibleTextGraphic.hideTextBlock();
}
textGraphic.showTextBlockAt(x, y);
currentlyVisibleTextGraphic = textGraphic;
}
foundGraphic = true;
break;
}
}
}
if (!foundGraphic && currentlyVisibleTextGraphic != null) {
currentlyVisibleTextGraphic.hideTextBlock();
currentlyVisibleTextGraphic = null;
}
return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
setVisibility(View.VISIBLE);
synchronized (lock) {
if (currentlyVisibleTextGraphic != null) {
currentlyVisibleTextGraphic.hideTextBlock();
currentlyVisibleTextGraphic = null;
}
return true;
}
}
return super.onTouchEvent(event);
}
}

View File

@ -0,0 +1,180 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.PixelFormat
import android.os.Build
import android.util.DisplayMetrics
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import androidx.core.content.ContextCompat.getColor
import androidx.recyclerview.widget.LinearLayoutManager
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.keepmodel.Language
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.databinding.LayoutSusChangelanBinding
class LanguageSelectorView(private val context: Context) {
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private var binding: LayoutSusChangelanBinding? = null
private var lastTranslateLanguage = false
private lateinit var fullScreenView: View
fun createFloatingView() {
// 添加全屏透明视图,点击区域外关闭
fullScreenView = View(context)
val fullScreenLayoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
fullScreenLayoutParams.gravity = Gravity.TOP or Gravity.LEFT
windowManager.addView(fullScreenView, fullScreenLayoutParams)
// 使用ViewBinding来加载自定义的布局
binding = LayoutSusChangelanBinding.inflate(LayoutInflater.from(context))
// 获取屏幕的高度
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val screenHeight = displayMetrics.heightPixels
// 设置布局参数
val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
screenHeight * 3 / 4, // 高度设置为屏幕的四分之三
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
// 设置悬浮窗的位置
params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
initView()
initClick()
initAllList()
// 将布局添加到window
windowManager.addView(binding?.root, params)
// 设置触摸监听器
fullScreenView.setOnTouchListener { _, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
removeFloatingView()
true
} else {
false
}
}
}
private fun initView() {
binding?.tvChangeSource?.text = PreferenceLanguageUtils.getString("language_source")
binding?.tvChangeTarget?.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
private fun updateRecentLanguages() {
val recentLanguages = PreferenceLanguageUtils.getRecentLanguages()
if (recentLanguages.isNotEmpty()) {
val recentAdapter =
LanguageAdapter(context, ArrayList(recentLanguages)) { _, language ->
Log.d("LanguageChange", language.language)
if (lastTranslateLanguage) {
PreferenceLanguageUtils.putString("language_target", language.language)
removeFloatingView()
} else {
PreferenceLanguageUtils.putString("language_source", language.language)
removeFloatingView()
}
PreferenceLanguageUtils.addRecentLanguage(language)
binding?.tvChangeSource?.text =
PreferenceLanguageUtils.getString("language_source")
binding?.tvChangeTarget?.text =
PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
binding?.listLanCommon5?.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
binding?.listLanCommon5?.adapter = recentAdapter
}
}
private fun initClick() {
binding?.tvChangeSource?.setOnClickListener {
lastTranslateLanguage = false
binding?.tvChangeSource?.setTextColor(getColor(context, R.color.main_text_ff0e8ce8))
binding?.tvChangeTarget?.setTextColor(getColor(context, R.color.main_text_ff1f1724))
}
binding?.tvChangeTarget?.setOnClickListener {
lastTranslateLanguage = true
binding?.tvChangeSource?.setTextColor(getColor(context, R.color.main_text_ff1f1724))
binding?.tvChangeTarget?.setTextColor(getColor(context, R.color.main_text_ff0e8ce8))
}
binding?.tvExchange?.setOnClickListener {
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding?.tvChangeSource?.text = currentTargetLanguage
binding?.tvChangeTarget?.text = currentSourceLanguage
removeFloatingView()
}
}
private fun initAllList() {
val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(context)
if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(context, languages) { _, language ->
Log.d("LanguageChange", language.language)
if (lastTranslateLanguage) {
PreferenceLanguageUtils.putString("language_target", language.language)
removeFloatingView()
} else {
PreferenceLanguageUtils.putString("language_source", language.language)
removeFloatingView()
}
PreferenceLanguageUtils.addRecentLanguage(language)
binding?.tvChangeSource?.text = PreferenceLanguageUtils.getString("language_source")
binding?.tvChangeTarget?.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
binding?.listLanguages?.layoutManager =
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
binding?.listLanguages?.adapter = adapter
}
}
fun removeFloatingView() {
binding?.root?.let {
windowManager.removeView(it)
}
binding = null
}
}

View File

@ -0,0 +1,135 @@
package com.assimilate.alltrans.curview
import android.content.Context
import android.graphics.*
import android.media.ImageReader
import android.os.Build
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import android.widget.Toast
import com.assimilate.alltrans.R
import com.assimilate.alltrans.databinding.LayoutSusSelectBinding
class SelectionView(private val context: Context) {
private lateinit var binding: LayoutSusSelectBinding
private lateinit var selectionView: View
private lateinit var overlayView: FrameLayout
private val windowManager: WindowManager =
context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private var startX = 0
private var startY = 0
private var endX = 0
private var endY = 0
fun addSelectionView(imageReader: ImageReader) {
binding = LayoutSusSelectBinding.inflate(LayoutInflater.from(context))
overlayView = FrameLayout(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
overlayView.addView(binding.root)
selectionView = SelectionRectView(context)
overlayView.addView(selectionView)
val overlayParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
windowManager.addView(overlayView, overlayParams)
Toast.makeText(context, context.getString(R.string.select_toast), Toast.LENGTH_SHORT).show()
overlayView.setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.rawX.toInt()
startY = event.rawY.toInt()
true
}
MotionEvent.ACTION_MOVE -> {
endX = event.rawX.toInt()
endY = event.rawY.toInt()
updateSelectionRect()
true
}
MotionEvent.ACTION_UP -> {
endX = event.rawX.toInt()
endY = event.rawY.toInt()
Log.d("SelectionView", "Selected area: ($startX, $startY) to ($endX, $endY)")
removeSelectionView()
// 确保坐标顺序正确
val left = Math.min(startX, endX)
val top = Math.min(startY, endY)
val right = Math.max(startX, endX)
val bottom = Math.max(startY, endY)
Log.d(
"SelectionView",
"Corrected selected area: ($left, $top) to ($right, $bottom)"
)
// 调用 DistrictView 的方法
val districtView = DistrictView(context)
districtView.showDistrictView(imageReader, left, top, right, bottom)
true
}
else -> false
}
}
binding.susGlobalClose.setOnClickListener {
removeSelectionView()
}
}
private fun updateSelectionRect() {
val left = Math.min(startX, endX)
val top = Math.min(startY, endY)
val right = Math.max(startX, endX)
val bottom = Math.max(startY, endY)
(selectionView as SelectionRectView).updateRect(Rect(left, top, right, bottom))
}
fun removeSelectionView() {
windowManager.removeView(overlayView)
}
inner class SelectionRectView(context: Context) : View(context) {
private val paint = Paint().apply {
color = Color.WHITE
style = Paint.Style.FILL
alpha = 128
}
private var rect: Rect? = null
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
rect?.let {
canvas.drawRect(it, paint)
}
}
fun updateRect(rect: Rect) {
this.rect = rect
invalidate()
}
}
}

View File

@ -1,26 +0,0 @@
package com.assimilate.alltrans.curview
import android.view.View
import android.view.WindowManager
class SusView : View.OnClickListener {
private var windowManager: WindowManager? = null
private var layoutParams: WindowManager.LayoutParams? = null
companion object {
}
override fun onClick(v: View?) {
// TODO("Not yet implemented")
}
private fun initView() {
}
}

View File

@ -1,10 +1,12 @@
package com.assimilate.alltrans.http;
import android.text.TextUtils;
import android.util.Log;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import java.io.IOException;
import java.util.HashMap;
import retrofit2.Call;
@ -16,47 +18,36 @@ import retrofit2.http.Query;
import retrofit2.http.Url;
public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslateCallback> {
private final static String URL = "https://translate.googleapis.com/translate_a/single";
private final static String CLIENT = "gtx";
private final static String DT = "t";
/**
* {
* "inputEncode": "UTF-8", 输入编码有默认值可不传递
* "outputEncode": "UTF-8", 响应编码有默认值可不传递
* "sourceLanguage": "English", 原文语言不可为空
* "translationLanguage": "Chinese", 译文语言不可为空
* "text": "翻译内容"
* }
* @param params 输入参数集
* @param googleTranslateCallback 响应集合
*/
@Override
public void translate(HashMap<String, String> params, GoogleTranslateCallback googleTranslateCallback) {
if (null == googleTranslateCallback) {
return;
}
if (null == params || params.isEmpty()) {
googleTranslateCallback.onResponse(null);
googleTranslateCallback.onResponse(null, "Params are null or empty");
return;
}
String text = params.get("text");
if (TextUtils.isEmpty(text) || text.trim().isEmpty()) {
googleTranslateCallback.onResponse(null);
googleTranslateCallback.onResponse(null, "Text is empty");
return;
}
else {
} else {
text = text.trim();
}
String sl = params.get("sourceLanguage");
if (TextUtils.isEmpty(sl)) {
googleTranslateCallback.onResponse(null);
googleTranslateCallback.onResponse(null, "Source language is empty");
return;
}
String tl = params.get("translationLanguage");
if (TextUtils.isEmpty(sl)) {
googleTranslateCallback.onResponse(null);
if (TextUtils.isEmpty(tl)) {
googleTranslateCallback.onResponse(null, "Translation language is empty");
return;
}
@ -73,6 +64,7 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
).enqueue(new Callback<JsonArray>() {
@Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
if (response.isSuccessful()) {
JsonArray jsonArray = response.body();
if (null != jsonArray && !jsonArray.isEmpty()) {
try {
@ -82,19 +74,30 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
for (JsonElement jsonElement : array) {
builder.append(jsonElement.getAsJsonArray().get(0).getAsString());
}
googleTranslateCallback.onResponse(builder.toString());
googleTranslateCallback.onResponse(builder.toString(), null);
} else {
googleTranslateCallback.onResponse(null, "Empty translation array");
}
} catch (Exception e) {
googleTranslateCallback.onResponse(null);
googleTranslateCallback.onResponse(null, "Parsing error: " + e.getMessage());
}
} else {
googleTranslateCallback.onResponse(null);
googleTranslateCallback.onResponse(null, "Empty response body");
}
} else {
googleTranslateCallback.onResponse(null, "Response not successful: " + response.message());
}
}
@Override
public void onFailure(Call<JsonArray> call, Throwable t) {
googleTranslateCallback.onResponse(null);
if (t instanceof IOException) {
Log.e("GoogleTranslator", "Network error", t);
googleTranslateCallback.onFailure("Network error: " + t.getMessage());
} else {
Log.e("GoogleTranslator", "Conversion error", t);
googleTranslateCallback.onFailure("Conversion error: " + t.getMessage());
}
}
});
}
@ -102,9 +105,9 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
public interface GoogleTranslateApi {
@GET
Call<JsonArray> api(
@Url String url, // google 固定值
@Query("client") String client, // google 固定值
@Query("dt") String dt, // google 固定值
@Url String url,
@Query("client") String client,
@Query("dt") String dt,
@Query("ie") String ie,
@Query("oe") String oe,
@Query("sl") String sl,
@ -113,6 +116,7 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
}
public interface GoogleTranslateCallback {
void onResponse(String val);
void onResponse(String val, String errorMessage);
void onFailure(String errorMessage);
}
}

View File

@ -1,4 +1,4 @@
package com.assimilate.alltrans.common;
package com.assimilate.alltrans.keepmodel;
import android.os.Parcel;
import android.os.Parcelable;

View File

@ -1,4 +1,4 @@
package com.assimilate.alltrans.common;
package com.assimilate.alltrans.keepmodel;
import android.content.Context;
import android.net.Uri;
@ -7,6 +7,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.assimilate.alltrans.R;
import com.assimilate.alltrans.common.Logger;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@ -72,7 +73,15 @@ public class LanguagesConstants {
return lang;
}
}
return null;
// 返回一个默认的Language对象避免返回null
return getDefaultLanguage();
}
// 新增一个方法来获取默认的Language对象
private Language getDefaultLanguage() {
// 返回一个你希望的默认语言对象
return new Language("English", "en","en");
}
// 获取 languageCode

View File

@ -1,4 +1,4 @@
package com.assimilate.alltrans.common
package com.assimilate.alltrans.keepmodel
import android.content.Context
import android.content.SharedPreferences
@ -12,7 +12,7 @@ object PreferenceLanguageUtils {
private const val KEY_RECENT_LANGUAGES = "recent_languages"
private const val MAX_RECENT_LANGUAGES = 5
private const val PREF_KEY_FIRST_TIME = "first_time"
private val LANGUAGE_LIST_TYPE = object : TypeToken<List<Language>>() {}.type
@Volatile
private var sharedPreferences: SharedPreferences? = null
@ -31,7 +31,7 @@ object PreferenceLanguageUtils {
getSharedPreferences().edit().putString(key, value).apply()
}
fun getString(key: String, defValue: String = "english"): String {
fun getString(key: String, defValue: String = "English"): String {
val value = getSharedPreferences().getString(key, defValue) ?: defValue
return value
}
@ -61,11 +61,11 @@ object PreferenceLanguageUtils {
saveRecentLanguages(recentLanguages)
}
fun getRecentLanguages(): List<Language> {
val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null)
return if (json != null) {
val type = object : TypeToken<List<Language>>() {}.type
Gson().fromJson(json, type)
Gson().fromJson(json, LANGUAGE_LIST_TYPE)
} else {
emptyList()
}
@ -80,6 +80,7 @@ object PreferenceLanguageUtils {
fun isFirstTime(): Boolean {
return getSharedPreferences().getBoolean(PREF_KEY_FIRST_TIME, true)
}
// 设置已经不是第一次进入应用了
fun setNotFirstTime() {
getSharedPreferences().edit().putBoolean(PREF_KEY_FIRST_TIME, false).apply()

View File

@ -0,0 +1,7 @@
package com.assimilate.alltrans.listeners
import android.graphics.Rect
interface OnRegionSelectedListener {
fun onRegionSelected(rect: Rect)
}

View File

@ -10,7 +10,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.assimilate.alltrans.common.Logger;
import java.util.ArrayList;
@ -32,8 +31,8 @@ public class DbTranslation extends SQLiteOpenHelper {
"target_lan TEXT, " +
"target_txt TEXT, " +
"current_time_millis INTEGER, " +
"exist int DEFAULT 1, " +
"collection int DEFAULT 0);"
"exist INTEGER DEFAULT 1, " +
"collection INTEGER DEFAULT 0);"
);
}
@ -113,29 +112,71 @@ public class DbTranslation extends SQLiteOpenHelper {
Log.d("SQLite: ", "at removeTranslations: " + exception.getMessage());
}
}
/**
* @param collect 收藏不收藏
* 收藏不收藏
*/
public boolean collectJust(boolean collect) {
final int value = collect ? 1 : 0;
try (SQLiteDatabase database = getWritableDatabase();) {
public boolean toggleCollect() {
try (SQLiteDatabase database = getWritableDatabase();
Cursor cursor = database.rawQuery("SELECT collection FROM " + TABLE + " ORDER BY id DESC LIMIT 1", null)) {
if (cursor.moveToFirst()) {
int collectionIndex = cursor.getColumnIndex("collection");
if (collectionIndex != -1) {
int currentValue = cursor.getInt(collectionIndex);
int newValue = (currentValue == 1) ? 0 : 1;
ContentValues values = new ContentValues();
values.put("collection", value);
values.put("collection", newValue);
String selection = "id = (SELECT MAX(id) FROM " + TABLE + ")";
int result = database.update(TABLE, values, selection, null);
return result > 0;
if (result > 0) {
try (Cursor checkCursor = database.rawQuery("SELECT collection FROM " + TABLE + " WHERE id = (SELECT MAX(id) FROM " + TABLE + ")", null)) {
if (checkCursor.moveToFirst()) {
int checkCollectionIndex = checkCursor.getColumnIndex("collection");
if (checkCollectionIndex != -1) {
return checkCursor.getInt(checkCollectionIndex) == 1;
}
}
}
}
}
}
return false;
} catch (Exception exception) {
Log.d("SQLite: ", "at collectionJust: " + exception.getMessage());
Log.e("SQLite: ", "at toggleCollect: " + exception.getMessage());
return false;
}
}
/**
* 得到翻译记录
* @param filterUnCollect 是否需要过滤掉没有收藏的记录
* 检查最后一个翻译是否被收藏
*/
public ArrayList<Translations> getTranslations(boolean filterUnCollect) {
public boolean isLastTranslationCollected() {
try (SQLiteDatabase database = getWritableDatabase();
Cursor cursor = database.rawQuery("SELECT collection FROM " + TABLE + " ORDER BY id DESC LIMIT 1", null)) {
if (cursor.moveToFirst()) {
int collectionIndex = cursor.getColumnIndex("collection");
if (collectionIndex != -1) {
int collection = cursor.getInt(collectionIndex);
Log.d("SQLite: ", "Last translation collection value: " + collection);
return collection == 1;
}
} else {
Log.d("SQLite: ", "No translations found.");
}
} catch (Exception exception) {
Log.e("SQLite: ", "at isLastTranslationCollected: " + exception.getMessage());
}
return false;
}
/**
* 得到翻译记录
* @param filterCollect 是否需要过滤掉没有收藏的记录
*/
public ArrayList<Translations> getTranslations(boolean filterCollect) {
final ArrayList<Translations> logs = new ArrayList<>();
Cursor cursor = null;
try (SQLiteDatabase database = getWritableDatabase();) {
@ -169,7 +210,7 @@ public class DbTranslation extends SQLiteOpenHelper {
if (exist == 1) {
// 是否需要过滤掉没有收藏的简便写法
if (!filterUnCollect || collection == 1) {
if (!filterCollect || collection == 1) {
Translations translations = new Translations(id, sourceLanguage, sourceText, targetLanguage, targetText, currentTimeMillis, exist, collection);
logs.add(translations);
}
@ -190,4 +231,5 @@ public class DbTranslation extends SQLiteOpenHelper {
Collections.reverse(logs);
return logs;
}
}

View File

@ -0,0 +1,86 @@
package com.assimilate.alltrans.viewui
import android.os.AsyncTask
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.assimilate.alltrans.R
import org.json.JSONArray
import org.json.JSONException
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class DicActivity : AppCompatActivity() {
private lateinit var editTextWord: EditText
private lateinit var buttonSearch: Button
private lateinit var textViewDefinition: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dic)
editTextWord = findViewById(R.id.editTextWord)
buttonSearch = findViewById(R.id.buttonSearch)
textViewDefinition = findViewById(R.id.textViewDefinition)
buttonSearch.setOnClickListener {
val word = editTextWord.text.toString().trim()
if (word.isNotEmpty()) {
FetchDefinitionTask().execute(word)
}
}
}
private inner class FetchDefinitionTask : AsyncTask<String, Void, String?>() {
override fun doInBackground(vararg params: String): String? {
val word = params[0]
val apiUrl = "https://api.dictionaryapi.dev/api/v2/entries/en/$word"
return try {
val url = URL(apiUrl)
val urlConnection = url.openConnection() as HttpURLConnection
urlConnection.requestMethod = "GET"
val responseCode = urlConnection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = BufferedReader(InputStreamReader(urlConnection.inputStream))
val response = StringBuilder()
var inputLine: String?
while (inputStream.readLine().also { inputLine = it } != null) {
response.append(inputLine)
}
inputStream.close()
response.toString()
} else {
null
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
override fun onPostExecute(result: String?) {
if (result != null) {
try {
val jsonArray = JSONArray(result)
val jsonObject = jsonArray.getJSONObject(0)
val meanings = jsonObject.getJSONArray("meanings")
val meaning = meanings.getJSONObject(0)
val definitions = meaning.getJSONArray("definitions")
val definition = definitions.getJSONObject(0)
val definitionText = definition.getString("definition")
textViewDefinition.text = definitionText
} catch (e: JSONException) {
e.printStackTrace()
textViewDefinition.text = "Error parsing definition"
}
} else {
textViewDefinition.text = "Definition not found"
}
}
}
}

View File

@ -16,7 +16,6 @@ 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
class HistoryActivity : AppCompatActivity() {
@ -40,6 +39,7 @@ class HistoryActivity : AppCompatActivity() {
ids = HashSet()
items = HashSet()
val extra = intent.getStringExtra(COMMAND)
operationCollection = COMMAND_COLLECTION == extra
@ -49,20 +49,45 @@ class HistoryActivity : AppCompatActivity() {
)
}
initClick()
}
override fun onResume() {
super.onResume()
initSet()
}
private fun initSet() {
val translations = ArrayList<Translations>()
if (!operationCollection) {
// 查出收藏的翻译记录
binding!!.tvFuncTrans.text = getString(R.string.favor_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(true)
if (null != list && !list.isEmpty()) {
if (list.isEmpty()) {
binding!!.defEmpty.visibility = View.VISIBLE
binding!!.remove.visibility = View.GONE
binding!!.defEmpty.text = getString(R.string.favor_emtpoy)
} else {
binding!!.defEmpty.visibility = View.GONE
binding!!.remove.visibility = View.GONE
}
if (null != list && list.isNotEmpty()) {
translations.addAll(list)
}
} else {
// 查出所有的翻译记录
binding!!.tvFuncTrans.text = getString(R.string.his_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(false)
if (list.isEmpty()) {
binding!!.remove.visibility = View.GONE
binding!!.defEmpty.visibility = View.VISIBLE
binding!!.defEmpty.text = getString(R.string.his_emtpoy)
} else {
binding!!.remove.visibility = View.GONE
binding!!.defEmpty.visibility = View.GONE
}
if (null != list && list.isNotEmpty()) {
translations.addAll(list)
}
@ -91,6 +116,7 @@ class HistoryActivity : AppCompatActivity() {
private fun add(id: Long, index: Int) {
ids!!.add(id)
items!!.add(index)
}
@ -101,7 +127,7 @@ class HistoryActivity : AppCompatActivity() {
private fun updateBtn() {
if (ids!!.isEmpty()) {
binding!!.remove.visibility = View.INVISIBLE
binding!!.remove.visibility = View.GONE
} else {
binding!!.remove.visibility = View.VISIBLE
}
@ -114,13 +140,13 @@ class HistoryActivity : AppCompatActivity() {
override fun onClick(v: View) {
if (ids!!.isEmpty()) {
Widget.makeToast(this@HistoryActivity, "Noting to remove.")
Widget.makeToast(this@HistoryActivity, "Nothing to remove.")
return
}
if (!ids!!.isEmpty()) {
if (ids!!.isNotEmpty()) {
val longArrayList = ArrayList(ids)
if (operationCollection) {
if (!operationCollection) {
getDbTranslation(this@HistoryActivity).removeCollectTranslations(
longArrayList
)
@ -129,13 +155,13 @@ class HistoryActivity : AppCompatActivity() {
}
ids!!.clear()
}
if (!items!!.isEmpty()) {
if (items!!.isNotEmpty()) {
val integerArrayList = ArrayList(items)
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
integerArrayList.sortWith { o1, o2 -> o2.compareTo(o1) }
adapter.updateSet(integerArrayList)
items!!.clear()
}
binding!!.remove.visibility = View.VISIBLE
binding!!.remove.visibility = View.GONE // 隐藏删除按钮
}
private fun getDbTranslation(context: Context): DbTranslation {
@ -148,19 +174,16 @@ class HistoryActivity : AppCompatActivity() {
binding!!.histories.layoutManager = layoutManager
binding!!.histories.adapter = adapter
}
override fun onBackPressed() {
super.onBackPressed()
}
fun clickBack(view: View?) {
onBackPressed()
private fun initClick() {
binding?.ivHisBack?.setOnClickListener { onBackPressed() }
}
companion object {
const val COMMAND: String = "remove"
const val COMMAND_COLLECTION: String = "remove_collection"
const val COMMAND_HISTORY: String = "remove_history"
}
}

View File

@ -1,18 +1,28 @@
package com.assimilate.alltrans.viewui
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.keepmodel.Language
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LanguageChangeActivity : AppCompatActivity() {
private lateinit var binding: ActivityLanguageChangeBinding
@ -32,12 +42,33 @@ class LanguageChangeActivity : AppCompatActivity() {
initView()
initSearch() // 添加这一行
initList()
lifecycleScope.launch {
withContext(Dispatchers.IO) {
// 耗时操作
initAllList()
}
}
initClick()
}
private fun initSearch() {
binding.chSearch.setOnQueryTextListener(object :
binding.chSearch.apply {
// 获取关闭按钮的ImageView
val closeButton = findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
// 设置关闭按钮一直可见
closeButton.visibility = View.VISIBLE
// 确保 SearchView 处于可点击状态
isFocusable = true
isIconified = false
// 延迟请求焦点以避免弹出软件盘
post {
clearFocus()
}
setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
@ -48,15 +79,43 @@ class LanguageChangeActivity : AppCompatActivity() {
return true
}
})
}
// 设置点击关闭按钮的行为
closeButton.setOnClickListener {
// 清空搜索框
setQuery("", false)
// 清除焦点
clearFocus()
// 隐藏软件盘
val imm =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0)
}
}
}
private fun initView() {
val intentT = intent
if (intentT.getBooleanExtra("choose_t", false)) {
lastTranslateLanguage = true
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff1f1724))
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff0e8ce8))
} else {
lastTranslateLanguage = false
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff0e8ce8))
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff1f1724))
}
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages()
}
private fun updateRecentLanguages() {
val recentLanguages = PreferenceLanguageUtils.getRecentLanguages()
if (recentLanguages.isNotEmpty()) {
@ -67,6 +126,7 @@ class LanguageChangeActivity : AppCompatActivity() {
onBackPressed()
} else {
PreferenceLanguageUtils.putString("language_source", language.language)
onBackPressed()
}
PreferenceLanguageUtils.addRecentLanguage(language)
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
@ -107,16 +167,19 @@ class LanguageChangeActivity : AppCompatActivity() {
}
private fun initList() {
private fun initAllList() {
val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(this)
if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(this, languages) { _, language ->
Log.d("LanguageChange", language.language)
if (lastTranslateLanguage) {
PreferenceLanguageUtils.putString("language_target", language.language)
FirebaseAnalyticsHelper.languageChooseEvent("target_" + language.language)
onBackPressed()
} else {
PreferenceLanguageUtils.putString("language_source", language.language)
FirebaseAnalyticsHelper.languageChooseEvent("source_" + language.language)
onBackPressed()
}
PreferenceLanguageUtils.addRecentLanguage(language)
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")

View File

@ -1,6 +1,6 @@
package com.assimilate.alltrans.viewui
import android.app.Activity
import android.app.ActivityManager
import android.content.ActivityNotFoundException
import android.content.ClipDescription
import android.content.ClipboardManager
@ -10,41 +10,61 @@ import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.Settings
import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.util.Log
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.assimilate.alltrans.R
import com.assimilate.alltrans.allservice.SusService
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityMainBinding
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
class MainActivity : AppCompatActivity() {
private var launcher: ActivityResultLauncher<Intent>? = null
private var isBackPressedOnce = false
private var serverIsStart = false
private val backPressHandler = Handler(Looper.getMainLooper())
private var launcher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK && result.data != null) {
val speech =
result.data!!.getStringArrayListExtra("android.speech.extra.RESULTS")?.get(0)
if (speech != null) {
binding.etText.setText(speech)
} else {
FirebaseAnalyticsHelper.textVoiceResultEvent("speech_is_null")
}
}
}
private lateinit var binding: ActivityMainBinding
private lateinit var lcm: LocalBroadcastManager
private lateinit var mediaProjectionManager: MediaProjectionManager
private val REQUEST_CODE = 1000
private val REQUEST_CODE_OVERLAY = 1011
private val REQUEST_CODE_MEDIA_PROJECTION = 1012
private val mpResultCode = "mpResultCode"
private val mpData = "mpData"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
@ -57,42 +77,30 @@ class MainActivity : AppCompatActivity() {
insets
}
initSet()
initClick()
registerResult()
// 初始化
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
// 检查并请求悬浮窗权限
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, REQUEST_CODE)
}
}
private fun registerResult() {
launcher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val data = result.data
if (result.resultCode == RESULT_OK && data != null) {
val speech =
data.getStringArrayListExtra("android.speech.extra.RESULTS")?.get(0)
if (!TextUtils.isEmpty(speech)) {
binding.etText.setText(speech)
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
if (requestCode == REQUEST_CODE_OVERLAY) {
// 检查悬浮窗权限是否已授予
if (Settings.canDrawOverlays(this)) {
checkAndRequestMediaProjectionPermission()
} else {
Widget.makeToast(this, "To allow floating windows for quick translation.")
}
} else if (requestCode == REQUEST_CODE_MEDIA_PROJECTION) {
// 处理mediaProjectionManager权限请求结果
if (resultCode == RESULT_OK) {
serverIsStart = true
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick)
FirebaseAnalyticsHelper.hoverButtonClickEvent()
// 权限授予,启动截图
val serviceIntent = Intent(this, SusService::class.java).apply {
putExtra(mpResultCode, resultCode)
putExtra(mpData, data)
@ -103,7 +111,10 @@ class MainActivity : AppCompatActivity() {
startService(serviceIntent)
}
} else {
Log.e("MainActivity", "Screen capture permission denied")
Widget.makeToast(
this,
"Please allow access to screen information to identify the content that needs translation."
)
}
}
}
@ -114,8 +125,117 @@ class MainActivity : AppCompatActivity() {
}
private fun initSet() {
lcm = LocalBroadcastManager.getInstance(this)
FirebaseAnalyticsHelper.homePvEvent("create")
val isRunning = isServiceRunning(this, SusService::class.java)
if (isRunning) {
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick)
} else {
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick_def)
Widget.startScaleAnimation(binding.ivQuickStart)
}
backPressedCall()
// 初始化截屏
mediaProjectionManager =
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
}
private fun initClick() {
binding.tvMainDic.setOnClickListener {
// startActivity(Intent(this,DicActivity::class.java))
}
binding.tvMainPhotoTrans.setOnClickListener {
startActivity(
Intent(this, PhotoImageActivity::class.java)
)
}
binding.tvMainVoice.setOnClickListener {
voiceToText()
toTextTransResult()
FirebaseAnalyticsHelper.textVoiceClickEvent()
}
binding.tvMainPaste.setOnClickListener {
pasteFromClipboard()
FirebaseAnalyticsHelper.textTransPasteEvent()
}
binding.tvMainTrans.setOnClickListener {
toTextTransResult()
FirebaseAnalyticsHelper.textTransClickEvent()
}
binding.ivMainSetting.setOnClickListener {
startActivity(
Intent(this, SettingsActivity::class.java)
)
}
binding.chSourceLanguage.setOnClickListener {
startActivity(
Intent(this, LanguageChangeActivity::class.java)
)
}
binding.chTargetLanguage.setOnClickListener {
val intentT = Intent(this, LanguageChangeActivity::class.java)
intentT.putExtra("choose_t", true)
startActivity(intentT)
}
binding.ivMainHistory.setOnClickListener {
intent = Intent(this, HistoryActivity::class.java)
intent.putExtra("remove", "remove_collection")
startActivity(
intent
)
}
binding.llQuickSet.setOnClickListener {
startActivity(
Intent(this, QuickSetActivity::class.java)
)
}
binding.ivQuickStart.setOnClickListener {
if (!serverIsStart) {
// 检查并请求悬浮窗权限
if (!Settings.canDrawOverlays(this)) {
val intent = Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName")
)
startActivityForResult(intent, REQUEST_CODE_OVERLAY)
} else {
checkAndRequestMediaProjectionPermission()
}
} else {
stopService(Intent(this, SusService::class.java))
serverIsStart = false
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick_def)
FirebaseAnalyticsHelper.hoverButtonCancelEvent()
}
}
binding.ivMainExChange.setOnClickListener {
// 读取当前的源语言和目标语言
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding.chSourceLanguage.text = currentTargetLanguage
binding.chTargetLanguage.text = currentSourceLanguage
}
binding.etText.setOnClickListener {
FirebaseAnalyticsHelper.textInputEvent()
}
// 监听EditText的文本变化
binding.etText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
@ -147,79 +267,16 @@ class MainActivity : AppCompatActivity() {
}
})
}
private fun initClick() {
binding.tvMainPhotoTrans.setOnClickListener {
startActivity(
Intent(this, StillImageActivity::class.java)
)
}
binding.tvMainVoice.setOnClickListener {
voiceToText()
toTextTransResult()
}
binding.tvMainPaste.setOnClickListener {
pasteFromClipboard(binding.etText)
toTextTransResult()
}
binding.tvMainTrans.setOnClickListener {
toTextTransResult()
}
binding.ivMainSetting.setOnClickListener {
startActivity(
Intent(this, SettingsActivity::class.java)
)
}
binding.chSourceLanguage.setOnClickListener {
startActivity(
Intent(this, LanguageChangeActivity::class.java)
)
}
binding.chTargetLanguage.setOnClickListener {
startActivity(
Intent(this, LanguageChangeActivity::class.java)
)
}
binding.ivMainHistory.setOnClickListener {
intent = Intent(this, HistoryActivity::class.java)
intent.putExtra("remove", "remove_collection")
startActivity(
intent
)
}
binding.llQuickSet.setOnClickListener {
startActivity(
Intent(this, QuickSetActivity::class.java)
)
}
binding.ivQuickStart.setOnClickListener {
private fun checkAndRequestMediaProjectionPermission() {
// 启动截图
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
REQUEST_CODE
REQUEST_CODE_MEDIA_PROJECTION
)
}
binding.ivMainExChange.setOnClickListener {
// 读取当前的源语言和目标语言
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding.chSourceLanguage.text = currentTargetLanguage
binding.chTargetLanguage.text = currentSourceLanguage
}
}
private fun toTextTransResult() {
@ -262,28 +319,51 @@ class MainActivity : AppCompatActivity() {
//
// )
try {
launcher?.launch(speechIntent)
launcher.launch(speechIntent)
} catch (ea: ActivityNotFoundException) {
FirebaseAnalyticsHelper.textVoiceResultEvent("device_not_support")
Widget.makeToast(this, getString(R.string.main_voice_to_text))
}
}
private fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val services = activityManager.getRunningServices(Integer.MAX_VALUE)
for (service in services) {
if (serviceClass.name == service.service.className) {
return true
}
}
return false
}
// 粘贴文本
private fun pasteFromClipboard(etText: EditText) {
private fun pasteFromClipboard() {
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
if (clipboard.hasPrimaryClip() && clipboard.primaryClipDescription!!.hasMimeType(
ClipDescription.MIMETYPE_TEXT_PLAIN
)
) {
val item = clipboard.primaryClip!!.getItemAt(0)
val text = item.text.toString()
if (text.isNotEmpty()) {
// 在EditText中显示粘贴的文本
etText.setText(text)
if (clipboard.hasPrimaryClip()) {
val clipData = clipboard.primaryClip
if (clipData != null && clipData.itemCount > 0) {
val item = clipData.getItemAt(0)
val pasteData = item.coerceToText(this).toString()
if (pasteData.isNotEmpty()) {
binding.etText.setText(pasteData)
binding.etText.requestFocus() // 获取焦点
toTextTransResult()
} else {
Toast.makeText(
this,
resources.getString(R.string.main_paste_empty),
Toast.LENGTH_SHORT
).show()
etText.requestFocus() // 获取焦点
}
} else {
Log.e("PasteFromClipboard", "No items in clipboard.")
}
} else {
Log.e("PasteFromClipboard", "Clipboard has no primary clip.")
}
}
@ -292,12 +372,36 @@ class MainActivity : AppCompatActivity() {
initView()
}
override fun onRestart() {
super.onRestart()
FirebaseAnalyticsHelper.homePvEvent("cap")
}
override fun onDestroy() {
super.onDestroy()
if (null != launcher) {
launcher!!.unregister()
launcher = null
launcher.unregister()
}
//定义手势返回
private fun backPressedCall() {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (isBackPressedOnce) {
finishAffinity()
} else {
isBackPressedOnce = true
Toast.makeText(
applicationContext,
"Press the back again to exit.",
Toast.LENGTH_SHORT
).show()
backPressHandler.postDelayed({ isBackPressedOnce = false }, 2000)
}
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
}

View File

@ -3,27 +3,18 @@ package com.assimilate.alltrans.viewui
import android.Manifest
import android.app.Activity
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.util.Pair
import android.view.MenuItem
import android.view.View
import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.SearchView
import android.widget.Spinner
import android.widget.TextView
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
@ -42,17 +33,16 @@ import androidx.recyclerview.widget.RecyclerView
import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.BitmapUtils
import com.assimilate.alltrans.common.Language
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.keepmodel.Language
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.curview.GraphicOverlay
import com.assimilate.alltrans.databinding.ActivityStillImageBinding
import com.google.android.gms.common.annotation.KeepName
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.common.util.concurrent.ListenableFuture
import com.google.mlkit.common.model.LocalModel
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
@ -61,16 +51,13 @@ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
/** Activity demonstrating different image detector features with a still image from camera. */
@KeepName
class StillImageActivity : AppCompatActivity() {
private var preview: ImageView? = null
private var graphicOverlay: GraphicOverlay? = null
class PhotoImageActivity : AppCompatActivity() {
private var selectedMode = TEXT_RECOGNITION_CHINESE
private var selectedSize: String? = SIZE_SCREEN
private var isLandScape = false
@ -86,22 +73,21 @@ class StillImageActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION = 100
private lateinit var bottomSheetDialog: BottomSheetDialog
private var chooseLanguage: Boolean = false
private lateinit var binding: ActivityStillImageBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_still_image)
binding = ActivityStillImageBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom)
v.setPadding(0, systemBars.top + 26, 0, systemBars.bottom)
insets
}
Widget.makeSnackbar(this, "Photographing text for translation")
preview = findViewById(R.id.preview)
graphicOverlay = findViewById(R.id.graphic_overlay)
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
savedInstanceState?.let {
imageUri = it.getParcelable(KEY_IMAGE_URI)
@ -146,16 +132,42 @@ class StillImageActivity : AppCompatActivity() {
}
private fun initView() {
findViewById<TextView>(R.id.still_source_language).text =
binding.photoPreview.visibility = View.VISIBLE
binding.stillSourceLanguage.text =
PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text =
binding.stillTargetLanguage.text =
PreferenceLanguageUtils.getString("language_target")
bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog = BottomSheetDialog(this, R.style.CustomBottomSheetDialogTheme)
bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan)
bottomSheetDialog.dismissWithAnimation = true
bottomSheetDialog.findViewById<androidx.appcompat.widget.SearchView>(R.id.ph_search)
?.setOnQueryTextListener(object :
?.let { searchView ->
// 设置关闭按钮一直显示
searchView.isIconified = false
searchView.clearFocus()
// 设置点击事件
searchView.setOnClickListener {
// 清空搜索框
searchView.setQuery("", false)
// 清除焦点
searchView.clearFocus()
// 隐藏软件盘
val imm =
this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(searchView.windowToken, 0)
// 保持关闭按钮显示
searchView.isIconified = false
}
// 设置查询文本监听器
searchView.setOnQueryTextListener(object :
androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return false
@ -165,11 +177,19 @@ class StillImageActivity : AppCompatActivity() {
(bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.adapter as LanguageAdapter).filter.filter(
newText
)
return true
}
})
// 设置关闭按钮点击事件
searchView.findViewById<View>(androidx.appcompat.R.id.search_close_btn)
?.setOnClickListener {
searchView.setQuery("", false)
searchView.clearFocus()
searchView.isIconified = false // 保持关闭按钮显示
}
}
initList()
}
@ -178,16 +198,22 @@ class StillImageActivity : AppCompatActivity() {
if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(this, languages) { _, language ->
if (!chooseLanguage) {
PreferenceLanguageUtils.putString("language_source", language.language)
selectedMode = PreferenceLanguageUtils.getString("language_source")
tryReloadAndDetectInImage()
bottomSheetDialog.dismiss()
} else {
PreferenceLanguageUtils.putString("language_target", language.language)
tryReloadAndDetectInImage()
bottomSheetDialog.dismiss()
}
findViewById<TextView>(R.id.still_source_language).text =
binding.stillSourceLanguage.text =
PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text =
binding.stillTargetLanguage.text =
PreferenceLanguageUtils.getString("language_target")
}
bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.layoutManager =
@ -197,25 +223,40 @@ class StillImageActivity : AppCompatActivity() {
}
private fun initClick() {
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
findViewById<ImageView>(R.id.iv_still_back).setOnClickListener { onBackPressed() }
findViewById<ImageView>(R.id.iv_still_buling).setOnClickListener {
binding.ivStillPic.setOnClickListener {
binding.photoPreview.visibility = View.INVISIBLE
startChooseImageIntentForResult()
}
binding.ivStillTake.setOnClickListener {
binding.photoPreview.visibility = View.VISIBLE
takePhoto()
}
binding.ivStillBack.setOnClickListener { onBackPressed() }
binding.ivStillBuling.setOnClickListener {
toggleFlash()
updateFlashButtonUI()
}
findViewById<TextView>(R.id.still_source_language).setOnClickListener {
binding.stillSourceLanguage.setOnClickListener {
chooseLanguage = false
bottomSheetDialog.show()
}
findViewById<TextView>(R.id.still_target_language).setOnClickListener {
binding.stillTargetLanguage.setOnClickListener {
chooseLanguage = true
bottomSheetDialog.show()
}
findViewById<ImageView>(R.id.still_exChange).setOnClickListener {
binding.stillExChange.setOnClickListener {
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
// 交换源语言和目标语言
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
// 更新界面显示
binding.stillSourceLanguage.text = currentTargetLanguage
binding.stillTargetLanguage.text = currentSourceLanguage
}
}
@ -226,7 +267,7 @@ class StillImageActivity : AppCompatActivity() {
}
private fun updateFlashButtonUI() {
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
binding.ivStillBuling.setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
}
private fun startCamera() {
@ -340,27 +381,27 @@ class StillImageActivity : AppCompatActivity() {
imageProcessor = when (selectedMode) {
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
ChineseTextRecognizerOptions.Builder().build(), true, true
)
"Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
this,
DevanagariTextRecognizerOptions.Builder().build()
DevanagariTextRecognizerOptions.Builder().build(), true, true
)
TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
this,
JapaneseTextRecognizerOptions.Builder().build()
JapaneseTextRecognizerOptions.Builder().build(), true, true
)
TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
this,
KoreanTextRecognizerOptions.Builder().build()
KoreanTextRecognizerOptions.Builder().build(), true, true
)
else -> TextRecognitionProcessor(
this,
TextRecognizerOptions.Builder().build()
TextRecognizerOptions.Builder().build(), true, true
)
}
@ -394,10 +435,10 @@ class StillImageActivity : AppCompatActivity() {
(imageBitmap.height / scaleFactor).toInt(),
true
)
preview?.setImageBitmap(resizedBitmap)
binding.preview.setImageBitmap(resizedBitmap)
processImage(resizedBitmap)
} else {
preview?.setImageBitmap(imageBitmap)
binding.preview.setImageBitmap(imageBitmap)
processImage(imageBitmap)
}
} catch (e: IOException) {
@ -420,8 +461,8 @@ class StillImageActivity : AppCompatActivity() {
}
private fun processImage(bitmap: Bitmap) {
graphicOverlay?.clear()
imageProcessor?.processBitmap(bitmap, graphicOverlay)
binding.graphicOverlay.clear()
imageProcessor?.processBitmap(bitmap, binding.graphicOverlay)
}
private fun startChooseImageIntentForResult() {

View File

@ -1,12 +1,15 @@
package com.assimilate.alltrans.viewui
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivitySettingsBinding
import com.google.android.material.bottomsheet.BottomSheetDialog
@ -18,10 +21,10 @@ class SettingsActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySettingsBinding.inflate(layoutInflater)
enableEdgeToEdge()
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
@ -37,10 +40,15 @@ class SettingsActivity
bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.setContentView(R.layout.bottomsheet_rate)
bottomSheetDialog.dismissWithAnimation = true
bottomSheetDialog.findViewById<TextView>(R.id.tv_rate_commit)?.setOnClickListener {
Widget.makeSnackbar(this, getString(R.string.settings_rate_toast))
bottomSheetDialog.dismiss()
}
}
private fun initClick() {
binding.ivSetBack.setOnClickListener { onBackPressed() }
binding.llRate.setOnClickListener {
bottomSheetDialog.show()
@ -51,5 +59,13 @@ class SettingsActivity
startActivity(intent)
}
binding.llPrivacyPolicy.setOnClickListener {
val url = "https://translark-privacy.mystrikingly.com"
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
startActivity(intent)
}
}
}

View File

@ -1,5 +1,6 @@
package com.assimilate.alltrans.viewui
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
@ -8,15 +9,17 @@ import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.LanguagesConstants
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityTextResultBinding
import com.assimilate.alltrans.http.GoogleTranslator
@ -25,6 +28,7 @@ import com.assimilate.alltrans.mydb.DbTranslation
import com.assimilate.alltrans.mydb.Translations
import java.util.Locale
class TextResultActivity : AppCompatActivity() {
private lateinit var binding: ActivityTextResultBinding
@ -41,7 +45,6 @@ class TextResultActivity : AppCompatActivity() {
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initSet()
initClick()
}
@ -59,6 +62,7 @@ class TextResultActivity : AppCompatActivity() {
Locale.getDefault()
)
}
// checkLastTranslationCollectedAndSetIcon()
}
@ -66,9 +70,12 @@ class TextResultActivity : AppCompatActivity() {
binding.ivTrBack.setOnClickListener { onBackPressed() }
binding.tvTrNewTrans.setOnClickListener { onBackPressed() }
binding.ivReToPhoto.setOnClickListener {
startActivity(Intent(this, StillImageActivity::class.java))
startActivity(Intent(this, PhotoImageActivity::class.java))
}
binding.ivTrCopy.setOnClickListener {
copyToClipboard()
FirebaseAnalyticsHelper.textTransCopyEvent()
}
binding.ivTrCopy.setOnClickListener { copyToClipboard() }
binding.ivSourceClear.setOnClickListener { onBackPressed() }
binding.ivSourceTts.setOnClickListener {
readText(
@ -83,12 +90,45 @@ class TextResultActivity : AppCompatActivity() {
PreferenceLanguageUtils.getString("language_target"),
this
)
FirebaseAnalyticsHelper.textTransPlayEvent()
}
binding.ivTrTargetShare.setOnClickListener {
shareText(binding.tvTrTarget.text.toString())
FirebaseAnalyticsHelper.textTransShareEvent()
}
binding.ivTrCollect.setOnClickListener {
val dbTranslation = DbTranslation(this)
val isCollected = dbTranslation.toggleCollect()
updateCollectIcon(isCollected)
if (isCollected) {
Toast.makeText(this, "Collection successful", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Uncollect", Toast.LENGTH_SHORT).show()
}
FirebaseAnalyticsHelper.textTransLikeEvent()
}
binding.ivTrTargetShare.setOnClickListener { shareText(binding.tvTrTarget.text.toString()) }
binding.ivTrCollect.setOnClickListener { addCollect() }
}
// 更新图标状态的方法
private fun updateCollectIcon(isCollected: Boolean) {
if (isCollected) {
binding.ivTrCollect.setImageResource(R.drawable.ic_like_yes) // 已收藏的图标
} else {
binding.ivTrCollect.setImageResource(R.drawable.ic_like_def) // 未收藏的图标
}
}
// 检查最后一个翻译是否被收藏并更新图标
private fun checkLastTranslationCollectedAndSetIcon() {
val dbTranslation = DbTranslation(this)
val lastTranslationCollected = dbTranslation.isLastTranslationCollected
updateCollectIcon(lastTranslationCollected)
}
private fun translate(text: String) {
if (text.isEmpty() || translating) {
Logger.d("log", "translating(not post data)...")
@ -114,19 +154,46 @@ class TextResultActivity : AppCompatActivity() {
binding.tvTrTarget.text = "translating..."
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator()
translator.translate(param,
GoogleTranslator.GoogleTranslateCallback { result ->
translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
override fun onResponse(result: String?, errorMessage: String?) {
translating = false
if (!TextUtils.isEmpty(result)) {
runOnUiThread {
if (!TextUtils.isEmpty(result)) {
binding.tvTrTarget.text = result
}
addHistory(result)
} else {
// 处理错误信息
if (!TextUtils.isEmpty(errorMessage)) {
// 显示错误信息或记录日志
if (errorMessage != null) {
Log.e("TranslationError", errorMessage)
}
}
binding.tvTrTarget.text = "Translation failed: $errorMessage"
}
}
})
}
private fun addHistory(transResult: String) {
override fun onFailure(errorMessage: String?) {
translating = false
FirebaseAnalyticsHelper.textTransResultEvent("Translation failed: $errorMessage")
runOnUiThread {
// 显示失败信息或记录日志
if (!TextUtils.isEmpty(errorMessage)) {
if (errorMessage != null) {
Log.e("TranslationFailure", errorMessage)
}
binding.tvTrTarget.text = "Translation failed: $errorMessage"
} else {
binding.tvTrTarget.text = "Translation failed: Unknown error"
}
}
}
})
}
private fun addHistory(transResult: String?) {
val dbTranslation = DbTranslation(this)
val translations = Translations(
PreferenceLanguageUtils.getString("language_source"),
@ -138,13 +205,6 @@ class TextResultActivity : AppCompatActivity() {
dbTranslation.addTranslation(translations)
}
private fun addCollect() {
if (translating) return
val dbTranslation = DbTranslation(this)
dbTranslation.collectJust(true)
binding.ivTrCollect.setImageResource(R.drawable.ic_like_yes)
}
private fun shareText(text: String) {
val share: String = text.trim()

View File

@ -0,0 +1,80 @@
package com.assimilate.alltrans.viewui
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.databinding.ActivityWelBinding
class WelActivity : AppCompatActivity() {
private lateinit var binding: ActivityWelBinding
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
binding = ActivityWelBinding.inflate(layoutInflater)
setContentView(binding.root)
//debugFirebase()
ViewCompat.setOnApplyWindowInsetsListener(binding.main) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
initSet()
}
private fun debugFirebase() {
val crashButton = Button(this)
crashButton.text = "Test Crash"
crashButton.setOnClickListener {
throw RuntimeException("Test Crash") // Force a crash
}
addContentView(
crashButton, ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
}
private fun initSet() {
backPressedCall()
Handler(Looper.getMainLooper()).postDelayed({
startActivity(
Intent(this, MainActivity::class.java)
)
}, 2444)
}
private fun backPressedCall() {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
}
onBackPressedDispatcher.addCallback(this, callback)
}
override fun onResume() {
super.onResume()
FirebaseAnalyticsHelper.launchPvEvent()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This is an example InsetDrawable. It should be manually reviewed. -->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/ic_switch_camera_white_48dp_inset"
android:insetTop="3.75dp"
android:insetLeft="3.75dp"
android:insetBottom="7.75dp"
android:insetRight="3.75dp"
android:visible="true" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="20dp" />
<solid android:color="#99000000" />
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="7dp" />
<solid android:color="#FFD7D7D7" />
</shape>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景条 -->
<item>
<shape android:shape="rectangle">
<solid android:color="#FFE2ECF6" /> <!-- 背景颜色 -->
</shape>
</item>
<!-- 渐变条 -->
<item>
<shape android:shape="rectangle">
<gradient
android:angle="180"
android:endColor="#ff14cbf9"
android:startColor="#ff2ca7ff" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:endColor="#FF1099FC"
android:angle="10"
android:startColor="#FF14CBF9"
android:type="linear" />
</shape>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M10,5a2,2 0 1,0 4,0a2,2 0 1,0 -4,0zM10,12a2,2 0 1,0 4,0a2,2 0 1,0 -4,0zM10,19a2,2 0 1,0 4,0a2,2 0 1,0 -4,0z" />
</vector>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/editTextWord"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter a word" />
<Button
android:id="@+id/buttonSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Search" />
<TextView
android:id="@+id/textViewDefinition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp" />
</LinearLayout>

View File

@ -10,9 +10,9 @@
<ImageView
android:id="@+id/iv_his_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="clickBack"
android:padding="16dp"
android:src="@drawable/ic_back"
app:layout_constraintStart_toStartOf="parent"
@ -45,6 +45,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="13dp"
app:layout_constraintBottom_toTopOf="@id/remove"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
@ -60,8 +61,21 @@
android:text="@string/his_delete"
android:textColor="#FFFF5F5F"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/def_empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="@id/histories"
app:layout_constraintEnd_toEndOf="@id/histories"
app:layout_constraintStart_toStartOf="@id/histories"
app:layout_constraintTop_toTopOf="@id/histories" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -53,7 +53,9 @@
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:textColor="@color/main_text_ff0e8ce8"
@ -82,29 +84,35 @@
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/change_language">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<androidx.appcompat.widget.SearchView
android:id="@+id/ch_search"
android:layout_width="match_parent"
@ -131,8 +139,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/button_r10_gray_bg"
android:minHeight="210dp" />
android:background="@drawable/button_r10_gray_bg" />
<TextView
android:layout_width="match_parent"
@ -150,7 +157,7 @@
android:layout_marginTop="16dp"
android:background="@drawable/button_r10_gray_bg" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -12,16 +12,6 @@
tools:context=".viewui.MainActivity">
<TextView
android:id="@+id/tv_aaaaa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_main_history"
android:layout_width="wrap_content"
@ -73,7 +63,9 @@
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:text="@string/text_source_language"
@ -103,7 +95,9 @@
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:text="@string/text_target_language"
@ -163,14 +157,14 @@
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="@drawable/button_r10_gray_bg"
android:drawableStart="@drawable/main_paste"
android:drawablePadding="4dp"
android:gravity="center"
android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="4dp"
android:text="@string/main_paste_text"
app:drawableStartCompat="@drawable/main_paste" />
android:text="@string/main_paste_text" />
<ImageView
android:id="@+id/tv_main_voice"
@ -246,6 +240,7 @@
android:text="@string/main_quick_set"
android:textColor="@color/main_text_ff808080"
android:textSize="12sp"
android:visibility="gone"
app:drawableEndCompat="@drawable/ic_arrow_right" />
@ -256,8 +251,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="19dp"
android:src="@drawable/main_setting_quick" />
android:paddingTop="11dp"
android:paddingBottom="11dp"
android:src="@drawable/main_setting_quick_def" />
</LinearLayout>
<LinearLayout
@ -273,7 +269,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="12dp"
@ -288,6 +284,7 @@
app:drawableTopCompat="@drawable/main_photo" />
<TextView
android:id="@+id/tv_main_dic"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -302,6 +299,7 @@
android:textColor="@color/main_text_ff1f1724"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="gone"
app:drawableTopCompat="@drawable/main_dic" />
</LinearLayout>

View File

@ -1,79 +1,353 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_fff6f6f6"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="Settings" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white">
<ImageView
android:id="@+id/qs_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:padding="16dp"
android:src="@drawable/ic_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="悬浮球设置" />
android:layout_alignTop="@+id/qs_back"
android:layout_alignBottom="@+id/qs_back"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="@string/settings"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
android:textStyle="bold" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/qs_back"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:layout_marginBottom="38dp"
android:src="@drawable/ic_sus_little_ball" />
</RelativeLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:orientation="vertical">
<com.google.android.material.slider.Slider
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/quick_sus_xf_set" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<com.google.android.material.slider.Slider
android:id="@+id/slider_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="5"
android:stepSize="1" />
<TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/quick_set_touming" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/quick_set_touming_descri" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/main_text_fff4f4f4" />
<TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/quick_set_zd_time" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/quick_set_zd_time" />
android:layout_marginTop="8dp"
android:drawableEnd="@drawable/ic_next"
android:text="@string/quick_set_zd_time_num" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/main_text_fff4f4f4" />
<TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/quick_set_gd_xfq" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal">
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:text="@string/quick_set_gd_description" />
<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="20dp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/main_text_fff4f4f4" />
<TextView
style="@style/SusTextStyleMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/quick_set_reset" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:text="@string/quick_set_reset_description" />
</LinearLayout>
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/quick_set_qj_title" />
<LinearLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp">
<TextView
android:id="@+id/qj_tv1"
style="@style/SusTextStyleMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/quick_set_qj_tmbg" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/qj_tv1"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:text="@string/quick_set_qj_tmbg_description" />
<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<TextView
android:id="@+id/qj_tv2"
style="@style/SusTextStyleMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/quick_set_qj_color_t" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/qj_tv2"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginEnd="80dp"
android:text="@string/quick_set_qj_color" />
<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="20dp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="20dp">
<TextView
android:id="@+id/qj_tv3"
style="@style/SusTextStyleMain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="@string/quick_set_qj_kbgb_t" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/qj_tv3"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginEnd="80dp"
android:text="@string/quick_set_qj_kbgb" />
<androidx.appcompat.widget.SwitchCompat
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="20dp" />
</RelativeLayout>
</LinearLayout>
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/quick_set_kj_t" />
<LinearLayout
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/quick_set_kj_long" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawableEnd="@drawable/ic_next"
android:text="@string/quick_set_kj_long_to" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/main_text_fff4f4f4" />
<TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/quick_set_kj_double" />
<TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="20dp"
android:drawableEnd="@drawable/ic_next"
android:text="@string/quick_set_kj_long_to" />
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -1,17 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF9F9F9"
android:orientation="vertical"
android:padding="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_set_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
tools:context=".viewui.SettingsActivity">
android:src="@drawable/ic_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/settings"
android:textColor="@color/main_text_ff1f1724"
android:textSize="18sp"
android:textStyle="bold" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical">
<!-- 语言设置 -->
@ -42,11 +64,10 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:paddingEnd="12dp"
android:text="English"
android:textColor="@color/main_text_ff1f1724"
android:textSize="14sp"
app:drawableEndCompat="@drawable/ic_next" />
android:textSize="14sp" />
</LinearLayout>
<View
@ -76,6 +97,7 @@
android:text="@string/favorite"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
app:drawableEndCompat="@drawable/ic_next"
app:drawableStartCompat="@drawable/ic_favorite" />
</LinearLayout>
@ -86,7 +108,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:background="@drawable/button_r20_white_bg"
android:orientation="vertical">
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:id="@+id/ll_rate"
@ -228,10 +251,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="12dp"
android:text="1.0.1"
android:text="1.0.2"
android:textColor="@color/main_text_ff1f1724"
android:textSize="14sp"
app:drawableEndCompat="@drawable/ic_next" />
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>

View File

@ -4,7 +4,7 @@
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_53514c"
android:background="#000"
android:keepScreenOn="true">
<androidx.camera.view.PreviewView
@ -12,10 +12,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintBottom_toTopOf="@id/control"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintBottom_toBottomOf="@id/preview"
app:layout_constraintEnd_toEndOf="@id/preview"
app:layout_constraintStart_toStartOf="@id/preview"
app:layout_constraintTop_toTopOf="@id/preview" />
<ImageView
android:id="@+id/preview"
@ -89,11 +89,6 @@
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<Spinner
android:id="@+id/feature_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
</LinearLayout>
@ -117,8 +112,9 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_99000000_bg"
android:background="@drawable/button_r20_black_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
@ -134,7 +130,7 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/button_r20_99000000_bg"
android:background="@drawable/button_r20_white_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
@ -146,8 +142,9 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_99000000_bg"
android:background="@drawable/button_r20_black_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
@ -161,7 +158,7 @@
android:id="@+id/iv_still_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:padding="26dp"
android:src="@drawable/ic_back_white"
app:layout_constraintStart_toStartOf="@id/root"
app:layout_constraintTop_toTopOf="@id/root" />

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/ic_wel_bg"
tools:context=".viewui.WelActivity">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="120dp"
android:src="@drawable/ic_wel_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.airbnb.lottie.LottieAnimationView
android:layout_width="71dp"
android:layout_height="22dp"
android:layout_marginBottom="44dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:lottie_autoPlay="true"
app:lottie_fileName="tran_wel_1.json"
app:lottie_loop="true" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="31dp"
android:src="@drawable/set_sheet_icon" />
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="16dp"
android:text="@string/sheet_text"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp" />
<androidx.appcompat.widget.AppCompatRatingBar
android:id="@+id/rate1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="32dp" />
<TextView
android:id="@+id/tv_rate_commit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="43dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="43dp"
android:layout_marginBottom="75dp"
android:background="@drawable/button_r24_blue_bg"
android:gravity="center"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:text="@string/rate"
android:textColor="@color/main_text_ffffffff"
android:textSize="17sp" />
</LinearLayout>

View File

@ -30,6 +30,7 @@
android:layout_marginTop="32dp" />
<TextView
android:id="@+id/tv_rate_commit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.SearchView
android:id="@+id/ph_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:background="@drawable/button_r10_gray_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/change_language"
app:queryHint="Search" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_languages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp" />
</LinearLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/floating_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/ic_sus_little_ball" />
</FrameLayout>

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/button_r10_gray_bg"
android:orientation="vertical">
<LinearLayout
android:id="@+id/change_language"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="16dp"
android:layout_marginTop="13dp"
android:layout_marginEnd="16dp"
android:elevation="2dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_change_source"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:textColor="@color/main_text_ff0e8ce8"
android:textSize="16sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" />
<ImageView
android:id="@+id/tv_exchange"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/button_r20_white_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
android:src="@drawable/ic_exchage" />
<TextView
android:id="@+id/tv_change_target"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="12dp"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<!-- <androidx.appcompat.widget.SearchView-->
<!-- android:id="@+id/ch_search"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="16dp"-->
<!-- android:background="@drawable/button_r10_gray_bg"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toBottomOf="@id/change_language"-->
<!-- app:queryBackground="@drawable/button_r10_gray_bg"-->
<!-- app:queryHint="Search" />-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/tr_common"
android:textColor="@color/bg_ff605c62"
android:textSize="12sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_lan_common5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/button_r10_gray_bg" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/tr_other"
android:textColor="@color/bg_ff605c62"
android:textSize="12sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_languages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:background="@drawable/button_r10_gray_bg" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<com.assimilate.alltrans.curview.GraphicOverlay
android:id="@+id/sus_graphic_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom" />
<ImageView
android:id="@+id/sus_global_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:paddingStart="1dp"
android:paddingTop="26dp"
android:paddingEnd="16dp"
android:src="@drawable/ic_close" />
<LinearLayout
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="60dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<TextView
android:id="@+id/tb_sus_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:background="@drawable/button_r4_huid7d7d7_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
android:text="@string/sus_copy_reset"
android:textColor="@color/main_text_ff1f1724"
android:textSize="12sp"
android:visibility="gone" />
<TextView
android:id="@+id/bt_sus_select_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="16dp"
android:background="@drawable/button_r4_huid7d7d7_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
android:text="@string/sus_copy_selectall"
android:textColor="@color/main_text_ff1f1724"
android:textSize="12sp"
android:visibility="gone" />
<TextView
android:id="@+id/bt_sus_copy_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="20dp"
android:background="@drawable/button_r4_huid7d7d7_bg"
android:paddingStart="12dp"
android:paddingTop="6dp"
android:paddingEnd="12dp"
android:paddingBottom="6dp"
android:text="@string/sus_copy_copyall"
android:textColor="@color/main_text_ff1f1724"
android:textSize="12sp" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/button_r20_white_bg">
<TextView
android:id="@+id/tv_view1"
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_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_to_app_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginEnd="16dp"
android:src="@drawable/more_options_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/ll_main_enter_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
android:orientation="vertical"
android:paddingStart="12dp"
android:paddingTop="16dp"
android:paddingEnd="12dp"
android:paddingBottom="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_view1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_tr_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@android:color/transparent"
android:gravity="start|top"
android:maxLines="5"
android:minHeight="70dp"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_source_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="12dp"
android:src="@drawable/ic_tr_close" />
</LinearLayout>
<ImageView
android:id="@+id/iv_source_tts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:visibility="invisible"
android:layout_marginBottom="10dp"
android:src="@drawable/ic_voice" />
<View
android:layout_width="wrap_content"
android:layout_height="5dp"
android:background="@drawable/ic_dashed_line" />
<TextView
android:id="@+id/tv_tr_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@android:color/transparent"
android:gravity="start|top"
android:maxLines="5"
android:minHeight="70dp"
android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_target_tts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_voice" />
<ImageView
android:id="@+id/iv_tr_copy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_copy" />
<ImageView
android:id="@+id/iv_tr_target_share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_share" />
<ImageView
android:id="@+id/iv_tr_target_dic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_dic" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<ImageView
android:id="@+id/iv_tr_collect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="12dp"
android:src="@drawable/ic_like_def" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

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

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000">
<ImageView
android:id="@+id/sus_global_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:paddingStart="1dp"
android:paddingTop="26dp"
android:paddingEnd="16dp"
android:src="@drawable/ic_close" />
</FrameLayout>

View File

@ -56,7 +56,6 @@
<TextView
android:id="@+id/tv_sus_photo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -70,7 +69,6 @@
<TextView
android:id="@+id/tv_sus_district"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -113,13 +111,13 @@
android:src="@drawable/sus_trans_home" />
<ImageView
android:id="@+id/iv_sus_move"
android:id="@+id/iv_sus_change"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="19dp"
android:layout_weight="1"
android:gravity="center"
android:src="@drawable/sus_trans_move" />
android:src="@drawable/sus_trans_changelan" />
</LinearLayout>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -16,6 +16,7 @@
<color name="main_text_fff4f4f4">#FFF4F4F4</color>
<!-- quick_set_page-->
<color name="bg_fff6f6f6">#FFF6F6F6</color>
<color name="bg_ffaaaaaa">#FFAAAAAA</color>
<!-- sus_view-->
<color name="bg_ff2f2f2f">#FF2F2F2F</color>
<!-- change_language-->

View File

@ -1,5 +1,5 @@
<resources>
<string name="app_name">alltrans</string>
<string name="app_name">Translark</string>
<string name="pref_key_info_hide" translatable="false">ih</string>
<string name="pref_key_group_recognized_text_in_blocks" translatable="false">grtib</string>
@ -15,7 +15,10 @@
<string name="text_target_language">English</string>
<string name="main_text_enter">Enter text</string>
<string name="main_paste_text">Paste</string>
<string name="main_paste_empty">Clipboard contains empty or invalid data.</string>
<string name="main_voice_to_text">Your device may not support speech-to-text.</string>
<string name="main_try_text">Translate</string>
<string name="main_quick_text">Quick Translate</string>
<string name="main_quick_set">settings</string>
@ -32,28 +35,52 @@
<string name="tr_other">other</string>
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
<string name="tr_title">Translator</string>
<!-- sus_view-->
<string name="global_translation">Global Translation</string>
<string name="copy_text">Copy Text</string>
<string name="photo_translation">Photo Translation</string>
<string name="district_translation">District Translation</string>
<string name="sus_copy_reset">reset</string>
<string name="select_toast">Please select the area to be translated</string>
<string name="sus_copy_selectall">Select All </string>
<string name="sus_copy_copyall">Copy All</string>
<!-- his_page-->
<string name="his_delete">Delete</string>
<string name="his_title">History</string>
<string name="his_emtpoy">No translation records</string>
<string name="favor_title">Favorite</string>
<string name="favor_emtpoy">No collection record</string>
<!--settings_page-->
<string name="settings">Settings</string>
<string name="languages">Languages</string>
<string name="version_update">Version_update</string>
<string name="privacy_policy">Privacy_policy</string>
<string name="share_app">Share_app</string>
<string name="languages">App Languages</string>
<string name="version_update">Version</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="share_app">Share App</string>
<string name="favorite">Favorite</string>
<string name="rate">Rate</string>
<string name="sheet_text">Your encouragement makes us better!</string>
<string name="quick_set_touming">透明度</string>
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
<string name="quick_set_zd_time">自动折叠时间</string>
<string name="favor_title">Favorite</string>
<!-- sus_view-->
<string name="global_translation">Global Translation</string>
<string name="copy_text">Copy Text</string>
<string name="photo_translation">Photo Translation</string>
<string name="district_translation">District Translation</string>
<!-- his_page-->
<string name="his_delete">Delete</string>
<string name="his_title">History record</string>
<string name="quick_set_zd_time_num">3s</string>
<string name="quick_set_gd_xfq">固定悬浮球</string>
<string name="quick_set_reset">重置</string>
<string name="quick_set_gd_description">拖拽翻译完成松手后,悬浮球会回到固定位置,如果想改变位置可点击悬浮球在功能面板移动</string>
<string name="settings_rate_toast"> Thank you for your comments. We will do better.</string>
<string name="quick_set_reset_description">清除以上设置,并恢复默认样式</string>
<string name="quick_set_qj_title">全局翻译</string>
<string name="quick_set_qj_tmbg">透明背景</string>
<string name="quick_set_qj_tmbg_description">全局翻译时文字使用透明背景</string>
<string name="quick_set_qj_color">自动识别文字背景和颜色,开启后下面设置的文字颜色和背景将失效</string>
<string name="quick_set_qj_kbgb">点击无文字的空表区域,关闭全局翻译页面</string>
<string name="quick_set_qj_kbgb_t">点击空白区域关闭</string>
<string name="quick_set_qj_color_t">智能背景</string>
<string name="quick_set_kj_t">快捷操作</string>
<string name="quick_sus_xf_set">悬浮球设置</string>
<string name="quick_set_kj_long">长按悬浮球</string>
<string name="quick_set_kj_long_to">打开应用</string>
<string name="quick_set_kj_double">双击悬浮球</string>
</resources>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 悬浮窗 TextStyle -->
<style name="SusTextStyleMain" parent="TextAppearance.AppCompat">
<item name="android:textColor">@color/main_text_ff1f1724</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">bold</item>
</style>
<style name="SusTextStyleSecond" parent="TextAppearance.AppCompat">
<item name="android:textColor">@color/bg_ffaaaaaa</item>
<item name="android:textSize">14sp</item>
<item name="android:textStyle">normal</item>
</style>
<style name="SusSlider" parent="Widget.MaterialComponents.Slider">
<item name="trackColor">#FFE2ECF6</item> <!-- 背景颜色 -->
<item name="trackColorActive">#FF00FF00</item> <!-- 已填充部分颜色 -->
</style>
</resources>

View File

@ -6,4 +6,12 @@
</style>
<style name="Theme.Alltrans" parent="Base.Theme.Alltrans" />
<style name="CustomBottomSheetDialogTheme" parent="Theme.Material3.Light.BottomSheetDialog">
<!-- 自定义颜色属性 -->
<item name="colorPrimary">@color/white</item> <!-- 替换为你想要的颜色 -->
<item name="colorPrimaryDark">@color/white</item> <!-- 替换为你想要的颜色 -->
<item name="colorAccent">@color/white</item> <!-- 替换为你想要的颜色 -->
</style>
</resources>

View File

@ -2,4 +2,8 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
// Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
id("com.google.gms.google-services") version "4.4.2" apply false
// Add the dependency for the Crashlytics Gradle plugin
id("com.google.firebase.crashlytics") version "3.0.2" apply false
}

BIN
gradle/newtrans01 Normal file

Binary file not shown.