mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-09-06 18:01:11 +00:00
Fixed toast threading (bitfireAT/davx5#256)
* Fixed toast threading * Added `AndroidViewModel.context` Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Moved to `AndroidViewModel` Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Changed error invocation Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Added back constructor inject Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Save Application with val instead of util method * Use LiveData for error message --------- Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
parent
2ebb40b152
commit
9748b75671
|
@ -6,17 +6,26 @@ package at.bitfire.davdroid.ui
|
|||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Application
|
||||
import android.app.usage.UsageStatsManager
|
||||
import android.content.*
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.os.LocaleList
|
||||
import android.os.PowerManager
|
||||
import android.os.StatFs
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.ContactsContract
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
@ -27,9 +36,8 @@ import androidx.core.content.FileProvider
|
|||
import androidx.core.content.getSystemService
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkQuery
|
||||
|
@ -51,22 +59,25 @@ import at.bitfire.ical4android.TaskProvider
|
|||
import at.bitfire.ical4android.TaskProvider.ProviderName
|
||||
import at.bitfire.ical4android.util.MiscUtils.ContentProviderClientHelper.closeCompat
|
||||
import at.techbee.jtx.JtxContract
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.HttpUrl
|
||||
import org.apache.commons.io.ByteOrderMark
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||
import org.apache.commons.text.WordUtils
|
||||
import org.dmfs.tasks.contract.TaskContract
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
import java.io.File
|
||||
import java.io.IOError
|
||||
import java.io.IOException
|
||||
import java.io.StringReader
|
||||
import java.io.Writer
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import java.util.logging.Level
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
@ -116,9 +127,9 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
binding.model = model
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
model.cause.observe(this, Observer { cause ->
|
||||
model.cause.observe(this) { cause ->
|
||||
if (cause == null)
|
||||
return@Observer
|
||||
return@observe
|
||||
|
||||
binding.causeCaption.text = when (cause) {
|
||||
is HttpException -> getString(if (cause.code / 100 == 5) R.string.debug_info_server_error else R.string.debug_info_http_error)
|
||||
|
@ -138,11 +149,15 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
else
|
||||
R.string.debug_info_unexpected_error
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
model.debugInfo.observe(this, Observer { debugInfo ->
|
||||
model.debugInfo.observe(this) { debugInfo ->
|
||||
val showDebugInfo = View.OnClickListener {
|
||||
val uri = FileProvider.getUriForFile(this, getString(R.string.authority_debug_provider), debugInfo)
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
getString(R.string.authority_debug_provider),
|
||||
debugInfo
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "text/plain")
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
@ -156,9 +171,9 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
isEnabled = true
|
||||
}
|
||||
binding.zipShare.setOnClickListener { shareArchive() }
|
||||
})
|
||||
}
|
||||
|
||||
model.logFile.observe(this, Observer { logs ->
|
||||
model.logFile.observe(this) { logs ->
|
||||
binding.logsView.setOnClickListener {
|
||||
val uri = FileProvider.getUriForFile(this, getString(R.string.authority_debug_provider), logs)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
|
@ -166,27 +181,50 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
startActivity(Intent.createChooser(intent, null))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun shareArchive() {
|
||||
model.generateZip { zipFile ->
|
||||
val builder = ShareCompat.IntentBuilder.from(this)
|
||||
model.zipFile.observe(this) { zipFile ->
|
||||
if (zipFile != null) {
|
||||
// ZIP file is ready
|
||||
val builder = ShareCompat.IntentBuilder(this)
|
||||
.setSubject("${getString(R.string.app_name)} ${BuildConfig.VERSION_NAME} debug info")
|
||||
.setText(getString(R.string.debug_info_attached))
|
||||
.setType("*/*") // application/zip won't show all apps that can manage binary files, like ShareViaHttp
|
||||
.setStream(FileProvider.getUriForFile(this, getString(R.string.authority_debug_provider), zipFile))
|
||||
.setStream(
|
||||
FileProvider.getUriForFile(
|
||||
this,
|
||||
getString(R.string.authority_debug_provider),
|
||||
zipFile
|
||||
)
|
||||
)
|
||||
builder.intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
builder.startChooser()
|
||||
|
||||
// Not beautiful, because it changes model data from the view.
|
||||
// See https://github.com/android/architecture-components-samples/issues/63
|
||||
model.zipFile.value = null
|
||||
}
|
||||
}
|
||||
|
||||
model.error.observe(this) { message ->
|
||||
if (message != null) {
|
||||
Snackbar.make(binding.fab, message, Snackbar.LENGTH_LONG).show()
|
||||
|
||||
// Reset error message so that it won't be shown when activity is re-created
|
||||
model.error.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shareArchive() {
|
||||
model.generateZip()
|
||||
}
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class ReportModel @Inject constructor(
|
||||
@ApplicationContext val context: Context
|
||||
) : ViewModel() {
|
||||
|
||||
val context: Application
|
||||
) : AndroidViewModel(context) {
|
||||
@Inject
|
||||
lateinit var db: AppDatabase
|
||||
@Inject
|
||||
|
@ -200,11 +238,13 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
val remoteResource = MutableLiveData<String>()
|
||||
val debugInfo = MutableLiveData<File>()
|
||||
|
||||
// feedback for UI
|
||||
val zipProgress = MutableLiveData(false)
|
||||
val zipFile = MutableLiveData<File>()
|
||||
val error = MutableLiveData<String>()
|
||||
|
||||
// private storage, not readable by others
|
||||
private val debugInfoDir = File(context.filesDir, "debug")
|
||||
private val debugInfoDir = File(getApplication<Application>().filesDir, "debug")
|
||||
|
||||
init {
|
||||
// create debug info directory
|
||||
|
@ -339,7 +379,7 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
info.packageName, info.versionName, PackageInfoCompat.getLongVersionCode(info),
|
||||
pm.getInstallerPackageName(info.packageName) ?: '—', notes.joinToString(", ")
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
} catch (ignored: PackageManager.NameNotFoundException) {
|
||||
}
|
||||
writer.append(table.toString())
|
||||
} catch (e: Exception) {
|
||||
|
@ -504,14 +544,13 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
debugInfo.postValue(debugInfoFile)
|
||||
}
|
||||
|
||||
fun generateZip(onSuccess: (File) -> Unit) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
fun generateZip() {
|
||||
try {
|
||||
zipProgress.postValue(true)
|
||||
|
||||
val zipFile = File(debugInfoDir, "davx5-debug.zip")
|
||||
Logger.log.fine("Writing debug info to ${zipFile.absolutePath}")
|
||||
ZipOutputStream(zipFile.outputStream().buffered()).use { zip ->
|
||||
val file = File(debugInfoDir, "davx5-debug.zip")
|
||||
Logger.log.fine("Writing debug info to ${file.absolutePath}")
|
||||
ZipOutputStream(file.outputStream().buffered()).use { zip ->
|
||||
zip.setLevel(9)
|
||||
debugInfo.value?.let { debugInfo ->
|
||||
zip.putNextEntry(ZipEntry("debug-info.txt"))
|
||||
|
@ -542,14 +581,12 @@ class DebugInfoActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
onSuccess(zipFile)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// creating attachment with debug info failed
|
||||
Logger.log.log(Level.SEVERE, "Couldn't attach debug info", e)
|
||||
Toast.makeText(context, e.toString(), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
// success, show ZIP file
|
||||
zipFile.postValue(file)
|
||||
} catch (e: Exception) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't generate debug info ZIP", e)
|
||||
error.postValue(e.localizedMessage)
|
||||
} finally {
|
||||
zipProgress.postValue(false)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue