version 1.0.2 -Improve and optimize
2
.idea/deploymentTargetSelector.xml
generated
@ -4,7 +4,7 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2024-07-19T10:26:54.102874Z">
|
||||
<DropdownSelection timestamp="2024-07-26T05:55:38.949778Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=bd95a93d" />
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.jetbrains.kotlin.android)
|
||||
// Make sure that you have the Google services Gradle plugin
|
||||
id("com.google.gms.google-services")
|
||||
// Add the Crashlytics Gradle plugin
|
||||
id("com.google.firebase.crashlytics")
|
||||
}
|
||||
|
||||
android {
|
||||
@ -11,8 +18,8 @@ android {
|
||||
applicationId = "com.assimilate.alltrans"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
versionCode = 2
|
||||
versionName = "1.0.2"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@ -24,16 +31,37 @@ android {
|
||||
|
||||
buildTypes {
|
||||
|
||||
debug { }
|
||||
debug {
|
||||
isShrinkResources = true
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = true
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// applicationVariants.all {
|
||||
// outputs.all {
|
||||
// val appName = "Translark" // 替换为你的应用名称
|
||||
// val versionName = versionName
|
||||
// val date = SimpleDateFormat("yyyyMMdd-HHmm").format(Date())
|
||||
// val newApkName = "${appName}-${date}-${versionName}.apk"
|
||||
// (this as com.android.build.gradle.internal.api.BaseVariantOutputImpl).outputFileName = newApkName
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
@ -45,6 +73,15 @@ android {
|
||||
|
||||
dependencies {
|
||||
|
||||
// Import the BoM for the Firebase platform
|
||||
implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
|
||||
|
||||
// Add the dependencies for the Crashlytics and Analytics libraries
|
||||
// When using the BoM, you don't specify versions in Firebase library dependencies
|
||||
implementation("com.google.firebase:firebase-crashlytics")
|
||||
implementation("com.google.firebase:firebase-analytics")
|
||||
|
||||
|
||||
// To recognize Latin script
|
||||
implementation("com.google.mlkit:text-recognition:16.0.0")
|
||||
// To recognize Chinese script
|
||||
|
||||
29
app/google-services.json
Executable file
@ -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"
|
||||
}
|
||||
23
app/proguard-rules.pro
vendored
@ -16,6 +16,29 @@
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# 保留com.assimilate.alltrans.keepmodel包及其子包的所有类
|
||||
-keep class com.assimilate.alltrans.keepmodel.** { *; }
|
||||
|
||||
# 保留com.assimilate.alltrans.http包及其子包的所有类
|
||||
-keep class com.assimilate.alltrans.http.** { *; }
|
||||
|
||||
# 保留Gson相关的混淆规则
|
||||
-keep class com.google.gson.** { *; }
|
||||
-keepattributes Signature
|
||||
-keepattributes *Annotation*
|
||||
-keep class **$Gson$Types { *; }
|
||||
-keep class **$Gson$Preconditions { *; }
|
||||
|
||||
# 保留使用的注解
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# 保留Retrofit类和方法
|
||||
-keep class retrofit2.** { *; }
|
||||
-dontwarn retrofit2.**
|
||||
-keep class okhttp3.** { *; }
|
||||
-dontwarn okhttp3.**
|
||||
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
BIN
app/release/baselineProfiles/0/app-release.dm
Normal file
BIN
app/release/baselineProfiles/1/app-release.dm
Normal file
37
app/release/output-metadata.json
Normal 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
|
||||
}
|
||||
BIN
app/release/transk_app-08021155.apk
Normal file
@ -7,7 +7,6 @@
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
@ -17,26 +16,29 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:icon="@mipmap/app_icon"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:largeHeap="true"
|
||||
android:roundIcon="@mipmap/app_icon_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Alltrans"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".viewui.MainActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTask" />
|
||||
|
||||
<service
|
||||
android:name=".allservice.SusService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaProjection" />
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".viewui.QuickSetActivity"
|
||||
android:exported="false" />
|
||||
@ -45,7 +47,8 @@
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".viewui.LanguageChangeActivity"
|
||||
android:exported="false" />
|
||||
android:exported="false"
|
||||
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
|
||||
<activity
|
||||
android:name=".viewui.TextResultActivity"
|
||||
android:exported="false" />
|
||||
@ -54,10 +57,10 @@
|
||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||
android:value="ocr" /> <!-- To use multiple models: android:value="ocr,ocr_chinese,ocr_devanagari,ocr_japanese,ocr_korean,..." -->
|
||||
<activity
|
||||
android:name=".viewui.StillImageActivity"
|
||||
android:name=".viewui.PhotoImageActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".viewui.MainActivity"
|
||||
android:name=".viewui.WelActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -66,6 +69,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".viewui.HistoryActivity" />
|
||||
<activity android:name=".viewui.DicActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
1
app/src/main/assets/tran_wel_1.json
Normal file
@ -5,34 +5,26 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.webkit.WebView
|
||||
import com.assimilate.alltrans.common.Language
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.Logger
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.common.ScreenUtils
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
|
||||
class MyApp : Application() {
|
||||
|
||||
init {
|
||||
instance = this
|
||||
}
|
||||
|
||||
private fun setSystemLanguage() {
|
||||
|
||||
// 检查是否是第一次进入应用
|
||||
if (PreferenceLanguageUtils.isFirstTime()) {
|
||||
// 第一次进入应用的逻辑
|
||||
val config = applicationContext().resources.configuration
|
||||
val locale = config.locale.toString() // 获取完整的语言环境信息,如 "en_US" 或 "zh_CN"
|
||||
Logger.d("dslocaleds", locale)
|
||||
val languages = LanguagesConstants.getInstance().getLanguageByLanguageCode(locale, this)
|
||||
Logger.d("dslocaledsa", languages.language)
|
||||
|
||||
PreferenceLanguageUtils.putString(
|
||||
"language_source",
|
||||
"language_target",
|
||||
languages.language.toString().trim()
|
||||
)
|
||||
|
||||
// 设置已经不是第一次进入应用了
|
||||
PreferenceLanguageUtils.setNotFirstTime()
|
||||
}
|
||||
@ -50,7 +42,8 @@ class MyApp : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
// ScreenUtils.init(this)
|
||||
// ScreenUtils.init(this)
|
||||
|
||||
setSystemLanguage()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
@ -74,5 +67,46 @@ class MyApp : Application() {
|
||||
|
||||
object Config {
|
||||
const val openSusViewMode: String = "open_sus_view"
|
||||
|
||||
|
||||
// firebase_event_key
|
||||
const val launchPv = "launch_pv"
|
||||
const val homePv = "home_pv"
|
||||
const val languageChoose = "language_choose"
|
||||
const val languageFrom = "language_from"
|
||||
const val textTransSwitch = "text_trans_switch"
|
||||
const val textInput = "text_input"
|
||||
const val textTransClick = "text_trans_click"
|
||||
const val textTransResult = "text_trans_result"
|
||||
const val textTransPaste = "text_trans_paste"
|
||||
|
||||
const val textVoiceClick = "text_voice_click"
|
||||
const val textVoiceResult = "text_voice_result"
|
||||
const val textTransPlay = "text_trans_play"
|
||||
const val textTransCopy = "text_trans_copy"
|
||||
const val textTransShare = "text_trans_share"
|
||||
const val textTransLike = "text_trans_like"
|
||||
|
||||
|
||||
const val hoverButtonClick = "hover_button_click"
|
||||
const val hoverButtonCancel = "hover_button_cancel"
|
||||
const val hover_button_agree = "hover_button_agree"
|
||||
const val hover_screen_click = "hover_screen_click"
|
||||
const val hover_screen_imp = "hover_screen_imp"
|
||||
const val hover_screen_global = "hover_screen_global"
|
||||
const val hover_global_result = "hover_global_result"
|
||||
const val hover_screen_district = "hover_screen_district"
|
||||
const val hover_district_result = "hover_district_result"
|
||||
|
||||
const val image_click = "image_click"
|
||||
const val image_trans_camera = "image_trans_camera"
|
||||
const val image_camera_result = "image_camera_result"
|
||||
const val image_trans_photo = "image_trans_photo"
|
||||
const val image_photo_result = "image_photo_result"
|
||||
|
||||
const val history_click = "history_click"
|
||||
const val history_delete = "history_delete"
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
package com.assimilate.alltrans.adapters;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
@ -13,7 +12,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.assimilate.alltrans.R;
|
||||
import com.assimilate.alltrans.common.Language;
|
||||
import com.assimilate.alltrans.keepmodel.Language;
|
||||
import com.assimilate.alltrans.databinding.LanguageItemLayoutBinding;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
@ -22,14 +21,15 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.LanguageHolder> implements Filterable {
|
||||
private final Activity mActivity;
|
||||
|
||||
private final Context mContext;
|
||||
private final ArrayList<Language> languages;
|
||||
private final ArrayList<Language> filteredLanguages;
|
||||
private final OnClickListener listener;
|
||||
private final RequestOptions options;
|
||||
|
||||
public LanguageAdapter(@NonNull final Activity activity, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
|
||||
this.mActivity = activity;
|
||||
public LanguageAdapter(@NonNull final Context mContext, @NonNull final ArrayList<Language> languageList, final OnClickListener onClickListener) {
|
||||
this.mContext = mContext;
|
||||
this.languages = new ArrayList<>(languageList);
|
||||
this.filteredLanguages = new ArrayList<>(languageList);
|
||||
this.listener = onClickListener;
|
||||
@ -55,7 +55,7 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
|
||||
if (!TextUtils.isEmpty(currentLanguage.getLanguage())) {
|
||||
holder.mBinding.language.setText(currentLanguage.getLanguage());
|
||||
} else {
|
||||
holder.mBinding.language.setText(mActivity.getString(R.string.app_name));
|
||||
holder.mBinding.language.setText(mContext.getString(R.string.app_name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,14 +72,13 @@ public class LanguageAdapter extends RecyclerView.Adapter<LanguageAdapter.Langua
|
||||
}
|
||||
|
||||
private void drawImage(@NonNull final String url, @NonNull final ImageView view) {
|
||||
if (mActivity.isFinishing() || mActivity.isDestroyed()) return;
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
Glide.with(mActivity)
|
||||
Glide.with(mContext)
|
||||
.applyDefaultRequestOptions(options)
|
||||
.load(R.mipmap.ic_launcher)
|
||||
.into(view);
|
||||
} else {
|
||||
Glide.with(mActivity)
|
||||
Glide.with(mContext)
|
||||
.applyDefaultRequestOptions(options)
|
||||
.load(url)
|
||||
.into(view);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package com.assimilate.alltrans.adapters;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CompoundButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@ -25,9 +25,9 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
|
||||
if (!list.isEmpty()) {
|
||||
for (Translations translations : list) {
|
||||
items.add(new TranslationItem(
|
||||
translations.getId(), translations.getSourceLanguage(), translations.getSourceTxt(),
|
||||
translations.getTargetLanguage(), translations.getTargetTxt(), translations.getCurrentTimeMillis(),
|
||||
translations.getExist(), translations.getCollection()
|
||||
translations.getId(), translations.getSourceLanguage(), translations.getSourceTxt(),
|
||||
translations.getTargetLanguage(), translations.getTargetTxt(), translations.getCurrentTimeMillis(),
|
||||
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) {
|
||||
callback.updateList(Operation.ADD, item.getId(), holder.getAdapterPosition());
|
||||
} else {
|
||||
callback.updateList(Operation.REMOVE, item.getId(), holder.getAdapterPosition());
|
||||
}
|
||||
// 为checkboxCurrent设置点击事件
|
||||
holder.mBinding.checkboxCurrent.setOnClickListener(v -> {
|
||||
boolean isChecked = holder.mBinding.checkboxCurrent.isChecked();
|
||||
item.currentChecked = isChecked;
|
||||
|
||||
Log.d("fadsfdas", "Checkbox clicked");
|
||||
|
||||
if (isChecked) {
|
||||
callback.updateList(Operation.ADD, item.getId(), holder.getAdapterPosition());
|
||||
} else {
|
||||
callback.updateList(Operation.REMOVE, item.getId(), holder.getAdapterPosition());
|
||||
}
|
||||
});
|
||||
|
||||
// 为整个item设置点击事件
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
holder.mBinding.checkboxCurrent.performClick();
|
||||
});
|
||||
|
||||
final String itemSourceTxt = item.getSourceTxt();
|
||||
if (!TextUtils.isEmpty(itemSourceTxt)) {
|
||||
holder.mBinding.tvSource.setText(itemSourceTxt);
|
||||
@ -116,10 +123,11 @@ public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.
|
||||
|
||||
public interface TranslationItemCallback {
|
||||
void updateList(Operation operation, long id, int position);
|
||||
|
||||
void speech(String value);
|
||||
}
|
||||
|
||||
public enum Operation{
|
||||
public enum Operation {
|
||||
REMOVE, ADD
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,80 +7,34 @@ import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.PixelFormat
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.hardware.display.VirtualDisplay
|
||||
import android.media.Image
|
||||
import android.media.ImageReader
|
||||
import android.media.projection.MediaProjection
|
||||
import android.media.projection.MediaProjectionManager
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.os.Looper
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.common.TextRecognitionProcessor
|
||||
import com.assimilate.alltrans.common.VisionImageProcessor
|
||||
import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding
|
||||
import com.assimilate.alltrans.databinding.SusControlViewBinding
|
||||
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
|
||||
import java.io.IOException
|
||||
import com.assimilate.alltrans.common.ScreenCaptureManager
|
||||
import com.assimilate.alltrans.curview.ControlView
|
||||
import com.assimilate.alltrans.curview.CopyTextView
|
||||
import com.assimilate.alltrans.curview.FloatingView
|
||||
import com.assimilate.alltrans.curview.GlobalView
|
||||
import com.assimilate.alltrans.curview.LanguageSelectorView
|
||||
import com.assimilate.alltrans.curview.SelectionView
|
||||
|
||||
class SusService : Service() {
|
||||
private var imageProcessor: VisionImageProcessor? = null
|
||||
private lateinit var mediaProjectionManager: MediaProjectionManager
|
||||
private var mediaProjection: MediaProjection? = null
|
||||
private var virtualDisplay: VirtualDisplay? = null
|
||||
private lateinit var displayMetrics: DisplayMetrics
|
||||
private lateinit var imageReader: ImageReader
|
||||
|
||||
private lateinit var windowManager: WindowManager
|
||||
private lateinit var floatingView: View
|
||||
private lateinit var globalView: View
|
||||
|
||||
private lateinit var bindingSusControl: SusControlViewBinding
|
||||
private lateinit var bindingSubGlobal: LayoutSusGlobalBinding
|
||||
|
||||
private var mResultCode = 0
|
||||
private var mResultData: Intent? = null
|
||||
private val mpResultCode = "mpResultCode"
|
||||
private val mpData = "mpData"
|
||||
private val ball_time = 5000L
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val hideRunnable = Runnable {
|
||||
// floatingView.visibility = View.GONE
|
||||
// val layoutParams = floatingView.layoutParams as WindowManager.LayoutParams
|
||||
// val iconLayoutParams = WindowManager.LayoutParams(
|
||||
// 100, 100, // Set the size of the icon as needed
|
||||
// layoutParams.type,
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
// PixelFormat.TRANSLUCENT
|
||||
// )
|
||||
// iconLayoutParams.gravity = layoutParams.gravity
|
||||
// iconLayoutParams.x = layoutParams.x
|
||||
// iconLayoutParams.y = layoutParams.y
|
||||
//
|
||||
// val iconView = ImageView(this).apply {
|
||||
// setImageResource(R.drawable.ic_like_yes) // Set your icon drawable here
|
||||
// setOnClickListener {
|
||||
// floatingView.visibility = View.VISIBLE
|
||||
// windowManager.removeView(this)
|
||||
// windowManager.addView(floatingView, layoutParams)
|
||||
// startHideTimer()
|
||||
// }
|
||||
// }
|
||||
// windowManager.addView(iconView, iconLayoutParams)
|
||||
}
|
||||
private lateinit var languageSelectorView: LanguageSelectorView
|
||||
|
||||
private lateinit var screenCaptureManager: ScreenCaptureManager
|
||||
private lateinit var floatingView: FloatingView
|
||||
private lateinit var controlView: ControlView
|
||||
private lateinit var globalView: GlobalView
|
||||
private lateinit var selectionView: SelectionView
|
||||
private lateinit var copyTextView: CopyTextView
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
@ -95,9 +49,11 @@ class SusService : Service() {
|
||||
val data = intent.getParcelableExtra<Intent>(mpData)
|
||||
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mResultCode = resultCode
|
||||
mResultData = data
|
||||
startProjection()
|
||||
screenCaptureManager = ScreenCaptureManager(this)
|
||||
screenCaptureManager.setProjectionParams(resultCode, data)
|
||||
screenCaptureManager.startProjection()
|
||||
addFloatingView()
|
||||
|
||||
} else {
|
||||
Log.e("SusService", "Intent or data is null")
|
||||
}
|
||||
@ -107,253 +63,54 @@ class SusService : Service() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
displayMetrics = DisplayMetrics()
|
||||
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
|
||||
|
||||
initControlView()
|
||||
|
||||
|
||||
imageProcessor = TextRecognitionProcessor(
|
||||
this,
|
||||
ChineseTextRecognizerOptions.Builder().build()
|
||||
)
|
||||
|
||||
startForeground(1, createNotification())
|
||||
|
||||
}
|
||||
|
||||
private fun initControlView() {
|
||||
bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this))
|
||||
floatingView = bindingSusControl.root
|
||||
private fun addFloatingView() {
|
||||
floatingView = FloatingView(this)
|
||||
floatingView.attachToWindow()
|
||||
floatingView.setOnMovePointListener(object : FloatingView.OnMovePointListener {
|
||||
override fun onMovePoint() {
|
||||
Log.d("SusService_f", "FloatingView moved")
|
||||
floatingView.setImageReader(screenCaptureManager.getImageReader())
|
||||
}
|
||||
})
|
||||
floatingView.setOnClickListener(object : FloatingView.OnClickListener {
|
||||
override fun onClick() {
|
||||
Log.d("SusService_f", "FloatingView clicked")
|
||||
addControlView()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
private fun addControlView() {
|
||||
controlView = ControlView(this)
|
||||
|
||||
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 {
|
||||
private var startX = 0f
|
||||
private var startY = 0f
|
||||
private var touchX = 0f
|
||||
private var touchY = 0f
|
||||
override fun onDistrictClick() {
|
||||
selectionView = SelectionView(this@SusService)
|
||||
selectionView.addSelectionView(screenCaptureManager.getImageReader())
|
||||
}
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
startX = layoutParams.x.toFloat()
|
||||
startY = layoutParams.y.toFloat()
|
||||
touchX = event.rawX
|
||||
touchY = event.rawY
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
return true
|
||||
}
|
||||
|
||||
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
|
||||
override fun onChangeLan() {
|
||||
languageSelectorView = LanguageSelectorView(this@SusService)
|
||||
languageSelectorView.createFloatingView()
|
||||
}
|
||||
})
|
||||
|
||||
startHideTimer() // Start the timer when the view is initialized
|
||||
}
|
||||
|
||||
private fun startHideTimer() {
|
||||
handler.removeCallbacks(hideRunnable)
|
||||
|
||||
handler.postDelayed(hideRunnable, ball_time)
|
||||
}
|
||||
|
||||
private fun initControlClick() {
|
||||
|
||||
bindingSusControl.ivSusMove.setOnClickListener {
|
||||
bindingSusControl.ivSusMove.setImageResource(R.drawable.ic_little_ball)
|
||||
bindingSusControl.susLl1.visibility = View.GONE
|
||||
bindingSusControl.susLl2.visibility = View.GONE
|
||||
bindingSusControl.ivSusHome.visibility = View.GONE
|
||||
}
|
||||
|
||||
bindingSusControl.tvSusGlobal.setOnClickListener {
|
||||
bindingSusControl.susControlRoot.visibility = View.INVISIBLE
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
addGlobalView()
|
||||
captureScreenshot()
|
||||
}, 555)
|
||||
}
|
||||
|
||||
bindingSusControl.tvSusCopy.setOnClickListener {
|
||||
Log.d("SusService", "Copy text clicked")
|
||||
}
|
||||
|
||||
bindingSusControl.tvSusPhoto.setOnClickListener {
|
||||
Log.d("SusService", "Photo translation clicked")
|
||||
}
|
||||
|
||||
bindingSusControl.tvSusDistrict.setOnClickListener {
|
||||
Log.d("SusService", "District translation clicked")
|
||||
}
|
||||
|
||||
bindingSusControl.ivSusHome.setOnClickListener {
|
||||
Log.d("SusService", "Home clicked")
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun addGlobalView() {
|
||||
bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this))
|
||||
globalView = bindingSubGlobal.root
|
||||
|
||||
val layoutParams = WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||
} else {
|
||||
WindowManager.LayoutParams.TYPE_PHONE
|
||||
},
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT
|
||||
)
|
||||
layoutParams.gravity = Gravity.TOP or Gravity.LEFT
|
||||
layoutParams.x = 0
|
||||
layoutParams.y = 0
|
||||
|
||||
windowManager.addView(globalView, layoutParams)
|
||||
|
||||
initGlobalClick()
|
||||
|
||||
}
|
||||
|
||||
private fun initGlobalClick() {
|
||||
bindingSubGlobal.susGlobalClose.setOnClickListener {
|
||||
windowManager.removeView(globalView)
|
||||
bindingSusControl.susControlRoot.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun startProjection() {
|
||||
if (mResultData == null) {
|
||||
Log.e("SusService", "mResultData is null, cannot start projection")
|
||||
return
|
||||
}
|
||||
|
||||
if (mediaProjection == null) {
|
||||
mediaProjectionManager =
|
||||
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
mediaProjection =
|
||||
mediaProjectionManager.getMediaProjection(mResultCode, mResultData!!)
|
||||
mediaProjection?.registerCallback(object : MediaProjection.Callback() {}, null)
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
try {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
||||
|
||||
val density = displayMetrics.densityDpi
|
||||
val width = displayMetrics.widthPixels
|
||||
val height = displayMetrics.heightPixels
|
||||
|
||||
imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2)
|
||||
|
||||
virtualDisplay = mediaProjection?.createVirtualDisplay(
|
||||
"ScreenCapture",
|
||||
width,
|
||||
height,
|
||||
density,
|
||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
||||
imageReader.surface,
|
||||
null,
|
||||
null
|
||||
)
|
||||
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("SusService", "Error starting projection", e)
|
||||
}
|
||||
}, 1234)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun stopProjection() {
|
||||
// Release the virtual display
|
||||
try {
|
||||
virtualDisplay?.release()
|
||||
virtualDisplay = null
|
||||
} catch (e: Exception) {
|
||||
Log.e("SusService", "Error releasing virtual display", e)
|
||||
}
|
||||
|
||||
// Stop the media projection
|
||||
try {
|
||||
mediaProjection?.stop()
|
||||
mediaProjection = null
|
||||
} catch (e: Exception) {
|
||||
Log.e("SusService", "Error stopping media projection", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun captureScreenshot() {
|
||||
val image: Image? = imageReader.acquireLatestImage()
|
||||
image?.let {
|
||||
val bitmap = imageToBitmap(it)
|
||||
image.close()
|
||||
bindingSubGlobal.susPreview.setImageBitmap(bitmap)
|
||||
tryReloadAndDetectInImage(bitmap)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun imageToBitmap(image: Image): Bitmap {
|
||||
val planes = image.planes
|
||||
val buffer = planes[0].buffer
|
||||
val pixelStride = planes[0].pixelStride
|
||||
val rowStride = planes[0].rowStride
|
||||
val rowPadding = rowStride - pixelStride * image.width
|
||||
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
image.width + rowPadding / pixelStride,
|
||||
image.height,
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
bitmap.copyPixelsFromBuffer(buffer)
|
||||
return Bitmap.createBitmap(bitmap, 0, 0, image.width, image.height)
|
||||
controlView.addControlView()
|
||||
}
|
||||
|
||||
private fun createNotification(): Notification {
|
||||
@ -372,8 +129,8 @@ class SusService : Service() {
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this, notificationChannelId)
|
||||
.setContentTitle("悬浮窗服务")
|
||||
.setContentText("悬浮窗服务正在运行")
|
||||
.setContentTitle("Screen Translation Service")
|
||||
.setContentText("Screen translation is running...")
|
||||
.setSmallIcon(R.drawable.ic_close)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setCategory(Notification.CATEGORY_SERVICE)
|
||||
@ -381,38 +138,27 @@ class SusService : Service() {
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun tryReloadAndDetectInImage(bitmap: Bitmap) {
|
||||
try {
|
||||
bindingSubGlobal.susGraphicOverlay.clear()
|
||||
|
||||
if (imageProcessor != null) {
|
||||
bindingSubGlobal.susGraphicOverlay.setImageSourceInfo(
|
||||
bitmap.width,
|
||||
bitmap.height,
|
||||
false
|
||||
)
|
||||
imageProcessor!!.processBitmap(bitmap, bindingSubGlobal.susGraphicOverlay)
|
||||
} else {
|
||||
Log.e(
|
||||
"SusService",
|
||||
"Null imageProcessor, please check adb logs for imageProcessor creation error"
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e("SusService", "Error retrieving saved image", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
Log.d("SusService", "Service onDestroy")
|
||||
if (floatingView.isAttachedToWindow) {
|
||||
windowManager.removeView(floatingView)
|
||||
}
|
||||
if (::globalView.isInitialized && globalView.isAttachedToWindow) {
|
||||
windowManager.removeView(globalView)
|
||||
}
|
||||
stopProjection()
|
||||
}
|
||||
|
||||
if (::controlView.isInitialized) {
|
||||
controlView.removeControlView()
|
||||
}
|
||||
|
||||
if (::floatingView.isInitialized) {
|
||||
floatingView.detachFromWindow()
|
||||
}
|
||||
|
||||
if (::selectionView.isInitialized) {
|
||||
selectionView.removeSelectionView()
|
||||
}
|
||||
|
||||
if (::screenCaptureManager.isInitialized) {
|
||||
screenCaptureManager.stopProjection()
|
||||
}
|
||||
if (::copyTextView.isInitialized) {
|
||||
copyTextView.removeView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,18 +6,25 @@ import android.graphics.Canvas;
|
||||
|
||||
import com.assimilate.alltrans.curview.GraphicOverlay;
|
||||
|
||||
/** Draw camera image to background. */
|
||||
/**
|
||||
* Draw camera image to background.
|
||||
*/
|
||||
public class CameraImageGraphic extends GraphicOverlay.Graphic {
|
||||
|
||||
private final Bitmap bitmap;
|
||||
private final Bitmap bitmap;
|
||||
|
||||
public CameraImageGraphic(GraphicOverlay overlay, Bitmap bitmap) {
|
||||
super(overlay);
|
||||
this.bitmap = bitmap;
|
||||
}
|
||||
public CameraImageGraphic(GraphicOverlay overlay, Bitmap bitmap) {
|
||||
super(overlay);
|
||||
this.bitmap = bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.drawBitmap(bitmap, getTransformationMatrix(), null);
|
||||
}
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.drawBitmap(bitmap, getTransformationMatrix(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(float x, float y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -5,18 +5,12 @@ import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
import android.util.Log
|
||||
import com.assimilate.alltrans.MyApp
|
||||
import com.assimilate.alltrans.curview.GraphicOverlay
|
||||
import com.assimilate.alltrans.http.GoogleTranslator
|
||||
import com.assimilate.alltrans.http.Translator
|
||||
import com.google.mlkit.vision.text.Text
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@ -25,154 +19,147 @@ class TextGraphic(
|
||||
private val text: Text,
|
||||
private val shouldGroupTextInBlocks: Boolean,
|
||||
private val showLanguageTag: Boolean,
|
||||
private val showConfidence: Boolean
|
||||
private val showConfidence: Boolean,
|
||||
private val textShow: Boolean,
|
||||
private val needTrans: Boolean
|
||||
) : GraphicOverlay.Graphic(overlay) {
|
||||
|
||||
private val rectPaint: Paint = Paint()
|
||||
private val textPaint: TextPaint
|
||||
|
||||
private val labelPaint: Paint
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private val executor = Executors.newSingleThreadExecutor()
|
||||
private val textPaint: TextPaint = TextPaint().apply {
|
||||
color = TEXT_COLOR
|
||||
textSize = TEXT_SIZE
|
||||
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
||||
isFakeBoldText = true
|
||||
}
|
||||
private val labelPaint: Paint = Paint().apply {
|
||||
color = MARKER_COLOR
|
||||
style = Paint.Style.FILL
|
||||
}
|
||||
private var hiddenTextBlocks: MutableList<Boolean> = MutableList(text.textBlocks.size) { !textShow }
|
||||
private var isVisible: Boolean
|
||||
|
||||
init {
|
||||
prepareTranslation()
|
||||
rectPaint.color = MARKER_COLOR
|
||||
rectPaint.style = Paint.Style.STROKE
|
||||
rectPaint.strokeWidth = STROKE_WIDTH
|
||||
textPaint = TextPaint()
|
||||
textPaint.color = TEXT_COLOR
|
||||
textPaint.textSize = TEXT_SIZE
|
||||
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
|
||||
textPaint.isFakeBoldText = true
|
||||
labelPaint = Paint()
|
||||
labelPaint.color = MARKER_COLOR
|
||||
labelPaint.style = Paint.Style.FILL
|
||||
isVisible = textShow
|
||||
|
||||
if (needTrans) {
|
||||
TranslationManager(text) { translatedTextPairs ->
|
||||
translatedTextBlocks = translatedTextPairs.map { it.first }
|
||||
// 可以同时打印原Text和翻译后的结果
|
||||
translatedTextPairs.forEach { (translated, original) ->
|
||||
Log.d("Translation", "Original: $original -> Translated: $translated")
|
||||
}
|
||||
postInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
// Redraw the overlay, as this graphic has been added.
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
private var translatedTextBlocks: List<String> = listOf()
|
||||
|
||||
// Method to prepare translation before drawing
|
||||
private fun prepareTranslation() {
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (!isVisible) return
|
||||
|
||||
|
||||
executor.execute {
|
||||
|
||||
val textToTranslate = StringBuilder()
|
||||
|
||||
// Collect all text to be translated and append delimiter
|
||||
for (textBlock in text.textBlocks) {
|
||||
val textWithDelimiter = textBlock.text + DELIMITER
|
||||
textToTranslate.append(textWithDelimiter)
|
||||
}
|
||||
val lanSourceCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
|
||||
PreferenceLanguageUtils.getString("language_source"),
|
||||
MyApp.applicationContext()
|
||||
)
|
||||
val lanTargetCode = LanguagesConstants.getInstance().getLanguageCodeByLanguage(
|
||||
PreferenceLanguageUtils.getString("language_target"),
|
||||
MyApp.applicationContext()
|
||||
)
|
||||
|
||||
if (lanTargetCode == null || lanSourceCode == null || textToTranslate.toString()
|
||||
.isEmpty()
|
||||
) {
|
||||
return@execute
|
||||
}
|
||||
|
||||
// Define translation parameters
|
||||
val param = HashMap<String, String>().apply {
|
||||
put("sourceLanguage", lanSourceCode)
|
||||
put("translationLanguage", lanTargetCode)
|
||||
put("text", textToTranslate.toString())
|
||||
}
|
||||
Log.d("fdasfas_sou", lanSourceCode)
|
||||
Log.d("fdasfas_tar", lanTargetCode)
|
||||
Log.d("fdasfas_tex", textToTranslate.toString())
|
||||
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> =
|
||||
GoogleTranslator()
|
||||
|
||||
// Perform translation
|
||||
translator.translate(param, GoogleTranslator.GoogleTranslateCallback { translatedText ->
|
||||
// Split translated text by delimiter
|
||||
translatedTextBlocks =
|
||||
translatedText.split(DELIMITER.toRegex()).filter { it.isNotEmpty() }
|
||||
|
||||
// Update UI thread
|
||||
handler.post {
|
||||
postInvalidate()
|
||||
}
|
||||
})
|
||||
for ((index, textBlock) in text.textBlocks.withIndex()) {
|
||||
if (hiddenTextBlocks[index]) continue
|
||||
drawTextBlock(textBlock, canvas, index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
private fun drawTextBlock(textBlock: Text.TextBlock, canvas: Canvas, index: Int) {
|
||||
val translatedBlockText = translatedTextBlocks.getOrNull(index) ?: textBlock.text
|
||||
|
||||
|
||||
for ((translatedIndex, textBlock) in text.textBlocks.withIndex()) {
|
||||
val translatedBlockText =
|
||||
if (translatedIndex < translatedTextBlocks.size) translatedTextBlocks[translatedIndex] else textBlock.text
|
||||
|
||||
if (shouldGroupTextInBlocks) {
|
||||
drawText(
|
||||
getFormattedText(
|
||||
translatedBlockText,
|
||||
textBlock.recognizedLanguage,
|
||||
confidence = null
|
||||
),
|
||||
RectF(textBlock.boundingBox),
|
||||
canvas
|
||||
)
|
||||
Log.d(
|
||||
"fdgfsdfsdfas", getFormattedText(
|
||||
translatedBlockText,
|
||||
textBlock.recognizedLanguage,
|
||||
confidence = null
|
||||
)
|
||||
)
|
||||
} // 不需要单行(删除)
|
||||
if (shouldGroupTextInBlocks) {
|
||||
val rect = RectF(textBlock.boundingBox)
|
||||
drawText(
|
||||
getFormattedText(translatedBlockText, textBlock.recognizedLanguage, null),
|
||||
rect,
|
||||
canvas,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun contains(x: Float, y: Float): Boolean {
|
||||
for (textBlock in text.textBlocks) {
|
||||
val rect = RectF(textBlock.boundingBox)
|
||||
val x0 = translateX(rect.left)
|
||||
val x1 = translateX(rect.right)
|
||||
rect.left = min(x0, x1)
|
||||
rect.right = max(x0, x1)
|
||||
rect.top = translateY(rect.top)
|
||||
rect.bottom = translateY(rect.bottom)
|
||||
if (rect.contains(x, y)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun showTextBlockAt(x: Float, y: Float) {
|
||||
isVisible = true
|
||||
for ((index, textBlock) in text.textBlocks.withIndex()) {
|
||||
val rect = RectF(textBlock.boundingBox)
|
||||
val x0 = translateX(rect.left)
|
||||
val x1 = translateX(rect.right)
|
||||
rect.left = min(x0, x1)
|
||||
rect.right = max(x0, x1)
|
||||
rect.top = translateY(rect.top)
|
||||
rect.bottom = translateY(rect.bottom)
|
||||
if (rect.contains(x, y)) {
|
||||
hiddenTextBlocks[index] = false
|
||||
postInvalidate()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun hideTextBlock() {
|
||||
isVisible = false
|
||||
for (i in hiddenTextBlocks.indices) {
|
||||
hiddenTextBlocks[i] = true
|
||||
}
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
private fun getFormattedText(text: String, languageTag: String, confidence: Float?): String {
|
||||
val res =
|
||||
if (showLanguageTag) String.format(
|
||||
TEXT_WITH_LANGUAGE_TAG_FORMAT,
|
||||
languageTag,
|
||||
text
|
||||
) else text
|
||||
return if (showConfidence && confidence != null) String.format("%s (%.2f)", res, confidence)
|
||||
else res
|
||||
val res = if (showLanguageTag) String.format(
|
||||
TEXT_WITH_LANGUAGE_TAG_FORMAT,
|
||||
languageTag,
|
||||
text
|
||||
) else text
|
||||
return if (showConfidence && confidence != null) String.format(
|
||||
"%s (%.2f)",
|
||||
res,
|
||||
confidence
|
||||
) else res
|
||||
}
|
||||
|
||||
private fun drawText(text: String, rect: RectF, canvas: Canvas) {
|
||||
// 如果图像是翻转的,将左边翻译到右边,右边翻译到左边。
|
||||
private fun drawText(text: String, rect: RectF, canvas: Canvas, paint: TextPaint) {
|
||||
val x0 = translateX(rect.left)
|
||||
val x1 = translateX(rect.right)
|
||||
rect.left = min(x0, x1)
|
||||
rect.right = max(x0, x1)
|
||||
rect.top = translateY(rect.top)
|
||||
rect.bottom = translateY(rect.bottom)
|
||||
|
||||
// 绘制背景矩形
|
||||
canvas.drawRect(rect, labelPaint)
|
||||
|
||||
// 设置文本大小以适应矩形
|
||||
val textPaintCopy = TextPaint(textPaint)
|
||||
// 准备文本绘制
|
||||
val textPaintCopy = TextPaint(paint)
|
||||
val availableWidth = rect.width().toInt()
|
||||
val availableHeight = rect.height().toInt()
|
||||
var textSize = textPaintCopy.textSize
|
||||
|
||||
// 调整文本大小以适应矩形
|
||||
var textLayout: StaticLayout
|
||||
while (textSize > 0) {
|
||||
textPaintCopy.textSize = textSize
|
||||
textLayout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setLineSpacing(0.0f, 1.0f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setLineSpacing(0.0f, 1.0f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
|
||||
if (textLayout.height <= availableHeight) {
|
||||
break
|
||||
@ -180,13 +167,11 @@ class TextGraphic(
|
||||
textSize -= 1
|
||||
}
|
||||
|
||||
// 使用 StaticLayout 绘制文本
|
||||
textLayout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setLineSpacing(0.0f, 1.0f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
textLayout = StaticLayout.Builder.obtain(text, 0, text.length, textPaintCopy, availableWidth)
|
||||
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
|
||||
.setLineSpacing(0.0f, 1.0f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
|
||||
canvas.save()
|
||||
canvas.translate(rect.left, rect.top)
|
||||
@ -195,12 +180,10 @@ class TextGraphic(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DELIMITER = "0`~`0"
|
||||
private const val TAG = "TextGraphic"
|
||||
private const val TEXT_WITH_LANGUAGE_TAG_FORMAT = "%s:%s"
|
||||
private val TEXT_COLOR = Color.parseColor("#FF474747")
|
||||
private val MARKER_COLOR = Color.parseColor("#FFD9D9D9")
|
||||
private const val STROKE_WIDTH = 2.0f
|
||||
private const val TEXT_SIZE = 44.0f
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import android.content.Context
|
||||
import android.util.Log
|
||||
import com.assimilate.alltrans.MyApp
|
||||
import com.assimilate.alltrans.curview.GraphicOverlay
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
import com.google.android.gms.tasks.Task
|
||||
import com.google.mlkit.vision.common.InputImage
|
||||
import com.google.mlkit.vision.text.Text
|
||||
@ -14,8 +16,16 @@ import com.google.mlkit.vision.text.TextRecognizerOptionsInterface
|
||||
/** Processor for the text detector demo. */
|
||||
class TextRecognitionProcessor(
|
||||
private val context: Context,
|
||||
textRecognizerOptions: TextRecognizerOptionsInterface
|
||||
) : VisionProcessorBase<Text>(context) {
|
||||
textRecognizerOptions: TextRecognizerOptionsInterface,
|
||||
private val textShow:Boolean,
|
||||
private val needTrans: Boolean,
|
||||
private val callback: TextRecognitionCallback? = null // 添加回调
|
||||
) : VisionProcessorBase<Text>(context) {
|
||||
|
||||
interface TextRecognitionCallback {
|
||||
fun onTextRecognized(text: String)
|
||||
}
|
||||
|
||||
private val textRecognizer: TextRecognizer = TextRecognition.getClient(textRecognizerOptions)
|
||||
private val shouldGroupRecognizedTextInBlocks: Boolean =
|
||||
PreferenceUtils.shouldGroupRecognizedTextInBlocks(context)
|
||||
@ -31,7 +41,6 @@ class TextRecognitionProcessor(
|
||||
return textRecognizer.process(image)
|
||||
}
|
||||
|
||||
|
||||
override fun onSuccess(text: Text, graphicOverlay: GraphicOverlay) {
|
||||
PreferenceLanguageUtils.putString("language_source", getMostFrequentLanguage(text))
|
||||
|
||||
@ -43,16 +52,20 @@ class TextRecognitionProcessor(
|
||||
text,
|
||||
shouldGroupRecognizedTextInBlocks,
|
||||
showLanguageTag,
|
||||
showConfidence
|
||||
showConfidence,
|
||||
textShow,
|
||||
needTrans
|
||||
)
|
||||
)
|
||||
|
||||
// 调用回调
|
||||
callback?.onTextRecognized(text.text)
|
||||
}
|
||||
|
||||
override fun onFailure(e: Exception) {
|
||||
Log.w(TAG, "Text detection failed.$e")
|
||||
}
|
||||
|
||||
|
||||
// 推测最可能的语言
|
||||
private fun getMostFrequentLanguage(text: Text): String {
|
||||
val languageCount = mutableMapOf<String, Int>()
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,16 +321,7 @@ abstract class VisionProcessorBase<T>(context: Context) : VisionImageProcessor {
|
||||
graphicOverlay.add(CameraImageGraphic(graphicOverlay, originalCameraImage))
|
||||
}
|
||||
this@VisionProcessorBase.onSuccess(results, graphicOverlay)
|
||||
if (!PreferenceUtils.shouldHideDetectionInfo(graphicOverlay.context)) {
|
||||
graphicOverlay.add(
|
||||
InferenceInfoGraphic(
|
||||
graphicOverlay,
|
||||
currentFrameLatencyMs,
|
||||
currentDetectorLatencyMs,
|
||||
if (shouldShowFps) framesPerSecond else null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
graphicOverlay.postInvalidate()
|
||||
}
|
||||
)
|
||||
|
||||
@ -4,11 +4,18 @@ import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
|
||||
import com.assimilate.alltrans.MyApp;
|
||||
import com.assimilate.alltrans.R;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
public class Widget {
|
||||
@ -23,16 +30,51 @@ public class Widget {
|
||||
}
|
||||
|
||||
public static void makeSnackbar(@NonNull final Activity mActivity, @NonNull final String msg) {
|
||||
View view = mActivity.findViewById(android.R.id.content);
|
||||
Snackbar snackbar = Snackbar.make(view, msg, Snackbar.LENGTH_LONG);
|
||||
View snackbarView = snackbar.getView();
|
||||
snackbarView.setBackgroundColor(Color.parseColor("#66000000"));
|
||||
Snackbar snackbar = Snackbar.make(mActivity.findViewById(android.R.id.content), msg, Snackbar.LENGTH_SHORT);
|
||||
View view = snackbar.getView();
|
||||
view.setBackground(AppCompatResources.getDrawable(mActivity,R.drawable.button_r20_black_bg));
|
||||
|
||||
TextView textView = snackbarView.findViewById(com.google.android.material.R.id.snackbar_text);
|
||||
textView.setTextColor(Color.WHITE);
|
||||
textView.setTextSize(16f);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
|
||||
params.gravity = Gravity.CENTER;
|
||||
view.setLayoutParams(params);
|
||||
// 获取Snackbar的TextView并设置文字居中
|
||||
TextView textView = view.findViewById(com.google.android.material.R.id.snackbar_text);
|
||||
if (textView != null) {
|
||||
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
|
||||
}
|
||||
|
||||
snackbar.show();
|
||||
}
|
||||
// 封装放大动画的方法
|
||||
public static void startScaleAnimation(final ImageView imageView) {
|
||||
final ScaleAnimation scaleAnimation = new ScaleAnimation(
|
||||
1.0f, 1.2f, // 从1倍放大到1.2倍
|
||||
1.0f, 1.2f, // 从1倍放大到1.2倍
|
||||
Animation.RELATIVE_TO_SELF, 0.5f, // 以自身中心X轴为缩放中心
|
||||
Animation.RELATIVE_TO_SELF, 0.5f // 以自身中心Y轴为缩放中心
|
||||
);
|
||||
scaleAnimation.setDuration(700); // 动画持续时间,单位为毫秒
|
||||
scaleAnimation.setFillAfter(true); // 动画结束后保持放大状态
|
||||
|
||||
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
|
||||
private int repeatCount = 2;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (repeatCount > 1) {
|
||||
repeatCount--;
|
||||
imageView.startAnimation(scaleAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {}
|
||||
});
|
||||
|
||||
// 开始动画
|
||||
imageView.startAnimation(scaleAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
154
app/src/main/java/com/assimilate/alltrans/curview/ControlView.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
128
app/src/main/java/com/assimilate/alltrans/curview/GlobalView.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,36 +9,29 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.assimilate.alltrans.common.TextGraphic;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A view which renders a series of custom graphics to be overlayed on top of an associated preview
|
||||
* (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove
|
||||
* them, triggering the appropriate drawing and invalidation within the view.
|
||||
*
|
||||
* <p>Supports scaling and mirroring of the graphics relative the camera's preview properties. The
|
||||
* idea is that detection items are expressed in terms of an image size, but need to be scaled up to
|
||||
* the full view size, and also mirrored in the case of the front-facing camera.
|
||||
*
|
||||
* <p>Associated {@link Graphic} items should use the following methods to convert to view
|
||||
* coordinates for the graphics that are drawn:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@link Graphic#scale(float)} adjusts the size of the supplied value from the image scale to
|
||||
* the view scale.
|
||||
* <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the
|
||||
* coordinate from the image's coordinate system to the view coordinate system.
|
||||
* </ol>
|
||||
*/
|
||||
|
||||
public class GraphicOverlay extends View {
|
||||
private final Object lock = new Object();
|
||||
|
||||
public List<Graphic> getGraphics() {
|
||||
return graphics;
|
||||
}
|
||||
|
||||
public Object getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
private final List<Graphic> graphics = new ArrayList<>();
|
||||
// Matrix for transforming from image coordinates to overlay view coordinates.
|
||||
private final Matrix transformationMatrix = new Matrix();
|
||||
@ -64,11 +57,23 @@ public class GraphicOverlay extends View {
|
||||
*/
|
||||
public abstract static class Graphic {
|
||||
private GraphicOverlay overlay;
|
||||
private boolean textVisible = true; // 添加可见性
|
||||
|
||||
public Graphic(GraphicOverlay overlay) {
|
||||
this.overlay = overlay;
|
||||
}
|
||||
|
||||
|
||||
public boolean isVisible() {
|
||||
return textVisible;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
this.textVisible = visible;
|
||||
overlay.postInvalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw the graphic on the supplied canvas. Drawing should use the following methods to convert
|
||||
* to view coordinates for the graphics that are drawn:
|
||||
@ -84,6 +89,9 @@ public class GraphicOverlay extends View {
|
||||
*/
|
||||
public abstract void draw(Canvas canvas);
|
||||
|
||||
|
||||
public abstract boolean contains(float x, float y);
|
||||
|
||||
protected void drawRect(
|
||||
Canvas canvas, float left, float top, float right, float bottom, Paint paint) {
|
||||
canvas.drawRect(left, top, right, bottom, paint);
|
||||
@ -140,6 +148,7 @@ public class GraphicOverlay extends View {
|
||||
overlay.postInvalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given the {@code zInImagePixel}, update the color for the passed in {@code paint}. The color will be
|
||||
* more red if the {@code zInImagePixel} is smaller, or more blue ish vice versa. This is
|
||||
@ -208,6 +217,7 @@ public class GraphicOverlay extends View {
|
||||
needUpdateTransformation = true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all graphics from the overlay.
|
||||
*/
|
||||
@ -311,21 +321,56 @@ public class GraphicOverlay extends View {
|
||||
}
|
||||
}
|
||||
|
||||
private TextGraphic currentlyVisibleTextGraphic = null;
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
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_CANCEL:
|
||||
|
||||
setVisibility(View.VISIBLE);
|
||||
return true;
|
||||
synchronized (lock) {
|
||||
if (currentlyVisibleTextGraphic != null) {
|
||||
currentlyVisibleTextGraphic.hideTextBlock();
|
||||
currentlyVisibleTextGraphic = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,12 @@
|
||||
package com.assimilate.alltrans.http;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import retrofit2.Call;
|
||||
@ -15,48 +17,37 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
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 CLIENT = "gtx";
|
||||
private final static String DT = "t";
|
||||
|
||||
/**
|
||||
* {
|
||||
* "inputEncode": "UTF-8", 输入编码,有默认值,可不传递
|
||||
* "outputEncode": "UTF-8", 响应编码,有默认值,可不传递
|
||||
* "sourceLanguage": "English", 原文语言,不可为空
|
||||
* "translationLanguage": "Chinese", 译文语言,不可为空
|
||||
* "text": "翻译内容"
|
||||
* }
|
||||
* @param params 输入参数集
|
||||
* @param googleTranslateCallback 响应集合
|
||||
*/
|
||||
@Override
|
||||
public void translate(HashMap<String, String> params, GoogleTranslateCallback googleTranslateCallback) {
|
||||
if (null == googleTranslateCallback) {
|
||||
return;
|
||||
}
|
||||
if (null == params || params.isEmpty()) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
googleTranslateCallback.onResponse(null, "Params are null or empty");
|
||||
return;
|
||||
}
|
||||
|
||||
String text = params.get("text");
|
||||
if (TextUtils.isEmpty(text) || text.trim().isEmpty()) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
googleTranslateCallback.onResponse(null, "Text is empty");
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
text = text.trim();
|
||||
}
|
||||
String sl = params.get("sourceLanguage");
|
||||
if (TextUtils.isEmpty(sl)) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
googleTranslateCallback.onResponse(null, "Source language is empty");
|
||||
return;
|
||||
}
|
||||
String tl = params.get("translationLanguage");
|
||||
if (TextUtils.isEmpty(sl)) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
if (TextUtils.isEmpty(tl)) {
|
||||
googleTranslateCallback.onResponse(null, "Translation language is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -69,32 +60,44 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
|
||||
Retrofit retrofit = retrofitClient.getRetrofitClient();
|
||||
GoogleTranslateApi googleTranslateApi = retrofit.create(GoogleTranslateApi.class);
|
||||
googleTranslateApi.api(
|
||||
URL, CLIENT, DT, ie, oe, sl, tl, text
|
||||
URL, CLIENT, DT, ie, oe, sl, tl, text
|
||||
).enqueue(new Callback<JsonArray>() {
|
||||
@Override
|
||||
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
|
||||
JsonArray jsonArray = response.body();
|
||||
if (null != jsonArray && !jsonArray.isEmpty()) {
|
||||
try {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
JsonArray array = jsonArray.get(0).getAsJsonArray();
|
||||
if (null != array && !array.isEmpty()) {
|
||||
for (JsonElement jsonElement : array) {
|
||||
builder.append(jsonElement.getAsJsonArray().get(0).getAsString());
|
||||
if (response.isSuccessful()) {
|
||||
JsonArray jsonArray = response.body();
|
||||
if (null != jsonArray && !jsonArray.isEmpty()) {
|
||||
try {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
JsonArray array = jsonArray.get(0).getAsJsonArray();
|
||||
if (null != array && !array.isEmpty()) {
|
||||
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) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
} else {
|
||||
googleTranslateCallback.onResponse(null, "Empty response body");
|
||||
}
|
||||
} else {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
googleTranslateCallback.onResponse(null, "Response not successful: " + response.message());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<JsonArray> call, Throwable t) {
|
||||
googleTranslateCallback.onResponse(null);
|
||||
if (t instanceof IOException) {
|
||||
Log.e("GoogleTranslator", "Network error", t);
|
||||
googleTranslateCallback.onFailure("Network error: " + t.getMessage());
|
||||
} else {
|
||||
Log.e("GoogleTranslator", "Conversion error", t);
|
||||
googleTranslateCallback.onFailure("Conversion error: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -102,9 +105,9 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
|
||||
public interface GoogleTranslateApi {
|
||||
@GET
|
||||
Call<JsonArray> api(
|
||||
@Url String url, // google 固定值
|
||||
@Query("client") String client, // google 固定值
|
||||
@Query("dt") String dt, // google 固定值
|
||||
@Url String url,
|
||||
@Query("client") String client,
|
||||
@Query("dt") String dt,
|
||||
@Query("ie") String ie,
|
||||
@Query("oe") String oe,
|
||||
@Query("sl") String sl,
|
||||
@ -113,6 +116,7 @@ public class GoogleTranslator extends Translator<GoogleTranslator.GoogleTranslat
|
||||
}
|
||||
|
||||
public interface GoogleTranslateCallback {
|
||||
void onResponse(String val);
|
||||
void onResponse(String val, String errorMessage);
|
||||
void onFailure(String errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.assimilate.alltrans.common;
|
||||
package com.assimilate.alltrans.keepmodel;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@ -1,4 +1,4 @@
|
||||
package com.assimilate.alltrans.common;
|
||||
package com.assimilate.alltrans.keepmodel;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
@ -7,6 +7,7 @@ import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.assimilate.alltrans.R;
|
||||
import com.assimilate.alltrans.common.Logger;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@ -72,7 +73,15 @@ public class LanguagesConstants {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
// 返回一个默认的Language对象,避免返回null
|
||||
return getDefaultLanguage();
|
||||
}
|
||||
|
||||
// 新增一个方法来获取默认的Language对象
|
||||
private Language getDefaultLanguage() {
|
||||
// 返回一个你希望的默认语言对象
|
||||
return new Language("English", "en","en");
|
||||
}
|
||||
|
||||
// 获取 languageCode
|
||||
@ -1,4 +1,4 @@
|
||||
package com.assimilate.alltrans.common
|
||||
package com.assimilate.alltrans.keepmodel
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
@ -12,7 +12,7 @@ object PreferenceLanguageUtils {
|
||||
private const val KEY_RECENT_LANGUAGES = "recent_languages"
|
||||
private const val MAX_RECENT_LANGUAGES = 5
|
||||
private const val PREF_KEY_FIRST_TIME = "first_time"
|
||||
|
||||
private val LANGUAGE_LIST_TYPE = object : TypeToken<List<Language>>() {}.type
|
||||
|
||||
@Volatile
|
||||
private var sharedPreferences: SharedPreferences? = null
|
||||
@ -31,7 +31,7 @@ object PreferenceLanguageUtils {
|
||||
getSharedPreferences().edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
fun getString(key: String, defValue: String = "english"): String {
|
||||
fun getString(key: String, defValue: String = "English"): String {
|
||||
val value = getSharedPreferences().getString(key, defValue) ?: defValue
|
||||
return value
|
||||
}
|
||||
@ -61,11 +61,11 @@ object PreferenceLanguageUtils {
|
||||
saveRecentLanguages(recentLanguages)
|
||||
}
|
||||
|
||||
|
||||
fun getRecentLanguages(): List<Language> {
|
||||
val json = getSharedPreferences().getString(KEY_RECENT_LANGUAGES, null)
|
||||
return if (json != null) {
|
||||
val type = object : TypeToken<List<Language>>() {}.type
|
||||
Gson().fromJson(json, type)
|
||||
Gson().fromJson(json, LANGUAGE_LIST_TYPE)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
@ -80,6 +80,7 @@ object PreferenceLanguageUtils {
|
||||
fun isFirstTime(): Boolean {
|
||||
return getSharedPreferences().getBoolean(PREF_KEY_FIRST_TIME, true)
|
||||
}
|
||||
|
||||
// 设置已经不是第一次进入应用了
|
||||
fun setNotFirstTime() {
|
||||
getSharedPreferences().edit().putBoolean(PREF_KEY_FIRST_TIME, false).apply()
|
||||
@ -0,0 +1,7 @@
|
||||
package com.assimilate.alltrans.listeners
|
||||
|
||||
import android.graphics.Rect
|
||||
|
||||
interface OnRegionSelectedListener {
|
||||
fun onRegionSelected(rect: Rect)
|
||||
}
|
||||
@ -10,7 +10,6 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
||||
import com.assimilate.alltrans.common.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -30,10 +29,10 @@ public class DbTranslation extends SQLiteOpenHelper {
|
||||
"source_lan TEXT, " +
|
||||
"source_txt TEXT, " +
|
||||
"target_lan TEXT, " +
|
||||
"target_txt TEXT," +
|
||||
"current_time_millis INTEGER," +
|
||||
"exist int DEFAULT 1, " +
|
||||
"collection int DEFAULT 0);"
|
||||
"target_txt TEXT, " +
|
||||
"current_time_millis INTEGER, " +
|
||||
"exist INTEGER DEFAULT 1, " +
|
||||
"collection INTEGER DEFAULT 0);"
|
||||
);
|
||||
}
|
||||
|
||||
@ -113,29 +112,71 @@ public class DbTranslation extends SQLiteOpenHelper {
|
||||
Log.d("SQLite: ", "at removeTranslations: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param collect 收藏|不收藏
|
||||
* 收藏|不收藏
|
||||
*/
|
||||
public boolean collectJust(boolean collect) {
|
||||
final int value = collect ? 1 : 0;
|
||||
try (SQLiteDatabase database = getWritableDatabase();) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("collection", value);
|
||||
String selection = "id = (SELECT MAX(id) FROM " + TABLE +")";
|
||||
int result = database.update(TABLE, values, selection, null);
|
||||
return result > 0;
|
||||
public boolean toggleCollect() {
|
||||
try (SQLiteDatabase database = getWritableDatabase();
|
||||
Cursor cursor = database.rawQuery("SELECT collection FROM " + TABLE + " ORDER BY id DESC LIMIT 1", null)) {
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
int collectionIndex = cursor.getColumnIndex("collection");
|
||||
if (collectionIndex != -1) {
|
||||
int currentValue = cursor.getInt(collectionIndex);
|
||||
int newValue = (currentValue == 1) ? 0 : 1;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("collection", 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) {
|
||||
Log.d("SQLite: ", "at collectionJust: " + exception.getMessage());
|
||||
Log.e("SQLite: ", "at toggleCollect: " + exception.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到翻译记录
|
||||
* @param filterUnCollect 是否需要过滤掉没有收藏的记录
|
||||
* 检查最后一个翻译是否被收藏
|
||||
*/
|
||||
public ArrayList<Translations> getTranslations(boolean filterUnCollect) {
|
||||
public boolean isLastTranslationCollected() {
|
||||
try (SQLiteDatabase database = getWritableDatabase();
|
||||
Cursor cursor = database.rawQuery("SELECT collection FROM " + TABLE + " ORDER BY id DESC LIMIT 1", null)) {
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
int collectionIndex = cursor.getColumnIndex("collection");
|
||||
if (collectionIndex != -1) {
|
||||
int collection = cursor.getInt(collectionIndex);
|
||||
Log.d("SQLite: ", "Last translation collection value: " + collection);
|
||||
return collection == 1;
|
||||
}
|
||||
} else {
|
||||
Log.d("SQLite: ", "No translations found.");
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Log.e("SQLite: ", "at isLastTranslationCollected: " + exception.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到翻译记录
|
||||
* @param filterCollect 是否需要过滤掉没有收藏的记录
|
||||
*/
|
||||
public ArrayList<Translations> getTranslations(boolean filterCollect) {
|
||||
final ArrayList<Translations> logs = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
try (SQLiteDatabase database = getWritableDatabase();) {
|
||||
@ -169,7 +210,7 @@ public class DbTranslation extends SQLiteOpenHelper {
|
||||
|
||||
if (exist == 1) {
|
||||
// 是否需要过滤掉没有收藏的简便写法
|
||||
if (!filterUnCollect || collection == 1) {
|
||||
if (!filterCollect || collection == 1) {
|
||||
Translations translations = new Translations(id, sourceLanguage, sourceText, targetLanguage, targetText, currentTimeMillis, exist, collection);
|
||||
logs.add(translations);
|
||||
}
|
||||
@ -190,4 +231,5 @@ public class DbTranslation extends SQLiteOpenHelper {
|
||||
Collections.reverse(logs);
|
||||
return logs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,6 @@ import com.assimilate.alltrans.common.Widget
|
||||
import com.assimilate.alltrans.databinding.ActivityHistoryBinding
|
||||
import com.assimilate.alltrans.mydb.DbTranslation
|
||||
import com.assimilate.alltrans.mydb.Translations
|
||||
import java.util.Collections
|
||||
import java.util.Locale
|
||||
|
||||
class HistoryActivity : AppCompatActivity() {
|
||||
@ -40,6 +39,7 @@ class HistoryActivity : AppCompatActivity() {
|
||||
ids = HashSet()
|
||||
items = HashSet()
|
||||
|
||||
|
||||
val extra = intent.getStringExtra(COMMAND)
|
||||
operationCollection = COMMAND_COLLECTION == extra
|
||||
|
||||
@ -49,20 +49,45 @@ class HistoryActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
initClick()
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
initSet()
|
||||
}
|
||||
|
||||
private fun initSet() {
|
||||
val translations = ArrayList<Translations>()
|
||||
if (!operationCollection) {
|
||||
// 查出收藏的翻译记录
|
||||
binding!!.tvFuncTrans.text = getString(R.string.favor_title)
|
||||
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
||||
val list = DbTranslation(this).getTranslations(true)
|
||||
if (null != list && !list.isEmpty()) {
|
||||
if (list.isEmpty()) {
|
||||
binding!!.defEmpty.visibility = View.VISIBLE
|
||||
binding!!.remove.visibility = View.GONE
|
||||
binding!!.defEmpty.text = getString(R.string.favor_emtpoy)
|
||||
} else {
|
||||
binding!!.defEmpty.visibility = View.GONE
|
||||
binding!!.remove.visibility = View.GONE
|
||||
}
|
||||
if (null != list && list.isNotEmpty()) {
|
||||
translations.addAll(list)
|
||||
}
|
||||
} else {
|
||||
// 查出所有的翻译记录
|
||||
binding!!.tvFuncTrans.text = getString(R.string.his_title)
|
||||
// binding.ivFuncTrans.setImageResource(R.drawable.ic_add);
|
||||
val list = DbTranslation(this).getTranslations(false)
|
||||
if (list.isEmpty()) {
|
||||
binding!!.remove.visibility = View.GONE
|
||||
binding!!.defEmpty.visibility = View.VISIBLE
|
||||
binding!!.defEmpty.text = getString(R.string.his_emtpoy)
|
||||
} else {
|
||||
binding!!.remove.visibility = View.GONE
|
||||
binding!!.defEmpty.visibility = View.GONE
|
||||
}
|
||||
if (null != list && list.isNotEmpty()) {
|
||||
translations.addAll(list)
|
||||
}
|
||||
@ -91,6 +116,7 @@ class HistoryActivity : AppCompatActivity() {
|
||||
|
||||
private fun add(id: Long, index: Int) {
|
||||
ids!!.add(id)
|
||||
|
||||
items!!.add(index)
|
||||
}
|
||||
|
||||
@ -101,7 +127,7 @@ class HistoryActivity : AppCompatActivity() {
|
||||
|
||||
private fun updateBtn() {
|
||||
if (ids!!.isEmpty()) {
|
||||
binding!!.remove.visibility = View.INVISIBLE
|
||||
binding!!.remove.visibility = View.GONE
|
||||
} else {
|
||||
binding!!.remove.visibility = View.VISIBLE
|
||||
}
|
||||
@ -114,13 +140,13 @@ class HistoryActivity : AppCompatActivity() {
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (ids!!.isEmpty()) {
|
||||
Widget.makeToast(this@HistoryActivity, "Noting to remove.")
|
||||
Widget.makeToast(this@HistoryActivity, "Nothing to remove.")
|
||||
return
|
||||
}
|
||||
|
||||
if (!ids!!.isEmpty()) {
|
||||
if (ids!!.isNotEmpty()) {
|
||||
val longArrayList = ArrayList(ids)
|
||||
if (operationCollection) {
|
||||
if (!operationCollection) {
|
||||
getDbTranslation(this@HistoryActivity).removeCollectTranslations(
|
||||
longArrayList
|
||||
)
|
||||
@ -129,13 +155,13 @@ class HistoryActivity : AppCompatActivity() {
|
||||
}
|
||||
ids!!.clear()
|
||||
}
|
||||
if (!items!!.isEmpty()) {
|
||||
if (items!!.isNotEmpty()) {
|
||||
val integerArrayList = ArrayList(items)
|
||||
Collections.sort(integerArrayList) { o1, o2 -> o2.compareTo(o1) }
|
||||
integerArrayList.sortWith { o1, o2 -> o2.compareTo(o1) }
|
||||
adapter.updateSet(integerArrayList)
|
||||
items!!.clear()
|
||||
}
|
||||
binding!!.remove.visibility = View.VISIBLE
|
||||
binding!!.remove.visibility = View.GONE // 隐藏删除按钮
|
||||
}
|
||||
|
||||
private fun getDbTranslation(context: Context): DbTranslation {
|
||||
@ -148,19 +174,16 @@ class HistoryActivity : AppCompatActivity() {
|
||||
|
||||
binding!!.histories.layoutManager = layoutManager
|
||||
binding!!.histories.adapter = adapter
|
||||
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
fun clickBack(view: View?) {
|
||||
onBackPressed()
|
||||
private fun initClick() {
|
||||
binding?.ivHisBack?.setOnClickListener { onBackPressed() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val COMMAND: String = "remove"
|
||||
const val COMMAND_COLLECTION: String = "remove_collection"
|
||||
const val COMMAND_HISTORY: String = "remove_history"
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,28 @@
|
||||
package com.assimilate.alltrans.viewui
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.assimilate.alltrans.MyApp
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.adapters.LanguageAdapter
|
||||
import com.assimilate.alltrans.common.Language
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
|
||||
import com.assimilate.alltrans.keepmodel.Language
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LanguageChangeActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityLanguageChangeBinding
|
||||
@ -32,31 +42,80 @@ class LanguageChangeActivity : AppCompatActivity() {
|
||||
|
||||
initView()
|
||||
initSearch() // 添加这一行
|
||||
initList()
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// 耗时操作
|
||||
initAllList()
|
||||
}
|
||||
}
|
||||
initClick()
|
||||
}
|
||||
|
||||
private fun initSearch() {
|
||||
binding.chSearch.setOnQueryTextListener(object :
|
||||
androidx.appcompat.widget.SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return false
|
||||
binding.chSearch.apply {
|
||||
// 获取关闭按钮的ImageView
|
||||
val closeButton = findViewById<ImageView>(androidx.appcompat.R.id.search_close_btn)
|
||||
|
||||
// 设置关闭按钮一直可见
|
||||
closeButton.visibility = View.VISIBLE
|
||||
|
||||
// 确保 SearchView 处于可点击状态
|
||||
isFocusable = true
|
||||
isIconified = false
|
||||
|
||||
// 延迟请求焦点以避免弹出软件盘
|
||||
post {
|
||||
clearFocus()
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
(binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText)
|
||||
return true
|
||||
setOnQueryTextListener(object :
|
||||
androidx.appcompat.widget.SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
(binding.listLanguages.adapter as LanguageAdapter).filter.filter(newText)
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// 设置点击关闭按钮的行为
|
||||
closeButton.setOnClickListener {
|
||||
// 清空搜索框
|
||||
setQuery("", false)
|
||||
// 清除焦点
|
||||
clearFocus()
|
||||
|
||||
// 隐藏软件盘
|
||||
val imm =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun initView() {
|
||||
|
||||
val intentT = intent
|
||||
if (intentT.getBooleanExtra("choose_t", false)) {
|
||||
lastTranslateLanguage = true
|
||||
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff1f1724))
|
||||
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff0e8ce8))
|
||||
} else {
|
||||
lastTranslateLanguage = false
|
||||
binding.tvChangeSource.setTextColor(getColor(R.color.main_text_ff0e8ce8))
|
||||
binding.tvChangeTarget.setTextColor(getColor(R.color.main_text_ff1f1724))
|
||||
}
|
||||
|
||||
|
||||
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
||||
binding.tvChangeTarget.text = PreferenceLanguageUtils.getString("language_target")
|
||||
|
||||
updateRecentLanguages()
|
||||
}
|
||||
|
||||
|
||||
private fun updateRecentLanguages() {
|
||||
val recentLanguages = PreferenceLanguageUtils.getRecentLanguages()
|
||||
if (recentLanguages.isNotEmpty()) {
|
||||
@ -67,6 +126,7 @@ class LanguageChangeActivity : AppCompatActivity() {
|
||||
onBackPressed()
|
||||
} else {
|
||||
PreferenceLanguageUtils.putString("language_source", language.language)
|
||||
onBackPressed()
|
||||
}
|
||||
PreferenceLanguageUtils.addRecentLanguage(language)
|
||||
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
||||
@ -107,16 +167,19 @@ class LanguageChangeActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
|
||||
private fun initList() {
|
||||
private fun initAllList() {
|
||||
val languages: ArrayList<Language> = LanguagesConstants.getInstance().getList(this)
|
||||
if (languages.isNotEmpty()) {
|
||||
val adapter = LanguageAdapter(this, languages) { _, language ->
|
||||
Log.d("LanguageChange", language.language)
|
||||
if (lastTranslateLanguage) {
|
||||
PreferenceLanguageUtils.putString("language_target", language.language)
|
||||
FirebaseAnalyticsHelper.languageChooseEvent("target_" + language.language)
|
||||
onBackPressed()
|
||||
} else {
|
||||
PreferenceLanguageUtils.putString("language_source", language.language)
|
||||
FirebaseAnalyticsHelper.languageChooseEvent("source_" + language.language)
|
||||
onBackPressed()
|
||||
}
|
||||
PreferenceLanguageUtils.addRecentLanguage(language)
|
||||
binding.tvChangeSource.text = PreferenceLanguageUtils.getString("language_source")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package com.assimilate.alltrans.viewui
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipDescription
|
||||
import android.content.ClipboardManager
|
||||
@ -10,41 +10,61 @@ import android.media.projection.MediaProjectionManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.speech.RecognizerIntent
|
||||
import android.text.Editable
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.allservice.SusService
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
|
||||
import com.assimilate.alltrans.common.Widget
|
||||
import com.assimilate.alltrans.databinding.ActivityMainBinding
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private var launcher: ActivityResultLauncher<Intent>? = null
|
||||
private var isBackPressedOnce = false
|
||||
private var serverIsStart = false
|
||||
private val backPressHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
private var launcher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == RESULT_OK && result.data != null) {
|
||||
val speech =
|
||||
result.data!!.getStringArrayListExtra("android.speech.extra.RESULTS")?.get(0)
|
||||
|
||||
if (speech != null) {
|
||||
binding.etText.setText(speech)
|
||||
} else {
|
||||
FirebaseAnalyticsHelper.textVoiceResultEvent("speech_is_null")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var lcm: LocalBroadcastManager
|
||||
|
||||
private lateinit var mediaProjectionManager: MediaProjectionManager
|
||||
private val REQUEST_CODE = 1000
|
||||
private val REQUEST_CODE_OVERLAY = 1011
|
||||
private val REQUEST_CODE_MEDIA_PROJECTION = 1012
|
||||
|
||||
private val mpResultCode = "mpResultCode"
|
||||
private val mpData = "mpData"
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
@ -57,42 +77,30 @@ class MainActivity : AppCompatActivity() {
|
||||
insets
|
||||
}
|
||||
initSet()
|
||||
|
||||
initClick()
|
||||
|
||||
registerResult()
|
||||
// 初始化
|
||||
mediaProjectionManager =
|
||||
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
|
||||
// 检查并请求悬浮窗权限
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:$packageName")
|
||||
)
|
||||
startActivityForResult(intent, REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerResult() {
|
||||
launcher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
val data = result.data
|
||||
if (result.resultCode == RESULT_OK && data != null) {
|
||||
val speech =
|
||||
data.getStringArrayListExtra("android.speech.extra.RESULTS")?.get(0)
|
||||
if (!TextUtils.isEmpty(speech)) {
|
||||
binding.etText.setText(speech)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
|
||||
if (requestCode == REQUEST_CODE_OVERLAY) {
|
||||
// 检查悬浮窗权限是否已授予
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
checkAndRequestMediaProjectionPermission()
|
||||
} else {
|
||||
Widget.makeToast(this, "To allow floating windows for quick translation.")
|
||||
}
|
||||
} else if (requestCode == REQUEST_CODE_MEDIA_PROJECTION) {
|
||||
// 处理mediaProjectionManager权限请求结果
|
||||
if (resultCode == RESULT_OK) {
|
||||
serverIsStart = true
|
||||
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick)
|
||||
FirebaseAnalyticsHelper.hoverButtonClickEvent()
|
||||
|
||||
// 权限授予,启动截图
|
||||
val serviceIntent = Intent(this, SusService::class.java).apply {
|
||||
putExtra(mpResultCode, resultCode)
|
||||
putExtra(mpData, data)
|
||||
@ -103,7 +111,10 @@ class MainActivity : AppCompatActivity() {
|
||||
startService(serviceIntent)
|
||||
}
|
||||
} else {
|
||||
Log.e("MainActivity", "Screen capture permission denied")
|
||||
Widget.makeToast(
|
||||
this,
|
||||
"Please allow access to screen information to identify the content that needs translation."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,8 +125,117 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun initSet() {
|
||||
lcm = LocalBroadcastManager.getInstance(this)
|
||||
|
||||
FirebaseAnalyticsHelper.homePvEvent("create")
|
||||
|
||||
val isRunning = isServiceRunning(this, SusService::class.java)
|
||||
if (isRunning) {
|
||||
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick)
|
||||
|
||||
} else {
|
||||
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick_def)
|
||||
Widget.startScaleAnimation(binding.ivQuickStart)
|
||||
|
||||
}
|
||||
|
||||
backPressedCall()
|
||||
// 初始化截屏
|
||||
mediaProjectionManager =
|
||||
getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun initClick() {
|
||||
binding.tvMainDic.setOnClickListener {
|
||||
// startActivity(Intent(this,DicActivity::class.java))
|
||||
}
|
||||
binding.tvMainPhotoTrans.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, PhotoImageActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.tvMainVoice.setOnClickListener {
|
||||
voiceToText()
|
||||
toTextTransResult()
|
||||
FirebaseAnalyticsHelper.textVoiceClickEvent()
|
||||
}
|
||||
binding.tvMainPaste.setOnClickListener {
|
||||
pasteFromClipboard()
|
||||
FirebaseAnalyticsHelper.textTransPasteEvent()
|
||||
}
|
||||
binding.tvMainTrans.setOnClickListener {
|
||||
toTextTransResult()
|
||||
FirebaseAnalyticsHelper.textTransClickEvent()
|
||||
}
|
||||
binding.ivMainSetting.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, SettingsActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.chSourceLanguage.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, LanguageChangeActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.chTargetLanguage.setOnClickListener {
|
||||
val intentT = Intent(this, LanguageChangeActivity::class.java)
|
||||
intentT.putExtra("choose_t", true)
|
||||
startActivity(intentT)
|
||||
}
|
||||
binding.ivMainHistory.setOnClickListener {
|
||||
intent = Intent(this, HistoryActivity::class.java)
|
||||
intent.putExtra("remove", "remove_collection")
|
||||
startActivity(
|
||||
intent
|
||||
)
|
||||
}
|
||||
binding.llQuickSet.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, QuickSetActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.ivQuickStart.setOnClickListener {
|
||||
if (!serverIsStart) {
|
||||
// 检查并请求悬浮窗权限
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:$packageName")
|
||||
)
|
||||
startActivityForResult(intent, REQUEST_CODE_OVERLAY)
|
||||
} else {
|
||||
checkAndRequestMediaProjectionPermission()
|
||||
}
|
||||
} else {
|
||||
stopService(Intent(this, SusService::class.java))
|
||||
|
||||
serverIsStart = false
|
||||
binding.ivQuickStart.setImageResource(R.drawable.main_setting_quick_def)
|
||||
FirebaseAnalyticsHelper.hoverButtonCancelEvent()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
binding.ivMainExChange.setOnClickListener {
|
||||
// 读取当前的源语言和目标语言
|
||||
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
|
||||
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
|
||||
|
||||
// 交换源语言和目标语言
|
||||
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
|
||||
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
|
||||
|
||||
// 更新界面显示
|
||||
binding.chSourceLanguage.text = currentTargetLanguage
|
||||
binding.chTargetLanguage.text = currentSourceLanguage
|
||||
|
||||
}
|
||||
|
||||
binding.etText.setOnClickListener {
|
||||
FirebaseAnalyticsHelper.textInputEvent()
|
||||
}
|
||||
// 监听EditText的文本变化
|
||||
binding.etText.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||
@ -147,79 +267,16 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun initClick() {
|
||||
binding.tvMainPhotoTrans.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, StillImageActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.tvMainVoice.setOnClickListener {
|
||||
voiceToText()
|
||||
toTextTransResult()
|
||||
}
|
||||
binding.tvMainPaste.setOnClickListener {
|
||||
pasteFromClipboard(binding.etText)
|
||||
toTextTransResult()
|
||||
|
||||
}
|
||||
binding.tvMainTrans.setOnClickListener {
|
||||
toTextTransResult()
|
||||
}
|
||||
binding.ivMainSetting.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, SettingsActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.chSourceLanguage.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, LanguageChangeActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.chTargetLanguage.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, LanguageChangeActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.ivMainHistory.setOnClickListener {
|
||||
intent = Intent(this, HistoryActivity::class.java)
|
||||
intent.putExtra("remove", "remove_collection")
|
||||
startActivity(
|
||||
intent
|
||||
)
|
||||
}
|
||||
binding.llQuickSet.setOnClickListener {
|
||||
startActivity(
|
||||
Intent(this, QuickSetActivity::class.java)
|
||||
)
|
||||
}
|
||||
binding.ivQuickStart.setOnClickListener {
|
||||
// 启动截图
|
||||
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 checkAndRequestMediaProjectionPermission() {
|
||||
// 启动截图
|
||||
startActivityForResult(
|
||||
mediaProjectionManager.createScreenCaptureIntent(),
|
||||
REQUEST_CODE_MEDIA_PROJECTION
|
||||
)
|
||||
}
|
||||
|
||||
private fun toTextTransResult() {
|
||||
@ -262,28 +319,51 @@ class MainActivity : AppCompatActivity() {
|
||||
//
|
||||
// )
|
||||
try {
|
||||
launcher?.launch(speechIntent)
|
||||
launcher.launch(speechIntent)
|
||||
} catch (ea: ActivityNotFoundException) {
|
||||
FirebaseAnalyticsHelper.textVoiceResultEvent("device_not_support")
|
||||
Widget.makeToast(this, getString(R.string.main_voice_to_text))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
|
||||
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val services = activityManager.getRunningServices(Integer.MAX_VALUE)
|
||||
|
||||
for (service in services) {
|
||||
if (serviceClass.name == service.service.className) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 粘贴文本
|
||||
private fun pasteFromClipboard(etText: EditText) {
|
||||
private fun pasteFromClipboard() {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
if (clipboard.hasPrimaryClip() && clipboard.primaryClipDescription!!.hasMimeType(
|
||||
ClipDescription.MIMETYPE_TEXT_PLAIN
|
||||
)
|
||||
) {
|
||||
val item = clipboard.primaryClip!!.getItemAt(0)
|
||||
val text = item.text.toString()
|
||||
if (text.isNotEmpty()) {
|
||||
// 在EditText中显示粘贴的文本
|
||||
etText.setText(text)
|
||||
if (clipboard.hasPrimaryClip()) {
|
||||
val clipData = clipboard.primaryClip
|
||||
if (clipData != null && clipData.itemCount > 0) {
|
||||
val item = clipData.getItemAt(0)
|
||||
val pasteData = item.coerceToText(this).toString()
|
||||
if (pasteData.isNotEmpty()) {
|
||||
binding.etText.setText(pasteData)
|
||||
binding.etText.requestFocus() // 获取焦点
|
||||
toTextTransResult()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this,
|
||||
resources.getString(R.string.main_paste_empty),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
etText.requestFocus() // 获取焦点
|
||||
}
|
||||
} else {
|
||||
Log.e("PasteFromClipboard", "No items in clipboard.")
|
||||
}
|
||||
} else {
|
||||
Log.e("PasteFromClipboard", "Clipboard has no primary clip.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,12 +372,36 @@ class MainActivity : AppCompatActivity() {
|
||||
initView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (null != launcher) {
|
||||
launcher!!.unregister()
|
||||
launcher = null
|
||||
}
|
||||
override fun onRestart() {
|
||||
super.onRestart()
|
||||
FirebaseAnalyticsHelper.homePvEvent("cap")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -3,27 +3,18 @@ package com.assimilate.alltrans.viewui
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.util.Pair
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageView
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.SearchView
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -42,17 +33,16 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.adapters.LanguageAdapter
|
||||
import com.assimilate.alltrans.common.BitmapUtils
|
||||
import com.assimilate.alltrans.common.Language
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.keepmodel.Language
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.common.TextRecognitionProcessor
|
||||
import com.assimilate.alltrans.common.VisionImageProcessor
|
||||
import com.assimilate.alltrans.common.Widget
|
||||
import com.assimilate.alltrans.curview.GraphicOverlay
|
||||
import com.assimilate.alltrans.databinding.ActivityStillImageBinding
|
||||
import com.google.android.gms.common.annotation.KeepName
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.google.mlkit.common.model.LocalModel
|
||||
import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions
|
||||
import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions
|
||||
import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions
|
||||
@ -61,16 +51,13 @@ import com.google.mlkit.vision.text.latin.TextRecognizerOptions
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.ArrayList
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/** Activity demonstrating different image detector features with a still image from camera. */
|
||||
@KeepName
|
||||
class StillImageActivity : AppCompatActivity() {
|
||||
private var preview: ImageView? = null
|
||||
private var graphicOverlay: GraphicOverlay? = null
|
||||
class PhotoImageActivity : AppCompatActivity() {
|
||||
|
||||
private var selectedMode = TEXT_RECOGNITION_CHINESE
|
||||
private var selectedSize: String? = SIZE_SCREEN
|
||||
private var isLandScape = false
|
||||
@ -85,23 +72,22 @@ class StillImageActivity : AppCompatActivity() {
|
||||
|
||||
private val REQUEST_CAMERA_PERMISSION = 100
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_still_image)
|
||||
binding = ActivityStillImageBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom)
|
||||
v.setPadding(0, systemBars.top + 26, 0, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
Widget.makeSnackbar(this, "Photographing text for translation")
|
||||
|
||||
preview = findViewById(R.id.preview)
|
||||
graphicOverlay = findViewById(R.id.graphic_overlay)
|
||||
|
||||
isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
savedInstanceState?.let {
|
||||
imageUri = it.getParcelable(KEY_IMAGE_URI)
|
||||
@ -146,29 +132,63 @@ class StillImageActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
findViewById<TextView>(R.id.still_source_language).text =
|
||||
|
||||
binding.photoPreview.visibility = View.VISIBLE
|
||||
|
||||
binding.stillSourceLanguage.text =
|
||||
PreferenceLanguageUtils.getString("language_source")
|
||||
findViewById<TextView>(R.id.still_target_language).text =
|
||||
binding.stillTargetLanguage.text =
|
||||
PreferenceLanguageUtils.getString("language_target")
|
||||
|
||||
bottomSheetDialog = BottomSheetDialog(this)
|
||||
bottomSheetDialog = BottomSheetDialog(this, R.style.CustomBottomSheetDialogTheme)
|
||||
bottomSheetDialog.setContentView(R.layout.bottomsheet_still_lan)
|
||||
bottomSheetDialog.dismissWithAnimation = true
|
||||
|
||||
bottomSheetDialog.findViewById<androidx.appcompat.widget.SearchView>(R.id.ph_search)
|
||||
?.setOnQueryTextListener(object :
|
||||
androidx.appcompat.widget.SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return false
|
||||
?.let { searchView ->
|
||||
// 设置关闭按钮一直显示
|
||||
searchView.isIconified = false
|
||||
searchView.clearFocus()
|
||||
|
||||
// 设置点击事件
|
||||
searchView.setOnClickListener {
|
||||
// 清空搜索框
|
||||
searchView.setQuery("", false)
|
||||
// 清除焦点
|
||||
searchView.clearFocus()
|
||||
|
||||
// 隐藏软件盘
|
||||
val imm =
|
||||
this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(searchView.windowToken, 0)
|
||||
|
||||
// 保持关闭按钮显示
|
||||
searchView.isIconified = false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
(bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.adapter as LanguageAdapter).filter.filter(
|
||||
newText
|
||||
)
|
||||
// 设置查询文本监听器
|
||||
searchView.setOnQueryTextListener(object :
|
||||
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()
|
||||
}
|
||||
@ -178,16 +198,22 @@ class StillImageActivity : AppCompatActivity() {
|
||||
if (languages.isNotEmpty()) {
|
||||
val adapter = LanguageAdapter(this, languages) { _, language ->
|
||||
if (!chooseLanguage) {
|
||||
|
||||
PreferenceLanguageUtils.putString("language_source", language.language)
|
||||
selectedMode = PreferenceLanguageUtils.getString("language_source")
|
||||
tryReloadAndDetectInImage()
|
||||
bottomSheetDialog.dismiss()
|
||||
} else {
|
||||
|
||||
PreferenceLanguageUtils.putString("language_target", language.language)
|
||||
tryReloadAndDetectInImage()
|
||||
bottomSheetDialog.dismiss()
|
||||
|
||||
}
|
||||
|
||||
findViewById<TextView>(R.id.still_source_language).text =
|
||||
binding.stillSourceLanguage.text =
|
||||
PreferenceLanguageUtils.getString("language_source")
|
||||
findViewById<TextView>(R.id.still_target_language).text =
|
||||
binding.stillTargetLanguage.text =
|
||||
PreferenceLanguageUtils.getString("language_target")
|
||||
}
|
||||
bottomSheetDialog.findViewById<RecyclerView>(R.id.list_languages)?.layoutManager =
|
||||
@ -197,25 +223,40 @@ class StillImageActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun initClick() {
|
||||
findViewById<ImageView>(R.id.iv_still_pic).setOnClickListener { startChooseImageIntentForResult() }
|
||||
findViewById<ImageView>(R.id.iv_still_take).setOnClickListener { takePhoto() }
|
||||
findViewById<ImageView>(R.id.iv_still_back).setOnClickListener { onBackPressed() }
|
||||
findViewById<ImageView>(R.id.iv_still_buling).setOnClickListener {
|
||||
binding.ivStillPic.setOnClickListener {
|
||||
binding.photoPreview.visibility = View.INVISIBLE
|
||||
startChooseImageIntentForResult()
|
||||
}
|
||||
binding.ivStillTake.setOnClickListener {
|
||||
binding.photoPreview.visibility = View.VISIBLE
|
||||
|
||||
takePhoto()
|
||||
}
|
||||
binding.ivStillBack.setOnClickListener { onBackPressed() }
|
||||
binding.ivStillBuling.setOnClickListener {
|
||||
toggleFlash()
|
||||
updateFlashButtonUI()
|
||||
}
|
||||
findViewById<TextView>(R.id.still_source_language).setOnClickListener {
|
||||
binding.stillSourceLanguage.setOnClickListener {
|
||||
chooseLanguage = false
|
||||
|
||||
bottomSheetDialog.show()
|
||||
}
|
||||
findViewById<TextView>(R.id.still_target_language).setOnClickListener {
|
||||
binding.stillTargetLanguage.setOnClickListener {
|
||||
chooseLanguage = true
|
||||
bottomSheetDialog.show()
|
||||
}
|
||||
findViewById<ImageView>(R.id.still_exChange).setOnClickListener {
|
||||
binding.stillExChange.setOnClickListener {
|
||||
|
||||
val currentSourceLanguage = PreferenceLanguageUtils.getString("language_source")
|
||||
val currentTargetLanguage = PreferenceLanguageUtils.getString("language_target")
|
||||
// 交换源语言和目标语言
|
||||
PreferenceLanguageUtils.putString("language_source", currentTargetLanguage)
|
||||
PreferenceLanguageUtils.putString("language_target", currentSourceLanguage)
|
||||
// 更新界面显示
|
||||
binding.stillSourceLanguage.text = currentTargetLanguage
|
||||
binding.stillTargetLanguage.text = currentSourceLanguage
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +267,7 @@ class StillImageActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateFlashButtonUI() {
|
||||
findViewById<ImageView>(R.id.iv_still_buling).setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
|
||||
binding.ivStillBuling.setImageResource(if (isFlashOn) R.drawable.ic_still_bulibuli else R.drawable.ic_still_notbuli)
|
||||
}
|
||||
|
||||
private fun startCamera() {
|
||||
@ -340,27 +381,27 @@ class StillImageActivity : AppCompatActivity() {
|
||||
imageProcessor = when (selectedMode) {
|
||||
TEXT_RECOGNITION_CHINESE -> TextRecognitionProcessor(
|
||||
this,
|
||||
ChineseTextRecognizerOptions.Builder().build()
|
||||
ChineseTextRecognizerOptions.Builder().build(), true, true
|
||||
)
|
||||
|
||||
"Hindi", "Marathi", "Nepali", "Sanskrit" -> TextRecognitionProcessor(
|
||||
this,
|
||||
DevanagariTextRecognizerOptions.Builder().build()
|
||||
DevanagariTextRecognizerOptions.Builder().build(), true, true
|
||||
)
|
||||
|
||||
TEXT_RECOGNITION_JAPANESE -> TextRecognitionProcessor(
|
||||
this,
|
||||
JapaneseTextRecognizerOptions.Builder().build()
|
||||
JapaneseTextRecognizerOptions.Builder().build(), true, true
|
||||
)
|
||||
|
||||
TEXT_RECOGNITION_KOREAN -> TextRecognitionProcessor(
|
||||
this,
|
||||
KoreanTextRecognizerOptions.Builder().build()
|
||||
KoreanTextRecognizerOptions.Builder().build(), true, true
|
||||
)
|
||||
|
||||
else -> TextRecognitionProcessor(
|
||||
this,
|
||||
TextRecognizerOptions.Builder().build()
|
||||
TextRecognizerOptions.Builder().build(), true, true
|
||||
)
|
||||
|
||||
}
|
||||
@ -394,10 +435,10 @@ class StillImageActivity : AppCompatActivity() {
|
||||
(imageBitmap.height / scaleFactor).toInt(),
|
||||
true
|
||||
)
|
||||
preview?.setImageBitmap(resizedBitmap)
|
||||
binding.preview.setImageBitmap(resizedBitmap)
|
||||
processImage(resizedBitmap)
|
||||
} else {
|
||||
preview?.setImageBitmap(imageBitmap)
|
||||
binding.preview.setImageBitmap(imageBitmap)
|
||||
processImage(imageBitmap)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
@ -420,8 +461,8 @@ class StillImageActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun processImage(bitmap: Bitmap) {
|
||||
graphicOverlay?.clear()
|
||||
imageProcessor?.processBitmap(bitmap, graphicOverlay)
|
||||
binding.graphicOverlay.clear()
|
||||
imageProcessor?.processBitmap(bitmap, binding.graphicOverlay)
|
||||
}
|
||||
|
||||
private fun startChooseImageIntentForResult() {
|
||||
@ -1,12 +1,15 @@
|
||||
package com.assimilate.alltrans.viewui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.common.Widget
|
||||
import com.assimilate.alltrans.databinding.ActivitySettingsBinding
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
|
||||
@ -18,10 +21,10 @@ class SettingsActivity
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
enableEdgeToEdge()
|
||||
setContentView(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
@ -37,10 +40,15 @@ class SettingsActivity
|
||||
bottomSheetDialog = BottomSheetDialog(this)
|
||||
bottomSheetDialog.setContentView(R.layout.bottomsheet_rate)
|
||||
bottomSheetDialog.dismissWithAnimation = true
|
||||
bottomSheetDialog.findViewById<TextView>(R.id.tv_rate_commit)?.setOnClickListener {
|
||||
Widget.makeSnackbar(this, getString(R.string.settings_rate_toast))
|
||||
bottomSheetDialog.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initClick() {
|
||||
binding.ivSetBack.setOnClickListener { onBackPressed() }
|
||||
binding.llRate.setOnClickListener {
|
||||
bottomSheetDialog.show()
|
||||
|
||||
@ -51,5 +59,13 @@ class SettingsActivity
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
binding.llPrivacyPolicy.setOnClickListener {
|
||||
val url = "https://translark-privacy.mystrikingly.com"
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse(url)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.assimilate.alltrans.viewui
|
||||
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
@ -8,15 +9,17 @@ import android.os.Bundle
|
||||
import android.speech.tts.TextToSpeech
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.assimilate.alltrans.MyApp
|
||||
import com.assimilate.alltrans.R
|
||||
import com.assimilate.alltrans.common.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.FirebaseAnalyticsHelper
|
||||
import com.assimilate.alltrans.keepmodel.LanguagesConstants
|
||||
import com.assimilate.alltrans.common.Logger
|
||||
import com.assimilate.alltrans.common.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.keepmodel.PreferenceLanguageUtils
|
||||
import com.assimilate.alltrans.common.Widget
|
||||
import com.assimilate.alltrans.databinding.ActivityTextResultBinding
|
||||
import com.assimilate.alltrans.http.GoogleTranslator
|
||||
@ -25,6 +28,7 @@ import com.assimilate.alltrans.mydb.DbTranslation
|
||||
import com.assimilate.alltrans.mydb.Translations
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
class TextResultActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityTextResultBinding
|
||||
@ -41,7 +45,6 @@ class TextResultActivity : AppCompatActivity() {
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
initSet()
|
||||
initClick()
|
||||
}
|
||||
@ -59,6 +62,7 @@ class TextResultActivity : AppCompatActivity() {
|
||||
Locale.getDefault()
|
||||
)
|
||||
}
|
||||
// checkLastTranslationCollectedAndSetIcon()
|
||||
|
||||
}
|
||||
|
||||
@ -66,9 +70,12 @@ class TextResultActivity : AppCompatActivity() {
|
||||
binding.ivTrBack.setOnClickListener { onBackPressed() }
|
||||
binding.tvTrNewTrans.setOnClickListener { onBackPressed() }
|
||||
binding.ivReToPhoto.setOnClickListener {
|
||||
startActivity(Intent(this, StillImageActivity::class.java))
|
||||
startActivity(Intent(this, PhotoImageActivity::class.java))
|
||||
}
|
||||
binding.ivTrCopy.setOnClickListener {
|
||||
copyToClipboard()
|
||||
FirebaseAnalyticsHelper.textTransCopyEvent()
|
||||
}
|
||||
binding.ivTrCopy.setOnClickListener { copyToClipboard() }
|
||||
binding.ivSourceClear.setOnClickListener { onBackPressed() }
|
||||
binding.ivSourceTts.setOnClickListener {
|
||||
readText(
|
||||
@ -83,12 +90,45 @@ class TextResultActivity : AppCompatActivity() {
|
||||
PreferenceLanguageUtils.getString("language_target"),
|
||||
this
|
||||
)
|
||||
FirebaseAnalyticsHelper.textTransPlayEvent()
|
||||
}
|
||||
binding.ivTrTargetShare.setOnClickListener {
|
||||
shareText(binding.tvTrTarget.text.toString())
|
||||
FirebaseAnalyticsHelper.textTransShareEvent()
|
||||
}
|
||||
binding.ivTrCollect.setOnClickListener {
|
||||
val dbTranslation = DbTranslation(this)
|
||||
val isCollected = dbTranslation.toggleCollect()
|
||||
updateCollectIcon(isCollected)
|
||||
if (isCollected) {
|
||||
Toast.makeText(this, "Collection successful", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Uncollect", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
FirebaseAnalyticsHelper.textTransLikeEvent()
|
||||
}
|
||||
binding.ivTrTargetShare.setOnClickListener { shareText(binding.tvTrTarget.text.toString()) }
|
||||
binding.ivTrCollect.setOnClickListener { addCollect() }
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 更新图标状态的方法
|
||||
private fun updateCollectIcon(isCollected: Boolean) {
|
||||
if (isCollected) {
|
||||
binding.ivTrCollect.setImageResource(R.drawable.ic_like_yes) // 已收藏的图标
|
||||
} else {
|
||||
binding.ivTrCollect.setImageResource(R.drawable.ic_like_def) // 未收藏的图标
|
||||
}
|
||||
}
|
||||
|
||||
// 检查最后一个翻译是否被收藏并更新图标
|
||||
private fun checkLastTranslationCollectedAndSetIcon() {
|
||||
val dbTranslation = DbTranslation(this)
|
||||
val lastTranslationCollected = dbTranslation.isLastTranslationCollected
|
||||
updateCollectIcon(lastTranslationCollected)
|
||||
}
|
||||
|
||||
|
||||
private fun translate(text: String) {
|
||||
if (text.isEmpty() || translating) {
|
||||
Logger.d("log", "translating(not post data)...")
|
||||
@ -114,19 +154,46 @@ class TextResultActivity : AppCompatActivity() {
|
||||
|
||||
binding.tvTrTarget.text = "translating..."
|
||||
val translator: Translator<GoogleTranslator.GoogleTranslateCallback> = GoogleTranslator()
|
||||
translator.translate(param,
|
||||
GoogleTranslator.GoogleTranslateCallback { result ->
|
||||
translator.translate(param, object : GoogleTranslator.GoogleTranslateCallback {
|
||||
override fun onResponse(result: String?, errorMessage: String?) {
|
||||
translating = false
|
||||
if (!TextUtils.isEmpty(result)) {
|
||||
runOnUiThread {
|
||||
runOnUiThread {
|
||||
if (!TextUtils.isEmpty(result)) {
|
||||
binding.tvTrTarget.text = result
|
||||
addHistory(result)
|
||||
} else {
|
||||
// 处理错误信息
|
||||
if (!TextUtils.isEmpty(errorMessage)) {
|
||||
// 显示错误信息或记录日志
|
||||
if (errorMessage != null) {
|
||||
Log.e("TranslationError", errorMessage)
|
||||
}
|
||||
}
|
||||
binding.tvTrTarget.text = "Translation failed: $errorMessage"
|
||||
}
|
||||
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 translations = Translations(
|
||||
PreferenceLanguageUtils.getString("language_source"),
|
||||
@ -138,13 +205,6 @@ class TextResultActivity : AppCompatActivity() {
|
||||
dbTranslation.addTranslation(translations)
|
||||
}
|
||||
|
||||
private fun addCollect() {
|
||||
if (translating) return
|
||||
val dbTranslation = DbTranslation(this)
|
||||
dbTranslation.collectJust(true)
|
||||
|
||||
binding.ivTrCollect.setImageResource(R.drawable.ic_like_yes)
|
||||
}
|
||||
|
||||
private fun shareText(text: String) {
|
||||
val share: String = text.trim()
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
BIN
app/src/main/res/drawable-xxxhdpi/ic_close.webp
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_dic.webp
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_sus_little_ball.webp
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_sus_little_ball_tie.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_sus_little_search.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
@ -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" />
|
||||
BIN
app/src/main/res/drawable-xxxhdpi/ic_tr_close.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_wel_icon.webp
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/main_setting_quick_def.webp
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/sus_trans_changelan.webp
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
6
app/src/main/res/drawable/button_r20_99000000_bg.xml
Normal 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>
|
||||
5
app/src/main/res/drawable/button_r4_huid7d7d7_bg.xml
Normal 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>
|
||||
18
app/src/main/res/drawable/gradient_slider_track.xml
Normal 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>
|
||||
11
app/src/main/res/drawable/ic_wel_bg.xml
Normal 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>
|
||||
|
||||
10
app/src/main/res/drawable/more_options_icon.xml
Normal 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>
|
||||
25
app/src/main/res/layout/activity_dic.xml
Normal 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>
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_his_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="clickBack"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_back"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -45,6 +45,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="13dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/remove"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tv_func_trans" />
|
||||
@ -60,8 +61,21 @@
|
||||
android:text="@string/his_delete"
|
||||
android:textColor="#FFFF5F5F"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/def_empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/histories"
|
||||
app:layout_constraintEnd_toEndOf="@id/histories"
|
||||
app:layout_constraintStart_toStartOf="@id/histories"
|
||||
app:layout_constraintTop_toTopOf="@id/histories" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -53,7 +53,9 @@
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textColor="@color/main_text_ff0e8ce8"
|
||||
@ -82,75 +84,80 @@
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:drawableEndCompat="@drawable/ic_down_choose" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/change_language">
|
||||
|
||||
<androidx.appcompat.widget.SearchView
|
||||
android:id="@+id/ch_search"
|
||||
<LinearLayout
|
||||
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" />
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<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.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" />
|
||||
|
||||
<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"
|
||||
android:minHeight="210dp" />
|
||||
<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" />
|
||||
|
||||
<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_lan_common5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/button_r10_gray_bg" />
|
||||
|
||||
<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" />
|
||||
<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>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -12,16 +12,6 @@
|
||||
tools:context=".viewui.MainActivity">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_aaaaa"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_main_history"
|
||||
android:layout_width="wrap_content"
|
||||
@ -73,7 +63,9 @@
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/text_source_language"
|
||||
@ -103,7 +95,9 @@
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/text_target_language"
|
||||
@ -163,14 +157,14 @@
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:background="@drawable/button_r10_gray_bg"
|
||||
android:drawableStart="@drawable/main_paste"
|
||||
android:drawablePadding="4dp"
|
||||
android:gravity="center"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="4dp"
|
||||
android:text="@string/main_paste_text"
|
||||
app:drawableStartCompat="@drawable/main_paste" />
|
||||
android:text="@string/main_paste_text" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/tv_main_voice"
|
||||
@ -246,6 +240,7 @@
|
||||
android:text="@string/main_quick_set"
|
||||
android:textColor="@color/main_text_ff808080"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"
|
||||
app:drawableEndCompat="@drawable/ic_arrow_right" />
|
||||
|
||||
|
||||
@ -256,8 +251,9 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="19dp"
|
||||
android:src="@drawable/main_setting_quick" />
|
||||
android:paddingTop="11dp"
|
||||
android:paddingBottom="11dp"
|
||||
android:src="@drawable/main_setting_quick_def" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
@ -273,7 +269,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginEnd="8dp"
|
||||
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:drawablePadding="12dp"
|
||||
@ -288,6 +284,7 @@
|
||||
app:drawableTopCompat="@drawable/main_photo" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_main_dic"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
@ -302,6 +299,7 @@
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:drawableTopCompat="@drawable/main_dic" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@ -1,79 +1,353 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/bg_fff6f6f6"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_layout"
|
||||
<RelativeLayout
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:liftOnScroll="true">
|
||||
android:background="@color/white">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
<ImageView
|
||||
android:id="@+id/qs_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:title="Settings" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
android:layout_alignParentStart="true"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="悬浮球设置" />
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/quick_sus_xf_set" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/slider_1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="5"
|
||||
android:stepSize="1" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/quick_set_touming" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/quick_set_touming_descri" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/main_text_fff4f4f4" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/quick_set_zd_time" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/quick_set_zd_time" />
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableEnd="@drawable/ic_next"
|
||||
android:text="@string/quick_set_zd_time_num" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/main_text_fff4f4f4" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/quick_set_gd_xfq" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/quick_set_gd_description" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="20dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/main_text_fff4f4f4" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/quick_set_reset" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/quick_set_reset_description" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/quick_set_qj_title" />
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qj_tv1"
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/quick_set_qj_tmbg" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/qj_tv1"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/quick_set_qj_tmbg_description" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="20dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qj_tv2"
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/quick_set_qj_color_t" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/qj_tv2"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="80dp"
|
||||
android:text="@string/quick_set_qj_color" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="20dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/qj_tv3"
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/quick_set_qj_kbgb_t" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/qj_tv3"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="80dp"
|
||||
android:text="@string/quick_set_qj_kbgb" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="20dp" />
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/quick_set_kj_t" />
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp">
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/quick_set_kj_long" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawableEnd="@drawable/ic_next"
|
||||
android:text="@string/quick_set_kj_long_to" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/main_text_fff4f4f4" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleMain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/quick_set_kj_double" />
|
||||
|
||||
<TextView
|
||||
style="@style/SusTextStyleSecond"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:drawableEnd="@drawable/ic_next"
|
||||
android:text="@string/quick_set_kj_long_to" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@ -1,17 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#FFF9F9F9"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
tools:context=".viewui.SettingsActivity">
|
||||
android:padding="16dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_set_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="@string/settings"
|
||||
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:orientation="vertical">
|
||||
<!-- 语言设置 -->
|
||||
@ -42,11 +64,10 @@
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="English"
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="14sp"
|
||||
app:drawableEndCompat="@drawable/ic_next" />
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
@ -76,6 +97,7 @@
|
||||
android:text="@string/favorite"
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="16sp"
|
||||
app:drawableEndCompat="@drawable/ic_next"
|
||||
app:drawableStartCompat="@drawable/ic_favorite" />
|
||||
|
||||
</LinearLayout>
|
||||
@ -86,7 +108,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_rate"
|
||||
@ -228,10 +251,9 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="12dp"
|
||||
android:text="1.0.1"
|
||||
android:text="1.0.2"
|
||||
android:textColor="@color/main_text_ff1f1724"
|
||||
android:textSize="14sp"
|
||||
app:drawableEndCompat="@drawable/ic_next" />
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/bg_53514c"
|
||||
android:background="#000"
|
||||
android:keepScreenOn="true">
|
||||
|
||||
<androidx.camera.view.PreviewView
|
||||
@ -12,10 +12,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/control"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
app:layout_constraintBottom_toBottomOf="@id/preview"
|
||||
app:layout_constraintEnd_toEndOf="@id/preview"
|
||||
app:layout_constraintStart_toStartOf="@id/preview"
|
||||
app:layout_constraintTop_toTopOf="@id/preview" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/preview"
|
||||
@ -89,11 +89,6 @@
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/feature_selector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@ -117,8 +112,9 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_99000000_bg"
|
||||
android:background="@drawable/button_r20_black_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
@ -134,7 +130,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:background="@drawable/button_r20_99000000_bg"
|
||||
android:background="@drawable/button_r20_white_bg"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
@ -146,8 +142,9 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/button_r20_99000000_bg"
|
||||
android:background="@drawable/button_r20_black_bg"
|
||||
android:drawablePadding="25dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="16dp"
|
||||
@ -161,7 +158,7 @@
|
||||
android:id="@+id/iv_still_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:padding="26dp"
|
||||
android:src="@drawable/ic_back_white"
|
||||
app:layout_constraintStart_toStartOf="@id/root"
|
||||
app:layout_constraintTop_toTopOf="@id/root" />
|
||||
|
||||
32
app/src/main/res/layout/activity_wel.xml
Normal 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>
|
||||
50
app/src/main/res/layout/bottomsheet_copy.xml
Normal 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>
|
||||
@ -30,6 +30,7 @@
|
||||
android:layout_marginTop="32dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_rate_commit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
|
||||
29
app/src/main/res/layout/bottomsheet_still_lan.xml
Normal 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>
|
||||
12
app/src/main/res/layout/floating_window.xml
Normal 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>
|
||||
129
app/src/main/res/layout/layout_sus_changelan.xml
Normal 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>
|
||||
85
app/src/main/res/layout/layout_sus_copy.xml
Normal 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>
|
||||
158
app/src/main/res/layout/layout_sus_district.xml
Normal 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>
|
||||
@ -4,14 +4,6 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sus_preview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="top"
|
||||
android:adjustViewBounds="true" />
|
||||
|
||||
<com.assimilate.alltrans.curview.GraphicOverlay
|
||||
android:id="@+id/sus_graphic_overlay"
|
||||
android:layout_width="match_parent"
|
||||
@ -19,14 +11,14 @@
|
||||
android:layout_gravity="bottom"
|
||||
android:background="@color/bg_40_000000" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sus_global_close"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:paddingStart="1dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingTop="26dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:src="@drawable/ic_close" />
|
||||
|
||||
</FrameLayout>
|
||||
17
app/src/main/res/layout/layout_sus_select.xml
Normal 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>
|
||||
@ -56,7 +56,6 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sus_photo"
|
||||
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
@ -70,7 +69,6 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sus_district"
|
||||
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
@ -113,13 +111,13 @@
|
||||
android:src="@drawable/sus_trans_home" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_sus_move"
|
||||
android:id="@+id/iv_sus_change"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="19dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/sus_trans_move" />
|
||||
android:src="@drawable/sus_trans_changelan" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/app_icon_round.webp
Normal file
|
After Width: | Height: | Size: 54 KiB |
@ -16,6 +16,7 @@
|
||||
<color name="main_text_fff4f4f4">#FFF4F4F4</color>
|
||||
<!-- quick_set_page-->
|
||||
<color name="bg_fff6f6f6">#FFF6F6F6</color>
|
||||
<color name="bg_ffaaaaaa">#FFAAAAAA</color>
|
||||
<!-- sus_view-->
|
||||
<color name="bg_ff2f2f2f">#FF2F2F2F</color>
|
||||
<!-- change_language-->
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">alltrans</string>
|
||||
<string name="app_name">Translark</string>
|
||||
|
||||
<string name="pref_key_info_hide" translatable="false">ih</string>
|
||||
<string name="pref_key_group_recognized_text_in_blocks" translatable="false">grtib</string>
|
||||
@ -15,7 +15,10 @@
|
||||
<string name="text_target_language">English</string>
|
||||
<string name="main_text_enter">Enter text</string>
|
||||
<string name="main_paste_text">Paste</string>
|
||||
<string name="main_paste_empty">Clipboard contains empty or invalid data.</string>
|
||||
<string name="main_voice_to_text">Your device may not support speech-to-text.</string>
|
||||
|
||||
|
||||
<string name="main_try_text">Translate</string>
|
||||
<string name="main_quick_text">Quick Translate</string>
|
||||
<string name="main_quick_set">settings</string>
|
||||
@ -32,28 +35,52 @@
|
||||
<string name="tr_other">other</string>
|
||||
<string name="tr_tts_error">Speech in this language is temporarily not supported.</string>
|
||||
<string name="tr_title">Translator</string>
|
||||
|
||||
<!-- sus_view-->
|
||||
<string name="global_translation">Global Translation</string>
|
||||
<string name="copy_text">Copy Text</string>
|
||||
<string name="photo_translation">Photo Translation</string>
|
||||
<string name="district_translation">District Translation</string>
|
||||
<string name="sus_copy_reset">reset</string>
|
||||
<string name="select_toast">Please select the area to be translated</string>
|
||||
<string name="sus_copy_selectall">Select All </string>
|
||||
<string name="sus_copy_copyall">Copy All</string>
|
||||
<!-- his_page-->
|
||||
<string name="his_delete">Delete</string>
|
||||
<string name="his_title">History</string>
|
||||
<string name="his_emtpoy">No translation records</string>
|
||||
<string name="favor_title">Favorite</string>
|
||||
<string name="favor_emtpoy">No collection record</string>
|
||||
<!--settings_page-->
|
||||
<string name="settings">Settings</string>
|
||||
<string name="languages">Languages</string>
|
||||
<string name="version_update">Version_update</string>
|
||||
<string name="privacy_policy">Privacy_policy</string>
|
||||
<string name="share_app">Share_app</string>
|
||||
<string name="languages">App Languages</string>
|
||||
<string name="version_update">Version</string>
|
||||
<string name="privacy_policy">Privacy Policy</string>
|
||||
<string name="share_app">Share App</string>
|
||||
<string name="favorite">Favorite</string>
|
||||
<string name="rate">Rate</string>
|
||||
<string name="sheet_text">Your encouragement makes us better!</string>
|
||||
<string name="quick_set_touming">透明度</string>
|
||||
<string name="quick_set_touming_descri">悬浮球显示时的透明度</string>
|
||||
<string name="quick_set_zd_time">自动折叠时间</string>
|
||||
<string name="favor_title">Favorite</string>
|
||||
<!-- sus_view-->
|
||||
<string name="global_translation">Global Translation</string>
|
||||
<string name="copy_text">Copy Text</string>
|
||||
<string name="photo_translation">Photo Translation</string>
|
||||
<string name="district_translation">District Translation</string>
|
||||
<!-- his_page-->
|
||||
<string name="his_delete">Delete</string>
|
||||
<string name="his_title">History record</string>
|
||||
|
||||
<string name="quick_set_zd_time_num">3s</string>
|
||||
<string name="quick_set_gd_xfq">固定悬浮球</string>
|
||||
<string name="quick_set_reset">重置</string>
|
||||
<string name="quick_set_gd_description">拖拽翻译完成松手后,悬浮球会回到固定位置,如果想改变位置可点击悬浮球在功能面板移动</string>
|
||||
<string name="settings_rate_toast"> Thank you for your comments. We will do better.</string>
|
||||
<string name="quick_set_reset_description">清除以上设置,并恢复默认样式</string>
|
||||
<string name="quick_set_qj_title">全局翻译</string>
|
||||
<string name="quick_set_qj_tmbg">透明背景</string>
|
||||
<string name="quick_set_qj_tmbg_description">全局翻译时文字使用透明背景</string>
|
||||
<string name="quick_set_qj_color">自动识别文字背景和颜色,开启后下面设置的文字颜色和背景将失效</string>
|
||||
<string name="quick_set_qj_kbgb">点击无文字的空表区域,关闭全局翻译页面</string>
|
||||
<string name="quick_set_qj_kbgb_t">点击空白区域关闭</string>
|
||||
<string name="quick_set_qj_color_t">智能背景</string>
|
||||
<string name="quick_set_kj_t">快捷操作</string>
|
||||
<string name="quick_sus_xf_set">悬浮球设置</string>
|
||||
<string name="quick_set_kj_long">长按悬浮球</string>
|
||||
<string name="quick_set_kj_long_to">打开应用</string>
|
||||
<string name="quick_set_kj_double">双击悬浮球</string>
|
||||
|
||||
|
||||
</resources>
|
||||
21
app/src/main/res/values/styles.xml
Normal 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>
|
||||
@ -6,4 +6,12 @@
|
||||
</style>
|
||||
|
||||
<style name="Theme.Alltrans" parent="Base.Theme.Alltrans" />
|
||||
|
||||
<style name="CustomBottomSheetDialogTheme" parent="Theme.Material3.Light.BottomSheetDialog">
|
||||
<!-- 自定义颜色属性 -->
|
||||
<item name="colorPrimary">@color/white</item> <!-- 替换为你想要的颜色 -->
|
||||
<item name="colorPrimaryDark">@color/white</item> <!-- 替换为你想要的颜色 -->
|
||||
<item name="colorAccent">@color/white</item> <!-- 替换为你想要的颜色 -->
|
||||
|
||||
</style>
|
||||
</resources>
|
||||
@ -2,4 +2,8 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.jetbrains.kotlin.android) apply false
|
||||
// Make sure that you have the Google services Gradle plugin 4.4.1+ dependency
|
||||
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||
// Add the dependency for the Crashlytics Gradle plugin
|
||||
id("com.google.firebase.crashlytics") version "3.0.2" apply false
|
||||
}
|
||||