Download user logs and developer logs as files.

This commit is contained in:
Kevin Gorham 2020-02-21 18:50:57 -05:00
parent 5803a9dd71
commit d5129e44fa
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
5 changed files with 88 additions and 19 deletions

View File

@ -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" />

View File

@ -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())

View File

@ -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
}
} }

View File

@ -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"

View File

@ -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>