In my android application, I am trying to let the user select a gallery image and show a high-quality image in another activity. is there a way to get an absolute path from URI? or is there an efficient way to do this?
This function opens the gallery and lets the user select an image:
private fun getGalleryImage() {
val galleryImageIntent =
Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
if (galleryImageIntent.resolveActivity(requireActivity().packageManager) != null) {
startActivityForResult(galleryImageIntent, REQUEST_GALLERY_IMAGE)
}
}
And the following gets the result, but I am not sure how to get the image path
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_GALLERY_IMAGE && resultCode == RESULT_OK) {
startActivity(
Intent(context, ImageViewActivity::class.java).putExtra(
"imagePath",
data?.data?.toString()
)
)
} else
super.onActivityResult(requestCode, resultCode, data)
}
Here is my ImageViewActivity:
class ImageViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_view)
backBtn.setOnClickListener {
finish()
}
val imagePath = intent?.extras?.get("imagePath")
BitmapFactory.decodeFile(imagePath?.toString())?.also { bitmap ->
imageHolder.setImageBitmap(bitmap)
}
}
}
I tried sending the bitmap itself to my ImageViewActivity but it keeps crashing, and from what I read online, it is not possible to send large amount of data to an Activity.
Related
I am currently trying to learn from a step-by-step tutorial to upload an Image or File to my server while using Volley. This tutorial is a little bit outdated and I really don't understand how I can fix these issues.
the tutorial
onActivityResult(Int, Int, Intent?): Unit' is deprecated. Deprecated in Java
Fragment is attempting to registerForActivityResult after being created. Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).
My code
//Uploading Photos
private fun launchGallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
var resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
// There are no request codes
val data: Intent? = result.data
}
}
}
private fun uploadImage() {
imageData?: return
val request = object : VolleyFileUploadRequest(
Request.Method.POST,
postURL,
{
println("response is: $it")
},
{
println("error is: $it")
}
) {
override fun getByteData(): MutableMap<String, FileDataPart> {
var params = HashMap<String, FileDataPart>()
params["imageFile"] = FileDataPart("image", imageData!!, "jpeg")
return params
}
}
Volley.newRequestQueue(requireContext()).add(request)
}
#Throws(IOException::class)
private fun createImageData(uri: Uri) {
val inputStream = requireContext().contentResolver.openInputStream(uri)
inputStream?.buffered()?.use {
imageData = it.readBytes()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_PICK_CODE) {
val uri = data?.data
if (uri != null) {
imageView.setImageURI(uri)
createImageData(uri)
}
}
super.onActivityResult(requestCode, resultCode, data)
}
}
you are trying to registerForActivityResult from a method "launchGallery()" which is not the way it should be done, "registerForActivityResult" should be initialized in the Activity/Fragment onCreate function and then you can use the "resultLauncher" variable to open the gallery/camera etc...
also when you are using the "registerForActivityResult" you don't need to override "onActivityResult" (which is now deprecated).
check the Activity Result Api to get a better understanding of how the new api works.
and here is a good tutorial that shows you how to use the Android Activity Result API for selecting and taking images
My application checks whether the new selected image is the same as the previous one so that I don't need to update the cloud data, but newBitmap.sameAs(oldBitmap) always return false even though I pick the same photo.
Piece of my code:
private lateinit var oldBitmap: Bitmap
private lateinit var newBitmap: Bitmap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_profile)
myProfilePictureUrl = intent.getStringExtra("myProfilePictureUrl")!!
if (myProfilePictureUrl.isNotEmpty()) {
Picasso.get().load(myProfilePictureUrl).into(my_profile_imageView)
}
oldBitmap = (my_profile_imageView.drawable as BitmapDrawable).bitmap //for later comparison
my_profile_imageView.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
startActivityForResult(intent, RESULT_CODE_PICK_IMAGE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RESULT_CODE_PICK_IMAGE && resultCode == Activity.RESULT_OK) {
val inputStream = contentResolver.openInputStream(data!!.data!!)
val bitmap = BitmapFactory.decodeStream(inputStream)
newBitmap = bitmap //for comparison
my_profile_imageView.setImageBitmap(bitmap)
}
}
And when the user click on the "save" button on the action bar will it first check whether they are the same photo:
private fun updateProfile(nickname: String) {
if (this::newBitmap.isInitialized && !newBitmap.sameAs(oldBitmap)) { //here
//upload task code
}
}
THE PROBLEM IS:
!newBitmap.sameAs(oldBitmap) always return false even they are the same photo, may be the problem of PICASSO? Or the way I used to compare is not right. Any help would be appreciated.
I am using image picker inside recyclerview
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvAnimalType.text = items[position].name
holder.image.setOnClickListener{
requestPermissions(it.context as Activity , arrayOf(WRITE_EXTERNAL_STORAGE),1)
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
(it.context as Activity).startActivityForResult(intent, 1)
}
}
In Main Activity
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
1 -> {/*file front*/
if (resultCode == Activity.RESULT_OK && data != null) {
val selectedImageUri = data.data as Uri
val selectedImageBitmap: Bitmap =
MediaStore.Images.Media.getBitmap(this.contentResolver, selectedImageUri)
}
}
}
}
My question is how to load the selected image in recylerview imageview ?
Try like the following
Pass item position as request_code.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvAnimalType.text = items[position].name
// here you can get image as bitmap using filepath and set in to your image view
val filepath = items[position].filePath
val bitmapImage = ...........[get bitmap from filepath]
holder.image.setImageBitmap(bitmapImage)
holder.image.setOnClickListener{
requestPermissions(it.context as Activity , arrayOf(WRITE_EXTERNAL_STORAGE),1)
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
(it.context as Activity).startActivityForResult(intent, position) // pass position as request code
}
}
And In activity do like the following
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && data != null) {
val selectedImageUri = data.data as Uri
val selectedImageBitmap: Bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, selectedImageUri)
// here you can get image file path using URI
val filePathFromURI = ......[get image path using URI]
// then save it to your list data like
items[requestCode].filePath = filePathFromURI // here items is your data set which passed in adapter from your activity you should replace it with yours.
// now you have to notify your adapter that your data set is changed
adapter.notifyDataSetChanged() // replace adapter with yours.
}
}
}
And one important thing you should have a property named filePath in your data class.
I am trying to pass a value from Activity 3 to Activity 2 but I am getting null Value. If I click Back button its going to previous Activity but value is null. Added the suggested Approach code below. but still not able to get the results.
Suggested Approach:
Activity :3
override fun onBackPressed() {
sendDataBackToPreviousActivity()
super.onBackPressed()
}
private fun sendDataBackToPreviousActivity()
{
val navBarTitle21=intent.getStringExtra(TestProjectMenuViewHolder.TEST_TITLE_NAME)
val intent=Intent().apply { putExtra("ReturnMessage",navBarTitle21)}
setResult(Activity.RESULT_OK,intent)
}
Activity:2
Main Class:
companion object {
const val START_ACTIVITY_3_REQUEST_CODE = 0
}
val intent=Intent(this,TestProjectMenuDetail::class.java)
startActivityForResult(intent, START_ACTIVITY_3_REQUEST_CODE)
Declared outside Main Class:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == START_ACTIVITY_3_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val message = data!!.getStringExtra("ReturnMessage")
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
println("Message Value: $message")
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
First Approach:
Activity:3
val navBarTitle= intent.getStringExtra(TestMenuViewHolder.TEST_TITLE_KEY)
supportActionBar?.title=navBarTitle//Something Like "StackOverFlow". THis is for back Button
TestMenuDetail:
val navBarTitle2=intent.getStringExtra(TestMenuViewHolder.TEST_TITLE_NAME)
val TestVar=Intent(this#TestMenuDetail,TestMenuList::class.java)
intent.putExtra("TestVar2",navBarTitle2)
println("Test Value $navBarTitle2")//Test Value Hello
Activity:2
TestMenuList:
val navBarTitle3=intent.getStringExtra("TestVar2")
println("Helllo Test: $navBarTitle3")//Helllo Test: null
You should use startActivityForResult API to achieve your task.
Activity2.kt
class Activity2 : AppCompatActivity() {
companion object {
const val START_ACTIVITY_3_REQUEST_CODE = 0
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity2)
// Start Activity3
val intent = Intent(this, Activity3::class.java)
startActivityForResult(intent, START_ACTIVITY_3_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == START_ACTIVITY_3_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val message = data!!.getStringExtra("message")
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
Activity3.kt
class Activity3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity3)
}
override fun onBackPressed() {
sendDataBackToPreviousActivity()
super.onBackPressed()
}
/**
* Send data back to previous activity which start this one, you can call this method when users press on back key
* or when users press on a view (button, image, etc) on this activity.
*/
private fun sendDataBackToPreviousActivity() {
val intent = Intent().apply {
putExtra("message", "This is a message from Activity3")
// Put your data here if you want.
}
setResult(Activity.RESULT_OK, intent)
}
}
Ok here is what I would do:
Override the onBackPressed method and then pass the variable value inside the method with an Intent. And in activity 2 receive the value from activity 3.
In activity 3
#override
public void onBackPressed (){
Intent intent = new Intent(getApplicationContext(), Activity2.class);
intent.put("value_key", value);
startActivity(intent);
}
Receive value in activity 2
getIntent.getValue("value_key");
Don't forget to check the syntax, I just wrote it from my phone. Hope it helps!
You can always use SharedPreferences, and then clear them, after receiving data in previous activity. It's 100% effective way. Put it:
val sharedPreference = getSharedPreferences("prefs name",Context.MODE_PRIVATE)
var editor = sharedPreference.edit()
editor.putString("your value name","value")
editor.commit()
and get it:
sharedPreference.getString("your value name","default value")
but of course you have to open preferences again in previous activity ;)
val sharedPreference = getSharedPreferences("prefs name",Context.MODE_PRIVATE)
How to pass Image selected in ImageView to another Activity in Android using Kotlin ?
This is the way to select the Image inside the ImageView using internal Storage and I need to pass the image to another activity
fun Loadimage()
{
var intent = Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent,ImageCode)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode==ImageCode && data!=null && resultCode== Activity.RESULT_OK)
{
val selectedImage = data.data
val filepath = arrayOf(MediaStore.Images.Media.DATA)
val cursor = contentResolver.query(selectedImage,filepath,null,null,null)
cursor.moveToFirst()
val Index = cursor.getColumnIndex(filepath[0])
val Picture = cursor.getString(Index)
cursor.close()
imageView.setImageBitmap(BitmapFactory.decodeFile(Picture))
}
}
You can pass Picture variable to next activity using Intents like below
val intent = Intent(this, NextActivity::class.java)
intent.putExtra("picture", Picture)
startActivity(intent)
Then in NextActivity, in onCreate method, you can get picture using
val Picture = getIntent().getStringExtra("picture")
In the NextActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val postImage = findViewById<ImageView>(R.id.post_image)
myPic = postImage
}
companion object {
lateinit var myPic: ImageView()
}
And in the first Activity:
NextActivity.myPic = Picture