Search code examples
androidandroid-studiokotlinandroid-activityandroid-appcompat

How to avoid spaghetti code in Android by Moving code in new Classes (Kotlin)


From the Main Class below, I am trying to move as much code as possible into 2 new class files named "PhotoCapture.kt" and "PhotoUpload.kt" respectively. I don't find a way to make it work. It looks like I am confined to work only within the MainActivity. For example I can't find a way to move the method "onActivityResult" in a new class. What do I need to know more in order to understand how the whole thing works?

Thank you friends!

package com.tabapab.takepictureandsave

import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.support.v4.content.FileProvider
import android.util.Log
import android.util.Base64
import android.widget.ImageView
import com.android.volley.AuthFailureError
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*


class MainActivity : AppCompatActivity() {
    var namaFile = ""
  //  var fileUri = Uri.parse("")
    val RC_CAMERA = 100

    val REQUEST_TAKE_PHOTO = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        capture_btn.setOnClickListener {
            dispatchTakePictureIntent()
        }
    }

    private fun dispatchTakePictureIntent() {
        Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
            // Ensure that there's a camera activity to handle the intent
            takePictureIntent.resolveActivity(packageManager)?.also {
                // Create the File where the photo should go
                val photoFile: File? = try {
                    createImageFile()
                } catch (ex: IOException) {
                    // Error occurred while creating the File

                    null
                }
                // Continue only if the File was successfully created
                photoFile?.also {
                    val photoURI: Uri = FileProvider.getUriForFile(
                            this,
                            "com.tabapab.takepictureandsave",
                            it
                    )
                    takePictureIntent.putExtra(MediaStore.ACTION_IMAGE_CAPTURE, photoURI)
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                    startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
                }
            }
        }
    }

    lateinit var currentPhotoPath: String

    @Throws(IOException::class)
    private fun createImageFile(): File {
        // Create an image file name
        val timeStamp: String = SimpleDateFormat("ddMMyyyy_HHmmss").format(Date())
        val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val filNam = File.createTempFile(
                "JPEG_${timeStamp}_", /* prefix */
                ".jpg", /* suffix */
                storageDir /* directory */
        ).apply {
            // Save a file: path for use with ACTION_VIEW intents
            currentPhotoPath = absolutePath
        }
        Log.d("myDebug", storageDir.toString())
        return filNam
    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
            //   val imageBitmap = data.extras.get("data") as Bitmap
            // image_view.setImageBitmap(imageBitmap)
            // galleryAddPic()
            val f = File(currentPhotoPath)
            Log.d("myDebug", currentPhotoPath)
       //     image_view.setImageURI(Uri.fromFile(f)) // this sets uncompressed,
        val imgStr =  getBitmapToString(image_view, Uri.fromFile(f))
            volleyImg(imgStr)
        }
    }

    fun bitmapToString(bmp: Bitmap): String {
        val outputStream = ByteArrayOutputStream()
        bmp.compress(Bitmap.CompressFormat.JPEG, 60, outputStream)
        val byteArray = outputStream.toByteArray()
        return Base64.encodeToString(byteArray, Base64.DEFAULT)


    }

    fun getBitmapToString(imV: ImageView, Fileuri: Uri): String {
            var bmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
            bmp = BitmapFactory.decodeFile(Fileuri.path)
            Log.d("myDebug", Fileuri.path)
            var dim = 720
            Log.d("myDebug", "height:" + bmp.height + " _width:"+ bmp.width)
            if (bmp.height > bmp.width) {
                bmp = Bitmap.createScaledBitmap(bmp,
                        (bmp.width*dim).div(bmp.height), dim, true)
            } else {
                bmp = Bitmap.createScaledBitmap(bmp,
                        dim, (bmp.height*dim).div(bmp.width), true)
            }
            imV.setImageBitmap(bmp)
            return bitmapToString(bmp)
            return ""
    }

    private fun volleyImg(imgStr: String) {

        msg_display.text = "Starting Volley Kick"

        val url:String = "http://192.168.10.6/tabapad1/modules/android/classes/main.php?op=filestr"
        //      val rq:RequestQueue=Volley.newRequestQueue(this)
        val stringRequest = object : StringRequest(Request.Method.POST, url,
                Response.Listener<String> { response ->
                    // Process the json
                    try {
                        val obj = JSONObject(response)
                        msg_display.text = obj.getString("message")
                    } catch (e: Exception) {
                        msg_display.text = "Exception: $e"
                    }

                }, Response.ErrorListener { error ->
            msg_display.text = error.message
        }) {


            @Throws(AuthFailureError::class)
            override fun getParams(): Map<String, String> {
                val params = HashMap<String, String>()
                params.put("filename", "Maria2.jpg")
                params.put("imstr", imgStr)
                msg_display.text = "Sets the Volley parameters"
                return params

            }

        }
        // Add the volley post request to the request queue
        VolleySingleton.getInstance(this).addToRequestQueue(stringRequest)


        //    rq.add(request)


    }


//    private fun galleryAddPic() {
//        // run this to add the photo to system's Media Provider's database
//        // It will be available in the Android Gallery application and other Apps
//        Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
//            val f = File(currentPhotoPath)
//            mediaScanIntent.data = Uri.fromFile(f)
//            sendBroadcast(mediaScanIntent)
//        }
//    }


}

Solution

  • You cannot move onActivityResult as it's tied with startActivityForResult. You are launching an intent to take a photo using the native camera and your activity is expecting a result. The result returns to the activity that launched the intent in the first place.

    Generally, you can remove methods/functions from an activity, when their use can be applied in various other places. Your bitmapToString function, for example, can be easily put in a Utils class as it receives generic variables and has a body that is not dependant on any other part of your activity. With some alterations, other functions could be extracted as well. For example if you add a variable for the file name as a parameter to createImageFile(), you can use that in other activities that don't necessarily want to have the same format of filename as the one used here.