From 4e66e7f84d411440341a18561fefeff54d647327 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 11 Jul 2024 17:54:19 +0800 Subject: [PATCH] update text translate --- app/src/main/AndroidManifest.xml | 12 + .../java/com/assimilate/alltrans/MyApp.kt | 156 ++++++ .../alltrans/adapters/ContainerAdapter.java | 33 -- .../alltrans/allservice/SusService.kt | 275 +++++++++++ .../alltrans/common/BitmapUtils.java | 461 +++++++++--------- .../alltrans/common/CameraImageGraphic.java | 15 - .../alltrans/common/FrameMetadata.java | 15 - .../alltrans/common/InferenceInfoGraphic.java | 15 - .../alltrans/common/PreferenceUtils.java | 15 - .../alltrans/common/ScopedExecutor.java | 15 - .../com/assimilate/alltrans/common/Sort.java | 5 - .../common/TextRecognitionProcessor.kt | 15 - .../alltrans/common/VisionProcessorBase.kt | 15 - .../alltrans/curview/GraphicOverlay.java | 15 - .../assimilate/alltrans/curview/SusView.kt | 26 + .../fragments/TranslateCameraFragment.java | 297 ----------- .../fragments/TranslateTextFragment.java | 280 ----------- .../fragments/TranslateVoiceFragment.java | 285 ----------- .../alltrans/mydb/DbTranslation.java | 1 - .../alltrans/viewui/HistoryActivity.java | 40 +- .../alltrans/viewui/LanguageChangeActivity.kt | 46 ++ .../alltrans/viewui/MainActivity.kt | 209 +++++++- .../alltrans/viewui/StillImageActivity.kt | 62 ++- .../alltrans/viewui/TextResultActivity.kt | 142 +++++- .../main/res/drawable/checkbox_selector.xml | 5 + .../res/drawable/ic_dashed_line_4b4b4b4.xml | 2 +- .../main/res/drawable/ic_his_choose_def.xml | 17 + .../main/res/drawable/ic_his_choose_yes.xml | 17 + app/src/main/res/drawable/ic_like_yes.xml | 21 + app/src/main/res/drawable/ic_remove.xml | 35 ++ app/src/main/res/drawable/main_bg.xml | 10 + app/src/main/res/layout/activity_history.xml | 13 +- .../res/layout/activity_language_change.xml | 25 +- app/src/main/res/layout/activity_main.xml | 9 +- .../main/res/layout/activity_text_result.xml | 11 +- .../main/res/layout/language_item_layout.xml | 6 +- .../res/layout/layout_item_translation.xml | 14 +- app/src/main/res/layout/layout_sus_global.xml | 26 + app/src/main/res/layout/sus_control_view.xml | 35 +- app/src/main/res/values/colors.xml | 5 + app/src/main/res/values/strings.xml | 4 +- 41 files changed, 1383 insertions(+), 1322 deletions(-) create mode 100644 app/src/main/java/com/assimilate/alltrans/MyApp.kt delete mode 100644 app/src/main/java/com/assimilate/alltrans/adapters/ContainerAdapter.java create mode 100644 app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt delete mode 100644 app/src/main/java/com/assimilate/alltrans/common/Sort.java create mode 100644 app/src/main/java/com/assimilate/alltrans/curview/SusView.kt delete mode 100644 app/src/main/java/com/assimilate/alltrans/fragments/TranslateCameraFragment.java delete mode 100644 app/src/main/java/com/assimilate/alltrans/fragments/TranslateTextFragment.java delete mode 100644 app/src/main/java/com/assimilate/alltrans/fragments/TranslateVoiceFragment.java create mode 100644 app/src/main/res/drawable/checkbox_selector.xml create mode 100644 app/src/main/res/drawable/ic_his_choose_def.xml create mode 100644 app/src/main/res/drawable/ic_his_choose_yes.xml create mode 100644 app/src/main/res/drawable/ic_like_yes.xml create mode 100644 app/src/main/res/drawable/ic_remove.xml create mode 100644 app/src/main/res/drawable/main_bg.xml create mode 100644 app/src/main/res/layout/layout_sus_global.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f77dee7..6d35de4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,12 +6,17 @@ android:name="android.hardware.camera" android:required="false" /> + + + + + + + + diff --git a/app/src/main/java/com/assimilate/alltrans/MyApp.kt b/app/src/main/java/com/assimilate/alltrans/MyApp.kt new file mode 100644 index 0000000..363a56c --- /dev/null +++ b/app/src/main/java/com/assimilate/alltrans/MyApp.kt @@ -0,0 +1,156 @@ +package com.assimilate.alltrans + +import android.app.ActivityManager +import android.app.Application +import android.content.Context +import android.os.Build +import android.webkit.WebView +import com.assimilate.alltrans.common.Language +import com.assimilate.alltrans.common.LanguagesConstants + +class MyApp : Application() { + + init { + instance = this + } + + private var sl: Language? = null + private var tl: Language? = null + + companion object { + private var instance: MyApp? = null + fun applicationContext(): Context { + return instance!!.applicationContext + } + + fun getSourceLanguageCode(): String { + return instance?.getSourceLanguageCode() ?: "zh" + } + + fun getSourceLanguage(): String { + return instance?.getSourceLanguage() ?: "English" + } + + fun getSourceSpeechCode(): String { + return instance?.getSourceSpeechCode() ?: "en-GB" + } + + fun getTargetLanguageCode(): String { + return instance?.getTargetLanguageCode() ?: "en" + } + + fun getTargetLanguage(): String { + return instance?.getTargetLanguage() ?: "English" + } + + fun getTargetSpeechCode(): String { + return instance?.getTargetSpeechCode() ?: "en-GB" + } + + fun setSourceLanguage(language: Language) { + instance?.setSourceLanguage(language) + } + + fun setTargetLanguage(language: Language) { + instance?.setTargetLanguage(language) + } + } + + override fun onCreate() { + super.onCreate() + initLanguage() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val processName = getUniqueProcessName() + if (processName != null && processName != packageName) { + WebView.setDataDirectorySuffix(processName) + } + } + } + + private fun getUniqueProcessName(): String? { + val pid = android.os.Process.myPid() + val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + for (processInfo in manager.runningAppProcesses) { + if (processInfo.pid == pid) { + return processInfo.processName + } + } + return null + } + + object Config { + const val openSusViewMode: String = "open_sus_view" + } + + private fun initLanguage() { + // 拿到最近一次的翻译情况,分别设置最近一次的,并赋值setSourceLanguage|setTargetLanguage + + // 以下是默认情况 + + val languages: ArrayList = LanguagesConstants.getInstance().getList(applicationContext) + if (languages.isNotEmpty()) { + for (language in languages) { + if ("Afrikaans" == language.language) { + tl = language + break + } + } + for (language in languages) { + if ("English" == language.language) { + sl = language + break + } + } + } + } + + private fun getSourceLanguageCode(): String { + if (sl == null) { + return "en" + } + return sl!!.languageCode + } + + private fun getSourceLanguage(): String { + if (sl == null) { + return "English" + } + return sl!!.language + } + + private fun getSourceSpeechCode(): String { + if (sl == null) { + return "en-GB" + } + return sl!!.speechCode + } + + private fun getTargetLanguageCode(): String { + if (tl == null) { + return "en" + } + return tl!!.languageCode + } + + private fun getTargetLanguage(): String { + if (tl == null) { + return "English" + } + return tl!!.language + } + + private fun getTargetSpeechCode(): String { + if (tl == null) { + return "en-GB" + } + return tl!!.speechCode + } + + private fun setSourceLanguage(language: Language) { + sl = language + } + + private fun setTargetLanguage(language: Language) { + tl = language + } +} diff --git a/app/src/main/java/com/assimilate/alltrans/adapters/ContainerAdapter.java b/app/src/main/java/com/assimilate/alltrans/adapters/ContainerAdapter.java deleted file mode 100644 index ec05c05..0000000 --- a/app/src/main/java/com/assimilate/alltrans/adapters/ContainerAdapter.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.assimilate.alltrans.adapters; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.viewpager2.adapter.FragmentStateAdapter; - -import java.util.ArrayList; - -public class ContainerAdapter extends FragmentStateAdapter { - - private final ArrayList fragments; - - public ContainerAdapter(@NonNull FragmentActivity fragmentActivity, @NonNull ArrayList list) { - super(fragmentActivity); - - this.fragments = new ArrayList<>(); - if (!list.isEmpty()) { - this.fragments.addAll(list); - } - } - - @NonNull - @Override - public Fragment createFragment(int position) { - return fragments.get(position); - } - - @Override - public int getItemCount() { - return fragments.size(); - } -} diff --git a/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt b/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt new file mode 100644 index 0000000..3eb84f1 --- /dev/null +++ b/app/src/main/java/com/assimilate/alltrans/allservice/SusService.kt @@ -0,0 +1,275 @@ +package com.assimilate.alltrans.allservice + +import android.app.* +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.PixelFormat +import android.hardware.display.DisplayManager +import android.hardware.display.VirtualDisplay +import android.media.ImageReader +import android.media.projection.MediaProjection +import android.media.projection.MediaProjectionManager +import android.os.* +import android.util.DisplayMetrics +import android.util.Log +import android.view.* +import androidx.core.app.NotificationCompat +import com.assimilate.alltrans.R +import com.assimilate.alltrans.common.TextRecognitionProcessor +import com.assimilate.alltrans.common.VisionImageProcessor +import com.assimilate.alltrans.databinding.SusControlViewBinding +import com.assimilate.alltrans.databinding.LayoutSusGlobalBinding +import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions +import java.io.IOException + +class SusService : Service() { + private var imageProcessor: VisionImageProcessor? = null + private lateinit var mediaProjectionManager: MediaProjectionManager + private lateinit var mediaProjection: MediaProjection + private lateinit var virtualDisplay: VirtualDisplay + private lateinit var displayMetrics: DisplayMetrics + + 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 + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onCreate() { + super.onCreate() + mediaProjectionManager = + getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + displayMetrics = DisplayMetrics() + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + windowManager.defaultDisplay?.getMetrics(displayMetrics) + + initSusView() + startForeground(1, createNotification()) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val resultCode = intent?.getIntExtra("resultCode", Activity.RESULT_OK) + val data: Intent? = intent?.getParcelableExtra("data") + if (resultCode != null && data != null) { + startScreenshot(resultCode, data) + } + return START_NOT_STICKY + } + + private fun initSusView() { + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + + bindingSusControl = SusControlViewBinding.inflate(LayoutInflater.from(this)) + floatingView = bindingSusControl.root + + val layoutParams = WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + } else { + WindowManager.LayoutParams.TYPE_PHONE + }, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT + ) + layoutParams.gravity = Gravity.TOP or Gravity.LEFT + layoutParams.x = 0 + layoutParams.y = 100 + + windowManager.addView(floatingView, layoutParams) + + // 设置点击事件 + bindingSusControl.tvSusGlobal.setOnClickListener { + // 处理全局翻译点击事件 + addGlobalView() + } + + bindingSusControl.tvSusCopy.setOnClickListener { + // 处理复制文本点击事件 + } + + bindingSusControl.tvSusPhoto.setOnClickListener { + // 处理照片翻译点击事件 + } + + bindingSusControl.tvSusDistrict.setOnClickListener { + // 处理地区翻译点击事件 + } + + bindingSusControl.ivSusHome.setOnClickListener { + // 处理返回主页点击事件 + } + + bindingSusControl.ivSusMove.setOnClickListener { + // 处理移动窗口点击事件 + } + + bindingSusControl.ivSusMove.setOnTouchListener(object : View.OnTouchListener { + 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 + return true + } + + MotionEvent.ACTION_MOVE -> { + layoutParams.x = (startX + (event.rawX - touchX)).toInt() + layoutParams.y = (startY + (event.rawY - touchY)).toInt() + windowManager.updateViewLayout(floatingView, layoutParams) + return true + } + + MotionEvent.ACTION_UP -> return true + } + return false + } + }) + } + + private fun addGlobalView() { + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + + bindingSubGlobal = LayoutSusGlobalBinding.inflate(LayoutInflater.from(this)) + globalView = bindingSubGlobal.root + imageProcessor = TextRecognitionProcessor( + this, + ChineseTextRecognizerOptions.Builder().build() + ) + + 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(globalView, layoutParams) + } + + override fun onDestroy() { + super.onDestroy() + if (floatingView.isAttachedToWindow) { + windowManager.removeView(floatingView) + } + if (::globalView.isInitialized && globalView.isAttachedToWindow) { + windowManager.removeView(globalView) + } + } + + private fun createNotification(): Notification { + val notificationChannelId = "FOREGROUND_SERVICE_CHANNEL" + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channelName = "Foreground Service Channel" + val chan = NotificationChannel( + notificationChannelId, + channelName, + NotificationManager.IMPORTANCE_NONE + ) + val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + manager.createNotificationChannel(chan) + } + + val builder = NotificationCompat.Builder(this, notificationChannelId) + .setContentTitle("悬浮窗服务") + .setContentText("悬浮窗服务正在运行") + .setSmallIcon(R.drawable.ic_close) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(Notification.CATEGORY_SERVICE) + + return builder.build() + } + + private fun tryReloadAndDetectInImage(bitmap: Bitmap) { + try { + // Clear the overlay first + 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") + } + } + + fun startScreenshot(resultCode: Int, data: Intent?) { + mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data!!) + val imageReader = ImageReader.newInstance( + displayMetrics.widthPixels, + displayMetrics.heightPixels, + PixelFormat.RGBA_8888, + 2 + ) + + virtualDisplay = mediaProjection.createVirtualDisplay( + "Screenshot", + displayMetrics.widthPixels, + displayMetrics.heightPixels, + displayMetrics.densityDpi, + DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, + imageReader.surface, + null, + null + ) + + // 初始化 globalView + addGlobalView() + + Handler(Looper.getMainLooper()).postDelayed({ + val image = imageReader.acquireLatestImage() + if (image != null) { + val planes = image.planes + val buffer = planes[0].buffer + val pixelStride = planes[0].pixelStride + val rowStride = planes[0].rowStride + val rowPadding = rowStride - pixelStride * displayMetrics.widthPixels + + val bitmap = Bitmap.createBitmap( + displayMetrics.widthPixels + rowPadding / pixelStride, + displayMetrics.heightPixels, + Bitmap.Config.ARGB_8888 + ) + bitmap.copyPixelsFromBuffer(buffer) + image.close() + bindingSubGlobal.preview.setImageBitmap(bitmap) + tryReloadAndDetectInImage(bitmap) + } + stopSelf() + }, 1000) + } +} diff --git a/app/src/main/java/com/assimilate/alltrans/common/BitmapUtils.java b/app/src/main/java/com/assimilate/alltrans/common/BitmapUtils.java index 42f32a4..caf957c 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/BitmapUtils.java +++ b/app/src/main/java/com/assimilate/alltrans/common/BitmapUtils.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; @@ -41,244 +26,254 @@ import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; -/** Utils functions for bitmap conversions. */ +/** + * Utils functions for bitmap conversions. + */ public class BitmapUtils { - private static final String TAG = "BitmapUtils"; + private static final String TAG = "BitmapUtils"; - /** Converts NV21 format byte buffer to bitmap. */ - @Nullable - public static Bitmap getBitmap(ByteBuffer data, FrameMetadata metadata) { - data.rewind(); - byte[] imageInBuffer = new byte[data.limit()]; - data.get(imageInBuffer, 0, imageInBuffer.length); - try { - YuvImage image = - new YuvImage( - imageInBuffer, ImageFormat.NV21, metadata.getWidth(), metadata.getHeight(), null); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - image.compressToJpeg(new Rect(0, 0, metadata.getWidth(), metadata.getHeight()), 80, stream); + /** + * Converts NV21 format byte buffer to bitmap. + */ + @Nullable + public static Bitmap getBitmap(ByteBuffer data, FrameMetadata metadata) { + data.rewind(); + byte[] imageInBuffer = new byte[data.limit()]; + data.get(imageInBuffer, 0, imageInBuffer.length); + try { + YuvImage image = + new YuvImage( + imageInBuffer, ImageFormat.NV21, metadata.getWidth(), metadata.getHeight(), null); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + image.compressToJpeg(new Rect(0, 0, metadata.getWidth(), metadata.getHeight()), 80, stream); - Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); + Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); - stream.close(); - return rotateBitmap(bmp, metadata.getRotation(), false, false); - } catch (Exception e) { - Log.e("VisionProcessorBase", "Error: " + e.getMessage()); - } - return null; - } - - /** Converts a YUV_420_888 image from CameraX API to a bitmap. */ - @RequiresApi(VERSION_CODES.LOLLIPOP) - @Nullable - @ExperimentalGetImage - public static Bitmap getBitmap(ImageProxy image) { - FrameMetadata frameMetadata = - new FrameMetadata.Builder() - .setWidth(image.getWidth()) - .setHeight(image.getHeight()) - .setRotation(image.getImageInfo().getRotationDegrees()) - .build(); - - ByteBuffer nv21Buffer = - yuv420ThreePlanesToNV21(image.getImage().getPlanes(), image.getWidth(), image.getHeight()); - return getBitmap(nv21Buffer, frameMetadata); - } - - /** Rotates a bitmap if it is converted from a bytebuffer. */ - private static Bitmap rotateBitmap( - Bitmap bitmap, int rotationDegrees, boolean flipX, boolean flipY) { - Matrix matrix = new Matrix(); - - // Rotate the image back to straight. - matrix.postRotate(rotationDegrees); - - // Mirror the image along the X or Y axis. - matrix.postScale(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f); - Bitmap rotatedBitmap = - Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); - - // Recycle the old bitmap if it has changed. - if (rotatedBitmap != bitmap) { - bitmap.recycle(); - } - return rotatedBitmap; - } - - @Nullable - public static Bitmap getBitmapFromContentUri(ContentResolver contentResolver, Uri imageUri) - throws IOException { - Bitmap decodedBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri); - if (decodedBitmap == null) { - return null; - } - int orientation = getExifOrientationTag(contentResolver, imageUri); - - int rotationDegrees = 0; - boolean flipX = false; - boolean flipY = false; - // See e.g. https://magnushoff.com/articles/jpeg-orientation/ for a detailed explanation on each - // orientation. - switch (orientation) { - case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: - flipX = true; - break; - case ExifInterface.ORIENTATION_ROTATE_90: - rotationDegrees = 90; - break; - case ExifInterface.ORIENTATION_TRANSPOSE: - rotationDegrees = 90; - flipX = true; - break; - case ExifInterface.ORIENTATION_ROTATE_180: - rotationDegrees = 180; - break; - case ExifInterface.ORIENTATION_FLIP_VERTICAL: - flipY = true; - break; - case ExifInterface.ORIENTATION_ROTATE_270: - rotationDegrees = -90; - break; - case ExifInterface.ORIENTATION_TRANSVERSE: - rotationDegrees = -90; - flipX = true; - break; - case ExifInterface.ORIENTATION_UNDEFINED: - case ExifInterface.ORIENTATION_NORMAL: - default: - // No transformations necessary in this case. + stream.close(); + return rotateBitmap(bmp, metadata.getRotation(), false, false); + } catch (Exception e) { + Log.e("VisionProcessorBase", "Error: " + e.getMessage()); + } + return null; } - return rotateBitmap(decodedBitmap, rotationDegrees, flipX, flipY); - } + /** + * Converts a YUV_420_888 image from CameraX API to a bitmap. + */ + @RequiresApi(VERSION_CODES.LOLLIPOP) + @Nullable + @ExperimentalGetImage + public static Bitmap getBitmap(ImageProxy image) { + FrameMetadata frameMetadata = + new FrameMetadata.Builder() + .setWidth(image.getWidth()) + .setHeight(image.getHeight()) + .setRotation(image.getImageInfo().getRotationDegrees()) + .build(); - private static int getExifOrientationTag(ContentResolver resolver, Uri imageUri) { - // We only support parsing EXIF orientation tag from local file on the device. - // See also: - // https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html - if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme()) - && !ContentResolver.SCHEME_FILE.equals(imageUri.getScheme())) { - return 0; + ByteBuffer nv21Buffer = + yuv420ThreePlanesToNV21(image.getImage().getPlanes(), image.getWidth(), image.getHeight()); + return getBitmap(nv21Buffer, frameMetadata); } - ExifInterface exif; - try (InputStream inputStream = resolver.openInputStream(imageUri)) { - if (inputStream == null) { - return 0; - } + /** + * Rotates a bitmap if it is converted from a bytebuffer. + */ + private static Bitmap rotateBitmap( + Bitmap bitmap, int rotationDegrees, boolean flipX, boolean flipY) { + Matrix matrix = new Matrix(); - exif = new ExifInterface(inputStream); - } catch (IOException e) { - Log.e(TAG, "failed to open file to read rotation meta data: " + imageUri, e); - return 0; + // Rotate the image back to straight. + matrix.postRotate(rotationDegrees); + + // Mirror the image along the X or Y axis. + matrix.postScale(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f); + Bitmap rotatedBitmap = + Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + + // Recycle the old bitmap if it has changed. + if (rotatedBitmap != bitmap) { + bitmap.recycle(); + } + return rotatedBitmap; } - return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); - } + @Nullable + public static Bitmap getBitmapFromContentUri(ContentResolver contentResolver, Uri imageUri) + throws IOException { + Bitmap decodedBitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri); + if (decodedBitmap == null) { + return null; + } + int orientation = getExifOrientationTag(contentResolver, imageUri); - /** - * Converts YUV_420_888 to NV21 bytebuffer. - * - *

The NV21 format consists of a single byte array containing the Y, U and V values. For an - * image of size S, the first S positions of the array contain all the Y values. The remaining - * positions contain interleaved V and U values. U and V are subsampled by a factor of 2 in both - * dimensions, so there are S/4 U values and S/4 V values. In summary, the NV21 array will contain - * S Y values followed by S/4 VU values: YYYYYYYYYYYYYY(...)YVUVUVUVU(...)VU - * - *

YUV_420_888 is a generic format that can describe any YUV image where U and V are subsampled - * by a factor of 2 in both dimensions. {@link Image#getPlanes} returns an array with the Y, U and - * V planes. The Y plane is guaranteed not to be interleaved, so we can just copy its values into - * the first part of the NV21 array. The U and V planes may already have the representation in the - * NV21 format. This happens if the planes share the same buffer, the V buffer is one position - * before the U buffer and the planes have a pixelStride of 2. If this is case, we can just copy - * them to the NV21 array. - */ - private static ByteBuffer yuv420ThreePlanesToNV21( - Plane[] yuv420888planes, int width, int height) { - int imageSize = width * height; - byte[] out = new byte[imageSize + 2 * (imageSize / 4)]; + int rotationDegrees = 0; + boolean flipX = false; + boolean flipY = false; + // See e.g. https://magnushoff.com/articles/jpeg-orientation/ for a detailed explanation on each + // orientation. + switch (orientation) { + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: + flipX = true; + break; + case ExifInterface.ORIENTATION_ROTATE_90: + rotationDegrees = 90; + break; + case ExifInterface.ORIENTATION_TRANSPOSE: + rotationDegrees = 90; + flipX = true; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + rotationDegrees = 180; + break; + case ExifInterface.ORIENTATION_FLIP_VERTICAL: + flipY = true; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + rotationDegrees = -90; + break; + case ExifInterface.ORIENTATION_TRANSVERSE: + rotationDegrees = -90; + flipX = true; + break; + case ExifInterface.ORIENTATION_UNDEFINED: + case ExifInterface.ORIENTATION_NORMAL: + default: + // No transformations necessary in this case. + } - if (areUVPlanesNV21(yuv420888planes, width, height)) { - // Copy the Y values. - yuv420888planes[0].getBuffer().get(out, 0, imageSize); - - ByteBuffer uBuffer = yuv420888planes[1].getBuffer(); - ByteBuffer vBuffer = yuv420888planes[2].getBuffer(); - // Get the first V value from the V buffer, since the U buffer does not contain it. - vBuffer.get(out, imageSize, 1); - // Copy the first U value and the remaining VU values from the U buffer. - uBuffer.get(out, imageSize + 1, 2 * imageSize / 4 - 1); - } else { - // Fallback to copying the UV values one by one, which is slower but also works. - // Unpack Y. - unpackPlane(yuv420888planes[0], width, height, out, 0, 1); - // Unpack U. - unpackPlane(yuv420888planes[1], width, height, out, imageSize + 1, 2); - // Unpack V. - unpackPlane(yuv420888planes[2], width, height, out, imageSize, 2); + return rotateBitmap(decodedBitmap, rotationDegrees, flipX, flipY); } - return ByteBuffer.wrap(out); - } + private static int getExifOrientationTag(ContentResolver resolver, Uri imageUri) { + // We only support parsing EXIF orientation tag from local file on the device. + // See also: + // https://android-developers.googleblog.com/2016/12/introducing-the-exifinterface-support-library.html + if (!ContentResolver.SCHEME_CONTENT.equals(imageUri.getScheme()) + && !ContentResolver.SCHEME_FILE.equals(imageUri.getScheme())) { + return 0; + } - /** Checks if the UV plane buffers of a YUV_420_888 image are in the NV21 format. */ - private static boolean areUVPlanesNV21(Plane[] planes, int width, int height) { - int imageSize = width * height; + ExifInterface exif; + try (InputStream inputStream = resolver.openInputStream(imageUri)) { + if (inputStream == null) { + return 0; + } - ByteBuffer uBuffer = planes[1].getBuffer(); - ByteBuffer vBuffer = planes[2].getBuffer(); + exif = new ExifInterface(inputStream); + } catch (IOException e) { + Log.e(TAG, "failed to open file to read rotation meta data: " + imageUri, e); + return 0; + } - // Backup buffer properties. - int vBufferPosition = vBuffer.position(); - int uBufferLimit = uBuffer.limit(); - - // Advance the V buffer by 1 byte, since the U buffer will not contain the first V value. - vBuffer.position(vBufferPosition + 1); - // Chop off the last byte of the U buffer, since the V buffer will not contain the last U value. - uBuffer.limit(uBufferLimit - 1); - - // Check that the buffers are equal and have the expected number of elements. - boolean areNV21 = - (vBuffer.remaining() == (2 * imageSize / 4 - 2)) && (vBuffer.compareTo(uBuffer) == 0); - - // Restore buffers to their initial state. - vBuffer.position(vBufferPosition); - uBuffer.limit(uBufferLimit); - - return areNV21; - } - - /** - * Unpack an image plane into a byte array. - * - *

The input plane data will be copied in 'out', starting at 'offset' and every pixel will be - * spaced by 'pixelStride'. Note that there is no row padding on the output. - */ - private static void unpackPlane( - Plane plane, int width, int height, byte[] out, int offset, int pixelStride) { - ByteBuffer buffer = plane.getBuffer(); - buffer.rewind(); - - // Compute the size of the current plane. - // We assume that it has the aspect ratio as the original image. - int numRow = (buffer.limit() + plane.getRowStride() - 1) / plane.getRowStride(); - if (numRow == 0) { - return; + return exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } - int scaleFactor = height / numRow; - int numCol = width / scaleFactor; - // Extract the data in the output buffer. - int outputPos = offset; - int rowStart = 0; - for (int row = 0; row < numRow; row++) { - int inputPos = rowStart; - for (int col = 0; col < numCol; col++) { - out[outputPos] = buffer.get(inputPos); - outputPos += pixelStride; - inputPos += plane.getPixelStride(); - } - rowStart += plane.getRowStride(); + /** + * Converts YUV_420_888 to NV21 bytebuffer. + * + *

The NV21 format consists of a single byte array containing the Y, U and V values. For an + * image of size S, the first S positions of the array contain all the Y values. The remaining + * positions contain interleaved V and U values. U and V are subsampled by a factor of 2 in both + * dimensions, so there are S/4 U values and S/4 V values. In summary, the NV21 array will contain + * S Y values followed by S/4 VU values: YYYYYYYYYYYYYY(...)YVUVUVUVU(...)VU + * + *

YUV_420_888 is a generic format that can describe any YUV image where U and V are subsampled + * by a factor of 2 in both dimensions. {@link Image#getPlanes} returns an array with the Y, U and + * V planes. The Y plane is guaranteed not to be interleaved, so we can just copy its values into + * the first part of the NV21 array. The U and V planes may already have the representation in the + * NV21 format. This happens if the planes share the same buffer, the V buffer is one position + * before the U buffer and the planes have a pixelStride of 2. If this is case, we can just copy + * them to the NV21 array. + */ + private static ByteBuffer yuv420ThreePlanesToNV21( + Plane[] yuv420888planes, int width, int height) { + int imageSize = width * height; + byte[] out = new byte[imageSize + 2 * (imageSize / 4)]; + + if (areUVPlanesNV21(yuv420888planes, width, height)) { + // Copy the Y values. + yuv420888planes[0].getBuffer().get(out, 0, imageSize); + + ByteBuffer uBuffer = yuv420888planes[1].getBuffer(); + ByteBuffer vBuffer = yuv420888planes[2].getBuffer(); + // Get the first V value from the V buffer, since the U buffer does not contain it. + vBuffer.get(out, imageSize, 1); + // Copy the first U value and the remaining VU values from the U buffer. + uBuffer.get(out, imageSize + 1, 2 * imageSize / 4 - 1); + } else { + // Fallback to copying the UV values one by one, which is slower but also works. + // Unpack Y. + unpackPlane(yuv420888planes[0], width, height, out, 0, 1); + // Unpack U. + unpackPlane(yuv420888planes[1], width, height, out, imageSize + 1, 2); + // Unpack V. + unpackPlane(yuv420888planes[2], width, height, out, imageSize, 2); + } + + return ByteBuffer.wrap(out); + } + + /** + * Checks if the UV plane buffers of a YUV_420_888 image are in the NV21 format. + */ + private static boolean areUVPlanesNV21(Plane[] planes, int width, int height) { + int imageSize = width * height; + + ByteBuffer uBuffer = planes[1].getBuffer(); + ByteBuffer vBuffer = planes[2].getBuffer(); + + // Backup buffer properties. + int vBufferPosition = vBuffer.position(); + int uBufferLimit = uBuffer.limit(); + + // Advance the V buffer by 1 byte, since the U buffer will not contain the first V value. + vBuffer.position(vBufferPosition + 1); + // Chop off the last byte of the U buffer, since the V buffer will not contain the last U value. + uBuffer.limit(uBufferLimit - 1); + + // Check that the buffers are equal and have the expected number of elements. + boolean areNV21 = + (vBuffer.remaining() == (2 * imageSize / 4 - 2)) && (vBuffer.compareTo(uBuffer) == 0); + + // Restore buffers to their initial state. + vBuffer.position(vBufferPosition); + uBuffer.limit(uBufferLimit); + + return areNV21; + } + + /** + * Unpack an image plane into a byte array. + * + *

The input plane data will be copied in 'out', starting at 'offset' and every pixel will be + * spaced by 'pixelStride'. Note that there is no row padding on the output. + */ + private static void unpackPlane( + Plane plane, int width, int height, byte[] out, int offset, int pixelStride) { + ByteBuffer buffer = plane.getBuffer(); + buffer.rewind(); + + // Compute the size of the current plane. + // We assume that it has the aspect ratio as the original image. + int numRow = (buffer.limit() + plane.getRowStride() - 1) / plane.getRowStride(); + if (numRow == 0) { + return; + } + int scaleFactor = height / numRow; + int numCol = width / scaleFactor; + + // Extract the data in the output buffer. + int outputPos = offset; + int rowStart = 0; + for (int row = 0; row < numRow; row++) { + int inputPos = rowStart; + for (int col = 0; col < numCol; col++) { + out[outputPos] = buffer.get(inputPos); + outputPos += pixelStride; + inputPos += plane.getPixelStride(); + } + rowStart += plane.getRowStride(); + } } - } } diff --git a/app/src/main/java/com/assimilate/alltrans/common/CameraImageGraphic.java b/app/src/main/java/com/assimilate/alltrans/common/CameraImageGraphic.java index c783a2e..285db42 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/CameraImageGraphic.java +++ b/app/src/main/java/com/assimilate/alltrans/common/CameraImageGraphic.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; diff --git a/app/src/main/java/com/assimilate/alltrans/common/FrameMetadata.java b/app/src/main/java/com/assimilate/alltrans/common/FrameMetadata.java index e9d2242..5364c29 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/FrameMetadata.java +++ b/app/src/main/java/com/assimilate/alltrans/common/FrameMetadata.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; diff --git a/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java b/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java index 2d070ca..703162c 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java +++ b/app/src/main/java/com/assimilate/alltrans/common/InferenceInfoGraphic.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; diff --git a/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java b/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java index 5c6246a..9e1c6e6 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java +++ b/app/src/main/java/com/assimilate/alltrans/common/PreferenceUtils.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; diff --git a/app/src/main/java/com/assimilate/alltrans/common/ScopedExecutor.java b/app/src/main/java/com/assimilate/alltrans/common/ScopedExecutor.java index 6a7793e..b6b305d 100755 --- a/app/src/main/java/com/assimilate/alltrans/common/ScopedExecutor.java +++ b/app/src/main/java/com/assimilate/alltrans/common/ScopedExecutor.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common; diff --git a/app/src/main/java/com/assimilate/alltrans/common/Sort.java b/app/src/main/java/com/assimilate/alltrans/common/Sort.java deleted file mode 100644 index 5fe4e31..0000000 --- a/app/src/main/java/com/assimilate/alltrans/common/Sort.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.assimilate.alltrans.common; - -public enum Sort { - language, languageCode, speechCode; -} diff --git a/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt b/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt index 7748dd9..fe9151a 100644 --- a/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt +++ b/app/src/main/java/com/assimilate/alltrans/common/TextRecognitionProcessor.kt @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common diff --git a/app/src/main/java/com/assimilate/alltrans/common/VisionProcessorBase.kt b/app/src/main/java/com/assimilate/alltrans/common/VisionProcessorBase.kt index f108a09..8d40793 100644 --- a/app/src/main/java/com/assimilate/alltrans/common/VisionProcessorBase.kt +++ b/app/src/main/java/com/assimilate/alltrans/common/VisionProcessorBase.kt @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.common diff --git a/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java b/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java index 5dbe45d..2b49a0e 100755 --- a/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java +++ b/app/src/main/java/com/assimilate/alltrans/curview/GraphicOverlay.java @@ -1,18 +1,3 @@ -/* - * Copyright 2020 Google LLC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.assimilate.alltrans.curview; diff --git a/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt b/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt new file mode 100644 index 0000000..10ba01c --- /dev/null +++ b/app/src/main/java/com/assimilate/alltrans/curview/SusView.kt @@ -0,0 +1,26 @@ +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() { + + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateCameraFragment.java b/app/src/main/java/com/assimilate/alltrans/fragments/TranslateCameraFragment.java deleted file mode 100644 index eea57f2..0000000 --- a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateCameraFragment.java +++ /dev/null @@ -1,297 +0,0 @@ -package com.assimilate.alltrans.fragments; - -import androidx.fragment.app.Fragment; - -public class TranslateCameraFragment extends Fragment { - -// private ActivityResultLauncher activityLauncher; -// private ActivityResultLauncher permissionLauncher; -// -// private FragmentTranslateCameraBinding mBinding; -// -// private TextToSpeech tts; -// -// private boolean translating = false; -// private boolean adLoading = false; // 广告是否处于加载中 -// private boolean collectCurrent = false; -// private String sourceText = ""; // 可能会有一种屌毛,翻译完成后,先去输入框删几个字符,然后再去点击收藏按钮。所以每次翻译前备份一下 -// -// @Override -// public void onCreate(@Nullable Bundle savedInstanceState) { -// super.onCreate(savedInstanceState); -// activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() { -// @Override -// public void onActivityResult(ActivityResult result) { -// if (Activity.RESULT_OK == result.getResultCode() && null != result.getData()) { -// Intent data = result.getData(); -// String recognizedText = data.getStringExtra("recognizedText"); -// if (!TextUtils.isEmpty(recognizedText)) { -// mBinding.inputText.setText(recognizedText); -// translate(recognizedText); -// } -// } -// } -// }); -// permissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), new ActivityResultCallback() { -// @Override -// public void onActivityResult(Boolean result) { -// if (result.booleanValue()) { -// launchCameraApi(); -// } else { -// Widget.makeToast(getActivity(), "permission denied"); -// } -// } -// }); -// -// translating = false; -// adLoading = false; -// collectCurrent = false; -// -// tts = new TextToSpeech(getActivity(), new TextToSpeech.OnInitListener() { -// @Override -// public void onInit(int status) { -// if (null != tts && TextToSpeech.SUCCESS == status) -// tts.setLanguage(Locale.getDefault()); -// } -// }); -// } -// -// @Override -// public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { -// mBinding = FragmentTranslateCameraBinding.inflate(getLayoutInflater()); -// -// // 朗读原文 -// mBinding.speakSource.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 清空翻译输入框与结果文本 -// mBinding.clear.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// mBinding.result.setText(""); -// mBinding.inputText.setText(""); -// -// reset(); -// } -// }); -// // 朗读译文 -// mBinding.speakTarget.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 分享译文 -// mBinding.shareTrans.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// if (translating) { -// Widget.makeToast(getActivity(), "Translating..."); -// return; -// } -// -// final String share = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(share)) { -// Intent intent = new Intent(Intent.ACTION_SEND); -// intent.setType("text/plain"); -// intent.putExtra(Intent.EXTRA_TEXT, share); -// startActivity(Intent.createChooser(intent, "Share " + getString(R.string.app_name))); -// } -// } -// }); -// // 复制到粘贴板 -// mBinding.copyTrans.setOnClickListener(new View.OnClickListener() { -// private final String tip = "Copied to clipboard!"; -// @Override -// public void onClick(View v) { -// final String share = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(share)) { -// ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); -// ClipData clipData = ClipData.newPlainText("targetValue", share); -// clipboardManager.setPrimaryClip(clipData); -// Widget.makeToast(getActivity(), tip); -// } -// } -// }); -// // 收藏按钮:翻译中禁止收藏, 无原文禁止收藏,无译文禁止收藏 -// mBinding.collectTrans.setOnClickListener(new View.OnClickListener() { -// private DbTranslation dbTranslation; -// -// @Override -// public void onClick(View v) { -// if (translating) return; -// -// final String sourceTxt = sourceText; -// if (TextUtils.isEmpty(sourceTxt)) return; -// -// if (collectCurrent) { -// collectCurrent = false; -// getDbTranslation(getActivity()).collectJust(false); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// } else { -// collectCurrent = true; -// getDbTranslation(getActivity()).collectJust(true); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collectedtrans); -// } -// } -// -// private DbTranslation getDbTranslation(Context context) { -// if (null == dbTranslation) { -// dbTranslation = new DbTranslation(context); -// } -// return dbTranslation; -// } -// }); -// -// mBinding.changeLanguage.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// launchLanguageSet(); -// } -// }); -// -// return mBinding.getRoot(); -// } -// -// @Override -// public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { -// super.onViewCreated(view, savedInstanceState); -// } -// -// @Override -// public void onResume() { -// super.onResume(); -// -// // 每次回来可能会更新 -// mBinding.languageSource.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.languageTarget.setText(TranslateWordApp.getTargetLanguage()); -// mBinding.sourceLanguage2.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.targetLanguage2.setText(TranslateWordApp.getTargetLanguage()); -// -// // TODO: 可以判断是否需要再次请求原生广告 -// } -// -// @Override -// public void onStop() { -// super.onStop(); -// -// if (null != tts) tts.stop(); -// } -// -// @Override -// public void onDestroy() { -// super.onDestroy(); -// if (null != tts) tts.shutdown(); -// } -// -// public void launchCamera() { -// if (withCameraPermission()) { -// launchCameraApi(); -// } else { -// if (null != permissionLauncher) { -// permissionLauncher.launch(Manifest.permission.CAMERA); -// } -// } -// } -// -// private void launchCameraApi() { -// if (null != activityLauncher) { -// activityLauncher.launch(new Intent(getActivity(), TranslateCameraActivity.class)); -// } -// } -// -// private void translate(@NonNull final String text) { -// // step1. 叫用户检查网络连接 -// if (translating) { -// // 第一次点击翻译按钮后 可能会延迟响应结果,翻译期间再次点击翻译按钮无效 -// Logger.d("log", "translating(not post data)..."); -// return; -// } -// Logger.d("log", "translating..."); -// reset(); -// -// translating = true; -// final HashMap param = new HashMap<>(); -// param.put("sourceLanguage", TranslateWordApp.getSourceLanguageCode()); -// param.put("translationLanguage", TranslateWordApp.getTargetLanguageCode()); -// param.put("text", text); -// -// sourceText = text; -// mBinding.result.setText("translating..."); -// Translator translator = new GoogleTranslator(); -// translator.translate(param, new GoogleTranslator.GoogleTranslateCallback() { -// @Override -// public void onResponse(String val) { -// translating = false; -// -// if (!TextUtils.isEmpty(val)) { -// TranslateMainActivity activity = null; -// if (getActivity() instanceof TranslateMainActivity) { -// activity = (TranslateMainActivity) getActivity(); -// } -// if (null != activity) { -// activity.runOnUiThread(new Runnable() { -// @Override -// public void run() { -// mBinding.result.setText(val); -// addLog(val); -// } -// }); -// } -// } -// } -// -// private void addLog(String targetTxt) { -// FragmentActivity activity = getActivity(); -// if (null != activity) { -// DbTranslation dbTranslation = new DbTranslation(activity); -// Translations translations = new Translations(TranslateWordApp.getSourceLanguage(), sourceText, TranslateWordApp.getTargetLanguage(), targetTxt); -// dbTranslation.addTranslation(translations); -// } -// } -// }); -// } -// -// private void reset() { -// // 归位收藏图片 -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// // 归位收藏备份文本 -// sourceText = ""; -// // 设置当前未处于收藏状态 -// collectCurrent = false; -// // 当前不处于翻译状态 -// translating = false; -// } -// -// private boolean withCameraPermission() { -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { -// FragmentActivity activity = getActivity(); -// if (null == activity) return false; -// return ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED; -// } else { -// return true; -// } -// } -// -// private void launchLanguageSet() { -// Intent intent = new Intent(getActivity(), TranslateChangeLanguageActivity.class); -// getActivity().startActivity(intent); -// } -// -// -} \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateTextFragment.java b/app/src/main/java/com/assimilate/alltrans/fragments/TranslateTextFragment.java deleted file mode 100644 index 029beb1..0000000 --- a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateTextFragment.java +++ /dev/null @@ -1,280 +0,0 @@ -package com.assimilate.alltrans.fragments; - -import androidx.fragment.app.Fragment; - -public class TranslateTextFragment extends Fragment { - -// private FragmentTranslateTextBinding mBinding; -// -// private TextToSpeech tts; -// -// private boolean translating = false; -// private boolean adLoading = false; // 广告是否处于加载中 -// private boolean collectCurrent = false; -// -// private String sourceText = ""; // 可能会有一种屌毛,翻译完成后,先去输入框删几个字符,然后再去点击收藏按钮。所以每次翻译前备份一下 -// -// @Override -// public void onCreate(@Nullable Bundle savedInstanceState) { -// super.onCreate(savedInstanceState); -// -// translating = false; -// adLoading = false; -// collectCurrent = false; -// -// tts = new TextToSpeech(getActivity(), new TextToSpeech.OnInitListener() { -// @Override -// public void onInit(int status) { -// if (null != tts && TextToSpeech.SUCCESS == status) -// tts.setLanguage(Locale.getDefault()); -// } -// }); -// } -// -// @SuppressLint("ClickableViewAccessibility") -// @Override -// public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { -// mBinding = FragmentTranslateTextBinding.inflate(getLayoutInflater()); -// -// // 朗读原文 -// mBinding.speakSource.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 清空翻译输入框与结果文本 -// mBinding.clear.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// mBinding.result.setText(""); -// mBinding.inputText.setText(""); -// -// reset(); -// } -// }); -// // 输入enter键后直接翻译 & 关闭软件盘与失去光标闪烁 -// mBinding.inputText.setOnTouchListener((view1, motionEvent) -> { -// mBinding.inputText.setCursorVisible(true); -// return false; -// }); -// mBinding.inputText.setOnEditorActionListener(new TextView.OnEditorActionListener() { -// @Override -// public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { -// if (i == EditorInfo.IME_ACTION_SEARCH) { -// hiddenSoftKeyboard(); -// final String text = mBinding.inputText.getText().toString(); -// if (!TextUtils.isEmpty(text)) { -// translate(text); -// } -// return true; -// } -// return false; -// } -// -// private void hiddenSoftKeyboard() { -// final FragmentActivity activity = getActivity(); -// final IBinder binder = mBinding.inputText.getWindowToken(); -// if (null != activity && null != binder) { -// InputMethodManager inputMethodManager = -// (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); -// boolean hideSoftInputFromWindow = -// inputMethodManager.hideSoftInputFromWindow(binder, InputMethodManager.HIDE_NOT_ALWAYS); -// if (hideSoftInputFromWindow) { -// mBinding.inputText.setCursorVisible(false); -// } -// } -// } -// }); -// -// // 朗读译文 -// mBinding.speakTarget.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 分享译文 -// mBinding.shareTrans.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// if (translating) { -// Widget.makeToast(getActivity(), "Translating..."); -// return; -// } -// -// final String share = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(share)) { -// Intent intent = new Intent(Intent.ACTION_SEND); -// intent.setType("text/plain"); -// intent.putExtra(Intent.EXTRA_TEXT, share); -// startActivity(Intent.createChooser(intent, "Share " + getString(R.string.app_name))); -// } -// } -// }); -// // 复制到粘贴板 -// mBinding.copyTrans.setOnClickListener(new View.OnClickListener() { -// private final String tip = "Copied to clipboard!"; -// @Override -// public void onClick(View v) { -// final String share = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(share)) { -// ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); -// ClipData clipData = ClipData.newPlainText("targetValue", share); -// clipboardManager.setPrimaryClip(clipData); -// Widget.makeToast(getActivity(), tip); -// } -// } -// }); -// // 收藏按钮:翻译中禁止收藏, 无原文禁止收藏,无译文禁止收藏 -// mBinding.collectTrans.setOnClickListener(new View.OnClickListener() { -// private DbTranslation dbTranslation; -// -// @Override -// public void onClick(View v) { -// if (translating) return; -// -// final String sourceTxt = sourceText; -// if (TextUtils.isEmpty(sourceTxt)) return; -// -// if (collectCurrent) { -// collectCurrent = false; -// getDbTranslation(getActivity()).collectJust(false); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// } else { -// collectCurrent = true; -// getDbTranslation(getActivity()).collectJust(true); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collectedtrans); -// } -// } -// -// private DbTranslation getDbTranslation(Context context) { -// if (null == dbTranslation) { -// dbTranslation = new DbTranslation(context); -// } -// return dbTranslation; -// } -// }); -// -// mBinding.changeLanguage.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// launchLanguageSet(); -// } -// }); -// -// return mBinding.getRoot(); -// } -// -// @Override -// public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { -// super.onViewCreated(view, savedInstanceState); -// } -// -// @Override -// public void onResume() { -// super.onResume(); -// -// // 每次回来可能会更新 -// mBinding.languageSource.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.languageTarget.setText(TranslateWordApp.getTargetLanguage()); -// mBinding.sourceLanguage2.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.targetLanguage2.setText(TranslateWordApp.getTargetLanguage()); -// -// // TODO: 可以判断是否需要再次请求原生广告 -// } -// -// @Override -// public void onStop() { -// super.onStop(); -// if (null != tts) tts.stop(); -// } -// -// @Override -// public void onDestroy() { -// super.onDestroy(); -// if (null != tts) tts.shutdown(); -// } -// -// private void translate(@NonNull final String text) { -// // step1. 叫用户检查网络连接 -// if (translating) { -// // 第一次点击翻译按钮后 可能会延迟响应结果,翻译期间再次点击翻译按钮无效 -// Logger.d("log", "translating(not post data)..."); -// return; -// } -// Logger.d("log", "translating..."); -// reset(); -// -// translating = true; -// final HashMap param = new HashMap<>(); -// param.put("sourceLanguage", TranslateWordApp.getSourceLanguageCode()); -// param.put("translationLanguage", TranslateWordApp.getTargetLanguageCode()); -// param.put("text", text); -// -// sourceText = text; -// mBinding.result.setText("translating..."); -// Translator translator = new GoogleTranslator(); -// translator.translate(param, new GoogleTranslator.GoogleTranslateCallback() { -// @Override -// public void onResponse(String val) { -// translating = false; -// -// if (!TextUtils.isEmpty(val)) { -// TranslateMainActivity activity = null; -// if (getActivity() instanceof TranslateMainActivity) { -// activity = (TranslateMainActivity) getActivity(); -// } -// if (null != activity) { -// activity.runOnUiThread(new Runnable() { -// @Override -// public void run() { -// mBinding.result.setText(val); -// addLog(val); -// } -// }); -// } -// } -// } -// -// private void addLog(String targetTxt) { -// FragmentActivity activity = getActivity(); -// if (null != activity) { -// DbTranslation dbTranslation = new DbTranslation(activity); -// Translations translations = new Translations(TranslateWordApp.getSourceLanguage(), sourceText, TranslateWordApp.getTargetLanguage(), targetTxt); -// dbTranslation.addTranslation(translations); -// } -// } -// }); -// } -// -// private void reset() { -// // 归位收藏图片 -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// // 归位收藏备份文本 -// sourceText = ""; -// // 设置当前未处于收藏状态 -// collectCurrent = false; -// // 当前不处于翻译状态 -// translating = false; -// } -// -// private void launchLanguageSet() { -// Intent intent = new Intent(getActivity(), TranslateChangeLanguageActivity.class); -// getActivity().startActivity(intent); -// } -// -// -} \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateVoiceFragment.java b/app/src/main/java/com/assimilate/alltrans/fragments/TranslateVoiceFragment.java deleted file mode 100644 index 46f5da9..0000000 --- a/app/src/main/java/com/assimilate/alltrans/fragments/TranslateVoiceFragment.java +++ /dev/null @@ -1,285 +0,0 @@ -package com.assimilate.alltrans.fragments; - - -import androidx.fragment.app.Fragment; - -// https://play.google.com/store/apps/details?id=com.google.android.tts -public class TranslateVoiceFragment extends Fragment { - -// private FragmentTranslateVoiceBinding mBinding; -// private ActivityResultLauncher launcher; -// -// private TextToSpeech tts; -// -// private boolean translating = false; -// private boolean adLoading = false; // 广告是否处于加载中 -// private boolean collectCurrent = false; -// -// private String sourceText = ""; // 可能会有一种屌毛,翻译完成后,先去输入框删几个字符,然后再去点击收藏按钮。所以每次翻译前备份一下 -// -// @Override -// public void onCreate(@Nullable Bundle savedInstanceState) { -// super.onCreate(savedInstanceState); -// -// translating = false; -// adLoading = false; -// collectCurrent = false; -// -// tts = new TextToSpeech(getActivity(), new TextToSpeech.OnInitListener() { -// @Override -// public void onInit(int status) { -// if (null != tts && TextToSpeech.SUCCESS == status) -// tts.setLanguage(Locale.getDefault()); -// } -// }); -// -// launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback() { -// @Override -// public void onActivityResult(ActivityResult result) { -// Intent data = result.getData(); -// if (Activity.RESULT_OK == result.getResultCode() && null != data) { -// String speech = data.getStringArrayListExtra("android.speech.extra.RESULTS").get(0); -// if (!TextUtils.isEmpty(speech)) { -// mBinding.inputText.setText(speech); -// translate(speech); -// } -// } -// } -// }); -// } -// -// @Override -// public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { -// mBinding = FragmentTranslateVoiceBinding.inflate(getLayoutInflater()); -// -// // 朗读原文 -// mBinding.speakSource.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 清空翻译输入框与结果文本 -// mBinding.clear.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// mBinding.result.setText(""); -// mBinding.inputText.setText(""); -// -// reset(); -// } -// }); -// // 说话完成后,自动翻译 -// mBinding.startSpeak.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// Intent speech_intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); -// speech_intent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, 5000); // 设置5秒的静默时间 -// speech_intent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, 5000); // 设置5秒的可能完全静默时间 -// -// speech_intent.putExtra("android.speech.extra.LANGUAGE_MODEL", TranslateWordApp.getSourceLanguage()); -// speech_intent.putExtra("android.speech.extra.LANGUAGE", TranslateWordApp.getSourceLanguageCode()); -// speech_intent.putExtra("android.speech.extra.LANGUAGE_PREFERENCE", TranslateWordApp.getSourceLanguage()); -// try { -// if (null != launcher) launcher.launch(speech_intent); -// } catch (ActivityNotFoundException ea) { -// Widget.makeToast(getActivity(), "Something went wrong."); -// } -// } -// }); -// -// // 朗读译文 -// mBinding.speakTarget.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// String speech = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(speech)) { -// if (null != tts -// && TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault())) { -// tts.speak(speech, 0, null, null); -// } -// } -// } -// }); -// // 分享译文 -// mBinding.shareTrans.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// if (translating) { -// Widget.makeToast(getActivity(), "Translating..."); -// return; -// } -// -// final String share = mBinding.result.getText().toString().trim(); -// if (!TextUtils.isEmpty(share)) { -// Intent intent = new Intent(Intent.ACTION_SEND); -// intent.setType("text/plain"); -// intent.putExtra(Intent.EXTRA_TEXT, share); -// startActivity(Intent.createChooser(intent, "Share " + getString(R.string.app_name))); -// } -// } -// }); -// // 复制到粘贴板 -// mBinding.copyTrans.setOnClickListener(new View.OnClickListener() { -// private final String tip = "Copied to clipboard!"; -// @Override -// public void onClick(View v) { -// final String share = mBinding.result.getText().toString().trim(); -// if (!translating && !TextUtils.isEmpty(share)) { -// ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); -// ClipData clipData = ClipData.newPlainText("targetValue", share); -// clipboardManager.setPrimaryClip(clipData); -// Widget.makeToast(getActivity(), tip); -// } -// } -// }); -// // 收藏按钮:翻译中禁止收藏, 无原文禁止收藏,无译文禁止收藏 -// mBinding.collectTrans.setOnClickListener(new View.OnClickListener() { -// private DbTranslation dbTranslation; -// -// @Override -// public void onClick(View v) { -// if (translating) return; -// -// final String sourceTxt = sourceText; -// if (TextUtils.isEmpty(sourceTxt)) return; -// -// if (collectCurrent) { -// collectCurrent = false; -// getDbTranslation(getActivity()).collectJust(false); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// } else { -// collectCurrent = true; -// getDbTranslation(getActivity()).collectJust(true); -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collectedtrans); -// } -// } -// -// private DbTranslation getDbTranslation(Context context) { -// if (null == dbTranslation) { -// dbTranslation = new DbTranslation(context); -// } -// return dbTranslation; -// } -// }); -// -// mBinding.changeLanguage.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// launchLanguageSet(); -// } -// }); -// -// return mBinding.getRoot(); -// } -// -// @Override -// public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { -// super.onViewCreated(view, savedInstanceState); -// -// } -// -// @Override -// public void onResume() { -// super.onResume(); -// -// mBinding.languageSource.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.languageTarget.setText(TranslateWordApp.getTargetLanguage()); -// mBinding.sourceLanguage2.setText(TranslateWordApp.getSourceLanguage()); -// mBinding.targetLanguage2.setText(TranslateWordApp.getTargetLanguage()); -// -// // TODO: 可以判断是否需要再次请求原生广告 -// } -// -// @Override -// public void onStop() { -// super.onStop(); -// if (null != tts) tts.stop(); -// } -// -// @Override -// public void onDestroy() { -// super.onDestroy(); -// if (null != tts) tts.shutdown(); -// if (null != launcher) { -// launcher.unregister(); -// launcher = null; -// } -// } -// -// private void translate(@NonNull final String text) { -// // step1. 叫用户检查网络连接 -// if (translating) { -// // 第一次点击翻译按钮后 可能会延迟响应结果,翻译期间再次点击翻译按钮无效 -// Logger.d("log", "translating(not post data)..."); -// return; -// } -// Logger.d("log", "translating..."); -// reset(); -// -// translating = true; -// final HashMap param = new HashMap<>(); -// param.put("sourceLanguage", TranslateWordApp.getSourceLanguageCode()); -// param.put("translationLanguage", TranslateWordApp.getTargetLanguageCode()); -// param.put("text", text); -// -// sourceText = text; -// mBinding.result.setText("translating..."); -// Translator translator = new GoogleTranslator(); -// translator.translate(param, new GoogleTranslator.GoogleTranslateCallback() { -// @Override -// public void onResponse(String val) { -// translating = false; -// -// if (!TextUtils.isEmpty(val)) { -// TranslateMainActivity activity = null; -// if (getActivity() instanceof TranslateMainActivity) { -// activity = (TranslateMainActivity) getActivity(); -// } -// if (null != activity) { -// activity.runOnUiThread(new Runnable() { -// @Override -// public void run() { -// mBinding.result.setText(val); -// addLog(val); -// } -// }); -// } -// } -// } -// -// private void addLog(String targetTxt) { -// FragmentActivity activity = getActivity(); -// if (null != activity) { -// DbTranslation dbTranslation = new DbTranslation(activity); -// Translations translations = new Translations(TranslateWordApp.getSourceLanguage(), sourceText, TranslateWordApp.getTargetLanguage(), targetTxt); -// dbTranslation.addTranslation(translations); -// } -// } -// }); -// } -// -// private void reset() { -// // 归位收藏图片 -// mBinding.collectTrans.setImageResource(R.mipmap.trw_ic_collecttrans); -// // 归位收藏备份文本 -// sourceText = ""; -// // 设置当前未处于收藏状态 -// collectCurrent = false; -// // 当前不处于翻译状态 -// translating = false; -// } -// -// private void launchLanguageSet() { -// Intent intent = new Intent(getActivity(), TranslateChangeLanguageActivity.class); -// getActivity().startActivity(intent); -// } -// - -} \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/mydb/DbTranslation.java b/app/src/main/java/com/assimilate/alltrans/mydb/DbTranslation.java index a7a20d0..713b6a2 100644 --- a/app/src/main/java/com/assimilate/alltrans/mydb/DbTranslation.java +++ b/app/src/main/java/com/assimilate/alltrans/mydb/DbTranslation.java @@ -115,7 +115,6 @@ public class DbTranslation extends SQLiteOpenHelper { } /** - * 是否收藏刚刚那条翻译 * @param collect 收藏|不收藏 */ public boolean collectJust(boolean collect) { diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java index 06be3ed..13c3a92 100644 --- a/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java +++ b/app/src/main/java/com/assimilate/alltrans/viewui/HistoryActivity.java @@ -6,6 +6,9 @@ import android.speech.tts.TextToSpeech; import android.view.View; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.LinearLayoutManager; import com.assimilate.alltrans.R; @@ -26,7 +29,7 @@ public class HistoryActivity extends AppCompatActivity { public final static String COMMAND_COLLECTION = "remove-collection"; public final static String COMMAND_HISTORY = "remove-history"; private TextToSpeech tts; - private ActivityHistoryBinding mBinding; + private ActivityHistoryBinding binding; private HashSet ids; // 通过id 删除数据库文件 private HashSet items; // 通许index 删除界面上面的数据 private boolean operationCollection = false; @@ -34,18 +37,19 @@ public class HistoryActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mBinding = ActivityHistoryBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); + binding = ActivityHistoryBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(26, systemBars.top, 26, systemBars.bottom); + return insets; + }); ids = new HashSet<>(); items = new HashSet<>(); String extra = getIntent().getStringExtra(COMMAND); - if (COMMAND_COLLECTION.equals(extra)) { - operationCollection = true; - } else { - operationCollection = false; - } + operationCollection = COMMAND_COLLECTION.equals(extra); tts = new TextToSpeech(this, new TextToSpeech.OnInitListener() { @Override @@ -58,16 +62,16 @@ public class HistoryActivity extends AppCompatActivity { ArrayList translations = new ArrayList<>(); if (operationCollection) { // 查出收藏的翻译记录 - mBinding.tvFuncTrans.setText("Collect"); - mBinding.ivFuncTrans.setImageResource(R.mipmap.ic_launcher); + binding.tvFuncTrans.setText("Collect"); + binding.ivFuncTrans.setImageResource(R.drawable.ic_add); ArrayList list = new DbTranslation(this).getTranslations(true); if (null != list && !list.isEmpty()) { translations.addAll(list); } } else { // 查出所有的翻译记录 - mBinding.tvFuncTrans.setText("History"); - mBinding.ivFuncTrans.setImageResource(R.mipmap.ic_launcher); + binding.tvFuncTrans.setText("History"); + binding.ivFuncTrans.setImageResource(R.drawable.ic_add); ArrayList list = new DbTranslation(this).getTranslations(false); if (null != list && !list.isEmpty()) { translations.addAll(list); @@ -104,15 +108,15 @@ public class HistoryActivity extends AppCompatActivity { private void updateBtn() { if (ids.isEmpty()) { - mBinding.remove.setImageResource(R.mipmap.ic_launcher); + binding.remove.setVisibility(View.INVISIBLE); } else { - mBinding.remove.setImageResource(R.mipmap.ic_launcher); + binding.remove.setVisibility(View.VISIBLE); } } }); final LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); - mBinding.remove.setOnClickListener(new View.OnClickListener() { + binding.remove.setOnClickListener(new View.OnClickListener() { private DbTranslation dbTranslation; @Override @@ -142,7 +146,7 @@ public class HistoryActivity extends AppCompatActivity { adapter.updateSet(integerArrayList); items.clear(); } - mBinding.remove.setImageResource(R.mipmap.ic_launcher); + binding.remove.setVisibility(View.VISIBLE); } private DbTranslation getDbTranslation(Context context) { @@ -153,8 +157,8 @@ public class HistoryActivity extends AppCompatActivity { } }); - mBinding.histories.setLayoutManager(layoutManager); - mBinding.histories.setAdapter(adapter); + binding.histories.setLayoutManager(layoutManager); + binding.histories.setAdapter(adapter); } @Override diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt index ad82128..8f77b68 100644 --- a/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt +++ b/app/src/main/java/com/assimilate/alltrans/viewui/LanguageChangeActivity.kt @@ -1,15 +1,22 @@ package com.assimilate.alltrans.viewui import android.os.Bundle +import android.util.Log import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.recyclerview.widget.LinearLayoutManager +import com.assimilate.alltrans.MyApp import com.assimilate.alltrans.R +import com.assimilate.alltrans.adapters.LanguageAdapter +import com.assimilate.alltrans.common.Language +import com.assimilate.alltrans.common.LanguagesConstants import com.assimilate.alltrans.databinding.ActivityLanguageChangeBinding class LanguageChangeActivity : AppCompatActivity() { private lateinit var binding: ActivityLanguageChangeBinding + private var lastTranslateLanguage = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLanguageChangeBinding.inflate(layoutInflater) @@ -20,5 +27,44 @@ class LanguageChangeActivity : AppCompatActivity() { v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } + + initList() + initClick() + } + + private fun initClick() { + binding.tvChangeSource.setOnClickListener { + lastTranslateLanguage = false + } + binding.tvChangeTarget.setOnClickListener { + lastTranslateLanguage = true + + } + } + + private fun initList() { + val languages: ArrayList = LanguagesConstants.getInstance().getList( + this + ) + if (languages.isNotEmpty()) { + val adapter = + LanguageAdapter( + this, languages + ) { _, language -> + + Log.d("fsdafsdfd", language.language) + if (lastTranslateLanguage) { + MyApp.setTargetLanguage(language) + onBackPressed() + } else { + MyApp.setSourceLanguage(language) + } + binding.tvChangeSource.text = MyApp.getSourceLanguage() + binding.tvChangeTarget.text = MyApp.getTargetLanguage() + } + val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + binding.listLanguages.setLayoutManager(layoutManager) + binding.listLanguages.setAdapter(adapter) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt index 52dfee4..b95fd5d 100644 --- a/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt +++ b/app/src/main/java/com/assimilate/alltrans/viewui/MainActivity.kt @@ -1,18 +1,44 @@ package com.assimilate.alltrans.viewui +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.ClipDescription +import android.content.ClipboardManager +import android.content.Context import android.content.Intent +import android.media.projection.MediaProjectionManager +import android.os.Build import android.os.Bundle +import android.speech.RecognizerIntent +import android.text.Editable +import android.text.TextUtils +import android.text.TextWatcher +import android.view.View +import android.widget.EditText import androidx.activity.enableEdgeToEdge +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.assimilate.alltrans.MyApp import com.assimilate.alltrans.R +import com.assimilate.alltrans.allservice.SusService +import com.assimilate.alltrans.common.Widget import com.assimilate.alltrans.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { + private var launcher: ActivityResultLauncher? = null + + private lateinit var binding: ActivityMainBinding + private lateinit var lcm: LocalBroadcastManager + + private lateinit var mediaProjectionManager: MediaProjectionManager + private val REQUEST_CODE_SCREEN_CAPTURE = 1001 override fun onCreate(savedInstanceState: Bundle?) { @@ -23,12 +49,84 @@ class MainActivity : AppCompatActivity() { ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + v.setPadding(26, systemBars.top + 26, 26, systemBars.bottom) insets } - + initSet() initClick() + registerResult() + + } + + 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) + } + } + } + } + + private fun startScreenCapture() { + val captureIntent = mediaProjectionManager.createScreenCaptureIntent() + startActivityForResult(captureIntent, REQUEST_CODE_SCREEN_CAPTURE) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == Activity.RESULT_OK) { + startSusService(resultCode, data) + } + } + + private fun startSusService(resultCode: Int, data: Intent?) { + val serviceIntent = Intent(this, SusService::class.java).apply { + putExtra("resultCode", resultCode) + putExtra("data", data) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(serviceIntent) + } else { + startService(serviceIntent) + } + } + + + private fun initView() { + binding.chSourceLanguage.text = MyApp.getSourceLanguage() + binding.chTargetLanguage.text = MyApp.getTargetLanguage() + } + + private fun initSet() { + lcm = LocalBroadcastManager.getInstance(this) + + // 监听EditText的文本变化 + binding.etText.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + // 根据EditText的内容显示或隐藏粘贴按钮 + if (s.isNullOrEmpty()) { + binding.tvMainTrans.visibility = View.GONE + + } else { + binding.tvMainTrans.visibility = View.VISIBLE + } + } + }) + } @@ -38,20 +136,29 @@ class MainActivity : AppCompatActivity() { Intent(this, StillImageActivity::class.java) ) } + binding.tvMainVoice.setOnClickListener { + voiceToText() + toTextTransResult() + } + binding.tvMainPaste.setOnClickListener { + pasteFromClipboard(binding.etText) + toTextTransResult() + + } binding.tvMainTrans.setOnClickListener { - startActivity(Intent(this, TextResultActivity::class.java)) + toTextTransResult() } binding.ivMainSetting.setOnClickListener { startActivity( Intent(this, SettingsActivity::class.java) ) } - binding.sourceLanguage2.setOnClickListener { + binding.chSourceLanguage.setOnClickListener { startActivity( Intent(this, LanguageChangeActivity::class.java) ) } - binding.targetLanguage2.setOnClickListener { + binding.chTargetLanguage.setOnClickListener { startActivity( Intent(this, LanguageChangeActivity::class.java) ) @@ -67,9 +174,95 @@ class MainActivity : AppCompatActivity() { ) } binding.ivQuickStart.setOnClickListener { - startActivity( - Intent(this, QuickSetActivity::class.java) - ) + + mediaProjectionManager = + getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager + startScreenCapture() + + val intent = Intent(this, SusService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(intent) + } else { + startService(intent) + } + } + + } + + private fun toTextTransResult() { + if (binding.etText.text.isEmpty()) { + return + } + val intent = Intent(this, TextResultActivity::class.java) + // 将字符串数据添加到Intent中 + intent.putExtra("source_text", binding.etText.text.toString()) + startActivity(intent) + binding.etText.text = null + } + + // 语音转文本 + private fun voiceToText() { + val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) + speechIntent.putExtra( + RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, + 5000 + ) // 设置5秒的静默时间 + speechIntent.putExtra( + RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, + 5000 + ) // 设置5秒的可能完全静默时间 + + speechIntent.putExtra( + "android.speech.extra.LANGUAGE_MODEL", + MyApp.getSourceLanguage() + ) + speechIntent.putExtra( + "android.speech.extra.LANGUAGE", + MyApp.getSourceLanguageCode() + ) + speechIntent.putExtra( + "android.speech.extra.LANGUAGE_PREFERENCE", + MyApp.getSourceLanguage() + ) + try { + launcher?.launch(speechIntent) + } catch (ea: ActivityNotFoundException) { + Widget.makeToast(this, "Something went wrong.") } } + + + // 粘贴文本 + private fun pasteFromClipboard(etText: EditText) { + val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + if (clipboard.hasPrimaryClip() && clipboard.primaryClipDescription!!.hasMimeType( + ClipDescription.MIMETYPE_TEXT_PLAIN + ) + ) { + val item = clipboard.primaryClip!!.getItemAt(0) + val text = item.text.toString() + if (text.isNotEmpty()) { + // 在EditText中显示粘贴的文本 + etText.setText(text) + + etText.requestFocus() // 获取焦点 + } + } + } + + + override fun onResume() { + super.onResume() + initView() + } + + override fun onDestroy() { + super.onDestroy() + if (null != launcher) { + launcher!!.unregister() + launcher = null + } + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt index a43927f..834be83 100644 --- a/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt +++ b/app/src/main/java/com/assimilate/alltrans/viewui/StillImageActivity.kt @@ -1,9 +1,11 @@ package com.assimilate.alltrans.viewui +import android.Manifest import android.app.Activity import android.content.ContentValues import android.content.Intent +import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Bitmap import android.net.Uri @@ -22,6 +24,8 @@ import android.widget.PopupMenu import android.widget.Spinner import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.content.ContextCompat import com.assimilate.alltrans.R import com.assimilate.alltrans.common.BitmapUtils import com.assimilate.alltrans.common.TextRecognitionProcessor @@ -53,9 +57,25 @@ class StillImageActivity : AppCompatActivity() { private var imageMaxHeight = 0 private var imageProcessor: VisionImageProcessor? = null + private val REQUEST_CAMERA_PERMISSION = 100 + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_still_image) + + // 检查并请求相机权限 + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED + ) { + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.CAMERA), + REQUEST_CAMERA_PERMISSION + ) + } + + findViewById(R.id.select_image_button).setOnClickListener { view: View -> // Menu for selecting either: a) take new photo b) select from existing val popup = PopupMenu(this@StillImageActivity, view) @@ -109,6 +129,32 @@ class StillImageActivity : AppCompatActivity() { } } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + when (requestCode) { + REQUEST_CAMERA_PERMISSION -> { + if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + // Permission is granted, proceed with camera + startCameraIntentForResult() + } else { + // Permission denied, show a message to the user + Toast.makeText( + this, + "Camera permission is required to use the camera", + Toast.LENGTH_SHORT + ).show() + } + return + } + } + } + + public override fun onResume() { super.onResume() Log.d(TAG, "onResume") @@ -200,7 +246,21 @@ class StillImageActivity : AppCompatActivity() { outState.putString(KEY_SELECTED_SIZE, selectedSize) } - private fun startCameraIntentForResult() { // Clean up last time's image + private fun startCameraIntentForResult() { + // Ensure permission is still granted before starting camera intent + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.CAMERA + ) != PackageManager.PERMISSION_GRANTED + ) { + Toast.makeText( + this, + "Camera permission is required to use the camera", + Toast.LENGTH_SHORT + ).show() + return + } + imageUri = null preview!!.setImageBitmap(null) val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) diff --git a/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt b/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt index 732d74a..27a3496 100644 --- a/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt +++ b/app/src/main/java/com/assimilate/alltrans/viewui/TextResultActivity.kt @@ -1,17 +1,32 @@ package com.assimilate.alltrans.viewui +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context import android.content.Intent import android.os.Bundle +import android.speech.tts.TextToSpeech +import android.text.TextUtils import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import com.assimilate.alltrans.MyApp import com.assimilate.alltrans.R +import com.assimilate.alltrans.common.Logger +import com.assimilate.alltrans.common.Widget import com.assimilate.alltrans.databinding.ActivityTextResultBinding +import com.assimilate.alltrans.http.GoogleTranslator +import com.assimilate.alltrans.http.Translator +import com.assimilate.alltrans.mydb.DbTranslation +import com.assimilate.alltrans.mydb.Translations +import java.util.Locale class TextResultActivity : AppCompatActivity() { private lateinit var binding: ActivityTextResultBinding + private lateinit var tts: TextToSpeech + private var translating = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -24,14 +39,133 @@ class TextResultActivity : AppCompatActivity() { insets } + initSet() initClick() } - private fun initClick() { - binding.ivReToPhoto.setOnClickListener { - startActivity( - Intent(this, StillImageActivity::class.java) + private fun initSet() { + // 获取Intent + val intent = intent + val receivedString = intent.getStringExtra("source_text") + binding.tvTrSource.text = receivedString + translate(binding.tvTrSource.text.toString()) + + + tts = TextToSpeech(this) { status -> + if (TextToSpeech.SUCCESS == status) tts.setLanguage( + Locale.getDefault() ) } + } + + private fun initClick() { + binding.ivTrBack.setOnClickListener { onBackPressed() } + binding.tvTrNewTrans.setOnClickListener { onBackPressed() } + binding.ivReToPhoto.setOnClickListener { + startActivity(Intent(this, StillImageActivity::class.java)) + } + binding.ivTrCopy.setOnClickListener { copyToClipboard() } + binding.ivSourceClear.setOnClickListener { onBackPressed() } + binding.ivSourceTts.setOnClickListener { readText(binding.tvTrSource.text.toString()) } + binding.ivTargetTts.setOnClickListener { readText(binding.tvTrTarget.text.toString()) } + binding.ivTrTargetShare.setOnClickListener { shareText(binding.tvTrTarget.text.toString()) } + binding.ivTrCollect.setOnClickListener { addCollect() } + + } + + private fun translate(text: String) { + if (text.isEmpty() || translating) { + Logger.d("log", "translating(not post data)...") + return + } + + translating = true + val param = HashMap().apply { + put("sourceLanguage", MyApp.getSourceLanguageCode()) + put("translationLanguage", MyApp.getTargetLanguageCode()) + put("text", text) + } + + binding.tvTrTarget.text = "translating..." + val translator: Translator = GoogleTranslator() + translator.translate(param, + GoogleTranslator.GoogleTranslateCallback { result -> + translating = false + if (!TextUtils.isEmpty(result)) { + runOnUiThread { + binding.tvTrTarget.text = result + } + addHistory(result) + } + }) + } + + private fun addHistory(transResult: String) { + val dbTranslation = DbTranslation(this) + val translations = Translations( + MyApp.getSourceLanguage(), + binding.tvTrSource.text.toString(), + MyApp.getTargetLanguage(), + transResult + + ) + dbTranslation.addTranslation(translations) + } + + private fun addCollect() { + if (translating) return + val dbTranslation = DbTranslation(this) + dbTranslation.collectJust(true) + + binding.ivTrCollect.setImageResource(R.drawable.ic_like_yes) + } + + private fun shareText(text: String) { + val share: String = text.trim() + if (!TextUtils.isEmpty(share)) { + val intent = Intent(Intent.ACTION_SEND) + intent.setType("text/plain") + intent.putExtra(Intent.EXTRA_TEXT, share) + startActivity(Intent.createChooser(intent, "Share " + getString(R.string.app_name))) + } + } + + private fun readText(text: String) { + val speech: String = text.trim() + if (!TextUtils.isEmpty(speech)) { + if (TextToSpeech.LANG_NOT_SUPPORTED != tts.isLanguageAvailable(Locale.getDefault()) + ) { + tts.speak(speech, 0, null, null) + } + } + + } + + // 复制到粘贴板 + private fun copyToClipboard() { + val tip = "Copied to clipboard!" + val tipNull = "text is null!" + val share = binding.tvTrTarget.text.toString().trim() + if (!TextUtils.isEmpty(share)) { + val clipboardManager = + getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipData = ClipData.newPlainText("targetValue", share) + clipboardManager.setPrimaryClip(clipData) + Widget.makeToast(this, tip) + } else { + Widget.makeToast(this, tipNull) + } + } + + override fun onStop() { + super.onStop() + tts.stop() + } + + override fun onDestroy() { + super.onDestroy() + tts.shutdown() + } + } \ No newline at end of file diff --git a/app/src/main/res/drawable/checkbox_selector.xml b/app/src/main/res/drawable/checkbox_selector.xml new file mode 100644 index 0000000..92b3afc --- /dev/null +++ b/app/src/main/res/drawable/checkbox_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_dashed_line_4b4b4b4.xml b/app/src/main/res/drawable/ic_dashed_line_4b4b4b4.xml index 8df3e00..535ff70 100644 --- a/app/src/main/res/drawable/ic_dashed_line_4b4b4b4.xml +++ b/app/src/main/res/drawable/ic_dashed_line_4b4b4b4.xml @@ -5,7 +5,7 @@ diff --git a/app/src/main/res/drawable/ic_his_choose_def.xml b/app/src/main/res/drawable/ic_his_choose_def.xml new file mode 100644 index 0000000..69044fd --- /dev/null +++ b/app/src/main/res/drawable/ic_his_choose_def.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_his_choose_yes.xml b/app/src/main/res/drawable/ic_his_choose_yes.xml new file mode 100644 index 0000000..e6c7cfe --- /dev/null +++ b/app/src/main/res/drawable/ic_his_choose_yes.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_like_yes.xml b/app/src/main/res/drawable/ic_like_yes.xml new file mode 100644 index 0000000..c59875f --- /dev/null +++ b/app/src/main/res/drawable/ic_like_yes.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_remove.xml b/app/src/main/res/drawable/ic_remove.xml new file mode 100644 index 0000000..1506f42 --- /dev/null +++ b/app/src/main/res/drawable/ic_remove.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/main_bg.xml b/app/src/main/res/drawable/main_bg.xml new file mode 100644 index 0000000..d0ae181 --- /dev/null +++ b/app/src/main/res/drawable/main_bg.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_history.xml b/app/src/main/res/layout/activity_history.xml index f8d9080..6f610d8 100644 --- a/app/src/main/res/layout/activity_history.xml +++ b/app/src/main/res/layout/activity_history.xml @@ -2,6 +2,7 @@ @@ -46,16 +47,20 @@ android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintHeight_percent="0.82" + app:layout_constraintHeight_percent="0.9" app:layout_constraintStart_toStartOf="parent" /> - diff --git a/app/src/main/res/layout/activity_language_change.xml b/app/src/main/res/layout/activity_language_change.xml index 16fe26d..c0f9663 100644 --- a/app/src/main/res/layout/activity_language_change.xml +++ b/app/src/main/res/layout/activity_language_change.xml @@ -5,6 +5,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="#FFF9F9F9" android:paddingTop="16dp" tools:context=".viewui.LanguageChangeActivity"> @@ -23,7 +24,7 @@ app:layout_constraintTop_toTopOf="parent"> + android:text="@string/tr_common" + android:textColor="@color/bg_ff605c62" + android:textSize="12sp" + android:textStyle="bold" /> + android:background="@drawable/button_r10_gray_bg" + android:minHeight="210dp" /> + android:text="@string/tr_other" + android:textColor="@color/bg_ff605c62" + android:textSize="12sp" + android:textStyle="bold" /> + android:layout_marginTop="16dp" + android:background="@drawable/button_r10_gray_bg" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index bdd4f09..a3ceb36 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/main_bg_fff9f9f9" + android:background="@drawable/main_bg" android:paddingStart="16dp" android:paddingTop="16dp" android:paddingEnd="16dp" @@ -67,7 +67,7 @@ app:layout_constraintTop_toBottomOf="@id/tv_main_title"> @@ -191,6 +193,7 @@ android:paddingBottom="9dp" android:text="@string/main_try_text" android:textColor="@color/main_bg_fff9f9f9" + android:visibility="gone" app:drawableEndCompat="@drawable/ic_arrow_try" /> diff --git a/app/src/main/res/layout/activity_text_result.xml b/app/src/main/res/layout/activity_text_result.xml index 45a03f0..16a6922 100644 --- a/app/src/main/res/layout/activity_text_result.xml +++ b/app/src/main/res/layout/activity_text_result.xml @@ -42,6 +42,7 @@ android:orientation="horizontal"> @@ -18,12 +19,15 @@ android:id="@+id/language" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="6dp" + android:layout_marginStart="12dp" android:layout_marginEnd="24dp" android:ellipsize="end" + android:paddingTop="12dp" + android:paddingBottom="12dp" android:singleLine="true" android:text="@string/app_name" android:textColor="@color/black" + android:textSize="16sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/language_flag" diff --git a/app/src/main/res/layout/layout_item_translation.xml b/app/src/main/res/layout/layout_item_translation.xml index 6a85c84..09cfe01 100644 --- a/app/src/main/res/layout/layout_item_translation.xml +++ b/app/src/main/res/layout/layout_item_translation.xml @@ -18,9 +18,11 @@ android:layout_gravity="center_vertical" android:layout_margin="8dp" android:layout_weight="1" + android:maxLines="7" android:gravity="center_vertical" android:textColor="@color/black" - android:textSize="18sp" /> + android:textSize="16sp" + android:textStyle="bold" /> + android:src="@mipmap/ic_launcher" + android:visibility="gone" /> + android:maxLines="7" + android:textColor="@color/text_ffa5a5a5" + android:textSize="12sp" /> + android:button="@drawable/checkbox_selector" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_sus_global.xml b/app/src/main/res/layout/layout_sus_global.xml new file mode 100644 index 0000000..f98032f --- /dev/null +++ b/app/src/main/res/layout/layout_sus_global.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/sus_control_view.xml b/app/src/main/res/layout/sus_control_view.xml index 5ab0ec9..06bbe68 100644 --- a/app/src/main/res/layout/sus_control_view.xml +++ b/app/src/main/res/layout/sus_control_view.xml @@ -2,14 +2,14 @@ + android:textSize="12sp" /> + android:textSize="12sp" /> #FFF9F9F9 + #FFE2EFFF #FF1F1724 #FF0E8CE8 #FFFFFFFF @@ -15,6 +16,10 @@ #FFF6F6F6 #FF2F2F2F + + #FF605C62 + + #FFA5A5A5 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a188fe1..e407a48 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,7 +10,7 @@ Select image Settings - translator + translate Chinese English enter text @@ -42,6 +42,8 @@ Copy Text Photo Translation District Translation + + Delete \ No newline at end of file