package com.assimilate.alltrans.viewui import android.app.Activity import android.content.ContentValues import android.content.Intent import android.content.res.Configuration import android.graphics.Bitmap import android.net.Uri import android.os.Bundle import android.provider.MediaStore import android.util.Log import android.util.Pair import android.view.MenuItem import android.view.View import android.view.ViewTreeObserver import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener import android.widget.ArrayAdapter import android.widget.ImageView import android.widget.PopupMenu import android.widget.Spinner import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.assimilate.alltrans.R import com.assimilate.alltrans.common.BitmapUtils import com.assimilate.alltrans.common.TextRecognitionProcessor import com.assimilate.alltrans.common.VisionImageProcessor import com.assimilate.alltrans.curview.GraphicOverlay import com.google.android.gms.common.annotation.KeepName import com.google.mlkit.vision.text.chinese.ChineseTextRecognizerOptions import com.google.mlkit.vision.text.devanagari.DevanagariTextRecognizerOptions import com.google.mlkit.vision.text.japanese.JapaneseTextRecognizerOptions import com.google.mlkit.vision.text.korean.KoreanTextRecognizerOptions import com.google.mlkit.vision.text.latin.TextRecognizerOptions import java.io.IOException /** Activity demonstrating different image detector features with a still image from camera. */ @KeepName class StillImageActivity : AppCompatActivity() { private var preview: ImageView? = null private var graphicOverlay: GraphicOverlay? = null private var selectedMode = TEXT_RECOGNITION_CHINESE private var selectedSize: String? = SIZE_SCREEN private var isLandScape = false private var imageUri: Uri? = null // Max width (portrait mode) private var imageMaxWidth = 0 // Max height (portrait mode) private var imageMaxHeight = 0 private var imageProcessor: VisionImageProcessor? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_still_image) 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) popup.setOnMenuItemClickListener { menuItem: MenuItem -> val itemId = menuItem.itemId if (itemId == R.id.select_images_from_local) { startChooseImageIntentForResult() return@setOnMenuItemClickListener true } else if (itemId == R.id.take_photo_using_camera) { startCameraIntentForResult() return@setOnMenuItemClickListener true } false } val inflater = popup.menuInflater inflater.inflate(R.menu.camera_button_menu, popup.menu) popup.show() } preview = findViewById(R.id.preview) graphicOverlay = findViewById(R.id.graphic_overlay) populateFeatureSelector() populateSizeSelector() isLandScape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE if (savedInstanceState != null) { imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI) imageMaxWidth = savedInstanceState.getInt(KEY_IMAGE_MAX_WIDTH) imageMaxHeight = savedInstanceState.getInt(KEY_IMAGE_MAX_HEIGHT) selectedSize = savedInstanceState.getString(KEY_SELECTED_SIZE) } val rootView = findViewById(R.id.root) rootView.viewTreeObserver.addOnGlobalLayoutListener( object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { rootView.viewTreeObserver.removeOnGlobalLayoutListener(this) imageMaxWidth = rootView.width imageMaxHeight = rootView.height - findViewById(R.id.control).height if (SIZE_SCREEN == selectedSize) { tryReloadAndDetectInImage() } } } ) val settingsButton = findViewById(R.id.settings_button) settingsButton.setOnClickListener { // val intent = Intent(applicationContext, SettingsActivity::class.java) // intent.putExtra(SettingsActivity.EXTRA_LAUNCH_SOURCE, LaunchSource.STILL_IMAGE) // startActivity(intent) } } public override fun onResume() { super.onResume() Log.d(TAG, "onResume") createImageProcessor() tryReloadAndDetectInImage() } public override fun onPause() { super.onPause() imageProcessor?.run { this.stop() } } public override fun onDestroy() { super.onDestroy() imageProcessor?.run { this.stop() } } private fun populateFeatureSelector() { val featureSpinner = findViewById(R.id.feature_selector) val options: MutableList = ArrayList() options.add(TEXT_RECOGNITION_CHINESE); // 识别中文文本 options.add(TEXT_RECOGNITION_LATIN); // 识别拉丁文本 options.add(TEXT_RECOGNITION_DEVANAGARI); // 识别梵文文本 options.add(TEXT_RECOGNITION_JAPANESE); // 识别日文文本 options.add(TEXT_RECOGNITION_KOREAN); // 识别韩文文本 // Creating adapter for featureSpinner val dataAdapter = ArrayAdapter(this, R.layout.spinner_style, options) // Drop down layout style - list view with radio button dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) // attaching data adapter to spinner featureSpinner.adapter = dataAdapter featureSpinner.onItemSelectedListener = object : OnItemSelectedListener { override fun onItemSelected( parentView: AdapterView<*>, selectedItemView: View?, pos: Int, id: Long ) { if (pos >= 0) { selectedMode = parentView.getItemAtPosition(pos).toString() createImageProcessor() tryReloadAndDetectInImage() } } override fun onNothingSelected(arg0: AdapterView<*>?) {} } } private fun populateSizeSelector() { val sizeSpinner = findViewById(R.id.size_selector) val options: MutableList = ArrayList() options.add(SIZE_SCREEN) options.add(SIZE_1024_768) options.add(SIZE_640_480) options.add(SIZE_ORIGINAL) // Creating adapter for featureSpinner val dataAdapter = ArrayAdapter(this, R.layout.spinner_style, options) // Drop down layout style - list view with radio button dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) // attaching data adapter to spinner sizeSpinner.adapter = dataAdapter sizeSpinner.onItemSelectedListener = object : OnItemSelectedListener { override fun onItemSelected( parentView: AdapterView<*>, selectedItemView: View?, pos: Int, id: Long ) { if (pos >= 0) { selectedSize = parentView.getItemAtPosition(pos).toString() tryReloadAndDetectInImage() } } override fun onNothingSelected(arg0: AdapterView<*>?) {} } } public override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putParcelable(KEY_IMAGE_URI, imageUri) outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth) outState.putInt(KEY_IMAGE_MAX_HEIGHT, imageMaxHeight) outState.putString(KEY_SELECTED_SIZE, selectedSize) } private fun startCameraIntentForResult() { // Clean up last time's image imageUri = null preview!!.setImageBitmap(null) val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) if (takePictureIntent.resolveActivity(packageManager) != null) { val values = ContentValues() values.put(MediaStore.Images.Media.TITLE, "New Picture") values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera") imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) } } private fun startChooseImageIntentForResult() { val intent = Intent() intent.type = "image/*" intent.action = Intent.ACTION_GET_CONTENT startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { tryReloadAndDetectInImage() } else if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == Activity.RESULT_OK) { // In this case, imageUri is returned by the chooser, save it. imageUri = data!!.data tryReloadAndDetectInImage() } else { super.onActivityResult(requestCode, resultCode, data) } } private fun tryReloadAndDetectInImage() { Log.d(TAG, "Try reload and detect image") try { if (imageUri == null) { return } if (SIZE_SCREEN == selectedSize && imageMaxWidth == 0) { // UI layout has not finished yet, will reload once it's ready. return } val imageBitmap = BitmapUtils.getBitmapFromContentUri(contentResolver, imageUri) ?: return // Clear the overlay first graphicOverlay!!.clear() val resizedBitmap: Bitmap resizedBitmap = if (selectedSize == SIZE_ORIGINAL) { imageBitmap } else { // Get the dimensions of the image view val targetedSize: Pair = targetedWidthHeight // Determine how much to scale down the image val scaleFactor = Math.max( imageBitmap.width.toFloat() / targetedSize.first.toFloat(), imageBitmap.height.toFloat() / targetedSize.second.toFloat() ) Bitmap.createScaledBitmap( imageBitmap, (imageBitmap.width / scaleFactor).toInt(), (imageBitmap.height / scaleFactor).toInt(), true ) } preview!!.setImageBitmap(resizedBitmap) if (imageProcessor != null) { graphicOverlay!!.setImageSourceInfo( resizedBitmap.width, resizedBitmap.height, /* isFlipped= */ false ) imageProcessor!!.processBitmap(resizedBitmap, graphicOverlay) } else { Log.e( TAG, "Null imageProcessor, please check adb logs for imageProcessor creation error" ) } } catch (e: IOException) { Log.e(TAG, "Error retrieving saved image") imageUri = null } } private val targetedWidthHeight: Pair get() { val targetWidth: Int val targetHeight: Int when (selectedSize) { SIZE_SCREEN -> { targetWidth = imageMaxWidth targetHeight = imageMaxHeight } SIZE_640_480 -> { targetWidth = if (isLandScape) 640 else 480 targetHeight = if (isLandScape) 480 else 640 } SIZE_1024_768 -> { targetWidth = if (isLandScape) 1024 else 768 targetHeight = if (isLandScape) 768 else 1024 } else -> throw IllegalStateException("Unknown size") } return Pair(targetWidth, targetHeight) } private fun createImageProcessor() { try { when (selectedMode) { TEXT_RECOGNITION_LATIN -> imageProcessor = TextRecognitionProcessor(this, TextRecognizerOptions.Builder().build()) TEXT_RECOGNITION_CHINESE -> imageProcessor = TextRecognitionProcessor( this, ChineseTextRecognizerOptions.Builder().build() ) TEXT_RECOGNITION_DEVANAGARI -> imageProcessor = TextRecognitionProcessor( this, DevanagariTextRecognizerOptions.Builder().build() ) TEXT_RECOGNITION_JAPANESE -> imageProcessor = TextRecognitionProcessor( this, JapaneseTextRecognizerOptions.Builder().build() ) TEXT_RECOGNITION_KOREAN -> imageProcessor = TextRecognitionProcessor( this, KoreanTextRecognizerOptions.Builder().build() ) else -> Log.e(TAG, "Unknown selectedMode: $selectedMode") } } catch (e: Exception) { Log.e(TAG, "Can not create image processor: $selectedMode", e) Toast.makeText( applicationContext, "Can not create image processor: " + e.message, Toast.LENGTH_LONG ) .show() } } companion object { private const val TAG = "StillImageActivity" private const val TEXT_RECOGNITION_LATIN = "Text Recognition Latin" private const val TEXT_RECOGNITION_CHINESE = "Text Recognition Chinese" private const val TEXT_RECOGNITION_DEVANAGARI = "Text Recognition Devanagari" private const val TEXT_RECOGNITION_JAPANESE = "Text Recognition Japanese" private const val TEXT_RECOGNITION_KOREAN = "Text Recognition Korean" private const val SIZE_SCREEN = "w:screen" // Match screen width private const val SIZE_1024_768 = "w:1024" // ~1024*768 in a normal ratio private const val SIZE_640_480 = "w:640" // ~640*480 in a normal ratio private const val SIZE_ORIGINAL = "w:original" // Original image size private const val KEY_IMAGE_URI = "com.google.mlkit.vision.demo.KEY_IMAGE_URI" private const val KEY_IMAGE_MAX_WIDTH = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_WIDTH" private const val KEY_IMAGE_MAX_HEIGHT = "com.google.mlkit.vision.demo.KEY_IMAGE_MAX_HEIGHT" private const val KEY_SELECTED_SIZE = "com.google.mlkit.vision.demo.KEY_SELECTED_SIZE" private const val REQUEST_IMAGE_CAPTURE = 1001 private const val REQUEST_CHOOSE_IMAGE = 1002 } }