diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
index f95e818237..5a8b03fdf8 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
@@ -478,9 +478,8 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
Log.d("Loki-Avatar", "Uploading Avatar Finished");
return Unit.INSTANCE;
});
- } catch (Exception exception) {
- // Do nothing
- Log.e("Loki-Avatar", "Uploading avatar failed", exception);
+ } catch (Exception e) {
+ Log.e("Loki-Avatar", "Uploading avatar failed.");
}
});
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/HelpSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/HelpSettingsActivity.kt
index f6efd041cd..e7e5f2d5f6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/HelpSettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/HelpSettingsActivity.kt
@@ -5,9 +5,14 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
+import android.widget.ProgressBar
+import android.widget.TextView
import android.widget.Toast
+import androidx.core.view.isInvisible
import androidx.preference.Preference
+
import network.loki.messenger.R
+import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.permissions.Permissions
@@ -67,6 +72,19 @@ class HelpSettingsFragment: CorrectedPreferenceFragment() {
}
}
+ private fun updateExportButtonAndProgressBarUI(exportJobRunning: Boolean) {
+ this.activity?.runOnUiThread(Runnable {
+ // Change export logs button text
+ val exportLogsButton = this.activity?.findViewById(R.id.export_logs_button) as TextView?
+ if (exportLogsButton == null) { Log.w("Loki", "Could not find export logs button view.") }
+ exportLogsButton?.text = if (exportJobRunning) getString(R.string.cancel) else getString(R.string.activity_help_settings__export_logs)
+
+ // Show progress bar
+ val exportProgressBar = this.activity?.findViewById(R.id.export_progress_bar) as ProgressBar?
+ exportProgressBar?.isInvisible = !exportJobRunning
+ })
+ }
+
private fun shareLogs() {
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
@@ -76,7 +94,7 @@ class HelpSettingsFragment: CorrectedPreferenceFragment() {
Toast.makeText(requireActivity(), R.string.MediaPreviewActivity_unable_to_write_to_external_storage_without_permission, Toast.LENGTH_LONG).show()
}
.onAllGranted {
- ShareLogsDialog().show(parentFragmentManager,"Share Logs Dialog")
+ ShareLogsDialog(::updateExportButtonAndProgressBarUI).show(parentFragmentManager,"Share Logs Dialog")
}
.execute()
}
diff --git a/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt
index 2dc5e75d98..9bfc1dabf2 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/preferences/ShareLogsDialog.kt
@@ -11,55 +11,73 @@ import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.webkit.MimeTypeMap
+import android.widget.ProgressBar
+import android.widget.TextView
import android.widget.Toast
+import androidx.core.view.isInvisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
+
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
+
import org.session.libsignal.utilities.ExternalStorageUtil
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.createSessionDialog
import org.thoughtcrime.securesms.util.FileProviderUtil
import org.thoughtcrime.securesms.util.StreamUtil
+
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.Objects
import java.util.concurrent.TimeUnit
-class ShareLogsDialog : DialogFragment() {
+class ShareLogsDialog(private val updateCallback: (Boolean)->Unit): DialogFragment() {
+
+ private val TAG = "ShareLogsDialog"
private var shareJob: Job? = null
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = createSessionDialog {
title(R.string.dialog_share_logs_title)
text(R.string.dialog_share_logs_explanation)
- button(R.string.share, dismiss = false) { shareLogs() }
- cancelButton { dismiss() }
+ button(R.string.share, dismiss = false) { runShareLogsJob() }
+ cancelButton { updateCallback(false) }
+ }
+
+ // If the share logs dialog loses focus the job gets cancelled so we'll update the UI state
+ override fun onPause() {
+ super.onPause()
+ updateCallback(false)
}
- private fun shareLogs() {
+ private fun runShareLogsJob() {
+ // Cancel any existing share job that might already be running to start anew
shareJob?.cancel()
+
+ updateCallback(true)
+
shareJob = lifecycleScope.launch(Dispatchers.IO) {
val persistentLogger = ApplicationContext.getInstance(context).persistentLogger
try {
+ Log.d(TAG, "Starting share logs job...")
+
val context = requireContext()
val outputUri: Uri = ExternalStorageUtil.getDownloadUri()
- val mediaUri = getExternalFile()
- if (mediaUri == null) {
- // show toast saying media saved
- dismiss()
- return@launch
- }
+ val mediaUri = getExternalFile() ?: return@launch
val inputStream = persistentLogger.logs.get().byteInputStream()
val updateValues = ContentValues()
+
+ // Add details into the output or media files as appropriate
if (outputUri.scheme == ContentResolver.SCHEME_FILE) {
FileOutputStream(mediaUri.path).use { outputStream ->
StreamUtil.copy(inputStream, outputStream)
@@ -73,6 +91,7 @@ class ShareLogsDialog : DialogFragment() {
}
}
}
+
if (Build.VERSION.SDK_INT > 28) {
updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
}
@@ -95,13 +114,35 @@ class ShareLogsDialog : DialogFragment() {
}
startActivity(Intent.createChooser(shareIntent, getString(R.string.share)))
}
-
- dismiss()
} catch (e: Exception) {
withContext(Main) {
Log.e("Loki", "Error saving logs", e)
Toast.makeText(context,"Error saving logs", Toast.LENGTH_LONG).show()
}
+ }
+ }.also { shareJob ->
+ shareJob.invokeOnCompletion { handler ->
+ // Note: Don't show Toasts here directly - use `withContext(Main)` or such if req'd
+ handler?.message.let { msg ->
+ if (shareJob.isCancelled) {
+ if (msg.isNullOrBlank()) {
+ Log.w(TAG, "Share logs job was cancelled.")
+ } else {
+ Log.d(TAG, "Share logs job was cancelled. Reason: $msg")
+ }
+
+ }
+ else if (shareJob.isCompleted) {
+ Log.d(TAG, "Share logs job completed. Msg: $msg")
+ }
+ else {
+ Log.w(TAG, "Share logs job finished while still Active. Msg: $msg")
+ }
+ }
+
+ // Regardless of the job's success it has now completed so update the UI
+ updateCallback(false)
+
dismiss()
}
}
@@ -158,5 +199,4 @@ class ShareLogsDialog : DialogFragment() {
return context.contentResolver.insert(outputUri, contentValues)
}
-
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/export_logs_widget.xml b/app/src/main/res/layout/export_logs_widget.xml
index 95c681d397..56f6bc07df 100644
--- a/app/src/main/res/layout/export_logs_widget.xml
+++ b/app/src/main/res/layout/export_logs_widget.xml
@@ -1,9 +1,10 @@
-
+
+ android:layout_height="wrap_content" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/preference_widget_progress.xml b/app/src/main/res/layout/preference_widget_progress.xml
index 3cce4b9483..4b44b9a55a 100644
--- a/app/src/main/res/layout/preference_widget_progress.xml
+++ b/app/src/main/res/layout/preference_widget_progress.xml
@@ -1,23 +1,19 @@
-
+
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_help.xml b/app/src/main/res/xml/preferences_help.xml
index 0372619ea3..9bc9bd13e1 100644
--- a/app/src/main/res/xml/preferences_help.xml
+++ b/app/src/main/res/xml/preferences_help.xml
@@ -6,39 +6,38 @@
android:key="export_logs"
android:title="@string/activity_help_settings__report_bug_title"
android:summary="@string/activity_help_settings__report_bug_summary"
- android:widgetLayout="@layout/export_logs_widget"/>
+ android:widgetLayout="@layout/export_logs_widget" />
+
+
+
+ android:widgetLayout="@layout/preference_external_link" />
+ android:widgetLayout="@layout/preference_external_link" />
+ android:widgetLayout="@layout/preference_external_link" />
+ android:widgetLayout="@layout/preference_external_link" />
\ No newline at end of file
diff --git a/libsession/src/main/java/org/session/libsession/avatars/AvatarHelper.java b/libsession/src/main/java/org/session/libsession/avatars/AvatarHelper.java
index 1588289017..cc0909a592 100644
--- a/libsession/src/main/java/org/session/libsession/avatars/AvatarHelper.java
+++ b/libsession/src/main/java/org/session/libsession/avatars/AvatarHelper.java
@@ -8,9 +8,11 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.session.libsession.utilities.Address;
+import org.session.libsignal.utilities.Log;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -22,9 +24,9 @@ public class AvatarHelper {
private static final String AVATAR_DIRECTORY = "avatars";
public static InputStream getInputStreamFor(@NonNull Context context, @NonNull Address address)
- throws IOException
+ throws FileNotFoundException
{
- return new FileInputStream(getAvatarFile(context, address));
+ return new FileInputStream(getAvatarFile(context, address));
}
public static List getAvatarFiles(@NonNull Context context) {
diff --git a/libsession/src/main/java/org/session/libsession/avatars/ProfileContactPhoto.java b/libsession/src/main/java/org/session/libsession/avatars/ProfileContactPhoto.java
index f8675b031f..76a3449625 100644
--- a/libsession/src/main/java/org/session/libsession/avatars/ProfileContactPhoto.java
+++ b/libsession/src/main/java/org/session/libsession/avatars/ProfileContactPhoto.java
@@ -8,6 +8,7 @@ import androidx.annotation.Nullable;
import org.session.libsession.utilities.Address;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
@@ -24,7 +25,7 @@ public class ProfileContactPhoto implements ContactPhoto {
}
@Override
- public InputStream openInputStream(Context context) throws IOException {
+ public InputStream openInputStream(Context context) throws FileNotFoundException {
return AvatarHelper.getInputStreamFor(context, address);
}