How to put the taken photos in RecyclerView in Kotlin - android

My application uses the camera to take pictures and then saves them in the MediaStore. I would like to put these pictures in my RecyclerView using Glide but I don't know how to do it.
A function that saves the image:
private fun imageCapture() {
// Set desired name and type of captured image
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "${what_is_that_insect_tv.text}")
put(MediaStore.MediaColumns.DATE_MODIFIED.format("MM/dd/yyyy"), (Calendar.getInstance().timeInMillis / 1000L))
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
}
// Create the output file option to store the captured image in MediaStore
val outputFileOptions = ImageCapture.OutputFileOptions
.Builder(resolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
.build()
// Initiate image capture
imageCapture?.takePicture(
outputFileOptions,
cameraExecutor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
// Image was successfully saved to `outputFileResults.savedUri`
}
override fun onError(exception: ImageCaptureException) {
val errorType = exception.imageCaptureError
Toast.makeText(requireContext(), "$errorType", Toast.LENGTH_SHORT).show()
}
})
}
Function in Adapter
fun bind(insect: Insect){
with(itemView){
name_insect_item.text = insect.name
Glide.with(this)
.load()
.into(this.image_insect_item)
}
}

In order to use Glide you must obtain the URI of the saved image, in the onImageSaved I believe you can call .getSavedUri() like so :
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val uri = ImageCapture.OutputFileResults.getSavedUri()
}
You should then be able to use the Uri in an ArrayList you feed to the adaptor. Got it from here :
https://developer.android.com/reference/androidx/camera/core/ImageCapture.OutputFileResults#getSavedUri()

I do this in my code like this:
Glide.with(holder.imageView.getContext())
.load(new File(hotel.imageId2))
.into(holder.imageView)
also this might help:
RecyclerView- Glide

Related

ImageCapture doesn't work as I can't get into the error function nor the success function

everyone. I have an app in which I want to take a photo. I followed Google's guide (https://developer.android.com/codelabs/camerax-getting-started#1). However, I cannot take a photo. I can't get to the error function nor the success function within takePicture(). I use SDK 30.
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
//Here the return is called
val imageCapture = imageCapture ?: return
// Create time stamped name and MediaStore entry.
val name = SimpleDateFormat(FILENAME_FORMAT, Locale.GERMAN)
.format(System.currentTimeMillis())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, name)
put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
}
}
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions
.Builder(contentResolver,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues)
.build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
//Will never be called
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
//Will never be called
override fun
onImageSaved(output: ImageCapture.OutputFileResults){
val msg = "Photo capture succeeded: ${output.savedUri}"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
}
)}

How to directly pass Image into ArthurHub / Android-Image-Cropper for cropping

I am using Jetpack compose for my UI, Activity Result Contracts and ArthutHub Android Image Cropper Library to crop images in my App. Everything works fine as expected with the following code:
val (result, launcher) = setUpContentPhotoCropImageIntent()//this activity result
//contracts in launched on a button press
result.value?.let {
//code here to save the image and show it in the UI, upload to cloud storage etc.
}
#Composable
fun setUpContentPhotoCropImageIntent(): Pair<MutableState<Uri?>,
ManagedActivityResultLauncher<Any?, Uri?>> {
val cropActivityResultContract = object : ActivityResultContract<Any?, Uri?>() {
override fun createIntent(context: Context, input: Any?): Intent {
CropImage.isExplicitCameraPermissionRequired(context)
CropImage.getPickImageChooserIntent(context)
return CropImage
.activity()
.setActivityTitle("Choose Photo")
.setCropMenuCropButtonTitle("Select")
.setAllowRotation(true)
.setGuidelines(CropImageView.Guidelines.ON_TOUCH)
.setCropShape(CropImageView.CropShape.RECTANGLE)
.setAllowFlipping(true)
.setOutputCompressQuality(100)
.setFixAspectRatio(true)
.setMaxCropResultSize(
2000,
2000
)
.getIntent(context)
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
return CropImage.getActivityResult(intent)?.uri
}
}
val result = remember { mutableStateOf<Uri?>(null) }
val launcher = rememberLauncherForActivityResult(cropActivityResultContract) {
result.value = it
}
return Pair(result, launcher)
}
So with this approach, the library takes care of not only cropping images but also capturing them via camera/gallery. I would like to pass a Bitmap image myself(by getting images using the built-in Android APIs to Take Picture and get Gallery Image) and use this library for just cropping, so that I can have 2 dedicated buttons for the user to either capture photo or choose from Gallery.
I have the image now, I want this library to just crop it for me, How do i Pass an Image into it?
Try to use the latest library https://github.com/CanHub/Android-Image-Cropper
and them you can pass the image like this:
class MainActivity {
private val cropImage = registerForActivityResult(CropImageContract()) { result ->
if (result.isSuccessful) {
// use the returned uri
val uriContent = result.uriContent
val uriFilePath = result.getUriFilePath(context) // optional usage
} else {
// an error occurred
val exception = result.error
}
}
private fun startCrop() {
// start cropping activity for pre-acquired image saved on the device and customize settings
cropImage.launch(
options(uri = imageUri) {
setGuidelines(Guidelines.ON)
setOutputCompressFormat(CompressFormat.PNG)
}
)
}
}

How do I keep an image saved on an application even after the app closes?

I'm making a card holder app, the user is supposed to take a picture of his id, contacts, notes etc. so he can later use them digitally. Problem is how do I take a camera input and save it as an image inside the application so it stays there?
You can simply use the native Camera Application of your device to get the image and then save it to the device . Android Team has done much easy for developers to perform such task.
You need to make use of ActivityContracts and MediaStore to take the image and store it into your device respectively.
Step 1 :
First Generate a Uri for your Image , in the following manner
#RequiresApi(Build.VERSION_CODES.Q)
suspend fun createPhotoUri(source: Source): Uri? {
val imageCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val dirDest = File(
Environment.DIRECTORY_PICTURES,
context.getString(R.string.app_name) + File.separator + "CAMERA"
)
val date = System.currentTimeMillis()
val fileName = "$date.jpg"
return withContext(Dispatchers.IO) {
val newImage = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
put(MediaStore.MediaColumns.RELATIVE_PATH, "$dirDest${File.separator}")
}
return#withContext context.contentResolver.insert(imageCollection, newImage)
}
}
Step 2:
Then when you want to capture the Image , then on the OnClickListener perform the following :
binding.takePictureButton.setOnClickListener {
viewLifecycleOwner.lifecycleScope.launch {
viewModel.createPhotoUri(Source.CAMERA)?.let { uri ->
actionTakePicture.launch(uri)
}
}
}
Step 3 :
The actionTakePicture ActivityContract is as follows :
private val actionTakePicture = registerForActivityResult(TakePicture()) { success ->
if (!success) {
Log.d(tag, "Image taken FAIL")
return#registerForActivityResult
}
Log.d(tag, "Image taken SUCCESS")
}
And you are done with capturing you Image and storing it .
Make sure you declare permission's before using the above code ,else it wont work .
The answer mentioned by #StefanoSansone can also be used . But the issue with that is you need to perfectly setup CameraX library and that might be tedious for your useCase . One should use library like CameraX if they want to have more control on Camera with other camera capabilities , when you application is more of a Camera Application . Else using the above method is perfectly fine . Saves one from tedious work .
If you are using Kotlin and Jetpack libraries, I suggest you to take a look to CameraX library.
You can use the takePicture method to take a photo with camera and save it in the storage.
A complete example can be found in the CameraX codelab
private fun takePhoto() {
// Get a stable reference of the modifiable image capture use case
val imageCapture = imageCapture ?: return
// Create time-stamped output file to hold the image
val photoFile = File(
outputDirectory,
SimpleDateFormat(FILENAME_FORMAT, Locale.US
).format(System.currentTimeMillis()) + ".jpg")
// Create output options object which contains file + metadata
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
// Set up image capture listener, which is triggered after photo has
// been taken
imageCapture.takePicture(
outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(photoFile)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
I had some difficulties last week with saving images.
I finally used sharedPreferences files and saved bitmap as text.
From what I've heard it's not a good practice, It's better to save in files and save the path.
However the code is very compact and it's working really well (in my case never have to load more than 4 pictures)
var bm= MediaStore.Images.Media.getBitmap(this.getContentResolver(), selectedImageUri)
bm.compress(Bitmap.CompressFormat.PNG, 100, baos) //bm is the bitmap object
val b = baos.toByteArray()
val encoded: String = Base64.encodeToString(b, Base64.DEFAULT)
editor.putString("backgroundBitmap",encoded)//put the bitmap as text in sharedPref files, use to back the bitmap in mainActivity
editor.commit()

Convert Uri to Bitmap

I want to convert a Uri from the .takePicture method to a Bitmap for later use, I searched for different methods but each gaved me an error saying that the bitmap is null and sometimes "int android.graphics.bitmap.getwidth()' on a null object reference".
var imageUri:Uri?=null
private fun takePhoto() {
val photofile = File(externalMediaDirs.firstOrNull(), "picture - ${System.currentTimeMillis()}.jpg")
val output = ImageCapture.OutputFileOptions.Builder(photofile).build()
var image_View = findViewById<ImageView>(R.id.imageView)
imageCapture?.takePicture(output, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Toast.makeText(applicationContext, "Pic saved.", Toast.LENGTH_LONG).show()
imageUri = outputFileResults.savedUri
//image_View.setImageURI(imageUri)
}
override fun onError(exception: ImageCaptureException) {
Toast.makeText(applicationContext, "Error saving pic!", Toast.LENGTH_LONG).show()
}
})
}
Try this snippet
Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageUri);
Quoting the documentation for getSavedUri():
This field is only returned if the ImageCapture.OutputFileOptions is backed by MediaStore constructed with #Builder(ContentResolver, Uri, ContentValues)
That is not the constructor that you are using for OutputFileOptions. So, check to see if getSavedUri() is returning null. If it is, you will need to use photofile, including taking steps to save that in the saved instance state Bundle.
If getSavedUri() is returning a non-null value, you might want to edit your question and supply the complete stack trace associated with your crash (rather than using a pastebin).

Convert CameraX Captured ImageProxy to Bitmap

I was working with CameraX and had hard time converting captured ImageProxy to Bitmap. After searching and trying, I formulated a solution. Later I found that it was not optimum so I changed the design. That forced me to drop hours of work.
Since I (or someone else) might need it in a future, I decided to post here as a question and post and answer to it for reference and scrutiny. Feel free to add better answer if you have one.
The relevant code is:
class ImagePickerActivity : AppCompatActivity() {
private var width = 325
private var height = 205
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_picker)
view_finder.post { startCamera() }
}
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun startCamera() {
// Create configuration object for the viewfinder use case
val previewConfig = PreviewConfig.Builder().apply {
setTargetAspectRatio(Rational(1, 1))
//setTargetResolution(Size(width, height))
setLensFacing(CameraX.LensFacing.BACK)
setTargetAspectRatio(Rational(width, height))
}.build()
}
// Create configuration object for the image capture use case
val imageCaptureConfig = ImageCaptureConfig.Builder()
.apply {
setTargetAspectRatio(Rational(1, 1))
// We don't set a resolution for image capture instead, we
// select a capture mode which will infer the appropriate
// resolution based on aspect ration and requested mode
setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
}.build()
// Build the image capture use case and attach button click listener
val imageCapture = ImageCapture(imageCaptureConfig)
capture_button.setOnClickListener {
imageCapture.takePicture(object : ImageCapture.OnImageCapturedListener() {
override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
//How do I get the bitmap here?
//imageView.setImageBitmap(someBitmap)
}
override fun onError(useCaseError: ImageCapture.UseCaseError?, message: String?, cause: Throwable?) {
val msg = "Photo capture failed: $message"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.e(localClassName, msg)
cause?.printStackTrace()
}
})
}
CameraX.bindToLifecycle(this, preview, imageCapture)
}
}
So the solution was to add extension method to Image and here is the code
class ImagePickerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_picker)
}
private fun startCamera() {
val imageCapture = ImageCapture(imageCaptureConfig)
capture_button.setOnClickListener {
imageCapture.takePicture(object : ImageCapture.OnImageCapturedListener() {
override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
imageView.setImageBitmap(image.image?.toBitmap())
}
//.....
})
}
}
}
fun Image.toBitmap(): Bitmap {
val buffer = planes[0].buffer
buffer.rewind()
val bytes = ByteArray(buffer.capacity())
buffer.get(bytes)
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}
Slightly modified version. Using the inline function use on the Closable ImageProxy
imageCapture.takePicture(
object : ImageCapture.OnImageCapturedListener() {
override fun onCaptureSuccess(image: ImageProxy?, rotationDegrees: Int) {
image.use { image ->
val bitmap: Bitmap? = image?.let {
imageProxyToBitmap(it)
} ?: return
}
}
})
private fun imageProxyToBitmap(image: ImageProxy): Bitmap {
val buffer: ByteBuffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}
Here is the safest approach, using MLKit's own implementation.
Tested and working on MLKit version 1.0.1
import com.google.mlkit.vision.common.internal.ImageConvertUtils;
Image mediaImage = imageProxy.getImage();
InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
Bitmap bitmap = ImageConvertUtils.getInstance().getUpRightBitmap(image)
Java Implementation of Backbelt's Answer.
private Bitmap imageProxyToBitmap(ImageProxy image) {
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
return BitmapFactory.decodeByteArray(bytes,0,bytes.length,null);
}
There is second version of takePicture method at the moment (CameraX version 1.0.0-beta03). It provides several ways to persist image (OutputStream or maybe File can be useful in your case).
If you still want to convert ImageProxy to Bitmap here is my answer to similar question, which gives the correct implemetation of this conversion.
Please kindly take a look at this answer. All you need to apply it to your question is to get Image out of your ImageProxy
Image img = imaget.getImage();

Categories

Resources