Download user logs and developer logs as files.
This commit is contained in:
parent
5803a9dd71
commit
d5129e44fa
|
@ -20,6 +20,17 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="cash.z.ecc.android.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true"
|
||||||
|
android:writePermission="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<!-- Firebase options -->
|
<!-- Firebase options -->
|
||||||
<meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="barcode" />
|
<meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="barcode" />
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,15 @@ import okio.Okio
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
class FeedbackFile(fileName: String = "feedback.log") :
|
class FeedbackFile(fileName: String = "user_log.txt") :
|
||||||
FeedbackCoordinator.FeedbackObserver {
|
FeedbackCoordinator.FeedbackObserver {
|
||||||
|
|
||||||
val file = File(ZcashWalletApp.instance.noBackupFilesDir, fileName)
|
val file = File("${ZcashWalletApp.instance.filesDir}/logs", fileName)
|
||||||
private val format = SimpleDateFormat("MM-dd HH:mm:ss.SSS")
|
private val format = SimpleDateFormat("MM-dd HH:mm:ss.SSS")
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!file.parentFile.exists()) file.parentFile.mkdirs()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onMetric(metric: Feedback.Metric) {
|
override fun onMetric(metric: Feedback.Metric) {
|
||||||
appendToFile(metric.toString())
|
appendToFile(metric.toString())
|
||||||
|
|
|
@ -1,23 +1,34 @@
|
||||||
package cash.z.ecc.android.ui.profile
|
package cash.z.ecc.android.ui.profile
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.content.FileProvider.getUriForFile
|
||||||
import cash.z.ecc.android.BuildConfig
|
import cash.z.ecc.android.BuildConfig
|
||||||
import cash.z.ecc.android.R
|
import cash.z.ecc.android.R
|
||||||
|
import cash.z.ecc.android.ZcashWalletApp
|
||||||
import cash.z.ecc.android.databinding.FragmentProfileBinding
|
import cash.z.ecc.android.databinding.FragmentProfileBinding
|
||||||
import cash.z.ecc.android.di.viewmodel.viewModel
|
import cash.z.ecc.android.di.viewmodel.viewModel
|
||||||
import cash.z.ecc.android.ext.onClick
|
import cash.z.ecc.android.ext.onClick
|
||||||
import cash.z.ecc.android.ext.onClickNavBack
|
import cash.z.ecc.android.ext.onClickNavBack
|
||||||
import cash.z.ecc.android.ext.onClickNavTo
|
import cash.z.ecc.android.ext.onClickNavTo
|
||||||
import cash.z.ecc.android.feedback.FeedbackFile
|
import cash.z.ecc.android.feedback.FeedbackFile
|
||||||
|
import cash.z.ecc.android.feedback.Report
|
||||||
|
import cash.z.ecc.android.feedback.Report.Tap.*
|
||||||
import cash.z.ecc.android.ui.base.BaseFragment
|
import cash.z.ecc.android.ui.base.BaseFragment
|
||||||
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
import cash.z.wallet.sdk.ext.toAbbreviatedAddress
|
||||||
|
import cash.z.wallet.sdk.ext.twig
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okio.Okio
|
import okio.Okio
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||||
|
override val screen = Report.Screen.PROFILE
|
||||||
|
|
||||||
private val viewModel: ProfileViewModel by viewModel()
|
private val viewModel: ProfileViewModel by viewModel()
|
||||||
|
|
||||||
|
@ -26,13 +37,20 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding.hitAreaClose.onClickNavBack()
|
binding.hitAreaClose.onClickNavBack() { tapped(PROFILE_CLOSE) }
|
||||||
binding.buttonBackup.onClickNavTo(R.id.action_nav_profile_to_nav_backup)
|
binding.buttonBackup.onClickNavTo(R.id.action_nav_profile_to_nav_backup) { tapped(PROFILE_BACKUP) }
|
||||||
binding.textVersion.text = BuildConfig.VERSION_NAME
|
binding.textVersion.text = BuildConfig.VERSION_NAME
|
||||||
onClick(binding.buttonLogs) {
|
onClick(binding.buttonLogs) {
|
||||||
|
tapped(PROFILE_VIEW_USER_LOGS)
|
||||||
onViewLogs()
|
onViewLogs()
|
||||||
}
|
}
|
||||||
|
binding.buttonLogs.setOnLongClickListener {
|
||||||
|
tapped(PROFILE_VIEW_DEV_LOGS)
|
||||||
|
onViewDevLogs()
|
||||||
|
true
|
||||||
|
}
|
||||||
onClick(binding.buttonFeedback) {
|
onClick(binding.buttonFeedback) {
|
||||||
|
tapped(PROFILE_SEND_FEEDBACK)
|
||||||
onSendFeedback()
|
onSendFeedback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,31 +63,63 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onViewLogs() {
|
private fun onViewLogs() {
|
||||||
loadLogFileAsText().let { logText ->
|
shareFile(userLogFile())
|
||||||
if (logText == null) {
|
}
|
||||||
mainActivity?.showSnackbar("Log file not found!")
|
|
||||||
} else {
|
|
||||||
val sendIntent: Intent = Intent().apply {
|
|
||||||
action = Intent.ACTION_SEND
|
|
||||||
putExtra(Intent.EXTRA_TEXT, logText)
|
|
||||||
type = "text/plain"
|
|
||||||
}
|
|
||||||
|
|
||||||
val shareIntent = Intent.createChooser(sendIntent, "Share Log File")
|
private fun onViewDevLogs() {
|
||||||
startActivity(shareIntent)
|
shareFile(writeLogcat())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shareFiles(vararg files: File?) {
|
||||||
|
val uris = arrayListOf<Uri>().apply {
|
||||||
|
files.filterNotNull().mapNotNull {
|
||||||
|
getUriForFile(ZcashWalletApp.instance, "${BuildConfig.APPLICATION_ID}.fileprovider", it)
|
||||||
|
}.forEach {
|
||||||
|
add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||||
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
|
||||||
|
type = "text/*"
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(intent, "Share Log Files"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shareFile(file: File?) {
|
||||||
|
file ?: return
|
||||||
|
val uri = getUriForFile(ZcashWalletApp.instance, "${BuildConfig.APPLICATION_ID}.fileprovider", file)
|
||||||
|
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
type = "text/plain"
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(intent, "Share Log File"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSendFeedback() {
|
private fun onSendFeedback() {
|
||||||
mainActivity?.showSnackbar("Feedback feature coming soon!")
|
mainActivity?.showSnackbar("Feedback feature coming soon!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun userLogFile(): File? {
|
||||||
|
return mainActivity?.feedbackCoordinator?.findObserver<FeedbackFile>()?.file
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadLogFileAsText(): String? {
|
private fun loadLogFileAsText(): String? {
|
||||||
val feedbackFile: FeedbackFile =
|
val feedbackFile: File = userLogFile() ?: return null
|
||||||
mainActivity?.feedbackCoordinator?.findObserver() ?: return null
|
Okio.buffer(Okio.source(feedbackFile)).use {
|
||||||
Okio.buffer(Okio.source(feedbackFile.file)).use {
|
|
||||||
return it.readUtf8()
|
return it.readUtf8()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun writeLogcat(): File? {
|
||||||
|
try {
|
||||||
|
val outputFile = File("${ZcashWalletApp.instance.filesDir}/logs", "developer_log.txt")
|
||||||
|
val cmd = arrayOf("/bin/sh", "-c", "logcat -v time -d | grep \"@TWIG\" > ${outputFile.absolutePath}")
|
||||||
|
Runtime.getRuntime().exec(cmd)
|
||||||
|
return outputFile
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
twig("Failed to create log")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -161,7 +161,7 @@
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
style="@style/TextAppearance.AppCompat.Body1"
|
style="@style/TextAppearance.AppCompat.Body1"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:text="See Application Log"
|
android:text="See Application Logs"
|
||||||
android:textColor="@color/selector_button_text_light_dimmed"
|
android:textColor="@color/selector_button_text_light_dimmed"
|
||||||
app:layout_constraintTop_toBottomOf="@id/button_backup"
|
app:layout_constraintTop_toBottomOf="@id/button_backup"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths>
|
||||||
|
<!-- Share files from a dedicated folder on the internal files storage. -->
|
||||||
|
<files-path name="logs" path="logs/"/>
|
||||||
|
</paths>
|
Loading…
Reference in New Issue