Using Camera in Android - Kotlin
Nowadays, most of our Android app require access to Camera to capture picture or Video. We can do that in two ways.
(i) Using built-in Camera,
(ii) Using custom Camera.
(i) Using built-in Camera,
(ii) Using custom Camera.
Using Built-in Camera
In this post, Let's see how to use the built-in camera to capture images and save it.
Its a simple way to capture pics. We can use the mobile's built-in camera app using Intent.
val REQUEST_IMAGE_CAPTURE = 1
private fun takePictureIntent() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(packageManager) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
After the picture has been captured, the result will received on onActivityResult method. There, we can get the data, in Bitmap form.
And on onActivityResult(), get the intent data in the form of Bitmap and display that in an ImageView.
Here, imageUri will be the custom path of captured image.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
CAMERA_REQUEST_CODE -> {
val extras = data?.getExtras()
val imageBitmap = extras?.get("data") as Bitmap
image.setImageBitmap(imageBitmap)
}
}
}
}
Implementation:
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
camera_intent.xml
Design a layout with a button, while clicking on it, Capture the picture and show it in an ImageView. Also add a TextView to display URI of the captured picture in it.<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="20dp"
android:src="@drawable/chris_tree"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/colorPrimary"
android:padding="5dp"
android:text="Capture"
android:textColor="@color/white"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/image" />
<TextView
android:id="@+id/uriText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="5dp"
android:textColor="@color/colorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/capture" />
</android.support.constraint.ConstraintLayout>
CameraIntentActivity.kt
This class is to open the camera app while clicking on a button. Before that, don't forget to check for permissions of Camera and Storage.And on onActivityResult(), get the intent data in the form of Bitmap and display that in an ImageView.
class CameraIntentActivity : AppCompatActivity(), View.OnClickListener {
private val CAMERA_REQUEST_CODE = 12345
private val REQUEST_GALLERY_CAMERA = 54654
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.camera_intent)
capture.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v) {
capture -> {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_GALLERY_CAMERA)
} else {
openCamera()
}
} else {
openCamera()
}
}
}
}
private fun openCamera() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null)
startActivityForResult(intent, CAMERA_REQUEST_CODE)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_GALLERY_CAMERA) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
Toast.makeText(this@CameraIntentActivity, getString(R.string.permission_denied), Toast.LENGTH_SHORT).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
CAMERA_REQUEST_CODE -> {
val extras = data?.getExtras()
val imageBitmap = extras?.get("data") as Bitmap
image.setImageBitmap(imageBitmap)
}
}
}
}
}
More Customization:
Above example is for simpler use like just displaying the captured picture in an ImageView. But in our real-time application, we need more than that. i.e., atleast URI of the captured picture. We can also use custom filepath, where the captured image can be saved. val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, CAMERA_REQUEST_CODE)
}
Here, imageUri will be the custom path of captured image.
file_paths.xml
We have to create directory xml and inside that, a file named file_paths.xml have to be created. In which, the external file path will be added.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Android/data/com.yamikrish.app/files/Pictures" />
</paths>
AndroidManifest.xml
Add the following lines in AndroidManifest.xml <provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.yamikrish.app.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
CameraIntentActivity.kt
We have to pass the custom URI as Intent's extra while opening the camera. Also we have grant permission to access FileProvider as per device's version. Otherwise, it won't work properly as expected.
Also, remember that the path we are passing in that function should be as same as the one we have declared in file_paths.xml.
Incase, if you would like to compress the image before upload it to the server, you can do that. I have included a class, CompressImage() to do that for you. That's it!! We 're done!!!
class CameraIntentActivity : AppCompatActivity(), View.OnClickListener {
private val CAMERA_REQUEST_CODE = 12345
private val REQUEST_GALLERY_CAMERA = 54654
var imageUri = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.camera_intent)
capture.setOnClickListener(this)
}
override fun onClick(v: View?) {
when (v) {
capture -> {
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_GALLERY_CAMERA)
} else {
openCamera()
}
} else {
openCamera()
}
}
}
}
private fun openCamera() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(packageManager) != null) {
var photoFile: File? = null
try {
photoFile = createImageFile()
} catch (ex: IOException) {
ex.printStackTrace()
}
if (photoFile != null) {
val photoURI = FileProvider.getUriForFile(this,
"com.yamikrish.app.android.fileprovider",
photoFile)
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
} else {
val resInfoList = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, CAMERA_REQUEST_CODE)
}
}
}
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imageFileName = "JPEG_" + timeStamp + "_"
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
)
// Save a file: path for use with ACTION_VIEW intents
imageUri = image.absolutePath
return image
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_GALLERY_CAMERA) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
Toast.makeText(this@CameraIntentActivity, getString(R.string.permission_denied), Toast.LENGTH_SHORT).show()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
CAMERA_REQUEST_CODE -> {
//CompressImage().execute(imageUri) //If you would like to compress the image size before upload it to server, use that class
uriText.text = imageUri
}
}
}
}
internal inner class CompressImage : AsyncTask<String, Void, String>() {
override fun onPreExecute() {
super.onPreExecute()
}
override fun doInBackground(vararg params: String): String {
val result = getRealPathFromURI(params[0])
val file = File(result!!)
val bitmap = decodeImageFile(file)
val stream = ByteArrayOutputStream()
bitmap!!.compress(Bitmap.CompressFormat.PNG, 100, stream)
val image = stream.toByteArray()
return Base64.encodeToString(image, Base64.DEFAULT).replace("\n".toRegex(), "")
}
override fun onPostExecute(result: String) {
super.onPostExecute(result)
try {
val jObject = JSONObject()
//Here you can upload the compressed image to your server
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
/* To get real path from URI of File */
private fun getRealPathFromURI(contentURI: String): String? {
val contentUri = Uri.parse(contentURI)
val cursor = contentResolver.query(contentUri, null, null, null, null)
if (cursor == null) {
return contentUri.path
} else {
var res: String? = null
if (cursor.moveToFirst()) {
val column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
res = cursor.getString(column_index)
}
cursor.close()
return res
}
}
/* To convert Image to Bitmap */
fun decodeImageFile(f: File): Bitmap? {
try {
val o = BitmapFactory.Options()
o.inJustDecodeBounds = true
BitmapFactory.decodeStream(FileInputStream(f), null, o)
val REQUIRED_SIZE = 150
var scale = 1
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2
val o2 = BitmapFactory.Options()
o2.inSampleSize = scale
return BitmapFactory.decodeStream(FileInputStream(f), null, o2)
} catch (e: FileNotFoundException) {
e.printStackTrace()
}
return null
}
}
Can you post source code?
ReplyDelete