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

View File

@ -1,6 +1,13 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android) 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 { android {
@ -11,8 +18,8 @@ android {
applicationId = "com.assimilate.alltrans" applicationId = "com.assimilate.alltrans"
minSdk = 23 minSdk = 23
targetSdk = 34 targetSdk = 34
versionCode = 1 versionCode = 2
versionName = "1.0" versionName = "1.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
@ -24,16 +31,37 @@ android {
buildTypes { buildTypes {
debug { } debug {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
release { release {
isMinifyEnabled = false isShrinkResources = true
isMinifyEnabled = true
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "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 { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
@ -45,6 +73,15 @@ android {
dependencies { 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 // To recognize Latin script
implementation("com.google.mlkit:text-recognition:16.0.0") implementation("com.google.mlkit:text-recognition:16.0.0")
// To recognize Chinese script // 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. # debugging stack traces.
#-keepattributes SourceFile,LineNumberTable #-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 # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-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" /> android:required="false" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -17,26 +16,29 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application <application
android:name=".MyApp" android:name=".MyApp"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/app_icon"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:largeHeap="true" android:largeHeap="true"
android:roundIcon="@mipmap/app_icon_round"
android:supportsRtl="true"
android:theme="@style/Theme.Alltrans" android:theme="@style/Theme.Alltrans"
tools:targetApi="31"> tools:targetApi="31">
<activity
android:name=".viewui.MainActivity"
android:exported="false"
android:launchMode="singleTask" />
<service <service
android:name=".allservice.SusService" android:name=".allservice.SusService"
android:enabled="true" android:enabled="true"
android:exported="true" android:exported="true"
android:foregroundServiceType="mediaProjection" /> android:foregroundServiceType="mediaProjection" />
<activity <activity
android:name=".viewui.QuickSetActivity" android:name=".viewui.QuickSetActivity"
android:exported="false" /> android:exported="false" />
@ -45,7 +47,8 @@
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".viewui.LanguageChangeActivity" android:name=".viewui.LanguageChangeActivity"
android:exported="false" /> android:exported="false"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
<activity <activity
android:name=".viewui.TextResultActivity" android:name=".viewui.TextResultActivity"
android:exported="false" /> android:exported="false" />
@ -54,10 +57,10 @@
android:name="com.google.mlkit.vision.DEPENDENCIES" 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,..." --> android:value="ocr" /> <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
<activity <activity
android:name=".viewui.StillImageActivity" android:name=".viewui.PhotoImageActivity"
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".viewui.MainActivity" android:name=".viewui.WelActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -66,6 +69,7 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".viewui.HistoryActivity" /> <activity android:name=".viewui.HistoryActivity" />
<activity android:name=".viewui.DicActivity" />
</application> </application>
</manifest> </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.content.Context
import android.os.Build import android.os.Build
import android.webkit.WebView import android.webkit.WebView
import com.assimilate.alltrans.common.Language import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.common.LanguagesConstants import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Logger
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.ScreenUtils
class MyApp : Application() { class MyApp : Application() {
init { init {
instance = this instance = this
} }
private fun setSystemLanguage() { private fun setSystemLanguage() {
// 检查是否是第一次进入应用 // 检查是否是第一次进入应用
if (PreferenceLanguageUtils.isFirstTime()) { if (PreferenceLanguageUtils.isFirstTime()) {
// 第一次进入应用的逻辑 // 第一次进入应用的逻辑
val config = applicationContext().resources.configuration val config = applicationContext().resources.configuration
val locale = config.locale.toString() // 获取完整的语言环境信息,如 "en_US" 或 "zh_CN" val locale = config.locale.toString() // 获取完整的语言环境信息,如 "en_US" 或 "zh_CN"
Logger.d("dslocaleds", locale)
val languages = LanguagesConstants.getInstance().getLanguageByLanguageCode(locale, this) val languages = LanguagesConstants.getInstance().getLanguageByLanguageCode(locale, this)
Logger.d("dslocaledsa", languages.language)
PreferenceLanguageUtils.putString( PreferenceLanguageUtils.putString(
"language_source", "language_target",
languages.language.toString().trim() languages.language.toString().trim()
) )
// 设置已经不是第一次进入应用了 // 设置已经不是第一次进入应用了
PreferenceLanguageUtils.setNotFirstTime() PreferenceLanguageUtils.setNotFirstTime()
} }
@ -50,7 +42,8 @@ class MyApp : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
instance = this instance = this
// ScreenUtils.init(this) // ScreenUtils.init(this)
setSystemLanguage() setSystemLanguage()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
@ -74,5 +67,46 @@ class MyApp : Application() {
object Config { object Config {
const val openSusViewMode: String = "open_sus_view" 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; package com.assimilate.alltrans.adapters;
import android.app.Activity; import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Filter; import android.widget.Filter;
import android.widget.Filterable; import android.widget.Filterable;
@ -13,7 +12,7 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.assimilate.alltrans.R; 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.assimilate.alltrans.databinding.LanguageItemLayoutBinding;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
@ -22,14 +21,15 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> implements Filterable { 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> languages;
private final ArrayList<Language> filteredLanguages; private final ArrayList<Language> filteredLanguages;
private final OnClickListener listener; private final OnClickListener listener;
private final RequestOptions options; private final RequestOptions options;
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) { public LanguageAdapter(@NonNull final Context mContext, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
this.mActivity = activity; this.mContext = mContext;
this.languages = new ArrayList<>(languageList); this.languages = new ArrayList<>(languageList);
this.filteredLanguages = new ArrayList<>(languageList); this.filteredLanguages = new ArrayList<>(languageList);
this.listener = onClickListener; this.listener = onClickListener;
@ -55,7 +55,7 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) { if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
holder.mBinding.language.setText(currentLanguage.getLanguage()); holder.mBinding.language.setText(currentLanguage.getLanguage());
} else { } 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) { private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
if (mActivity.isFinishing() || mActivity.isDestroyed()) return;
if (TextUtils.isEmpty(url)) { if (TextUtils.isEmpty(url)) {
Glide.with(mActivity) Glide.with(mContext)
.applyDefaultRequestOptions(options) .applyDefaultRequestOptions(options)
.load(R.mipmap.ic_launcher) .load(R.mipmap.ic_launcher)
.into(view); .into(view);
} else { } else {
Glide.with(mActivity) Glide.with(mContext)
.applyDefaultRequestOptions(options) .applyDefaultRequestOptions(options)
.load(url) .load(url)
.into(view); .into(view);

View File

@ -1,10 +1,10 @@
package com.assimilate.alltrans.adapters; package com.assimilate.alltrans.adapters;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CompoundButton;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -25,9 +25,9 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
if (!list.isEmpty()) { if (!list.isEmpty()) {
for (Translations translations : list) { for (Translations translations : list) {
items.add(new TranslationItem( items.add(new TranslationItem(
translations.getId(), translations.getSourceLanguage(), translations.getSourceTxt(), translations.getId(), translations.getSourceLanguage(), translations.getSourceTxt(),
translations.getTargetLanguage(), translations.getTargetTxt(), translations.getCurrentTimeMillis(), translations.getTargetLanguage(), translations.getTargetTxt(), translations.getCurrentTimeMillis(),
translations.getExist(), translations.getCollection() translations.getExist(), translations.getCollection()
)); ));
} }
} }
@ -52,19 +52,26 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
} }
} }
}); });
holder.mBinding.checkboxCurrent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
item.currentChecked = isChecked;
if (isChecked) { // 为checkboxCurrent设置点击事件
callback.updateList(Operation.ADD, item.getId(), holder.getAdapterPosition()); holder.mBinding.checkboxCurrent.setOnClickListener(v -> {
} else { boolean isChecked = holder.mBinding.checkboxCurrent.isChecked();
callback.updateList(Operation.REMOVE, item.getId(), holder.getAdapterPosition()); 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(); final String itemSourceTxt = item.getSourceTxt();
if (!TextUtils.isEmpty(itemSourceTxt)) { if (!TextUtils.isEmpty(itemSourceTxt)) {
holder.mBinding.tvSource.setText(itemSourceTxt); holder.mBinding.tvSource.setText(itemSourceTxt);
@ -116,10 +123,11 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
public interface TranslationItemCallback { public interface TranslationItemCallback {
void updateList(Operation operation, long id, int position); void updateList(Operation operation, long id, int position);
void speech(String value); void speech(String value);
} }
public enum Operation{ public enum Operation {
REMOVE, ADD REMOVE, ADD
} }
} }

View File

@ -7,80 +7,34 @@ import android.app.NotificationManager
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import android.graphics.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.Build
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log 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 androidx.core.app.NotificationCompat
import com.assimilate.alltrans.R import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.TextRecognitionProcessor import com.assimilate.alltrans.common.ScreenCaptureManager
import com.assimilate.alltrans.common.VisionImageProcessor import com.assimilate.alltrans.curview.ControlView
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding import com.assimilate.alltrans.curview.CopyTextView
import com.assimilate.alltrans.databinding.SusControlViewBinding import com.assimilate.alltrans.curview.FloatingView
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions import com.assimilate.alltrans.curview.GlobalView
import java.io.IOException import com.assimilate.alltrans.curview.LanguageSelectorView
import com.assimilate.alltrans.curview.SelectionView
class SusService : Service() { 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 mpResultCode = "mpResultCode"
private val mpData = "mpData" private val mpData = "mpData"
private val ball_time = 5000L
private val handler = Handler(Looper.getMainLooper()) private lateinit var languageSelectorView: LanguageSelectorView
private val hideRunnable = Runnable {
// floatingView.visibility = View.GONE private lateinit var screenCaptureManager: ScreenCaptureManager
// val layoutParams = floatingView.layoutParams as WindowManager.LayoutParams private lateinit var floatingView: FloatingView
// val iconLayoutParams = WindowManager.LayoutParams( private lateinit var controlView: ControlView
// 100, 100, // Set the size of the icon as needed private lateinit var globalView: GlobalView
// layoutParams.type, private lateinit var selectionView: SelectionView
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, private lateinit var copyTextView: CopyTextView
// PixelFormat.TRANSLUCENT
// )
// iconLayoutParams.gravity = layoutParams.gravity
// iconLayoutParams.x = layoutParams.x
// iconLayoutParams.y = layoutParams.y
//
// val iconView = ImageView(this).apply {
// setImageResource(R.drawable.ic_like_yes) // Set your icon drawable here
// setOnClickListener {
// floatingView.visibility = View.VISIBLE
// windowManager.removeView(this)
// windowManager.addView(floatingView, layoutParams)
// startHideTimer()
// }
// }
// windowManager.addView(iconView, iconLayoutParams)
}
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {
return null return null
@ -95,9 +49,11 @@ class SusService : Service() {
val data = intent.getParcelableExtra<Intent>(mpData) val data = intent.getParcelableExtra<Intent>(mpData)
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
mResultCode = resultCode screenCaptureManager = ScreenCaptureManager(this)
mResultData = data screenCaptureManager.setProjectionParams(resultCode, data)
startProjection() screenCaptureManager.startProjection()
addFloatingView()
} else { } else {
Log.e("SusService", "Intent or data is null") Log.e("SusService", "Intent or data is null")
} }
@ -107,253 +63,54 @@ class SusService : Service() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
displayMetrics = DisplayMetrics()
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
initControlView()
imageProcessor = TextRecognitionProcessor(
this,
ChineseTextRecognizerOptions.Builder().build()
)
startForeground(1, createNotification()) startForeground(1, createNotification())
} }
private fun initControlView() { private fun addFloatingView() {
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this)) floatingView = FloatingView(this)
floatingView = bindingSusControl.root 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()
}
})
}
val layoutParams = WindowManager.LayoutParams( private fun addControlView() {
WindowManager.LayoutParams.WRAP_CONTENT, controlView = ControlView(this)
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) controlView.setControlViewListener(object : ControlView.ControlViewListener {
override fun onGlobalClick() {
globalView = GlobalView(this@SusService)
Handler(Looper.getMainLooper()).postDelayed({
globalView.addGlobalView(screenCaptureManager.getImageReader())
}, 333)
}
initControlClick() override fun onCopyClick() {
copyTextView = CopyTextView(this@SusService)
copyTextView.addView(screenCaptureManager.getImageReader())
}
bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener { override fun onDistrictClick() {
private var startX = 0f selectionView = SelectionView(this@SusService)
private var startY = 0f selectionView.addSelectionView(screenCaptureManager.getImageReader())
private var touchX = 0f }
private var touchY = 0f
override fun onTouch(v: View, event: MotionEvent): Boolean { override fun onChangeLan() {
when (event.action) { languageSelectorView = LanguageSelectorView(this@SusService)
MotionEvent.ACTION_DOWN -> { languageSelectorView.createFloatingView()
startX = layoutParams.x.toFloat()
startY = layoutParams.y.toFloat()
touchX = event.rawX
touchY = event.rawY
handler.removeCallbacks(hideRunnable)
return true
}
MotionEvent.ACTION_MOVE -> {
layoutParams.x = (startX + (event.rawX - touchX)).toInt()
layoutParams.y = (startY + (event.rawY - touchY)).toInt()
windowManager.updateViewLayout(floatingView, layoutParams)
return true
}
MotionEvent.ACTION_UP -> {
val screenWidth = resources.displayMetrics.widthPixels
if (layoutParams.x + floatingView.width / 2 <= screenWidth / 2) {
layoutParams.x = 0
} else {
layoutParams.x = screenWidth - floatingView.width
}
v.performClick() // 手动触发点击事件
windowManager.updateViewLayout(floatingView, layoutParams)
startHideTimer()
return true
}
}
return false
} }
}) })
startHideTimer() // Start the timer when the view is initialized controlView.addControlView()
}
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)
} }
private fun createNotification(): Notification { private fun createNotification(): Notification {
@ -372,8 +129,8 @@ class SusService : Service() {
} }
val builder = NotificationCompat.Builder(this, notificationChannelId) val builder = NotificationCompat.Builder(this, notificationChannelId)
.setContentTitle("悬浮窗服务") .setContentTitle("Screen Translation Service")
.setContentText("悬浮窗服务正在运行") .setContentText("Screen translation is running...")
.setSmallIcon(R.drawable.ic_close) .setSmallIcon(R.drawable.ic_close)
.setPriority(NotificationCompat.PRIORITY_LOW) .setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(Notification.CATEGORY_SERVICE) .setCategory(Notification.CATEGORY_SERVICE)
@ -381,38 +138,27 @@ class SusService : Service() {
return builder.build() 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() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
Log.d("SusService", "Service 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,18 +6,25 @@ import android.graphics.Canvas;
import com.assimilate.alltrans.curview.GraphicOverlay; import com.assimilate.alltrans.curview.GraphicOverlay;
/** Draw camera image to background. */ /**
* Draw camera image to background.
*/
public class CameraImageGraphic extends GraphicOverlay.Graphic { public class CameraImageGraphic extends GraphicOverlay.Graphic {
private final Bitmap bitmap; private final Bitmap bitmap;
public CameraImageGraphic(GraphicOverlay overlay, Bitmap bitmap) { public CameraImageGraphic(GraphicOverlay overlay, Bitmap bitmap) {
super(overlay); super(overlay);
this.bitmap = bitmap; this.bitmap = bitmap;
} }
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, getTransformationMatrix(), null); 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.Paint
import android.graphics.RectF import android.graphics.RectF
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
import android.text.Layout import android.text.Layout
import android.text.StaticLayout import android.text.StaticLayout
import android.text.TextPaint import android.text.TextPaint
import android.util.Log import android.util.Log
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.curview.GraphicOverlay 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 com.google.mlkit.vision.text.Text
import java.util.concurrent.Executors
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -25,154 +19,147 @@ class TextGraphic(
private val text: Text, private val text: Text,
private val shouldGroupTextInBlocks: Boolean, private val shouldGroupTextInBlocks: Boolean,
private val showLanguageTag: Boolean, private val showLanguageTag: Boolean,
private val showConfidence: Boolean private val showConfidence: Boolean,
private val textShow: Boolean,
private val needTrans: Boolean
) : GraphicOverlay.Graphic(overlay) { ) : GraphicOverlay.Graphic(overlay) {
private val rectPaint: Paint = Paint() private val textPaint: TextPaint = TextPaint().apply {
private val textPaint: TextPaint color = TEXT_COLOR
textSize = TEXT_SIZE
private val labelPaint: Paint typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
private val handler = Handler(Looper.getMainLooper()) isFakeBoldText = true
private val executor = Executors.newSingleThreadExecutor() }
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 { init {
prepareTranslation() isVisible = textShow
rectPaint.color = MARKER_COLOR
rectPaint.style = Paint.Style.STROKE if (needTrans) {
rectPaint.strokeWidth = STROKE_WIDTH TranslationManager(text) { translatedTextPairs ->
textPaint = TextPaint() translatedTextBlocks = translatedTextPairs.map { it.first }
textPaint.color = TEXT_COLOR // 可以同时打印原Text和翻译后的结果
textPaint.textSize = TEXT_SIZE translatedTextPairs.forEach { (translated, original) ->
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) Log.d("Translation", "Original: $original -> Translated: $translated")
textPaint.isFakeBoldText = true }
labelPaint = Paint() postInvalidate()
labelPaint.color = MARKER_COLOR }
labelPaint.style = Paint.Style.FILL }
// Redraw the overlay, as this graphic has been added. // Redraw the overlay, as this graphic has been added.
postInvalidate() postInvalidate()
} }
private var translatedTextBlocks: List<String> = listOf() private var translatedTextBlocks: List<String> = listOf()
// Method to prepare translation before drawing override fun draw(canvas: Canvas) {
private fun prepareTranslation() { if (!isVisible) return
for ((index, textBlock) in text.textBlocks.withIndex()) {
executor.execute { if (hiddenTextBlocks[index]) continue
drawTextBlock(textBlock, canvas, index)
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) { private fun drawTextBlock(textBlock: Text.TextBlock, canvas: Canvas, index: Int) {
val translatedBlockText = translatedTextBlocks.getOrNull(index) ?: textBlock.text
if (shouldGroupTextInBlocks) {
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) { val rect = RectF(textBlock.boundingBox)
val translatedBlockText = drawText(
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text getFormattedText(translatedBlockText, textBlock.recognizedLanguage, null),
rect,
if (shouldGroupTextInBlocks) { canvas,
drawText( textPaint
getFormattedText( )
translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
),
RectF(textBlock.boundingBox),
canvas
)
Log.d(
"fdgfsdfsdfas", getFormattedText(
translatedBlockText,
textBlock.recognizedLanguage,
confidence = null
)
)
} // 不需要单行(删除)
} }
} }
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 { private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
val res = val res = if (showLanguageTag) String.format(
if (showLanguageTag) String.format( TEXT_WITH_LANGUAGE_TAG_FORMAT,
TEXT_WITH_LANGUAGE_TAG_FORMAT, languageTag,
languageTag, text
text ) else text
) else text return if (showConfidence && confidence != null) String.format(
return if (showConfidence && confidence != null) String.format("%s (%.2f)", res, confidence) "%s (%.2f)",
else res res,
confidence
) else res
} }
private fun drawText(text: String, rect: RectF, canvas: Canvas) { private fun drawText(text: String, rect: RectF, canvas: Canvas, paint: TextPaint) {
// 如果图像是翻转的,将左边翻译到右边,右边翻译到左边。
val x0 = translateX(rect.left) val x0 = translateX(rect.left)
val x1 = translateX(rect.right) val x1 = translateX(rect.right)
rect.left = min(x0, x1) rect.left = min(x0, x1)
rect.right = max(x0, x1) rect.right = max(x0, x1)
rect.top = translateY(rect.top) rect.top = translateY(rect.top)
rect.bottom = translateY(rect.bottom) rect.bottom = translateY(rect.bottom)
// 绘制背景矩形
canvas.drawRect(rect, labelPaint) canvas.drawRect(rect, labelPaint)
// 设置文本大小以适应矩形 // 准备文本绘制
val textPaintCopy = TextPaint(textPaint) val textPaintCopy = TextPaint(paint)
val availableWidth = rect.width().toInt() val availableWidth = rect.width().toInt()
val availableHeight = rect.height().toInt() val availableHeight = rect.height().toInt()
var textSize = textPaintCopy.textSize var textSize = textPaintCopy.textSize
// 调整文本大小以适应矩形
var textLayout: StaticLayout var textLayout: StaticLayout
while (textSize > 0) { while (textSize > 0) {
textPaintCopy.textSize = textSize textPaintCopy.textSize = textSize
textLayout = textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth) .setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.0f, 1.0f)
.setLineSpacing(0.0f, 1.0f) .setIncludePad(false)
.setIncludePad(false) .build()
.build()
if (textLayout.height <= availableHeight) { if (textLayout.height <= availableHeight) {
break break
@ -180,13 +167,11 @@ class TextGraphic(
textSize -= 1 textSize -= 1
} }
// 使用 StaticLayout 绘制文本 textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
textLayout = .setAlignment(Layout.Alignment.ALIGN_NORMAL)
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth) .setLineSpacing(0.0f, 1.0f)
.setAlignment(Layout.Alignment.ALIGN_NORMAL) .setIncludePad(false)
.setLineSpacing(0.0f, 1.0f) .build()
.setIncludePad(false)
.build()
canvas.save() canvas.save()
canvas.translate(rect.left, rect.top) canvas.translate(rect.left, rect.top)
@ -195,12 +180,10 @@ class TextGraphic(
} }
companion object { companion object {
private const val DELIMITER = "0`~`0"
private const val TAG = "TextGraphic" private const val TAG = "TextGraphic"
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s" private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
private val TEXT_COLOR = Color.parseColor("#FF474747") private val TEXT_COLOR = Color.parseColor("#FF474747")
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9") private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
private const val STROKE_WIDTH = 2.0f
private const val TEXT_SIZE = 44.0f private const val TEXT_SIZE = 44.0f
} }
} }

View File

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

View File

@ -4,11 +4,18 @@ import android.app.Activity;
import android.graphics.Color; import android.graphics.Color;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; 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.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; 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; import com.google.android.material.snackbar.Snackbar;
public class Widget { public class Widget {
@ -23,16 +30,51 @@ public class Widget {
} }
public static void makeSnackbar(@NonNull final Activity mActivity, @NonNull final String msg) { public static void makeSnackbar(@NonNull final Activity mActivity, @NonNull final String msg) {
View view = mActivity.findViewById(android.R.id.content); Snackbar snackbar = Snackbar.make(mActivity.findViewById(android.R.id.content), msg, Snackbar.LENGTH_SHORT);
Snackbar snackbar = Snackbar.make(view, msg, Snackbar.LENGTH_LONG); View view = snackbar.getView();
View snackbarView = snackbar.getView(); view.setBackground(AppCompatResources.getDrawable(mActivity,R.drawable.button_r20_black_bg));
snackbarView.setBackgroundColor(Color.parseColor("#66000000"));
TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text); FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
textView.setTextColor(Color.WHITE); params.gravity = Gravity.CENTER;
textView.setTextSize(16f); view.setLayoutParams(params);
textView.setGravity(Gravity.CENTER); // 获取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(); 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.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import com.assimilate.alltrans.common.TextGraphic;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 { public class GraphicOverlay extends View {
private final Object lock = new Object(); private final Object lock = new Object();
public List<Graphic> getGraphics() {
return graphics;
}
public Object getLock() {
return lock;
}
private final List<Graphic> graphics = new ArrayList<>(); private final List<Graphic> graphics = new ArrayList<>();
// Matrix for transforming from image coordinates to overlay view coordinates. // Matrix for transforming from image coordinates to overlay view coordinates.
private final Matrix transformationMatrix = new Matrix(); private final Matrix transformationMatrix = new Matrix();
@ -64,11 +57,23 @@ public class GraphicOverlay extends View {
*/ */
public abstract static class Graphic { public abstract static class Graphic {
private GraphicOverlay overlay; private GraphicOverlay overlay;
private boolean textVisible = true; // 添加可见性
public Graphic(GraphicOverlay overlay) { public Graphic(GraphicOverlay overlay) {
this.overlay = 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 * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
* to view coordinates for the graphics that are drawn: * 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 void draw(Canvas canvas);
public abstract boolean contains(float x, float y);
protected void drawRect( protected void drawRect(
Canvas canvas, float left, float top, float right, float bottom, Paint paint) { Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
canvas.drawRect(left, top, right, bottom, paint); canvas.drawRect(left, top, right, bottom, paint);
@ -140,6 +148,7 @@ public class GraphicOverlay extends View {
overlay.postInvalidate(); overlay.postInvalidate();
} }
/** /**
* Given the {@code zInImagePixel}, update the color for the passed in {@code paint}. The color will be * 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 * 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); needUpdateTransformation = true);
} }
/** /**
* Removes all graphics from the overlay. * Removes all graphics from the overlay.
*/ */
@ -311,21 +321,56 @@ public class GraphicOverlay extends View {
} }
} }
private TextGraphic currentlyVisibleTextGraphic = null;
@Override @Override
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
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;
}
setVisibility(View.INVISIBLE);
return true;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_CANCEL:
synchronized (lock) {
setVisibility(View.VISIBLE); if (currentlyVisibleTextGraphic != null) {
return true; currentlyVisibleTextGraphic.hideTextBlock();
currentlyVisibleTextGraphic = null;
}
return true;
}
} }
return super.onTouchEvent(event); 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; package com.assimilate.alltrans.http;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import retrofit2.Call; import retrofit2.Call;
@ -15,48 +17,37 @@ import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;
import retrofit2.http.Url; import retrofit2.http.Url;
public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslateCallback>{ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslateCallback> {
private final static String URL = "https://translate.googleapis.com/translate_a/single"; private final static String URL = "https://translate.googleapis.com/translate_a/single";
private final static String CLIENT = "gtx"; private final static String CLIENT = "gtx";
private final static String DT = "t"; private final static String DT = "t";
/**
* {
* "inputEncode": "UTF-8", 输入编码有默认值可不传递
* "outputEncode": "UTF-8", 响应编码有默认值可不传递
* "sourceLanguage": "English", 原文语言不可为空
* "translationLanguage": "Chinese", 译文语言不可为空
* "text": "翻译内容"
* }
* @param params 输入参数集
* @param googleTranslateCallback 响应集合
*/
@Override @Override
public void translate(HashMap<String, String> params, GoogleTranslateCallback googleTranslateCallback) { public void translate(HashMap<String, String> params, GoogleTranslateCallback googleTranslateCallback) {
if (null == googleTranslateCallback) { if (null == googleTranslateCallback) {
return; return;
} }
if (null == params || params.isEmpty()) { if (null == params || params.isEmpty()) {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Params are null or empty");
return; return;
} }
String text = params.get("text"); String text = params.get("text");
if (TextUtils.isEmpty(text) || text.trim().isEmpty()) { if (TextUtils.isEmpty(text) || text.trim().isEmpty()) {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Text is empty");
return; return;
} } else {
else {
text = text.trim(); text = text.trim();
} }
String sl = params.get("sourceLanguage"); String sl = params.get("sourceLanguage");
if (TextUtils.isEmpty(sl)) { if (TextUtils.isEmpty(sl)) {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Source language is empty");
return; return;
} }
String tl = params.get("translationLanguage"); String tl = params.get("translationLanguage");
if (TextUtils.isEmpty(sl)) { if (TextUtils.isEmpty(tl)) {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Translation language is empty");
return; return;
} }
@ -69,32 +60,44 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
Retrofit retrofit = retrofitClient.getRetrofitClient(); Retrofit retrofit = retrofitClient.getRetrofitClient();
GoogleTranslateApi googleTranslateApi = retrofit.create(GoogleTranslateApi.class); GoogleTranslateApi googleTranslateApi = retrofit.create(GoogleTranslateApi.class);
googleTranslateApi.api( googleTranslateApi.api(
URL, CLIENT, DT, ie, oe, sl, tl, text URL, CLIENT, DT, ie, oe, sl, tl, text
).enqueue(new Callback<JsonArray>() { ).enqueue(new Callback<JsonArray>() {
@Override @Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) { public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
JsonArray jsonArray = response.body(); if (response.isSuccessful()) {
if (null != jsonArray && !jsonArray.isEmpty()) { JsonArray jsonArray = response.body();
try { if (null != jsonArray && !jsonArray.isEmpty()) {
StringBuilder builder = new StringBuilder(); try {
JsonArray array = jsonArray.get(0).getAsJsonArray(); StringBuilder builder = new StringBuilder();
if (null != array && !array.isEmpty()) { JsonArray array = jsonArray.get(0).getAsJsonArray();
for (JsonElement jsonElement : array) { if (null != array && !array.isEmpty()) {
builder.append(jsonElement.getAsJsonArray().get(0).getAsString()); for (JsonElement jsonElement : array) {
builder.append(jsonElement.getAsJsonArray().get(0).getAsString());
}
googleTranslateCallback.onResponse(builder.toString(), null);
} else {
googleTranslateCallback.onResponse(null, "Empty translation array");
} }
googleTranslateCallback.onResponse(builder.toString()); } catch (Exception e) {
googleTranslateCallback.onResponse(null, "Parsing error: " + e.getMessage());
} }
} catch (Exception e) { } else {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Empty response body");
} }
} else { } else {
googleTranslateCallback.onResponse(null); googleTranslateCallback.onResponse(null, "Response not successful: " + response.message());
} }
} }
@Override @Override
public void onFailure(Call<JsonArray> call, Throwable t) { 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 { public interface GoogleTranslateApi {
@GET @GET
Call<JsonArray> api( Call<JsonArray> api(
@Url String url, // google 固定值 @Url String url,
@Query("client") String client, // google 固定值 @Query("client") String client,
@Query("dt") String dt, // google 固定值 @Query("dt") String dt,
@Query("ie") String ie, @Query("ie") String ie,
@Query("oe") String oe, @Query("oe") String oe,
@Query("sl") String sl, @Query("sl") String sl,
@ -113,6 +116,7 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
} }
public interface GoogleTranslateCallback { 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.Parcel;
import android.os.Parcelable; 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.content.Context;
import android.net.Uri; import android.net.Uri;
@ -7,6 +7,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.assimilate.alltrans.R; import com.assimilate.alltrans.R;
import com.assimilate.alltrans.common.Logger;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
@ -72,7 +73,15 @@ public class LanguagesConstants {
return lang; return lang;
} }
} }
return null;
// 返回一个默认的Language对象避免返回null
return getDefaultLanguage();
}
// 新增一个方法来获取默认的Language对象
private Language getDefaultLanguage() {
// 返回一个你希望的默认语言对象
return new Language("English", "en","en");
} }
// 获取 languageCode // 获取 languageCode

View File

@ -1,4 +1,4 @@
package com.assimilate.alltrans.common package com.assimilate.alltrans.keepmodel
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
@ -12,7 +12,7 @@ object PreferenceLanguageUtils {
private const val KEY_RECENT_LANGUAGES = "recent_languages" private const val KEY_RECENT_LANGUAGES = "recent_languages"
private const val MAX_RECENT_LANGUAGES = 5 private const val MAX_RECENT_LANGUAGES = 5
private const val PREF_KEY_FIRST_TIME = "first_time" private const val PREF_KEY_FIRST_TIME = "first_time"
private val LANGUAGE_LIST_TYPE = object : TypeToken<List<Language>>() {}.type
@Volatile @Volatile
private var sharedPreferences: SharedPreferences? = null private var sharedPreferences: SharedPreferences? = null
@ -31,7 +31,7 @@ object PreferenceLanguageUtils {
getSharedPreferences().edit().putString(key, value).apply() 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 val value = getSharedPreferences().getString(key, defValue) ?: defValue
return value return value
} }
@ -61,11 +61,11 @@ object PreferenceLanguageUtils {
saveRecentLanguages(recentLanguages) saveRecentLanguages(recentLanguages)
} }
fun getRecentLanguages(): List<Language> { fun getRecentLanguages(): List<Language> {
val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null) val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null)
return if (json != null) { return if (json != null) {
val type = object : TypeToken<List<Language>>() {}.type Gson().fromJson(json, LANGUAGE_LIST_TYPE)
Gson().fromJson(json, type)
} else { } else {
emptyList() emptyList()
} }
@ -80,6 +80,7 @@ object PreferenceLanguageUtils {
fun isFirstTime(): Boolean { fun isFirstTime(): Boolean {
return getSharedPreferences().getBoolean(PREF_KEY_FIRST_TIME, true) return getSharedPreferences().getBoolean(PREF_KEY_FIRST_TIME, true)
} }
// 设置已经不是第一次进入应用了 // 设置已经不是第一次进入应用了
fun setNotFirstTime() { fun setNotFirstTime() {
getSharedPreferences().edit().putBoolean(PREF_KEY_FIRST_TIME, false).apply() 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 androidx.annotation.NonNull;
import com.assimilate.alltrans.common.Logger; import com.assimilate.alltrans.common.Logger;
import java.util.ArrayList; import java.util.ArrayList;
@ -30,10 +29,10 @@ public class DbTranslation extends SQLiteOpenHelper {
"source_lan TEXT, " + "source_lan TEXT, " +
"source_txt TEXT, " + "source_txt TEXT, " +
"target_lan TEXT, " + "target_lan TEXT, " +
"target_txt TEXT," + "target_txt TEXT, " +
"current_time_millis INTEGER," + "current_time_millis INTEGER, " +
"exist int DEFAULT 1, " + "exist INTEGER DEFAULT 1, " +
"collection int DEFAULT 0);" "collection INTEGER DEFAULT 0);"
); );
} }
@ -113,29 +112,71 @@ public class DbTranslation extends SQLiteOpenHelper {
Log.d("SQLite: ", "at removeTranslations: " + exception.getMessage()); Log.d("SQLite: ", "at removeTranslations: " + exception.getMessage());
} }
} }
/** /**
* @param collect 收藏不收藏 * 收藏不收藏
*/ */
public boolean collectJust(boolean collect) { public boolean toggleCollect() {
final int value = collect ? 1 : 0; try (SQLiteDatabase database = getWritableDatabase();
try (SQLiteDatabase database = getWritableDatabase();) { Cursor cursor = database.rawQuery("SELECT collection FROM " + TABLE + " ORDER BY id DESC LIMIT 1", null)) {
ContentValues values = new ContentValues();
values.put("collection", value); if (cursor.moveToFirst()) {
String selection = "id = (SELECT MAX(id) FROM " + TABLE +")"; int collectionIndex = cursor.getColumnIndex("collection");
int result = database.update(TABLE, values, selection, null); if (collectionIndex != -1) {
return result > 0; int currentValue = cursor.getInt(collectionIndex);
int newValue = (currentValue == 1) ? 0 : 1;
ContentValues values = new ContentValues();
values.put("collection", newValue);
String selection = "id = (SELECT MAX(id) FROM " + TABLE + ")";
int result = database.update(TABLE, values, selection, null);
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) { } catch (Exception exception) {
Log.d("SQLite: ", "at collectionJust: " + exception.getMessage()); Log.e("SQLite: ", "at toggleCollect: " + exception.getMessage());
return false; 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<>(); final ArrayList<Translations> logs = new ArrayList<>();
Cursor cursor = null; Cursor cursor = null;
try (SQLiteDatabase database = getWritableDatabase();) { try (SQLiteDatabase database = getWritableDatabase();) {
@ -169,7 +210,7 @@ public class DbTranslation extends SQLiteOpenHelper {
if (exist == 1) { if (exist == 1) {
// 是否需要过滤掉没有收藏的简便写法 // 是否需要过滤掉没有收藏的简便写法
if (!filterUnCollect || collection == 1) { if (!filterCollect || collection == 1) {
Translations translations = new Translations(id, sourceLanguage, sourceText, targetLanguage, targetText, currentTimeMillis, exist, collection); Translations translations = new Translations(id, sourceLanguage, sourceText, targetLanguage, targetText, currentTimeMillis, exist, collection);
logs.add(translations); logs.add(translations);
} }
@ -190,4 +231,5 @@ public class DbTranslation extends SQLiteOpenHelper {
Collections.reverse(logs); Collections.reverse(logs);
return 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.databinding.ActivityHistoryBinding
import com.assimilate.alltrans.mydb.DbTranslation import com.assimilate.alltrans.mydb.DbTranslation
import com.assimilate.alltrans.mydb.Translations import com.assimilate.alltrans.mydb.Translations
import java.util.Collections
import java.util.Locale import java.util.Locale
class HistoryActivity : AppCompatActivity() { class HistoryActivity : AppCompatActivity() {
@ -40,6 +39,7 @@ class HistoryActivity : AppCompatActivity() {
ids = HashSet() ids = HashSet()
items = HashSet() items = HashSet()
val extra = intent.getStringExtra(COMMAND) val extra = intent.getStringExtra(COMMAND)
operationCollection = COMMAND_COLLECTION == extra 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>() val translations = ArrayList<Translations>()
if (!operationCollection) { if (!operationCollection) {
// 查出收藏的翻译记录 // 查出收藏的翻译记录
binding!!.tvFuncTrans.text = getString(R.string.favor_title) binding!!.tvFuncTrans.text = getString(R.string.favor_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(true) 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) translations.addAll(list)
} }
} else { } else {
// 查出所有的翻译记录 // 查出所有的翻译记录
binding!!.tvFuncTrans.text = getString(R.string.his_title) binding!!.tvFuncTrans.text = getString(R.string.his_title)
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
val list = DbTranslation(this).getTranslations(false) 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()) { if (null != list && list.isNotEmpty()) {
translations.addAll(list) translations.addAll(list)
} }
@ -91,6 +116,7 @@ class HistoryActivity : AppCompatActivity() {
private fun add(id: Long, index: Int) { private fun add(id: Long, index: Int) {
ids!!.add(id) ids!!.add(id)
items!!.add(index) items!!.add(index)
} }
@ -101,7 +127,7 @@ class HistoryActivity : AppCompatActivity() {
private fun updateBtn() { private fun updateBtn() {
if (ids!!.isEmpty()) { if (ids!!.isEmpty()) {
binding!!.remove.visibility = View.INVISIBLE binding!!.remove.visibility = View.GONE
} else { } else {
binding!!.remove.visibility = View.VISIBLE binding!!.remove.visibility = View.VISIBLE
} }
@ -114,13 +140,13 @@ class HistoryActivity : AppCompatActivity() {
override fun onClick(v: View) { override fun onClick(v: View) {
if (ids!!.isEmpty()) { if (ids!!.isEmpty()) {
Widget.makeToast(this@HistoryActivity, "Noting to remove.") Widget.makeToast(this@HistoryActivity, "Nothing to remove.")
return return
} }
if (!ids!!.isEmpty()) { if (ids!!.isNotEmpty()) {
val longArrayList = ArrayList(ids) val longArrayList = ArrayList(ids)
if (operationCollection) { if (!operationCollection) {
getDbTranslation(this@HistoryActivity).removeCollectTranslations( getDbTranslation(this@HistoryActivity).removeCollectTranslations(
longArrayList longArrayList
) )
@ -129,13 +155,13 @@ class HistoryActivity : AppCompatActivity() {
} }
ids!!.clear() ids!!.clear()
} }
if (!items!!.isEmpty()) { if (items!!.isNotEmpty()) {
val integerArrayList = ArrayList(items) val integerArrayList = ArrayList(items)
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) } integerArrayList.sortWith { o1, o2 -> o2.compareTo(o1) }
adapter.updateSet(integerArrayList) adapter.updateSet(integerArrayList)
items!!.clear() items!!.clear()
} }
binding!!.remove.visibility = View.VISIBLE binding!!.remove.visibility = View.GONE // 隐藏删除按钮
} }
private fun getDbTranslation(context: Context): DbTranslation { private fun getDbTranslation(context: Context): DbTranslation {
@ -148,19 +174,16 @@ class HistoryActivity : AppCompatActivity() {
binding!!.histories.layoutManager = layoutManager binding!!.histories.layoutManager = layoutManager
binding!!.histories.adapter = adapter binding!!.histories.adapter = adapter
} }
override fun onBackPressed() {
super.onBackPressed()
}
fun clickBack(view: View?) { private fun initClick() {
onBackPressed() binding?.ivHisBack?.setOnClickListener { onBackPressed() }
} }
companion object { companion object {
const val COMMAND: String = "remove" const val COMMAND: String = "remove"
const val COMMAND_COLLECTION: String = "remove_collection" 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 package com.assimilate.alltrans.viewui
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.Language import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.common.LanguagesConstants import com.assimilate.alltrans.keepmodel.Language
import com.assimilate.alltrans.common.PreferenceLanguageUtils import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class LanguageChangeActivity : AppCompatActivity() { class LanguageChangeActivity : AppCompatActivity() {
private lateinit var binding: ActivityLanguageChangeBinding private lateinit var binding: ActivityLanguageChangeBinding
@ -32,31 +42,80 @@ class LanguageChangeActivity : AppCompatActivity() {
initView() initView()
initSearch() // 添加这一行 initSearch() // 添加这一行
initList() lifecycleScope.launch {
withContext(Dispatchers.IO) {
// 耗时操作
initAllList()
}
}
initClick() initClick()
} }
private fun initSearch() { private fun initSearch() {
binding.chSearch.setOnQueryTextListener(object : binding.chSearch.apply {
androidx.appcompat.widget.SearchView.OnQueryTextListener { // 获取关闭按钮的ImageView
override fun onQueryTextSubmit(query: String?): Boolean { val closeButton = findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
return false
// 设置关闭按钮一直可见
closeButton.visibility = View.VISIBLE
// 确保 SearchView 处于可点击状态
isFocusable = true
isIconified = false
// 延迟请求焦点以避免弹出软件盘
post {
clearFocus()
} }
override fun onQueryTextChange(newText: String?): Boolean { setOnQueryTextListener(object :
(binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText) androidx.appcompat.widget.SearchView.OnQueryTextListener {
return true override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
(binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText)
return true
}
})
// 设置点击关闭按钮的行为
closeButton.setOnClickListener {
// 清空搜索框
setQuery("", false)
// 清除焦点
clearFocus()
// 隐藏软件盘
val imm =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0)
} }
}) }
} }
private fun initView() { 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.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target") binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
updateRecentLanguages() updateRecentLanguages()
} }
private fun updateRecentLanguages() { private fun updateRecentLanguages() {
val recentLanguages = PreferenceLanguageUtils.getRecentLanguages() val recentLanguages = PreferenceLanguageUtils.getRecentLanguages()
if (recentLanguages.isNotEmpty()) { if (recentLanguages.isNotEmpty()) {
@ -67,6 +126,7 @@ class LanguageChangeActivity : AppCompatActivity() {
onBackPressed() onBackPressed()
} else { } else {
PreferenceLanguageUtils.putString("language_source", language.language) PreferenceLanguageUtils.putString("language_source", language.language)
onBackPressed()
} }
PreferenceLanguageUtils.addRecentLanguage(language) PreferenceLanguageUtils.addRecentLanguage(language)
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source") 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) val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(this)
if (languages.isNotEmpty()) { if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(this, languages) { _, language -> val adapter = LanguageAdapter(this, languages) { _, language ->
Log.d("LanguageChange", language.language) Log.d("LanguageChange", language.language)
if (lastTranslateLanguage) { if (lastTranslateLanguage) {
PreferenceLanguageUtils.putString("language_target", language.language) PreferenceLanguageUtils.putString("language_target", language.language)
FirebaseAnalyticsHelper.languageChooseEvent("target_" + language.language)
onBackPressed() onBackPressed()
} else { } else {
PreferenceLanguageUtils.putString("language_source", language.language) PreferenceLanguageUtils.putString("language_source", language.language)
FirebaseAnalyticsHelper.languageChooseEvent("source_" + language.language)
onBackPressed()
} }
PreferenceLanguageUtils.addRecentLanguage(language) PreferenceLanguageUtils.addRecentLanguage(language)
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source") binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")

View File

@ -1,6 +1,6 @@
package com.assimilate.alltrans.viewui package com.assimilate.alltrans.viewui
import android.app.Activity import android.app.ActivityManager
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ClipDescription import android.content.ClipDescription
import android.content.ClipboardManager import android.content.ClipboardManager
@ -10,41 +10,61 @@ import android.media.projection.MediaProjectionManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.provider.Settings import android.provider.Settings
import android.speech.RecognizerIntent import android.speech.RecognizerIntent
import android.text.Editable import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher import android.text.TextWatcher
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.EditText import android.widget.EditText
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.assimilate.alltrans.R import com.assimilate.alltrans.R
import com.assimilate.alltrans.allservice.SusService import com.assimilate.alltrans.allservice.SusService
import com.assimilate.alltrans.common.LanguagesConstants import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
import com.assimilate.alltrans.common.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Widget import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityMainBinding import com.assimilate.alltrans.databinding.ActivityMainBinding
import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
class MainActivity : AppCompatActivity() { 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 binding: ActivityMainBinding
private lateinit var lcm: LocalBroadcastManager
private lateinit var mediaProjectionManager: MediaProjectionManager 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 mpResultCode = "mpResultCode"
private val mpData = "mpData" private val mpData = "mpData"
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
@ -57,42 +77,30 @@ class MainActivity : AppCompatActivity() {
insets insets
} }
initSet() initSet()
initClick() 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?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) 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 { val serviceIntent = Intent(this, SusService::class.java).apply {
putExtra(mpResultCode, resultCode) putExtra(mpResultCode, resultCode)
putExtra(mpData, data) putExtra(mpData, data)
@ -103,7 +111,10 @@ class MainActivity : AppCompatActivity() {
startService(serviceIntent) startService(serviceIntent)
} }
} else { } 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() { 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的文本变化 // 监听EditText的文本变化
binding.etText.addTextChangedListener(object : TextWatcher { binding.etText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
@ -147,79 +267,16 @@ class MainActivity : AppCompatActivity() {
} }
}) })
} }
private fun initClick() { private fun checkAndRequestMediaProjectionPermission() {
binding.tvMainPhotoTrans.setOnClickListener { // 启动截图
startActivity( startActivityForResult(
Intent(this, StillImageActivity::class.java) mediaProjectionManager.createScreenCaptureIntent(),
) REQUEST_CODE_MEDIA_PROJECTION
} )
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 {
// 启动截图
startActivityForResult(
mediaProjectionManager.createScreenCaptureIntent(),
REQUEST_CODE
)
}
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() { private fun toTextTransResult() {
@ -262,28 +319,51 @@ class MainActivity : AppCompatActivity() {
// //
// ) // )
try { try {
launcher?.launch(speechIntent) launcher.launch(speechIntent)
} catch (ea: ActivityNotFoundException) { } catch (ea: ActivityNotFoundException) {
FirebaseAnalyticsHelper.textVoiceResultEvent("device_not_support")
Widget.makeToast(this, getString(R.string.main_voice_to_text)) 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 val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
if (clipboard.hasPrimaryClip() && clipboard.primaryClipDescription!!.hasMimeType( if (clipboard.hasPrimaryClip()) {
ClipDescription.MIMETYPE_TEXT_PLAIN val clipData = clipboard.primaryClip
) if (clipData != null && clipData.itemCount > 0) {
) { val item = clipData.getItemAt(0)
val item = clipboard.primaryClip!!.getItemAt(0) val pasteData = item.coerceToText(this).toString()
val text = item.text.toString() if (pasteData.isNotEmpty()) {
if (text.isNotEmpty()) { binding.etText.setText(pasteData)
// 在EditText中显示粘贴的文本 binding.etText.requestFocus() // 获取焦点
etText.setText(text) 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() initView()
} }
override fun onDestroy() { override fun onRestart() {
super.onDestroy() super.onRestart()
if (null != launcher) { FirebaseAnalyticsHelper.homePvEvent("cap")
launcher!!.unregister()
launcher = null
}
} }
override fun onDestroy() {
super.onDestroy()
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.Manifest
import android.app.Activity import android.app.Activity
import android.content.ContentValues import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore
import android.util.Log import android.util.Log
import android.util.Pair import android.util.Pair
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.AdapterView import android.view.inputmethod.InputMethodManager
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.SearchView
import android.widget.Spinner
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -42,17 +33,16 @@ import androidx.recyclerview.widget.RecyclerView
import com.assimilate.alltrans.R import com.assimilate.alltrans.R
import com.assimilate.alltrans.adapters.LanguageAdapter import com.assimilate.alltrans.adapters.LanguageAdapter
import com.assimilate.alltrans.common.BitmapUtils import com.assimilate.alltrans.common.BitmapUtils
import com.assimilate.alltrans.common.Language import com.assimilate.alltrans.keepmodel.Language
import com.assimilate.alltrans.common.LanguagesConstants import com.assimilate.alltrans.keepmodel.LanguagesConstants
import com.assimilate.alltrans.common.PreferenceLanguageUtils import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.common.TextRecognitionProcessor import com.assimilate.alltrans.common.TextRecognitionProcessor
import com.assimilate.alltrans.common.VisionImageProcessor import com.assimilate.alltrans.common.VisionImageProcessor
import com.assimilate.alltrans.common.Widget import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.curview.GraphicOverlay import com.assimilate.alltrans.databinding.ActivityStillImageBinding
import com.google.android.gms.common.annotation.KeepName import com.google.android.gms.common.annotation.KeepName
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import com.google.mlkit.common.model.LocalModel
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
@ -61,16 +51,13 @@ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.Locale import java.util.Locale
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
/** Activity demonstrating different image detector features with a still image from camera. */
@KeepName @KeepName
class StillImageActivity : AppCompatActivity() { class PhotoImageActivity : AppCompatActivity() {
private var preview: ImageView? = null
private var graphicOverlay: GraphicOverlay? = null
private var selectedMode = TEXT_RECOGNITION_CHINESE private var selectedMode = TEXT_RECOGNITION_CHINESE
private var selectedSize: String? = SIZE_SCREEN private var selectedSize: String? = SIZE_SCREEN
private var isLandScape = false private var isLandScape = false
@ -85,23 +72,22 @@ class StillImageActivity : AppCompatActivity() {
private val REQUEST_CAMERA_PERMISSION = 100 private val REQUEST_CAMERA_PERMISSION = 100
private lateinit var bottomSheetDialog: BottomSheetDialog private lateinit var bottomSheetDialog: BottomSheetDialog
private var chooseLanguage :Boolean = false private var chooseLanguage: Boolean = false
private lateinit var binding: ActivityStillImageBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContentView(R.layout.activity_still_image) binding = ActivityStillImageBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets -> ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) 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 insets
} }
Widget.makeSnackbar(this, "Photographing text for translation") 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 isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
savedInstanceState?.let { savedInstanceState?.let {
imageUri = it.getParcelable(KEY_IMAGE_URI) imageUri = it.getParcelable(KEY_IMAGE_URI)
@ -146,29 +132,63 @@ class StillImageActivity : AppCompatActivity() {
} }
private fun initView() { private fun initView() {
findViewById<TextView>(R.id.still_source_language).text =
binding.photoPreview.visibility = View.VISIBLE
binding.stillSourceLanguage.text =
PreferenceLanguageUtils.getString("language_source") PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text = binding.stillTargetLanguage.text =
PreferenceLanguageUtils.getString("language_target") PreferenceLanguageUtils.getString("language_target")
bottomSheetDialog = BottomSheetDialog(this) bottomSheetDialog = BottomSheetDialog(this, R.style.CustomBottomSheetDialogTheme)
bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan) bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan)
bottomSheetDialog.dismissWithAnimation = true bottomSheetDialog.dismissWithAnimation = true
bottomSheetDialog.findViewById<androidx.appcompat.widget.SearchView>(R.id.ph_search) bottomSheetDialog.findViewById<androidx.appcompat.widget.SearchView>(R.id.ph_search)
?.setOnQueryTextListener(object : ?.let { searchView ->
androidx.appcompat.widget.SearchView.OnQueryTextListener { // 设置关闭按钮一直显示
override fun onQueryTextSubmit(query: String?): Boolean { searchView.isIconified = false
return 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
} }
override fun onQueryTextChange(newText: String?): Boolean { // 设置查询文本监听器
(bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.adapter as LanguageAdapter).filter.filter( searchView.setOnQueryTextListener(object :
newText androidx.appcompat.widget.SearchView.OnQueryTextListener {
) override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
return true override fun onQueryTextChange(newText: String?): Boolean {
} (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() initList()
} }
@ -178,16 +198,22 @@ class StillImageActivity : AppCompatActivity() {
if (languages.isNotEmpty()) { if (languages.isNotEmpty()) {
val adapter = LanguageAdapter(this, languages) { _, language -> val adapter = LanguageAdapter(this, languages) { _, language ->
if (!chooseLanguage) { if (!chooseLanguage) {
PreferenceLanguageUtils.putString("language_source", language.language) PreferenceLanguageUtils.putString("language_source", language.language)
selectedMode = PreferenceLanguageUtils.getString("language_source")
tryReloadAndDetectInImage()
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
} else { } else {
PreferenceLanguageUtils.putString("language_target", language.language) PreferenceLanguageUtils.putString("language_target", language.language)
tryReloadAndDetectInImage()
bottomSheetDialog.dismiss() bottomSheetDialog.dismiss()
} }
findViewById<TextView>(R.id.still_source_language).text = binding.stillSourceLanguage.text =
PreferenceLanguageUtils.getString("language_source") PreferenceLanguageUtils.getString("language_source")
findViewById<TextView>(R.id.still_target_language).text = binding.stillTargetLanguage.text =
PreferenceLanguageUtils.getString("language_target") PreferenceLanguageUtils.getString("language_target")
} }
bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.layoutManager = bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.layoutManager =
@ -197,25 +223,40 @@ class StillImageActivity : AppCompatActivity() {
} }
private fun initClick() { private fun initClick() {
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() } binding.ivStillPic.setOnClickListener {
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() } binding.photoPreview.visibility = View.INVISIBLE
findViewById<ImageView>(R.id.iv_still_back).setOnClickListener { onBackPressed() } startChooseImageIntentForResult()
findViewById<ImageView>(R.id.iv_still_buling).setOnClickListener { }
binding.ivStillTake.setOnClickListener {
binding.photoPreview.visibility = View.VISIBLE
takePhoto()
}
binding.ivStillBack.setOnClickListener { onBackPressed() }
binding.ivStillBuling.setOnClickListener {
toggleFlash() toggleFlash()
updateFlashButtonUI() updateFlashButtonUI()
} }
findViewById<TextView>(R.id.still_source_language).setOnClickListener { binding.stillSourceLanguage.setOnClickListener {
chooseLanguage = false chooseLanguage = false
bottomSheetDialog.show() bottomSheetDialog.show()
} }
findViewById<TextView>(R.id.still_target_language).setOnClickListener { binding.stillTargetLanguage.setOnClickListener {
chooseLanguage = true chooseLanguage = true
bottomSheetDialog.show() 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() { 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() { private fun startCamera() {
@ -340,27 +381,27 @@ class StillImageActivity : AppCompatActivity() {
imageProcessor = when (selectedMode) { imageProcessor = when (selectedMode) {
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor( TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
this, this,
ChineseTextRecognizerOptions.Builder().build() ChineseTextRecognizerOptions.Builder().build(), true, true
) )
"Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor( "Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
this, this,
DevanagariTextRecognizerOptions.Builder().build() DevanagariTextRecognizerOptions.Builder().build(), true, true
) )
TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor( TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
this, this,
JapaneseTextRecognizerOptions.Builder().build() JapaneseTextRecognizerOptions.Builder().build(), true, true
) )
TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor( TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
this, this,
KoreanTextRecognizerOptions.Builder().build() KoreanTextRecognizerOptions.Builder().build(), true, true
) )
else -> TextRecognitionProcessor( else -> TextRecognitionProcessor(
this, this,
TextRecognizerOptions.Builder().build() TextRecognizerOptions.Builder().build(), true, true
) )
} }
@ -394,10 +435,10 @@ class StillImageActivity : AppCompatActivity() {
(imageBitmap.height / scaleFactor).toInt(), (imageBitmap.height / scaleFactor).toInt(),
true true
) )
preview?.setImageBitmap(resizedBitmap) binding.preview.setImageBitmap(resizedBitmap)
processImage(resizedBitmap) processImage(resizedBitmap)
} else { } else {
preview?.setImageBitmap(imageBitmap) binding.preview.setImageBitmap(imageBitmap)
processImage(imageBitmap) processImage(imageBitmap)
} }
} catch (e: IOException) { } catch (e: IOException) {
@ -420,8 +461,8 @@ class StillImageActivity : AppCompatActivity() {
} }
private fun processImage(bitmap: Bitmap) { private fun processImage(bitmap: Bitmap) {
graphicOverlay?.clear() binding.graphicOverlay.clear()
imageProcessor?.processBitmap(bitmap, graphicOverlay) imageProcessor?.processBitmap(bitmap, binding.graphicOverlay)
} }
private fun startChooseImageIntentForResult() { private fun startChooseImageIntentForResult() {

View File

@ -1,12 +1,15 @@
package com.assimilate.alltrans.viewui package com.assimilate.alltrans.viewui
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.widget.TextView
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.R import com.assimilate.alltrans.R
import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivitySettingsBinding import com.assimilate.alltrans.databinding.ActivitySettingsBinding
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
@ -18,10 +21,10 @@ class SettingsActivity
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
enableEdgeToEdge()
setContentView(binding.root) 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()) val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets insets
@ -37,10 +40,15 @@ class SettingsActivity
bottomSheetDialog = BottomSheetDialog(this) bottomSheetDialog = BottomSheetDialog(this)
bottomSheetDialog.setContentView(R.layout.bottomsheet_rate) bottomSheetDialog.setContentView(R.layout.bottomsheet_rate)
bottomSheetDialog.dismissWithAnimation = true 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() { private fun initClick() {
binding.ivSetBack.setOnClickListener { onBackPressed() }
binding.llRate.setOnClickListener { binding.llRate.setOnClickListener {
bottomSheetDialog.show() bottomSheetDialog.show()
@ -51,5 +59,13 @@ class SettingsActivity
startActivity(intent) 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 package com.assimilate.alltrans.viewui
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
@ -8,15 +9,17 @@ import android.os.Bundle
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import com.assimilate.alltrans.MyApp import com.assimilate.alltrans.MyApp
import com.assimilate.alltrans.R 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.Logger
import com.assimilate.alltrans.common.PreferenceLanguageUtils import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
import com.assimilate.alltrans.common.Widget import com.assimilate.alltrans.common.Widget
import com.assimilate.alltrans.databinding.ActivityTextResultBinding import com.assimilate.alltrans.databinding.ActivityTextResultBinding
import com.assimilate.alltrans.http.GoogleTranslator import com.assimilate.alltrans.http.GoogleTranslator
@ -25,6 +28,7 @@ import com.assimilate.alltrans.mydb.DbTranslation
import com.assimilate.alltrans.mydb.Translations import com.assimilate.alltrans.mydb.Translations
import java.util.Locale import java.util.Locale
class TextResultActivity : AppCompatActivity() { class TextResultActivity : AppCompatActivity() {
private lateinit var binding: ActivityTextResultBinding private lateinit var binding: ActivityTextResultBinding
@ -41,7 +45,6 @@ class TextResultActivity : AppCompatActivity() {
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets insets
} }
initSet() initSet()
initClick() initClick()
} }
@ -59,6 +62,7 @@ class TextResultActivity : AppCompatActivity() {
Locale.getDefault() Locale.getDefault()
) )
} }
// checkLastTranslationCollectedAndSetIcon()
} }
@ -66,9 +70,12 @@ class TextResultActivity : AppCompatActivity() {
binding.ivTrBack.setOnClickListener { onBackPressed() } binding.ivTrBack.setOnClickListener { onBackPressed() }
binding.tvTrNewTrans.setOnClickListener { onBackPressed() } binding.tvTrNewTrans.setOnClickListener { onBackPressed() }
binding.ivReToPhoto.setOnClickListener { 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.ivSourceClear.setOnClickListener { onBackPressed() }
binding.ivSourceTts.setOnClickListener { binding.ivSourceTts.setOnClickListener {
readText( readText(
@ -83,12 +90,45 @@ class TextResultActivity : AppCompatActivity() {
PreferenceLanguageUtils.getString("language_target"), PreferenceLanguageUtils.getString("language_target"),
this 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) { private fun translate(text: String) {
if (text.isEmpty() || translating) { if (text.isEmpty() || translating) {
Logger.d("log", "translating(not post data)...") Logger.d("log", "translating(not post data)...")
@ -114,19 +154,46 @@ class TextResultActivity : AppCompatActivity() {
binding.tvTrTarget.text = "translating..." binding.tvTrTarget.text = "translating..."
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator() val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator()
translator.translate(param, translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
GoogleTranslator.GoogleTranslateCallback { result -> override fun onResponse(result: String?, errorMessage: String?) {
translating = false translating = false
if (!TextUtils.isEmpty(result)) { runOnUiThread {
runOnUiThread { if (!TextUtils.isEmpty(result)) {
binding.tvTrTarget.text = 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"
} }
addHistory(result)
} }
}) }
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) { private fun addHistory(transResult: String?) {
val dbTranslation = DbTranslation(this) val dbTranslation = DbTranslation(this)
val translations = Translations( val translations = Translations(
PreferenceLanguageUtils.getString("language_source"), PreferenceLanguageUtils.getString("language_source"),
@ -138,13 +205,6 @@ class TextResultActivity : AppCompatActivity() {
dbTranslation.addTranslation(translations) 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) { private fun shareText(text: String) {
val share: String = text.trim() 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 <ImageView
android:id="@+id/iv_his_back"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:onClick="clickBack"
android:padding="16dp" android:padding="16dp"
android:src="@drawable/ic_back" android:src="@drawable/ic_back"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -45,6 +45,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="13dp" android:layout_marginTop="13dp"
app:layout_constraintBottom_toTopOf="@id/remove"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" /> app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
@ -60,8 +61,21 @@
android:text="@string/his_delete" android:text="@string/his_delete"
android:textColor="#FFFF5F5F" android:textColor="#FFFF5F5F"
android:textSize="16sp" android:textSize="16sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<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> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -53,7 +53,9 @@
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_r20_white_bg" android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp" android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="12dp" android:paddingEnd="12dp"
android:textColor="@color/main_text_ff0e8ce8" android:textColor="@color/main_text_ff0e8ce8"
@ -82,75 +84,80 @@
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_r20_white_bg" android:background="@drawable/button_r20_white_bg"
android:drawablePadding="25dp" android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:maxLines="1"
android:paddingStart="16dp" android:paddingStart="16dp"
android:paddingEnd="12dp" android:paddingEnd="12dp"
android:textColor="@color/main_text_ff1f1724" android:textColor="@color/main_text_ff1f1724"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
app:drawableEndCompat="@drawable/ic_down_choose" /> app:drawableEndCompat="@drawable/ic_down_choose" />
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.core.widget.NestedScrollView
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:background="@drawable/button_r20_white_bg" android:background="@drawable/button_r20_white_bg"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/change_language"> app:layout_constraintTop_toBottomOf="@id/change_language">
<androidx.appcompat.widget.SearchView <LinearLayout
android:id="@+id/ch_search"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:orientation="vertical"
android:background="@drawable/button_r10_gray_bg" android:paddingStart="16dp"
app:layout_constraintEnd_toEndOf="parent" android:paddingEnd="16dp">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/change_language"
app:queryBackground="@drawable/button_r10_gray_bg"
app:queryHint="Search" />
<TextView <androidx.appcompat.widget.SearchView
android:layout_width="wrap_content" android:id="@+id/ch_search"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginTop="16dp" android:layout_height="wrap_content"
android:text="@string/tr_common" android:layout_marginTop="16dp"
android:textColor="@color/bg_ff605c62" android:background="@drawable/button_r10_gray_bg"
android:textSize="12sp" app:layout_constraintEnd_toEndOf="parent"
android:textStyle="bold" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/change_language"
app:queryBackground="@drawable/button_r10_gray_bg"
app:queryHint="Search" />
<androidx.recyclerview.widget.RecyclerView <TextView
android:id="@+id/list_lan_common5" android:layout_width="wrap_content"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="16dp"
android:layout_marginTop="16dp" android:text="@string/tr_common"
android:background="@drawable/button_r10_gray_bg" android:textColor="@color/bg_ff605c62"
android:minHeight="210dp" /> android:textSize="12sp"
android:textStyle="bold" />
<TextView <androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent" android:id="@+id/list_lan_common5"
android:layout_height="wrap_content" android:layout_width="match_parent"
android:layout_marginTop="16dp" android:layout_height="wrap_content"
android:text="@string/tr_other" android:layout_marginTop="16dp"
android:textColor="@color/bg_ff605c62" android:background="@drawable/button_r10_gray_bg" />
android:textSize="12sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView <TextView
android:id="@+id/list_languages" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="match_parent" android:layout_marginTop="16dp"
android:layout_marginTop="16dp" android:text="@string/tr_other"
android:background="@drawable/button_r10_gray_bg" /> 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> </LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

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

View File

@ -1,79 +1,353 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/bg_fff6f6f6" android:background="@color/bg_fff6f6f6"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true"
android:orientation="vertical">
<RelativeLayout
<com.google.android.material.appbar.AppBarLayout android:visibility="gone"
android:id="@+id/appbar_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fitsSystemWindows="true" android:background="@color/white">
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar <ImageView
android:id="@+id/toolbar" android:id="@+id/qs_back"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:title="Settings" /> android:layout_alignParentStart="true"
</com.google.android.material.appbar.AppBarLayout> android:padding="16dp"
android:src="@drawable/ic_back" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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 <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent" android:paddingStart="16dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> android:paddingEnd="16dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
style="@style/SusTextStyleSecond"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="悬浮球设置" /> android:layout_marginTop="16dp"
android:text="@string/quick_sus_xf_set" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> 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 <com.google.android.material.slider.Slider
android:layout_width="match_parent" android:id="@+id/slider_1"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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" /> android:text="@string/quick_set_touming" />
<TextView <TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/quick_set_touming_descri" /> 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 <TextView
style="@style/SusTextStyleMain"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/quick_set_zd_time" /> android:text="@string/quick_set_zd_time" />
<TextView <TextView
style="@style/SusTextStyleSecond"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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>
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

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

View File

@ -4,7 +4,7 @@
android:id="@+id/root" android:id="@+id/root"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/bg_53514c" android:background="#000"
android:keepScreenOn="true"> android:keepScreenOn="true">
<androidx.camera.view.PreviewView <androidx.camera.view.PreviewView
@ -12,10 +12,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
app:layout_constraintBottom_toTopOf="@id/control" app:layout_constraintBottom_toBottomOf="@id/preview"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="@id/preview"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="@id/preview"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="@id/preview" />
<ImageView <ImageView
android:id="@+id/preview" android:id="@+id/preview"
@ -89,11 +89,6 @@
android:orientation="horizontal" android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"> 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>
</LinearLayout> </LinearLayout>
@ -117,8 +112,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_r20_99000000_bg" android:background="@drawable/button_r20_black_bg"
android:drawablePadding="25dp" android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:paddingStart="16dp" android:paddingStart="16dp"
@ -134,7 +130,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:background="@drawable/button_r20_99000000_bg" android:background="@drawable/button_r20_white_bg"
android:paddingStart="12dp" android:paddingStart="12dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingEnd="12dp" android:paddingEnd="12dp"
@ -146,8 +142,9 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/button_r20_99000000_bg" android:background="@drawable/button_r20_black_bg"
android:drawablePadding="25dp" android:drawablePadding="25dp"
android:ellipsize="end"
android:gravity="center" android:gravity="center"
android:maxLines="1" android:maxLines="1"
android:paddingStart="16dp" android:paddingStart="16dp"
@ -161,7 +158,7 @@
android:id="@+id/iv_still_back" android:id="@+id/iv_still_back"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:padding="26dp"
android:src="@drawable/ic_back_white" android:src="@drawable/ic_back_white"
app:layout_constraintStart_toStartOf="@id/root" app:layout_constraintStart_toStartOf="@id/root"
app:layout_constraintTop_toTopOf="@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" /> android:layout_marginTop="32dp" />
<TextView <TextView
android:id="@+id/tv_rate_commit"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" 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:layout_height="match_parent"
android:background="@android:color/transparent"> android:background="@android:color/transparent">
<ImageView
android:id="@+id/sus_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:adjustViewBounds="true" />
<com.assimilate.alltrans.curview.GraphicOverlay <com.assimilate.alltrans.curview.GraphicOverlay
android:id="@+id/sus_graphic_overlay" android:id="@+id/sus_graphic_overlay"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -19,14 +11,14 @@
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:background="@color/bg_40_000000" /> android:background="@color/bg_40_000000" />
<ImageView <ImageView
android:id="@+id/sus_global_close" android:id="@+id/sus_global_close"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_gravity="end"
android:paddingStart="1dp" android:paddingStart="1dp"
android:paddingTop="16dp" android:paddingTop="26dp"
android:paddingEnd="16dp" android:paddingEnd="16dp"
android:src="@drawable/ic_close" /> android:src="@drawable/ic_close" />
</FrameLayout>
</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 <TextView
android:id="@+id/tv_sus_photo" android:id="@+id/tv_sus_photo"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@ -70,7 +69,6 @@
<TextView <TextView
android:id="@+id/tv_sus_district" android:id="@+id/tv_sus_district"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@ -113,13 +111,13 @@
android:src="@drawable/sus_trans_home" /> android:src="@drawable/sus_trans_home" />
<ImageView <ImageView
android:id="@+id/iv_sus_move" android:id="@+id/iv_sus_change"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginStart="19dp" android:layout_marginStart="19dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center" android:gravity="center"
android:src="@drawable/sus_trans_move" /> android:src="@drawable/sus_trans_changelan" />
</LinearLayout> </LinearLayout>
</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> <color name="main_text_fff4f4f4">#FFF4F4F4</color>
<!-- quick_set_page--> <!-- quick_set_page-->
<color name="bg_fff6f6f6">#FFF6F6F6</color> <color name="bg_fff6f6f6">#FFF6F6F6</color>
<color name="bg_ffaaaaaa">#FFAAAAAA</color>
<!-- sus_view--> <!-- sus_view-->
<color name="bg_ff2f2f2f">#FF2F2F2F</color> <color name="bg_ff2f2f2f">#FF2F2F2F</color>
<!-- change_language--> <!-- change_language-->

View File

@ -1,5 +1,5 @@
<resources> <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_info_hide" translatable="false">ih</string>
<string name="pref_key_group_recognized_text_in_blocks" translatable="false">grtib</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="text_target_language">English</string>
<string name="main_text_enter">Enter text</string> <string name="main_text_enter">Enter text</string>
<string name="main_paste_text">Paste</string> <string name="main_paste_text">Paste</string>
<string name="main_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_voice_to_text">Your device may not support speech-to-text.</string>
<string name="main_try_text">Translate</string> <string name="main_try_text">Translate</string>
<string name="main_quick_text">Quick Translate</string> <string name="main_quick_text">Quick Translate</string>
<string name="main_quick_set">settings</string> <string name="main_quick_set">settings</string>
@ -32,28 +35,52 @@
<string name="tr_other">other</string> <string name="tr_other">other</string>
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string> <string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
<string name="tr_title">Translator</string> <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--> <!--settings_page-->
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="languages">Languages</string> <string name="languages">App Languages</string>
<string name="version_update">Version_update</string> <string name="version_update">Version</string>
<string name="privacy_policy">Privacy_policy</string> <string name="privacy_policy">Privacy Policy</string>
<string name="share_app">Share_app</string> <string name="share_app">Share App</string>
<string name="favorite">Favorite</string> <string name="favorite">Favorite</string>
<string name="rate">Rate</string> <string name="rate">Rate</string>
<string name="sheet_text">Your encouragement makes us better!</string> <string name="sheet_text">Your encouragement makes us better!</string>
<string name="quick_set_touming">透明度</string> <string name="quick_set_touming">透明度</string>
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string> <string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
<string name="quick_set_zd_time">自动折叠时间</string> <string name="quick_set_zd_time">自动折叠时间</string>
<string name="favor_title">Favorite</string> <string name="quick_set_zd_time_num">3s</string>
<!-- sus_view--> <string name="quick_set_gd_xfq">固定悬浮球</string>
<string name="global_translation">Global Translation</string> <string name="quick_set_reset">重置</string>
<string name="copy_text">Copy Text</string> <string name="quick_set_gd_description">拖拽翻译完成松手后,悬浮球会回到固定位置,如果想改变位置可点击悬浮球在功能面板移动</string>
<string name="photo_translation">Photo Translation</string> <string name="settings_rate_toast"> Thank you for your comments. We will do better.</string>
<string name="district_translation">District Translation</string> <string name="quick_set_reset_description">清除以上设置,并恢复默认样式</string>
<!-- his_page--> <string name="quick_set_qj_title">全局翻译</string>
<string name="his_delete">Delete</string> <string name="quick_set_qj_tmbg">透明背景</string>
<string name="his_title">History record</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> </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>
<style name="Theme.Alltrans" parent="Base.Theme.Alltrans" /> <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> </resources>

View File

@ -2,4 +2,8 @@
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) 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.