Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b5cac5d8 authored by Moez Bhatti's avatar Moez Bhatti
Browse files

Use MediaStore for saving attachments to device

parent 53b2ac2b
Loading
Loading
Loading
Loading
+40 −17
Original line number Diff line number Diff line
@@ -26,8 +26,10 @@ import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.provider.Telephony
import android.provider.Telephony.Mms
import android.provider.Telephony.Sms
@@ -44,6 +46,8 @@ import com.klinker.android.send_message.Transaction
import com.moez.QKSMS.common.util.extensions.now
import com.moez.QKSMS.compat.TelephonyCompat
import com.moez.QKSMS.extensions.anyOf
import com.moez.QKSMS.extensions.isImage
import com.moez.QKSMS.extensions.isVideo
import com.moez.QKSMS.manager.ActiveConversationManager
import com.moez.QKSMS.manager.KeyManager
import com.moez.QKSMS.model.Attachment
@@ -61,6 +65,8 @@ import io.realm.Case
import io.realm.Realm
import io.realm.RealmResults
import io.realm.Sort
import okio.buffer
import okio.source
import timber.log.Timber
import java.io.File
import java.io.FileNotFoundException
@@ -167,36 +173,53 @@ class MessageRepositoryImpl @Inject constructor(
                .findAllAsync()
    }

    override fun savePart(id: Long): File? {
    override fun savePart(id: Long): Uri? {
        val part = getPart(id) ?: return null

        val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(part.type) ?: return null
        val date = part.messages?.first()?.date
        val dir = File(Environment.getExternalStorageDirectory(), "QKSMS/Media").apply { mkdirs() }
        val fileName = part.name?.takeIf { name -> name.endsWith(extension) }
                ?: "${part.type.split("/").last()}_$date.$extension"
        var file: File
        var index = 0
        do {
            file = File(dir, if (index == 0) fileName else fileName.replace(".$extension", " ($index).$extension"))
            index++
        } while (file.exists())

        try {
            FileOutputStream(file).use { outputStream ->
        val values = contentValuesOf(
                MediaStore.MediaColumns.DISPLAY_NAME to fileName,
                MediaStore.MediaColumns.MIME_TYPE to part.type,
        )

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.MediaColumns.IS_PENDING, 1)
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, when {
                part.isImage() -> "${Environment.DIRECTORY_PICTURES}/QKSMS"
                part.isVideo() -> "${Environment.DIRECTORY_MOVIES}/QKSMS"
                else -> "${Environment.DIRECTORY_DOWNLOADS}/QKSMS"
            })
        }

        val contentUri = when {
            part.isImage() -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            part.isVideo() -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> MediaStore.Downloads.EXTERNAL_CONTENT_URI
            else -> MediaStore.Files.getContentUri("external")
        }
        val resolver = context.contentResolver
        val uri = resolver.insert(contentUri, values)
        Timber.v("Saving $fileName (${part.type}) to $uri")

        uri?.let {
            resolver.openOutputStream(uri)?.use { outputStream ->
                context.contentResolver.openInputStream(part.getUri())?.use { inputStream ->
                    inputStream.copyTo(outputStream, 1024)
                }
            }
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
            Timber.v("Saved $fileName (${part.type}) to $uri")

        MediaScannerConnection.scanFile(context, arrayOf(file.path), null, null)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                resolver.update(uri, contentValuesOf(MediaStore.MediaColumns.IS_PENDING to 0), null, null)
                Timber.v("Marked $uri as not pending")
            }
        }

        return file.takeIf { it.exists() }
        return uri
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
 */
package com.moez.QKSMS.repository

import android.net.Uri
import com.moez.QKSMS.model.Attachment
import com.moez.QKSMS.model.Message
import com.moez.QKSMS.model.MmsPart
@@ -40,7 +41,7 @@ interface MessageRepository {

    fun getPartsForConversation(threadId: Long): RealmResults<MmsPart>

    fun savePart(id: Long): File?
    fun savePart(id: Long): Uri?

    /**
     * Retrieves the list of messages which should be shown in the notification
+7 −9
Original line number Diff line number Diff line
@@ -260,23 +260,21 @@ class Navigator @Inject constructor(
        startActivityExternal(intent)
    }

    fun viewFile(file: File) {
        val data = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
        val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.name.split(".").last())
    fun viewFile(uri: Uri, mimeType: String) {
        val intent = Intent(Intent.ACTION_VIEW)
                .setDataAndType(data, type)
                .setDataAndType(uri, mimeType.lowercase())
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                .let { Intent.createChooser(it, null) }

        startActivityExternal(intent)
    }

    fun shareFile(file: File) {
        val data = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
        val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(file.name.split(".").last())
    fun shareFile(uri: Uri, mimeType: String) {
        val intent = Intent(Intent.ACTION_SEND)
                .setType(type)
                .putExtra(Intent.EXTRA_STREAM, data)
                .setType(mimeType.lowercase())
                .putExtra(Intent.EXTRA_STREAM, uri)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                .let { Intent.createChooser(it, null) }

        startActivityExternal(intent)
    }
+0 −1
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import androidx.core.view.isVisible
import com.jakewharton.rxbinding2.view.clicks
import com.moez.QKSMS.R
import com.moez.QKSMS.common.base.QkController
import com.moez.QKSMS.common.util.DateFormatter
import com.moez.QKSMS.common.util.QkActivityResultContracts
import com.moez.QKSMS.common.util.extensions.getLabel
import com.moez.QKSMS.common.util.extensions.setBackgroundTint
+1 −1
Original line number Diff line number Diff line
@@ -433,7 +433,7 @@ class ComposeViewModel @Inject constructor(
                .autoDisposable(view.scope())
                .subscribe { part ->
                    if (permissionManager.hasStorage()) {
                        messageRepo.savePart(part.id)?.let(navigator::viewFile)
                        messageRepo.savePart(part.id)?.let { navigator.viewFile(it, part.type) }
                    } else {
                        view.requestStoragePermission()
                    }
Loading