4

This Code Works Fine With Media Files I want a solution For Document Files

I Don't Know how to put contentValues For Document Files

fun getFile(fileName: String): File? {
    with(sharePrefHelper.app){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val values = ContentValues()
            // Here is My Question That what should i Do Here Because this is for document not for image
            values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
            // for MIME_TYPE "image/jpg" this is working
            values.put(MediaStore.Images.Media.MIME_TYPE, "text/csv")
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/Donny")
            contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)?.let {
                it.path?.let { finalPath ->
                    return File(finalPath)
                }
            }
        } else {
            val directory: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM + "/Donny")
            if (!directory.exists()){
                directory.mkdirs()
            }
            return File(directory, fileName)
        }
        return null
    }
}

This Code Works Fine with media Files

My Question Here is How to save documents like CSV File in outer folder of android device

Gulab Sagevadiya
  • 872
  • 1
  • 8
  • 20
  • Where do you want to save your file ? Where stored initialy the files ? – Louis Chabert May 24 '22 at 14:11
  • I want to save in downloads or DCIM folder with app named folder Currently I am storing this files in app scope storage Android/data/appPackageName on Above API 28 – Gulab Sagevadiya May 25 '22 at 02:25
  • Ok, I did something similary but in java android not kotlin but I can give an example – Louis Chabert May 25 '22 at 06:53
  • Quick question, do you care about publishing the app on google play, or having the APK for yourself is enough? – H.A.H. May 27 '22 at 12:31
  • Yes, this is implemented on my live app that's why I am finding a proper solution that at least works for 2 years. with the right approach to deal all possible devices. That's why I have asked for the SAF method in the answer – Gulab Sagevadiya May 27 '22 at 12:53
  • @gulabpatel maybe this can help you : https://stackoverflow.com/questions/59511147/create-copy-file-in-android-q-using-mediastore – Louis Chabert May 31 '22 at 09:16

2 Answers2

3

EDIT :

Well well well, I'm still trying to add anytype of file in the "download" directory. Personnaly, I'm trying to copy a file from my assetFolder and paste it to the "Download" folder. I haven't succeeded yet. However, I can currently CREATE anytype of file in that folder, it's working with the method below. I hope this can help you.

Here is my code :

    public void saveFileToPhone(InputStream inputStream, String filename) {
    OutputStream outputStream;
    Context myContext = requireContext();
    try {
        if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.Q){
            ContentResolver contentResolver = requireContext().getContentResolver();
            ContentValues contentValues = new ContentValues();
            contentValues.put(MediaStore.Downloads.DISPLAY_NAME,filename);
            contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
            Uri collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
            Uri fileUri = contentResolver.insert(collection, contentValues);
            outputStream = contentResolver.openOutputStream(Objects.requireNonNull(fileUri));
            Objects.requireNonNull(outputStream);

        }
    }catch (FileNotFoundException e) {

        e.printStackTrace();
    }
}
Louis Chabert
  • 399
  • 2
  • 15
  • This will work in android 10 but from android 11 I need SAF compulsory to save document files. I did some research on this and this can only achieve by https://developer.android.com/reference/android/content/Intent#ACTION_CREATE_DOCUMENT You can see more about in scoped storage so I need a solution for at least android 12 – Gulab Sagevadiya May 25 '22 at 12:29
  • https://developer.android.com/training/data-storage#scoped-storage Here they have mentioned all types and methods. – Gulab Sagevadiya May 25 '22 at 12:30
  • 1
    So, you mentioned in your question API 28 but it's not on after API 28 but after API 30 ? – Louis Chabert May 25 '22 at 12:36
  • He indeed ask for after 28. This means FROM 28 TO the current version. You does not work on the current and previous version of android. – H.A.H. May 27 '22 at 12:30
  • yes this code works. Thanks – Gulab Sagevadiya Jun 01 '22 at 12:38
  • requireActivity().contentResolver.openFileDescriptor(fileUri, "w")?.use { parcelFileDescriptor -> FileOutputStream(parcelFileDescriptor.fileDescriptor).use { fos -> csvWriter().open(fos) { writeRow(allStudents[0].keys.toList()) writeRows(allStudents.map { it.values.toList() }) } toast("Generated Successfully!!!") } } I have used that uri like this Not sure how to write this in java but you may understand by names – Gulab Sagevadiya Jun 02 '22 at 09:26
  • 1
    0 byte file saved for me. You have inputStream para, which you didn't have used. So how to write this inputStream into that empty file. – Arnab Kundu Jul 01 '22 at 07:41
1

Here it is what i have done with clean way

This is file provider activity

class FileProviderActivity : AppCompatActivity() {

    var commonIntentLauncher: ActivityResultLauncher<Intent?> = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            result.data?.let {
                intent.getParcelableExtra<ResultReceiver>("FileReceiver")?.send(0, bundleOf(
                    "FileUri" to result?.data?.data.toString()
                ))
                finish()
            }
        }
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            val activityIntent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
                addCategory(Intent.CATEGORY_OPENABLE)
                type = intent.getStringExtra("fileType")
                putExtra(Intent.EXTRA_TITLE, intent.getStringExtra("fileName"))
                putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
            }
            commonIntentLauncher.launch(activityIntent)
        }else{
            val directory: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS + "/Dunny/CSVFiles")
            if (!directory.exists()){
                directory.mkdirs()
            }

            intent.getParcelableExtra<ResultReceiver>("FileReceiver")?.send(0, bundleOf(
                "FileUri" to File(directory, intent.getStringExtra("fileName")!!).toURI().toString()
            ))
            finish()
        }
    }
}

This FileProviderHelper

class MyFileProvider {

    companion object {
        fun with(context: Context) = FileRequest(context)
    }

    class FileRequest(private val context: Context) {

        fun request(fileName: String, fileType: String = "application/*", file: (Uri?) -> Unit ) {
            val intent = Intent(context, FileProviderActivity::class.java)
                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .putExtra("fileName",fileName)
                .putExtra("fileType", fileType)
                .putExtras(bundleOf("FileReceiver" to FileReceiver(file)))
            context.startActivity(intent)
        }
    }

    internal class FileReceiver(private val file: (Uri?) -> Unit) : ResultReceiver(Handler(Looper.getMainLooper())) {
        override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
            super.onReceiveResult(resultCode, resultData)
            resultData?.let {
                file(it.getString("FileUri")?.toUri())
            }
        }
    }
}

Here Is Use Of this Function

MyFileProvider.with(this).request("TestFile.csv","application/*") { fileUri ->
    toast(fileUri.toString())
}
Gulab Sagevadiya
  • 872
  • 1
  • 8
  • 20
  • do you know how to insert asset's file with your method ? Can you help me with this pls ? – Louis Chabert Jun 02 '22 at 08:08
  • requireActivity().contentResolver.openFileDescriptor(fileUri, "w")?.use { parcelFileDescriptor -> FileOutputStream(parcelFileDescriptor.fileDescriptor).use { fos -> csvWriter().open(fos) { writeRow(allStudents[0].keys.toList()) writeRows(allStudents.map { it.values.toList() }) } toast("Generated Successfully!!!") } } I have used that uri like this – Gulab Sagevadiya Jun 02 '22 at 09:25
  • I don't know how to use this with my code above but thank you for your try – Louis Chabert Jun 02 '22 at 09:48
  • OpenFileDescriptor then create fileOutputStream with that fileDescriptor and use that fileoutput stream to copy. Here in my case I am giving this fos to csvWriter object to write data in to that file – Gulab Sagevadiya Jun 03 '22 at 07:43