<|start_filename|>lobe_android/app/src/main/java/com/example/test/DetectorActivity.kt<|end_filename|>
package com.example.test

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.media.ImageReader
import android.os.Build
import android.util.Size
import android.widget.Toast
import androidx.annotation.RequiresApi
import com.example.test.env.ImageUtils
import com.example.test.env.Logger
import com.example.test.tflite.Classifier
import java.io.IOException
import kotlin.math.min

@RequiresApi(Build.VERSION_CODES.KITKAT)
class DetectorActivity : CameraActivity(), ImageReader.OnImageAvailableListener {

    private val LOGGER: Logger = Logger()
    private val TF_OD_API_INPUT_SIZE = 448

    // Minimum detection confidence to track a detection.
    private val MAINTAIN_ASPECT = false

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private val DESIRED_PREVIEW_SIZE = Size(1280, 960)
    private val SAVE_PREVIEW_BITMAP = false
    private var sensorOrientation: Int? = null
    private var detector: Classifier? = null
    private var croppedBitmap: Bitmap? = null
    private var computingDetection = false
    private var timestamp: Long = 0
    private var frameToCropTransform: Matrix? = null
    private var cropToFrameTransform: Matrix? = null

    private var imageSizeX = 0

    /** Input image size of the model along y axis.  */
    private var imageSizeY = 0


    private fun recreateClassifier(
        model: Classifier.Model,
        device: Classifier.Device,
        numThreads: Int
    ) {
        if (detector != null) {
            LOGGER.d("Closing classifier.")
            detector!!.close()
            detector = null
        }

        try {
            LOGGER.d(
                "Creating classifier (model=%s, device=%s, numThreads=%d)",
                model,
                device,
                numThreads
            )
            detector = Classifier.create(this, model, device, numThreads)
        } catch (e: IOException) {
            LOGGER.e(
                e,
                "Failed to create classifier."
            )
        }

        // Updates the input image size.
        imageSizeX = detector!!.getImageSizeX()
        imageSizeY = detector!!.getImageSizeY()
    }


    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onPreviewSizeChosen(size: Size?, rotation: Int) {
        var cropSize: Int = TF_OD_API_INPUT_SIZE
        try {
            recreateClassifier(getModel()!!, getDevice()!!, getNumThreads())
            cropSize = TF_OD_API_INPUT_SIZE
        } catch (e: IOException) {
            e.printStackTrace()
            LOGGER.e(e, "Exception initializing classifier!")
            val toast = Toast.makeText(
                applicationContext, "Classifier could not be initialized", Toast.LENGTH_SHORT
            )
            toast.show()
            finish()
        }

        previewWidth = size!!.width
        previewHeight = size.height

        sensorOrientation = rotation - getScreenOrientation()
        LOGGER.i(
            "Camera orientation relative to screen canvas: %d",
            sensorOrientation
        )

        LOGGER.i("Initializing at size %dx%d", previewWidth, previewHeight)
        rgbFrameBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888)
        croppedBitmap = Bitmap.createBitmap(cropSize, cropSize, Bitmap.Config.ARGB_8888)

        frameToCropTransform = ImageUtils.getTransformationMatrix(
            previewWidth, previewHeight,
            cropSize, cropSize,
            sensorOrientation!!, MAINTAIN_ASPECT
        )

        cropToFrameTransform = Matrix()
        frameToCropTransform!!.invert(cropToFrameTransform)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun processImage() {
        ++timestamp
        // No mutex needed as this method is not reentrant.
        if (computingDetection) {
            readyForNextImage()
            return
        }
        computingDetection = true
        if (useImage) {
            val cur = BitmapFactory.decodeByteArray(inputData, 0, inputData!!.size, null)
            rgbFrameBitmap = cur.copy(Bitmap.Config.ARGB_8888, true)
        } else {
            rgbFrameBitmap =
                Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888)
            rgbFrameBitmap!!.setPixels(
                getRgbBytes(),
                0,
                previewWidth,
                0,
                0,
                previewWidth,
                previewHeight
            )
        }
        readyForNextImage()
        // For examining the actual TF input.
        if (SAVE_PREVIEW_BITMAP) {
            ImageUtils.saveBitmap(croppedBitmap)
        }

        runInBackground(
            Runnable {
                var rawBitmap: Bitmap?
                if (!useImage) {
                    val matrix = Matrix()
                    matrix.postRotate(90f)
                    var targetWidth =
                        previewWidth.toFloat() / previewHeight.toFloat() * screenHeight!!.toFloat()
                    val scaledBitmap =
                        Bitmap.createScaledBitmap(
                            rgbFrameBitmap!!,
                            targetWidth.toInt(),
                            screenHeight!!,
                            true
                        )
                    val rotatedBitmap = Bitmap.createBitmap(
                        scaledBitmap,
                        0,
                        0,
                        scaledBitmap.width,
                        scaledBitmap.height,
                        matrix,
                        true
                    )

                    var w =
                        min(
                            screenWidth!!.toFloat() / screenHeight!!.toFloat() * rotatedBitmap.height,
                            rotatedBitmap.width.toFloat()
                        )
                    rawBitmap =
                        Bitmap.createBitmap(rotatedBitmap, 0, 0, w.toInt(), rotatedBitmap.height)
                } else {
                    rawBitmap = rgbFrameBitmap
                }

                val matrix = Matrix()
                matrix.postRotate(90f)
                var curWidth = rawBitmap!!.width
                var curHeight = rawBitmap.height
                var squareBitmap: Bitmap?
                if (curHeight > curWidth) {
                    squareBitmap = Bitmap.createBitmap(
                        rawBitmap,
                        0,
                        ((curHeight.toFloat() - curWidth.toFloat()) / 2.toFloat()).toInt(),
                        curWidth,
                        curWidth
                    )
                } else {
                    squareBitmap = Bitmap.createBitmap(
                        rawBitmap,
                        ((curWidth.toFloat() - curHeight.toFloat()) / 2.toFloat()).toInt(),
                        0,
                        curHeight,
                        curHeight
                    )
                }

                val canvas1 = Canvas(croppedBitmap!!)
                val trans = ImageUtils.getTransformationMatrix(
                    squareBitmap.width, squareBitmap.height,
                    croppedBitmap!!.width, croppedBitmap!!.height,
                    0, MAINTAIN_ASPECT
                )

                canvas1.drawBitmap(squareBitmap!!, trans, null)
                val results: List<Classifier.Recognition> =
                    detector!!.recognizeImage(croppedBitmap, sensorOrientation!!)
                adapter!!.setItems(results)
                computingDetection = false
            })
    }

    override fun getLayoutId(): Int {
        return R.layout.tfe_od_camera_connection_fragment_tracking
    }

    override fun getDesiredPreviewFrameSize(): Size {
        return DESIRED_PREVIEW_SIZE
    }

    private enum class DetectorMode {
        TF_OD_API
    }
}

<|start_filename|>lobe_android/app/src/main/java/com/example/test/LegacyCameraConnectionFragment.kt<|end_filename|>
package org.tensorflow.lite.examples.detection
/*
 * Copyright 2021 Microsoft. All Rights Reserved.
 *
 * Copyright 2019 The TensorFlow Authors. 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.
 *
 * This file has been modified by Microsoft to add support for using Lobe exported models.
 */
import android.annotation.SuppressLint
import android.app.Fragment
import android.graphics.SurfaceTexture
import android.hardware.Camera
import android.hardware.Camera.CameraInfo
import android.hardware.Camera.PreviewCallback
import android.os.Build
import android.os.Bundle
import android.os.HandlerThread
import android.util.DisplayMetrics
import android.util.Size
import android.util.SparseIntArray
import android.view.LayoutInflater
import android.view.Surface
import android.view.TextureView.SurfaceTextureListener
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import com.example.test.CameraConnectionFragment.Companion.chooseOptimalSize
import com.example.test.R
import com.example.test.customview.AutoFitTextureView
import com.example.test.env.ImageUtils
import com.example.test.env.Logger
import java.io.IOException

class LegacyCameraConnectionFragment @SuppressLint("ValidFragment") constructor(
    private val imageListener: PreviewCallback,
    /** The layout identifier to inflate for this Fragment.  */
    private val layout: Int,
    private val desiredSize: Size,
    useFront: Boolean,
    screenHeight: Int,
    screenWidth: Int
) :
    Fragment() {
    companion object {
        private val LOGGER: Logger = Logger()

        /** Conversion from screen rotation to JPEG orientation.  */
        private val ORIENTATIONS = SparseIntArray()

        init {
            ORIENTATIONS.append(
                Surface.ROTATION_0,
                90
            )
            ORIENTATIONS.append(
                Surface.ROTATION_90,
                0
            )
            ORIENTATIONS.append(
                Surface.ROTATION_180,
                270
            )
            ORIENTATIONS.append(
                Surface.ROTATION_270,
                180
            )
        }
    }

    private var camera: Camera? = null

    /** An [AutoFitTextureView] for camera preview.  */
    private var textureView: AutoFitTextureView? = null
    private var availableSurfaceTexture: SurfaceTexture? = null
    private var useFront = false
    var screenHeight = 1000
    var screenWidth = 1000

    /**
     * [TextureView.SurfaceTextureListener] handles several lifecycle events on a [ ].
     */
    private val surfaceTextureListener: SurfaceTextureListener = object : SurfaceTextureListener {
        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun onSurfaceTextureAvailable(
            texture: SurfaceTexture, width: Int, height: Int
        ) {
            availableSurfaceTexture = texture
            startCamera()
        }

        override fun onSurfaceTextureSizeChanged(
            texture: SurfaceTexture, width: Int, height: Int
        ) {
        }

        override fun onSurfaceTextureDestroyed(texture: SurfaceTexture): Boolean {
            return true
        }

        override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {}
    }

    /** An additional thread for running tasks that shouldn't block the UI.  */
    private var backgroundThread: HandlerThread? = null
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(layout, container, false)
    }

    override fun onViewCreated(
        view: View,
        savedInstanceState: Bundle?
    ) {
        textureView = view.findViewById<View>(R.id.texture) as AutoFitTextureView
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onResume() {
        super.onResume()
        startBackgroundThread()
        // When the screen is turned off and turned back on, the SurfaceTexture is already
        // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
        // a camera and start preview from here (otherwise, we wait until the surface is ready in
        // the SurfaceTextureListener).
        if (textureView!!.isAvailable()) {
            startCamera()
        } else {
            textureView!!.setSurfaceTextureListener(surfaceTextureListener)
        }
    }

    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    override fun onPause() {
        stopCamera()
        stopBackgroundThread()
        super.onPause()
    }

    /** Starts a background thread and its [Handler].  */
    private fun startBackgroundThread() {
        backgroundThread = HandlerThread("CameraBackground")
        backgroundThread!!.start()
    }

    /** Stops the background thread and its [Handler].  */
    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private fun stopBackgroundThread() {
        backgroundThread!!.quitSafely()
        try {
            backgroundThread!!.join()
            backgroundThread = null
        } catch (e: InterruptedException) {
            LOGGER.e(e, "Exception!")
        }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun startCamera() {
        val index = getCameraId()
        camera = Camera.open(index)
        try {
            val parameters = camera!!.getParameters()
            val focusModes =
                parameters?.supportedFocusModes
            if (focusModes != null
                && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)
            ) {
                parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
            }
            val cameraSizes =
                parameters?.supportedPreviewSizes
            var sizes = arrayOfNulls<Size>(cameraSizes!!.size)

            var i = 0
            for (size in cameraSizes) {
                sizes[i++] = Size(size.width, size.height)
            }

            val previewSize: Size? = chooseOptimalSize(
                sizes.copyOfRange(5,6), desiredSize.width, desiredSize.height
            )
            parameters!!.setPreviewSize(previewSize!!.width, previewSize!!.height)
            camera!!.setDisplayOrientation(90)
            camera!!.setParameters(parameters)
            camera!!.setPreviewTexture(availableSurfaceTexture)
        } catch (exception: IOException) {
            camera!!.release()
        }
        camera!!.setPreviewCallbackWithBuffer(imageListener)
        val s = camera!!.getParameters().previewSize
        camera!!.addCallbackBuffer(ByteArray(ImageUtils.getYUVByteSize(s.height, s.width)))
        textureView!!.setAspectRatio(s.height, s.width)

        textureView!!.setLayoutParams(FrameLayout.LayoutParams(10000, screenHeight))
//        textureView?.setAspectRatio(960, 1280);
        camera!!.startPreview()
    }

    protected fun stopCamera() {
        if (camera != null) {
            camera!!.stopPreview()
            camera!!.setPreviewCallback(null)
            camera!!.release()
            camera = null
        }
    }

    private fun getCameraId(): Int {
        val ci = CameraInfo()
        for (i in 0 until Camera.getNumberOfCameras()) {
            Camera.getCameraInfo(i, ci)
            if (useFront) {
                if (ci.facing == CameraInfo.CAMERA_FACING_FRONT) return i
            } else {
                if (ci.facing == CameraInfo.CAMERA_FACING_BACK) return i
            }
        }
        return -1 // No camera found
    }

    init {
        this.useFront = useFront
        this.screenHeight = screenHeight
        this.screenWidth = screenWidth
    }
}

<|start_filename|>lobe_android/app/src/main/java/com/example/test/PredictionAdapter.kt<|end_filename|>
package com.example.test

import android.app.Activity
import android.content.Context
import android.os.Build
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.ProgressBar
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.core.view.animation.PathInterpolatorCompat
import com.example.test.tflite.Classifier
import kotlin.math.min

class PredictionAdapter(context: Context) : BaseAdapter() {

    private val MAX_COUNT_SHOWN: Int = 3
    private val mInflator: LayoutInflater
    private val context: Context

    private var mList = arrayOf(
        Classifier.Recognition("0", "one", 0.8f, null),
        Classifier.Recognition("1", "two", 0.15f, null),
        Classifier.Recognition("2", "three", 0.05f, null)
    )
    private var mCountToShow: Int = MAX_COUNT_SHOWN


    fun setItems(predictions: List<Classifier.Recognition>) {
        var num = min(predictions.count(), mCountToShow) - 1
        mList = predictions.toTypedArray().sliceArray(0..num);
        (this.context as Activity).runOnUiThread(Runnable { notifyDataSetChanged() })
    }

    fun toggleCountShown() {
        this.mCountToShow = if (this.mCountToShow == 1) MAX_COUNT_SHOWN else 1
    }

    init {
        this.mInflator = LayoutInflater.from(context)
        this.context = context
    }

    override fun getCount(): Int {
        return mList.size
    }

    override fun getItem(position: Int): Any {
        return mList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun getView(position: Int, convertView: View?, container: ViewGroup?): View? {

        var view: View? = convertView
        if (view == null) {
            view = this.mInflator.inflate(R.layout.customlistview, container, false)
        }

        var progressBar: ProgressBar? = view!!.findViewById(R.id.customProgressBar)
        var textView: TextView? = view.findViewById(R.id.customTextView)

        var item = (this.getItem(position) as Classifier.Recognition);
        textView!!.text = item.title


        val displayMetrics: DisplayMetrics = this.context.resources.displayMetrics
        val dpWidth =
            Math.round(container!!.width / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))

        val minProgress = 32.0f / dpWidth * 100.0f

        val confidence: Float =
            ((100.0f - minProgress) / 100.0f) * (item.confidence * 100.0f) + minProgress

        val anim = ProgressBarAnimation(
            progressBar!!,
            if (position == 0) progressBar!!.progress * 1.0f else progressBar!!.secondaryProgress * 1.0f,
            confidence,
            position == 0
        )
        anim.duration = 300
        anim.interpolator = PathInterpolatorCompat.create(0.420f, 0.000f, 0.580f, 1.000f)
        progressBar.startAnimation(anim)

        return view
    }
}

<|start_filename|>lobe_android/app/src/main/java/com/example/test/ProgressBarAnimation.kt<|end_filename|>
package com.example.test

import android.view.animation.Animation
import android.view.animation.Transformation
import android.widget.ProgressBar

class ProgressBarAnimation(
    private val progressBar: ProgressBar,
    private val from: Float,
    private val to: Float,
    private val first: Boolean
) : Animation() {
    override fun applyTransformation(interpolatedTime: Float, t: Transformation?) {
        super.applyTransformation(interpolatedTime, t)
        val value = from + (to - from) * interpolatedTime
        progressBar.progress = if (first) value.toInt() else 0
        progressBar.secondaryProgress = value.toInt()
    }
}