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