0

I have the MainActivity from where I am starting the SecondActivity.

There, I select an image from gallery and I want to push it back to the MainActivity.

MainActivity:

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

        srButton = findViewById(R.id.super_resolution)
        srButton.setOnClickListener { view: View ->

            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)


            val bitmap = getIntent().getParcelableExtra<Bitmap>("bitmap")
        

SecondActivity:

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

        image = findViewById(R.id.the_selected_image);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
                val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
                requestPermissions(permissions, SelectedImage.PERMISSION_CODE)
            } else {
                chooseImageGallery()
            }
        } else {
            chooseImageGallery()
        }



    }

    // Receiver for camera
    private val getResultCamera =
        registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) {
            if (it.resultCode == Activity.RESULT_OK) {
                image.setImageURI(it.data?.data)
                imageUri = it.data?.data as Uri
                bitmap = uriToBitmap(imageUri!!)!!
                image.setImageBitmap(bitmap)
                val intent = Intent(this, MainActivity::class.java)
                //intent.data = imageUri;
                intent.putExtra("bitmap", bitmap)
                startActivity(intent)
            }
        }


    private fun chooseImageGallery() {
        val intent = Intent(Intent.ACTION_PICK)
        intent.type = "image/*"
        getResultCamera.launch(intent)
    }

Right now, I am receiving null for the bitmap.

Also, when I select the image from the gallery , the screen goes to MainActivity, so the user can't see the activity_selected_image layout (hence , the image)

George
  • 5,808
  • 15
  • 83
  • 160
  • Why r you using `startActivity` again ? You should be Using `setResult()` because your Activity is already in Stack . You finish the Second one and pass the result to first one . See [Docs](https://developer.android.com/training/basics/intents/result) . Why u Even have a Second Activity i don't understand it it just have two buttons and u are immediately sending the result back So i don't see any use of your Second Activity . – ADM Mar 10 '22 at 11:03
  • @ADM: I just want to see how I can push back the value. I deleted the `startActivity(intent)` from SecondActivity , but still `bitmap` is null. I am already using the `registerForActivityResult` – George Mar 10 '22 at 11:28
  • It will be null because you are not expecting result in first Activity i.e `registerForActivityResult` . Follow the Doc above or some example you need to make few Changes .. – ADM Mar 10 '22 at 11:30

2 Answers2

1

First of all, if you want to pass something back to the first activity, the second activity should be using setResult() and then finish() so it sends the object back to the existing MainActivity. The way you're doing it is creating a second copy of MainActivity on the stack.

Secondly, startActivity() calls are asynchronous. In your MainActivity's click listener, when you call startActivity(), it queues the second activity to start on the main thread, but it will finish the code that's already running on the main thread first, i.e., the rest of your click listener code. So your val bitmap line is evaluated before the second activity even appears. Anyway, you can't even guarantee that the MainActivity instance that will be around after the SecondActivity is done will even be the same instance you started with. The OS freely destroys and regenerates Activity instances under several types of conditions, one of the big ones being screen rotations.

The MainActivity should use registerActivityForResult() to start the second activity and respond to the result in the register contract.

class MainActivity: AppCompatActivity() {

    private val getImageResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val bitmap = result.data?.getParcelableExtra<Bitmap>("bitmap")
            if (bitmap != null) {
                //...
            }
        }
      }

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

        srButton = findViewById(R.id.super_resolution)
        srButton.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            getImageResult.launch(intent)
        }
    }
}
// In second activity:

    // Receiver for camera
    private val getResultCamera =
        registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ) {
            if (it.resultCode == Activity.RESULT_OK) {
                imageUri = it.data?.data as Uri
                bitmap = uriToBitmap(imageUri!!)!!

                val result = Intent()
                intent.putExtra("bitmap", bitmap)
                setResult(Activity.RESULT_OK, intent)
                finish()
            }
        }

I took out the part about setting the bitmap to an ImageView in the second activity, because the second activity will be destroyed before it can be seen.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Thanks for the explanation! Very helpful! At the `setResult(intent)`, it is red and shows me `Create function setResult`. It doesn't recognize it – George Mar 10 '22 at 12:31
  • Oops, you saw my code before I fixed that line. Sorry. You need to use `setResult(Activity.RESULT_OK, intent)` – Tenfour04 Mar 10 '22 at 12:32
  • Ok, it works but 1) `bitmap` is not null only inside `getImageResult`. I am using ` private var bitmap: Bitmap? = null` in this class. So , I use `bitmap = result.data?.getParcelableExtra("bitmap")` inside `getImageResult`. If I check the `bitmap` value right after `getImageResult.launch(intent)` call , is null. – George Mar 10 '22 at 12:55
  • 2) when I select the image from gallery, it doesn't show me the corresponding `activity_selected_image` layout (with the selected image) It goes immediately back to main activity. – George Mar 10 '22 at 12:55
  • 1) There’s no way to avoid that. It is asynchronous code and you may not even be in the same instance of MainActivity when it returns a result anyway. 2) I didn’t know how you wanted it to work. If you want to show the image and have the user press a button to return it, you need to break up your code so it is only setting the bitmap on your ImageView and enabling some button that calls the setResult/finish code. Or if you want to let the user back out of the second activity with any picture they may have picked, you can put `setResult` in `onStop` and remove the `finish()` call. – Tenfour04 Mar 10 '22 at 12:59
0

Activity 2:

try {
    //Write file
    String filename = "bitmap.png";
    FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);

    //Cleanup
    stream.close();
    bmp.recycle();

    //Pop intent
    Intent in1 = new Intent();
    in1.putExtra("image", filename);
    setResult(CODE,in1);
    startActivity(in1);
} catch (Exception e) {
    e.printStackTrace();
}

In Activity 1, load up the bitmap:

Bitmap bmp = null;
String filename = getIntent().getStringExtra("image");
try {
    FileInputStream is = this.openFileInput(filename);
    bmp = BitmapFactory.decodeStream(is);
    is.close();
} catch (Exception e) {
    e.printStackTrace();
}

UPDATE Activity 2

//Convert to byte array
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();

Intent in1 = new Intent();
in1.putExtra("image",byteArray);
setResult(CODE,in1)

Then in Activity 1:

byte[] byteArray = getIntent().getByteArrayExtra("image");
Bitmap bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
Siddarth Jain
  • 304
  • 1
  • 6