Skip to content

Commit e8a2bbe

Browse files
committed
fix: saving log external instead of session blob
1 parent a55c9a9 commit e8a2bbe

File tree

3 files changed

+111
-14
lines changed

3 files changed

+111
-14
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ dependencies {
154154
testImplementation 'org.robolectric:shadows-multidex:4.4'
155155
}
156156

157-
def canonicalVersionCode = 228
157+
def canonicalVersionCode = 229
158158
def canonicalVersionName = "1.11.11"
159159

160160
def postFixSize = 10

app/src/main/java/org/thoughtcrime/securesms/preferences/SettingsActivity.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import android.content.ClipboardManager
77
import android.content.Context
88
import android.content.Intent
99
import android.net.Uri
10-
import android.os.AsyncTask
11-
import android.os.Bundle
12-
import android.os.Handler
13-
import android.os.Looper
10+
import android.os.*
1411
import android.view.ActionMode
1512
import android.view.Menu
1613
import android.view.MenuItem
@@ -323,7 +320,17 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() {
323320
}
324321

325322
private fun shareLogs() {
326-
ShareLogsDialog().show(supportFragmentManager,"Share Logs Dialog")
323+
Permissions.with(this)
324+
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
325+
.maxSdkVersion(Build.VERSION_CODES.P)
326+
.withPermanentDenialDialog(getString(R.string.MediaPreviewActivity_signal_needs_the_storage_permission_in_order_to_write_to_external_storage_but_it_has_been_permanently_denied))
327+
.onAnyDenied {
328+
Toast.makeText(this, R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
329+
}
330+
.onAllGranted {
331+
ShareLogsDialog().show(supportFragmentManager,"Share Logs Dialog")
332+
}
333+
.execute()
327334
}
328335

329336
// endregion

app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
package org.thoughtcrime.securesms.preferences
22

3+
import android.content.ContentResolver
4+
import android.content.ContentValues
35
import android.content.Intent
6+
import android.media.MediaScannerConnection
7+
import android.net.Uri
48
import android.os.Build
9+
import android.os.Environment
10+
import android.provider.MediaStore
511
import android.view.LayoutInflater
12+
import android.webkit.MimeTypeMap
613
import android.widget.Toast
714
import androidx.appcompat.app.AlertDialog
815
import androidx.lifecycle.lifecycleScope
@@ -12,9 +19,15 @@ import kotlinx.coroutines.Job
1219
import kotlinx.coroutines.launch
1320
import network.loki.messenger.BuildConfig
1421
import network.loki.messenger.R
22+
import org.session.libsignal.utilities.ExternalStorageUtil
1523
import org.thoughtcrime.securesms.ApplicationContext
1624
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
17-
import org.thoughtcrime.securesms.providers.BlobProvider
25+
import org.thoughtcrime.securesms.util.StreamUtil
26+
import java.io.File
27+
import java.io.FileOutputStream
28+
import java.io.IOException
29+
import java.util.*
30+
import java.util.concurrent.TimeUnit
1831

1932
class ShareLogsDialog : BaseDialog() {
2033

@@ -39,16 +52,41 @@ class ShareLogsDialog : BaseDialog() {
3952
shareJob = lifecycleScope.launch(Dispatchers.IO) {
4053
val persistentLogger = ApplicationContext.getInstance(context).persistentLogger
4154
try {
42-
val logs = persistentLogger.logs.get()
43-
val fileName = "${Build.MANUFACTURER}-${Build.DEVICE}-API${Build.VERSION.SDK_INT}-v${BuildConfig.VERSION_NAME}.txt"
44-
val logUri = BlobProvider().forData(logs.toByteArray())
45-
.withFileName(fileName)
46-
.withMimeType("text/plain")
47-
.createForSingleSessionOnDisk(requireContext(),null)
55+
val context = requireContext()
56+
val outputUri: Uri = ExternalStorageUtil.getDownloadUri()
57+
val mediaUri = getExternalFile()
58+
if (mediaUri == null) {
59+
// show toast saying media saved
60+
dismiss()
61+
return@launch
62+
}
63+
64+
val inputStream = persistentLogger.logs.get().byteInputStream()
65+
val updateValues = ContentValues()
66+
if (outputUri.scheme == ContentResolver.SCHEME_FILE) {
67+
FileOutputStream(mediaUri.path).use { outputStream ->
68+
StreamUtil.copy(inputStream, outputStream)
69+
MediaScannerConnection.scanFile(context, arrayOf(mediaUri.path), arrayOf("text/plain"), null)
70+
}
71+
} else {
72+
context.contentResolver.openOutputStream(mediaUri, "w").use { outputStream ->
73+
val total: Long = StreamUtil.copy(inputStream, outputStream)
74+
if (total > 0) {
75+
updateValues.put(MediaStore.MediaColumns.SIZE, total)
76+
}
77+
}
78+
}
79+
if (Build.VERSION.SDK_INT > 28) {
80+
updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
81+
}
82+
if (updateValues.size() > 0) {
83+
requireContext().contentResolver.update(mediaUri, updateValues, null, null)
84+
}
4885

4986
val shareIntent = Intent().apply {
5087
action = Intent.ACTION_SEND
51-
putExtra(Intent.EXTRA_STREAM, logUri)
88+
putExtra(Intent.EXTRA_STREAM, mediaUri)
89+
data = mediaUri
5290
type = "text/plain"
5391
}
5492

@@ -62,4 +100,56 @@ class ShareLogsDialog : BaseDialog() {
62100
}
63101
}
64102

103+
@Throws(IOException::class)
104+
private fun pathTaken(outputUri: Uri, dataPath: String): Boolean {
105+
requireContext().contentResolver.query(outputUri, arrayOf(MediaStore.MediaColumns.DATA),
106+
MediaStore.MediaColumns.DATA + " = ?", arrayOf(dataPath),
107+
null).use { cursor ->
108+
if (cursor == null) {
109+
throw IOException("Something is wrong with the filename to save")
110+
}
111+
return cursor.moveToFirst()
112+
}
113+
}
114+
115+
private fun getExternalFile(): Uri? {
116+
val context = requireContext()
117+
val base = "${Build.MANUFACTURER}-${Build.DEVICE}-API${Build.VERSION.SDK_INT}-v${BuildConfig.VERSION_NAME}-${System.currentTimeMillis()}"
118+
val extension = "txt"
119+
val fileName = "$base.$extension"
120+
val mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("text/plain")
121+
val outputUri: Uri = ExternalStorageUtil.getDownloadUri()
122+
val contentValues = ContentValues()
123+
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
124+
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
125+
contentValues.put(MediaStore.MediaColumns.DATE_ADDED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))
126+
contentValues.put(MediaStore.MediaColumns.DATE_MODIFIED, TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))
127+
if (Build.VERSION.SDK_INT > 28) {
128+
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1)
129+
} else if (Objects.equals(outputUri.scheme, ContentResolver.SCHEME_FILE)) {
130+
val outputDirectory = File(outputUri.path)
131+
var outputFile = File(outputDirectory, "$base.$extension")
132+
var i = 0
133+
while (outputFile.exists()) {
134+
outputFile = File(outputDirectory, base + "-" + ++i + "." + extension)
135+
}
136+
if (outputFile.isHidden) {
137+
throw IOException("Specified name would not be visible")
138+
}
139+
return Uri.fromFile(outputFile)
140+
} else {
141+
var outputFileName = fileName
142+
val externalPath = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)!!
143+
var dataPath = String.format("%s/%s", externalPath, outputFileName)
144+
var i = 0
145+
while (pathTaken(outputUri, dataPath)) {
146+
outputFileName = base + "-" + ++i + "." + extension
147+
dataPath = String.format("%s/%s", externalPath, outputFileName)
148+
}
149+
contentValues.put(MediaStore.MediaColumns.DATA, dataPath)
150+
}
151+
return context.contentResolver.insert(outputUri, contentValues)
152+
}
153+
154+
65155
}

0 commit comments

Comments
 (0)