fix: saving log external instead of session blob

This commit is contained in:
Harris 2021-10-08 16:43:45 +11:00
parent a55c9a969b
commit e8a2bbe76d
3 changed files with 111 additions and 14 deletions

View File

@ -154,7 +154,7 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4' testImplementation 'org.robolectric:shadows-multidex:4.4'
} }
def canonicalVersionCode = 228 def canonicalVersionCode = 229
def canonicalVersionName = "1.11.11" def canonicalVersionName = "1.11.11"
def postFixSize = 10 def postFixSize = 10

View File

@ -7,10 +7,7 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.AsyncTask import android.os.*
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.ActionMode import android.view.ActionMode
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -323,8 +320,18 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
} }
private fun shareLogs() { private fun shareLogs() {
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P)
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
.onAnyDenied {
Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
}
.onAllGranted {
ShareLogsDialog().show(supportFragmentManager,"Share Logs Dialog") ShareLogsDialog().show(supportFragmentManager,"Share Logs Dialog")
} }
.execute()
}
// endregion // endregion

View File

@ -1,8 +1,15 @@
package org.thoughtcrime.securesms.preferences package org.thoughtcrime.securesms.preferences
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent import android.content.Intent
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.view.LayoutInflater import android.view.LayoutInflater
import android.webkit.MimeTypeMap
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -12,9 +19,15 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import network.loki.messenger.BuildConfig import network.loki.messenger.BuildConfig
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsignal.utilities.ExternalStorageUtil
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.util.StreamUtil
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit
class ShareLogsDialog : BaseDialog() { class ShareLogsDialog : BaseDialog() {
@ -39,16 +52,41 @@ class ShareLogsDialog : BaseDialog() {
shareJob = lifecycleScope.launch(Dispatchers.IO) { shareJob = lifecycleScope.launch(Dispatchers.IO) {
val persistentLogger = ApplicationContext.getInstance(context).persistentLogger val persistentLogger = ApplicationContext.getInstance(context).persistentLogger
try { try {
val logs = persistentLogger.logs.get() val context = requireContext()
val fileName = "${Build.MANUFACTURER}-${Build.DEVICE}-API${Build.VERSION.SDK_INT}-v${BuildConfig.VERSION_NAME}.txt" val outputUri: Uri = ExternalStorageUtil.getDownloadUri()
val logUri = BlobProvider().forData(logs.toByteArray()) val mediaUri = getExternalFile()
.withFileName(fileName) if (mediaUri == null) {
.withMimeType("text/plain") // show toast saying media saved
.createForSingleSessionOnDisk(requireContext(),null) dismiss()
return@launch
}
val inputStream = persistentLogger.logs.get().byteInputStream()
val updateValues = ContentValues()
if (outputUri.scheme == ContentResolver.SCHEME_FILE) {
FileOutputStream(mediaUri.path).use { outputStream ->
StreamUtil.copy(inputStream, outputStream)
MediaScannerConnection.scanFile(context, arrayOf(mediaUri.path), arrayOf("text/plain"), null)
}
} else {
context.contentResolver.openOutputStream(mediaUri, "w").use { outputStream ->
val total: Long = StreamUtil.copy(inputStream, outputStream)
if (total > 0) {
updateValues.put(MediaStore.MediaColumns.SIZE, total)
}
}
}
if (Build.VERSION.SDK_INT > 28) {
updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
}
if (updateValues.size() > 0) {
requireContext().contentResolver.update(mediaUri, updateValues, null, null)
}
val shareIntent = Intent().apply { val shareIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, logUri) putExtra(Intent.EXTRA_STREAM, mediaUri)
data = mediaUri
type = "text/plain" type = "text/plain"
} }
@ -62,4 +100,56 @@ class ShareLogsDialog : BaseDialog() {
} }
} }
@Throws(IOException::class)
private fun pathTaken(outputUri: Uri, dataPath: String): Boolean {
requireContext().contentResolver.query(outputUri, arrayOf(MediaStore.MediaColumns.DATA),
MediaStore.MediaColumns.DATA + " = ?", arrayOf(dataPath),
null).use { cursor ->
if (cursor == null) {
throw IOException("Something is wrong with the filename to save")
}
return cursor.moveToFirst()
}
}
private fun getExternalFile(): Uri? {
val context = requireContext()
val base = "${Build.MANUFACTURER}-${Build.DEVICE}-API${Build.VERSION.SDK_INT}-v${BuildConfig.VERSION_NAME}-${System.currentTimeMillis()}"
val extension = "txt"
val fileName = "$base.$extension"
val mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("text/plain")
val outputUri: Uri = ExternalStorageUtil.getDownloadUri()
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
contentValues.put(MediaStore.MediaColumns.DATE_ADDED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))
contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))
if (Build.VERSION.SDK_INT > 28) {
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1)
} else if (Objects.equals(outputUri.scheme, ContentResolver.SCHEME_FILE)) {
val outputDirectory = File(outputUri.path)
var outputFile = File(outputDirectory, "$base.$extension")
var i = 0
while (outputFile.exists()) {
outputFile = File(outputDirectory, base + "-" + ++i + "." + extension)
}
if (outputFile.isHidden) {
throw IOException("Specified name would not be visible")
}
return Uri.fromFile(outputFile)
} else {
var outputFileName = fileName
val externalPath = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)!!
var dataPath = String.format("%s/%s", externalPath, outputFileName)
var i = 0
while (pathTaken(outputUri, dataPath)) {
outputFileName = base + "-" + ++i + "." + extension
dataPath = String.format("%s/%s", externalPath, outputFileName)
}
contentValues.put(MediaStore.MediaColumns.DATA, dataPath)
}
return context.contentResolver.insert(outputUri, contentValues)
}
} }