How to share image from our app to another using intents and kotlin?

72 Views Asked by At

I am trying to share a sample picture from my app to other person using Whatsapp/or other photo sharing app. But I don't know why it is not sharing. I don't know where in my code I am doing wrong. Please help me to solve the problem. I am using Intent to share the image.

My tried code files are as follows->

  1. AndroidMenifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ImageShare"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.ImageShare">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
            android:name="com.example.imageshare.MyFileProvider"
            android:authorities="com.example.imageshare.MyFileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
        </provider>
    </application>
</manifest>
  1. file_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="." />
</paths>
  1. MainActivity.kt
package com.example.imageshare

import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.core.app.ActivityCompat
import androidx.core.app.ShareCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.example.imageshare.ui.theme.ImageShareTheme
import java.io.File


class MainActivity : ComponentActivity() {
    private val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 123 // You can use any integer value

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MainContent()
        }

        // Check if the permission is not granted
        if (ContextCompat.checkSelfPermission(
                this,
                android.Manifest.permission.READ_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            // Request the permission
            ActivityCompat.requestPermissions(
                this,
                arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),
                MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE
            )
        } else {
            // Permission is already granted, proceed with the operation
            setContent {
                MainContent()
            }
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // Permission granted, proceed with the operation
                    setContent {
                        MainContent()
                    }
                } else {
                    // Permission denied, handle accordingly
                    // You may show a message or take appropriate action
                }
            }
            // Handle other permissions if needed
        }
    }

    @Composable
    fun MainContent() {
        // UI code with the Button and performFileOperation
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                performFileOperation()
            }
        }
    }

    @Composable
    private fun performFileOperation() {
        // The code that involves interacting with external storage
        // For example, checking file existence and readability
        val imageName = "SolarSystem.jpg"
        val imageFile = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), imageName)

        Log.d("MainActivity_Response", "Image exist? ${imageFile.exists()}")
        Log.d("MainActivity_Response", "Image can be read? ${imageFile.canRead()}")
        Log.d("MainActivity_Response", "Absolute path is ${imageFile.absolutePath}")
        Log.d("MainActivity_Response", "One more value is ${getExternalFilesDir(Environment.DIRECTORY_PICTURES)}")

        // Create a Uri from the file using FileProvider
        val imageUri: Uri = FileProvider.getUriForFile(
            applicationContext,
            "com.example.imageshare.MyFileProvider",
            imageFile
        )

        // UI code with the Button
        Button(
            onClick = {
//                Intent(Intent.ACTION_SEND).apply {
//                    type = "image/jpeg"
//                    putExtra(Intent.EXTRA_STREAM, imageUri)
//                    startActivity(this)
//                }
                ShareCompat.IntentBuilder(this)
                    .setType("image/jpeg")
                    .addStream(imageUri)
                    .setChooserTitle("Share image")
                    .setSubject("Shared image")
                    .startChooser()
            }

        ) {
            Text(text = "Send")
        }

    }
}
  1. MyFileProvider.kt
package com.example.imageshare

import androidx.core.content.FileProvider

class MyFileProvider : FileProvider(R.xml.file_path) {
}

Edited 1 What's in the edit?

  1. I am sharing some logs, that I had generated from MainActivity.Kt file. a. "2024-03-01 06:57:12.905 3628-3628 MainActivity_Response com.example.imageshare D Image exist? false " b. "2024-03-01 06:57:12.905 3628-3628 MainActivity_Response com.example.imageshare D Image can be read? false " c. "2024-03-01 06:57:12.905 3628-3628 MainActivity_Response com.example.imageshare D Absolute path is /storage/emulated/0/Android/data/com.example.imageshare/files/Pictures/SolarSystem.jpg " d. "2024-03-01 06:57:12.907 3628-3628 MainActivity_Response com.example.imageshare D One more value is /storage/emulated/0/Android/data/com.example.imageshare/files/Pictures "

  2. I had used chatgpt too to resolve this issue. And it had asked to edit (which I had edited) some things -> a. Since, the first and second logs, as you can see, gives me false value. Therefore, it had suggested me to put the image in android file system using these two commands. "adb shell mkdir -p /sdcard/Android/data/com.example.imageshare/files/Pictures/ " "adb push C:\Users\Pcc\Desktop\SolarSystem.jpg /sdcard/ " "adb shell mv /sdcard/SolarSystem.jpg /sdcard/Android/data/com.example.imageshare/files/Pictures/ " b. It has also given the edited code to manage permissions.(I had updated my code files here accordingly). c. And also some small twitches.

Results I got after this modifications->

  1. For one to two times when after editing the code file accordingly, I started to get positive result. Even one to two times I successfully able to share the image in whatsapp and other platform too. The values for "Image Exist?" and "Image Can be found?" log was also started to returning true. But as you all know, "Curiosity kills the Cat.". After, when my app was perfectly working, since I had done many modifications in my code and I am very new to these concepts, I tried to understand the code by making small changes like commenting out some parts or maybe changing some approach. And then, after coming back to original correct code (maybe), here it goes again, MY APP AGAIN STARTED TO SHOW THE SAME RESULTS AS IT WAS SHOWING BEFORE. In whatsapp, it is showing"file format not supported.". And I had shared the logs with you also. I humbly request you to visit my edited code please.

Edit - 2 I tried another way to send the intent, by making resource uri of the image and then simply send it. And it was almost working but the issue arises was with the whatsapp app. I ckecked my code by doing work with this image using different apps like google Drive, Notes, Google search image they all are responding with correct image save . But when working with WhatsApp it is not sending "solarsystem.jpg" instead it is sending "null. bin" . I don't know what "null.bin" and why it is not getting the correct image. Please help me to resolve the issue for the whatsapp, because it is the app I want to use to allow the user to share the details for my main app. Code is ->

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            UriBasicsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Button(onClick = {
                            val imageUri = Uri.parse("android.resource://$packageName/drawable/solarsystem")

                            val intent = Intent(Intent.ACTION_SEND).apply {
                                type = "image/*"
                                putExtra(Intent.EXTRA_STREAM, imageUri)
                                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Add this line
                            }

                            if (intent.resolveActivity(packageManager) != null) {
                                startActivity(intent)
                            }
                        }) {
                            Text(text = "Click")
                        }
                    }
                }
            }
        }
    }
}

1

There are 1 best solutions below

3
Jan Itor On

FileProvider

You need to extend FileProvider and set the android:name attribute in your app manifest to the FileProvider you created, like described in the documentation.

package com.example.intentbasics

class MyFileProvider : FileProvider(R.xml.file_paths)

AndroidManifest.xml

        <provider
            android:name="com.example.intentbasics.MyFileProvider "

Use ShareCompat.IntentBuilder instead of creating and starting an Intent by yourself.

    Button(onClick = {
        ShareCompat.IntentBuilder(context)
            .setType("image/jpeg")
            .addStream(imageUri)
            .setChooserTitle("Share image")
            .setSubject("Shared image")
            .startChooser()
    }) {
        Text(text = "Send")
    }

File location

You use external storage as your file location by using external-path in the file_paths.xml and getExternalStorageDirectory() when creating image URI. To get a better understanding of what external storage is please read getExternalStorageDirectory() method documentation. Usually it's going to be /storage/emulated/0, where your phone directories like Download, Music or Pictures are located.

Permisson

You don't check or request for file access permission which is required for accessing external files. Also, WRITE_EXTERNAL_STORAGE is deprecated and you should add READ_MEDIA_IMAGES permission. Read this on media files permission changes in Android 13.

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

For testing purposes you can grant relevant permission explicitly in the App info screen, but in a real app you'll have to request this permission.