Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add link to send logcat messages via email #45

Merged
merged 4 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions v4/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>-->

<application
Expand Down Expand Up @@ -165,6 +168,16 @@
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="qr_code, data_matrix" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

</application>

<queries>
Expand Down
14 changes: 7 additions & 7 deletions v4/app/src/main/assets/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,21 @@
},
{
"title" : {
"text" : "APP.V4.SYSTEM_STATUS"
"text" : "APP.V4.DIRECTION_COLOR_PREFERENCE"
},
"link" : {
"text" : "settings/status"
"text" : "settings/direction_color_preference"
},
"field":{
"field":"direction_color_preference"
}
},
{
"title" : {
"text" : "APP.V4.DIRECTION_COLOR_PREFERENCE"
"text" : "APP.V4.SYSTEM_STATUS"
},
"link" : {
"text" : "settings/direction_color_preference"
},
"field":{
"field":"direction_color_preference"
"text" : "settings/status"
}
}
]
Expand Down
26 changes: 17 additions & 9 deletions v4/app/src/main/assets/settings_debug.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@
"field":"v4_theme"
}
},
{
"title" : {
"text" : "APP.V4.DIRECTION_COLOR_PREFERENCE"
},
"link" : {
"text" : "settings/direction_color_preference"
},
"field":{
"field":"direction_color_preference"
}
},
{
"title" : {
"text" : "APP.V4.TRADING_NETWORK"
Expand All @@ -44,29 +55,26 @@
},
{
"title" : {
"text" : "Feature Flag Overrides"
"text" : "APP.ISSUE_REPORT.SETTINGS_TITLE"
},
"link" : {
"text" : "features"
"text" : "settings/report_issue"
}
},
{
"title" : {
"text" : "Debug Settings"
"text" : "Feature Flag Overrides"
},
"link" : {
"text" : "settings/debug"
"text" : "features"
}
},
{
"title" : {
"text" : "APP.V4.DIRECTION_COLOR_PREFERENCE"
"text" : "Debug Settings"
},
"link" : {
"text" : "settings/direction_color_preference"
},
"field":{
"field":"direction_color_preference"
"text" : "settings/debug"
}
}
]
Expand Down
4 changes: 4 additions & 0 deletions v4/app/src/main/res/xml/file_paths.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="documents" path="." />
</paths>
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object ProfileRoutes {
const val help = "help"
const val rewards = "rewards"
const val debug_enable = "action/debug/enable"
const val report_issue = "settings/report_issue"
}

object NewsAlertsRoutes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import exchange.dydx.trading.feature.profile.help.DydxHelpView
import exchange.dydx.trading.feature.profile.history.DydxHistoryView
import exchange.dydx.trading.feature.profile.keyexport.DydxKeyExportView
import exchange.dydx.trading.feature.profile.language.DydxLanguageView
import exchange.dydx.trading.feature.profile.reportissue.DydxReportIssueView
import exchange.dydx.trading.feature.profile.rewards.DydxRewardsView
import exchange.dydx.trading.feature.profile.settings.DydxSettingsView
import exchange.dydx.trading.feature.profile.systemstatus.DydxSystemStatusView
Expand Down Expand Up @@ -161,4 +162,12 @@ fun NavGraphBuilder.profileGraph(
) { navBackStackEntry ->
DydxDebugEnableView.Content(Modifier)
}

dydxComposable(
router = appRouter,
route = ProfileRoutes.report_issue,
deepLinks = appRouter.deeplinks(ProfileRoutes.report_issue),
) { navBackStackEntry ->
DydxReportIssueView.Content(Modifier)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package exchange.dydx.trading.feature.profile.reportissue

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import exchange.dydx.abacus.protocols.LocalizerProtocol
import exchange.dydx.platformui.designSystem.theme.ThemeFont
import exchange.dydx.platformui.designSystem.theme.dydxDefault
import exchange.dydx.platformui.designSystem.theme.themeFont
import exchange.dydx.trading.common.component.DydxComponent
import exchange.dydx.trading.common.compose.collectAsStateWithLifecycle
import exchange.dydx.trading.common.theme.DydxThemedPreviewSurface
import exchange.dydx.trading.common.theme.MockLocalizer

@Preview
@Composable
fun Preview_DydxReportIssueView() {
DydxThemedPreviewSurface {
DydxReportIssueView.Content(Modifier, DydxReportIssueView.ViewState.preview)
}
}

object DydxReportIssueView : DydxComponent {
data class ViewState(
val localizer: LocalizerProtocol,
val text: String?,
) {
companion object {
val preview = ViewState(
localizer = MockLocalizer(),
text = "1.0M",
)
}
}

@Composable
override fun Content(modifier: Modifier) {
val viewModel: DydxReportIssueViewModel = hiltViewModel()

val state = viewModel.state.collectAsStateWithLifecycle(initialValue = null).value
Content(modifier, state)
}

@Composable
fun Content(modifier: Modifier, state: ViewState?) {
if (state == null) {
return
}
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
style = TextStyle.dydxDefault.themeFont(fontSize = ThemeFont.FontSize.extra),
text = state?.text ?: "",
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package exchange.dydx.trading.feature.profile.reportissue

import android.content.Context
import android.net.Uri
import android.os.Environment
import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import exchange.dydx.abacus.protocols.LocalizerProtocol
import exchange.dydx.platformui.components.PlatformInfo
import exchange.dydx.trading.common.DydxViewModel
import exchange.dydx.trading.common.navigation.DydxRouter
import exchange.dydx.utilities.utils.EmailUtils
import exchange.dydx.utilities.utils.FileUtils
import exchange.dydx.utilities.utils.LogCatReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject

@HiltViewModel
class DydxReportIssueViewModel @Inject constructor(
private val localizer: LocalizerProtocol,
private val router: DydxRouter,
@ApplicationContext private val context: Context,
val platformInfo: PlatformInfo,
) : ViewModel(), DydxViewModel {

private val textFlow = MutableStateFlow("")

val state: Flow<DydxReportIssueView.ViewState?> = textFlow.map { text ->
createViewState(text)
}

init {
textFlow.value = localizer.localize("APP.ISSUE_REPORT.LOADING_TITLE")

viewModelScope.launch {
var logUri: Uri? = null
withContext(Dispatchers.IO) {
// add a delay to show the loading text
kotlinx.coroutines.delay(500)
logUri = createLog()
}
if (logUri != null) {
textFlow.value = localizer.localize("APP.ISSUE_REPORT.LOADING_COMPLETED_TITLE")
EmailUtils.sendEmailWithAttachment(
context = context,
fileUri = logUri,
email = "",
subject = localizer.localize("APP.ISSUE_REPORT.EMAIL_SUBJECT"),
body = localizer.localize("APP.ISSUE_REPORT.EMAIL_BODY"),
mimeType = "application/x-zip",
chooserTitle = localizer.localize("APP.ISSUE_REPORT.CHOOSER_TITLE"),
)
} else {
val error = localizer.localize("APP.ISSUE_REPORT.LOADING_ERROR_TITLE")
textFlow.value = error
platformInfo.show(message = error)
}

router.navigateBack()
}
}

private fun createViewState(text: String): DydxReportIssueView.ViewState {
return DydxReportIssueView.ViewState(
localizer = localizer,
text = text,
)
}

private fun createLog(): Uri? {
val file =
File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), "dydx_app.log")
if (!LogCatReader.saveLogCatToFile(file)) {
return null
}
val zipFile = createZipFile(context, file)
file.delete()
return if (zipFile != null) {
FileProvider.getUriForFile(context, context.packageName, zipFile)
} else {
null
}
}

private fun createZipFile(context: Context, file: File): File? {
val fileName = file.name
val zipFileName = "$fileName.zip"
val zipFilePath =
File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), zipFileName)
return if (FileUtils.compressFile(context, file.absolutePath, zipFilePath.absolutePath)) {
zipFilePath
} else {
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package exchange.dydx.utilities.utils

import android.content.Context
import android.content.Intent
import android.net.Uri

object EmailUtils {
fun sendEmailWithAttachment(
context: Context,
fileUri: Uri?,
email: String,
subject: String?,
body: String?,
mimeType: String?,
chooserTitle: String?
) {
val emailIntent = Intent(Intent.ACTION_SEND)
emailIntent.setType(mimeType)
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
emailIntent.putExtra(Intent.EXTRA_TEXT, body)
emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri)
val chooser = Intent.createChooser(emailIntent, chooserTitle)
if (chooser.resolveActivity(context.packageManager) != null) {
chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(chooser)
}
}
}
Loading
Loading