Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ fun String.getBaseRoute(): String {
val Direction.baseRoute: String
get() = (this as? Route)?.baseRoute ?: route.getBaseRoute()

fun Direction.handleNavigation(context: Context, handleOtherDirection: (Direction) -> Unit) = when (this) {
fun Direction.handleNavigation(
context: Context,
handleOtherDirection: (Direction) -> Unit
) = when (this) {
is ExternalUriDirection -> CustomTabsHelper.launchUri(context, this.uri)
is ExternalUriStringResDirection -> CustomTabsHelper.launchUri(context, this.getUri(context.resources))
is IntentDirection -> context.startActivity(this.intent(context))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ import com.wire.android.R
import com.wire.android.util.EmailComposer
import com.wire.android.util.getDeviceIdString
import com.wire.android.util.getGitBuildId
import com.wire.android.util.getMimeType
import com.wire.android.util.getUrisOfFilesInDirectory
import com.wire.android.util.logging.LogFileWriter
import com.wire.android.util.multipleFileSharingIntent
import com.wire.android.util.sha256

/**
Expand Down Expand Up @@ -111,32 +107,6 @@ object GiveFeedbackDestination : IntentDirection {
get() = "wire-intent:give-feedback"
}

object ReportBugDestination : IntentDirection {
override fun intent(context: Context): Intent {
val dir = LogFileWriter.logsDirectory(context)
val logsUris = context.getUrisOfFilesInDirectory(dir)
val intent = context.multipleFileSharingIntent(logsUris)
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(context.getString(R.string.send_bug_report_email)))
intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.send_bug_report_subject))
intent.putExtra(
Intent.EXTRA_TEXT,
EmailComposer.reportBugEmailTemplate(
context.getDeviceIdString()?.sha256(),
context.getGitBuildId()
)
)
val mimeTypes = logsUris.mapNotNull { it.getMimeType(context) }.toSet()
if (mimeTypes.isNotEmpty()) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes.toTypedArray())
}
intent.type = "*/*"
return Intent.createChooser(intent, context.getString(R.string.send_feedback_choose_email))
}

override val route: String
get() = "wire-intent:report-bug"
}

object WelcomeToNewAndroidAppDestination : ExternalUriStringResDirection {
override val uriStringRes: Int
get() = R.string.url_welcome_to_new_android
Expand Down
45 changes: 21 additions & 24 deletions app/src/main/kotlin/com/wire/android/ui/debug/DebugScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

package com.wire.android.ui.debug

import com.wire.android.navigation.annotation.app.WireRootDestination
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
Expand All @@ -48,6 +46,7 @@ import com.wire.android.di.hiltViewModelScoped
import com.wire.android.model.Clickable
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
import com.wire.android.navigation.annotation.app.WireRootDestination
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.topappbar.NavigationIconType
Expand All @@ -60,13 +59,9 @@ import com.wire.android.ui.home.settings.backup.BackupAndRestoreDialog
import com.wire.android.ui.home.settings.backup.rememberBackUpAndRestoreStateHolder
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.AppNameUtil
import com.wire.android.util.getMimeType
import com.wire.android.util.getUrisOfFilesInDirectory
import com.wire.android.util.multipleFileSharingIntent
import com.wire.android.util.logging.LogShareLauncher
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.launch
import java.io.File

@WireRootDestination
Expand Down Expand Up @@ -193,14 +188,24 @@ fun rememberDebugContentState(logPath: String): DebugContentState {
val clipboardManager = LocalClipboardManager.current
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
val shareLogsFailureMessage = stringResource(R.string.label_share_logs_failed)
val logShareLauncher = remember(context, coroutineScope, shareLogsFailureMessage) {
LogShareLauncher(
context = context,
coroutineScope = coroutineScope,
onFailure = {
Toast.makeText(context, shareLogsFailureMessage, Toast.LENGTH_SHORT).show()
}
)
}

return remember {
return remember(context, clipboardManager, logPath, scrollState, logShareLauncher) {
DebugContentState(
context,
clipboardManager,
logPath,
scrollState,
coroutineScope
logShareLauncher
)
}
}
Expand All @@ -210,7 +215,7 @@ data class DebugContentState(
val clipboardManager: ClipboardManager,
val logPath: String,
val scrollState: ScrollState,
val coroutineScope: CoroutineScope
val logShareLauncher: LogShareLauncher
) {
fun copyToClipboard(text: String) {
clipboardManager.setText(AnnotatedString(text))
Expand All @@ -222,20 +227,12 @@ data class DebugContentState(
}

fun shareLogs(onFlushLogs: () -> Deferred<Unit>) {
coroutineScope.launch {
// Flush any buffered logs before sharing to ensure completeness
onFlushLogs().await()
val dir = File(logPath).parentFile
val fileUris =
if (dir != null && dir.exists()) context.getUrisOfFilesInDirectory(dir) else arrayListOf()
val intent = context.multipleFileSharingIntent(fileUris)
// The first log file is simply text, not compressed. Get its mime type separately
// and set it as the mime type for the intent.
intent.type = fileUris.firstOrNull()?.getMimeType(context) ?: "text/plain"
// Get all other mime types and add them
val mimeTypes = fileUris.drop(1).mapNotNull { it.getMimeType(context) }
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes.toSet().toTypedArray())
context.startActivity(intent)
val dir = File(logPath).parentFile
if (dir != null && dir.exists()) {
logShareLauncher.shareLogs(dir) {
// Flush any buffered logs before sharing to ensure completeness.
onFlushLogs().await()
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/kotlin/com/wire/android/ui/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ fun HomeScreen(
analyticsUsageViewModel: AnalyticsUsageViewModel = hiltViewModel(),
) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()

homeViewModel.checkRequirements()

Expand All @@ -147,7 +148,6 @@ fun HomeScreen(

val lifecycleOwner = LocalLifecycleOwner.current
val snackbarHostState = LocalSnackbarHostState.current
val coroutineScope = rememberCoroutineScope()

DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import com.wire.android.model.Clickable
import com.wire.android.navigation.GiveFeedbackDestination
import com.wire.android.navigation.PrivacyPolicyScreenDestination
import com.wire.android.navigation.ReportBugDestination
import com.wire.android.navigation.ReportMisuseScreenDestination
import com.wire.android.navigation.SupportScreenDestination
import com.wire.android.navigation.TermsOfUseScreenDestination
Expand Down Expand Up @@ -146,6 +145,11 @@
override val title: UIText
) : SettingsItem(id, title)

sealed class ActionItem(
override val id: String,
override val title: UIText
) : SettingsItem(id, title)

Check warning on line 151 in app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt#L148-L151

Added lines #L148 - L151 were not covered by tests

data object AppSettings : DirectionItem(
id = "general_app_settings",
title = UIText.StringResource(R.string.app_settings_screen_title),
Expand Down Expand Up @@ -242,10 +246,9 @@
direction = GiveFeedbackDestination
)

data object ReportBug : DirectionItem(
data object ReportBug : ActionItem(

Check warning on line 249 in app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt#L249

Added line #L249 was not covered by tests
id = "report_bug",
title = UIText.StringResource(R.string.report_bug_screen_title),
direction = ReportBugDestination
title = UIText.StringResource(R.string.report_bug_screen_title)

Check warning on line 251 in app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsItem.kt#L251

Added line #L251 was not covered by tests
)

data class AppLock(override val switchState: SwitchState) : SwitchItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,36 @@

package com.wire.android.ui.home.settings

import com.wire.android.navigation.annotation.app.WireHomeDestination
import android.widget.Toast
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.generated.app.destinations.SetLockCodeScreenDestination
import com.wire.android.BuildConfig
import com.wire.android.R
import com.wire.android.appLogger
import com.wire.android.model.Clickable
import com.wire.android.navigation.BackStackMode
import com.wire.android.navigation.HomeDestination
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.annotation.app.WireHomeDestination
import com.wire.android.navigation.handleNavigation
import com.wire.android.ui.common.visbility.rememberVisibilityState
import com.ramcosta.composedestinations.generated.app.destinations.SetLockCodeScreenDestination
import com.wire.android.ui.home.HomeStateHolder
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.debug.LocalFeatureVisibilityFlags
import com.wire.android.util.ui.sectionWithElements
import com.wire.android.util.logging.LogShareLauncher
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.android.util.ui.UIText
import com.wire.android.util.ui.sectionWithElements

@WireHomeDestination
@Composable
Expand All @@ -65,15 +68,39 @@ fun SettingsScreen(
}

val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val shareLogsFailureMessage = context.getString(R.string.label_share_logs_failed)
val logShareLauncher = remember(context, coroutineScope, shareLogsFailureMessage) {
LogShareLauncher(
context = context,
coroutineScope = coroutineScope,
onFailure = {
Toast.makeText(context, shareLogsFailureMessage, Toast.LENGTH_SHORT).show()
}
)
}
SettingsScreenContent(
lazyListState = homeStateHolder.lazyListStateFor(HomeDestination.Settings),
settingsState = viewModel.state,
onItemClicked = remember {
onItemClicked = remember(context, homeStateHolder.navigator, logShareLauncher, viewModel) {
{
it.direction.handleNavigation(
context = context,
handleOtherDirection = { homeStateHolder.navigator.navigate(NavigationCommand(it)) }
)
item ->
when (item) {
is SettingsItem.DirectionItem -> item.direction.handleNavigation(
context = context,
handleOtherDirection = { direction ->
homeStateHolder.navigator.navigate(NavigationCommand(direction))
}
)

is SettingsItem.ActionItem -> when (item) {
SettingsItem.ReportBug -> logShareLauncher.shareBugReport {
viewModel.flushLogs().await()
}
}

is SettingsItem.SwitchItem -> Unit
}
}
},
onAppLockSwitchChanged = onAppLockSwitchClicked
Expand All @@ -84,12 +111,11 @@ fun SettingsScreen(
@Composable
fun SettingsScreenContent(
settingsState: SettingsState,
onItemClicked: (SettingsItem.DirectionItem) -> Unit,
onItemClicked: (SettingsItem) -> Unit,
modifier: Modifier = Modifier,
lazyListState: LazyListState = rememberLazyListState(),
onAppLockSwitchChanged: (Boolean) -> Unit
) {
val context = LocalContext.current
val featureVisibilityFlags = LocalFeatureVisibilityFlags.current

with(featureVisibilityFlags) {
Expand Down Expand Up @@ -175,7 +201,7 @@ private fun LazyListScope.sectionWithElements(
header: UIText,
items: List<SettingsItem>,
trailingText: ((SettingsItem) -> String?)? = null,
onItemClicked: (SettingsItem.DirectionItem) -> Unit
onItemClicked: (SettingsItem) -> Unit
) {
sectionWithElements(
header = header,
Expand All @@ -184,17 +210,20 @@ private fun LazyListScope.sectionWithElements(
SettingsItem(
text = settingsItem.title.asString(),
switchState = (settingsItem as? SettingsItem.SwitchItem)?.switchState ?: SwitchState.None,
onRowPressed = remember {
Clickable(enabled = settingsItem is SettingsItem.DirectionItem) {
(settingsItem as? SettingsItem.DirectionItem)?.let(onItemClicked)
onRowPressed = remember(settingsItem) {
Clickable(enabled = settingsItem.isClickable) {
onItemClicked(settingsItem)
}
},
trailingIcon = if (settingsItem is SettingsItem.DirectionItem) R.drawable.ic_arrow_right else null,
trailingIcon = if (settingsItem.isClickable) R.drawable.ic_arrow_right else null,
trailingText = trailingText?.invoke(settingsItem),
)
}
}

private val SettingsItem.isClickable: Boolean
get() = this is SettingsItem.DirectionItem || this is SettingsItem.ActionItem

@PreviewMultipleThemes
@Composable
fun PreviewSettingsScreen() = WireTheme {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
import androidx.lifecycle.viewModelScope
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.android.util.logging.LogFileWriter
import com.wire.kalium.logic.feature.featureConfig.ObserveIsAppLockEditableUseCase
import com.wire.kalium.logic.feature.user.ObserveSelfUserUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
Expand All @@ -41,6 +44,7 @@
private val observeIsAppLockEditable: ObserveIsAppLockEditableUseCase,
private val getSelf: ObserveSelfUserUseCase,
private val dispatchers: DispatcherProvider,
private val logFileWriter: LogFileWriter,

Check warning on line 47 in app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt#L47

Added line #L47 was not covered by tests
) : ViewModel() {
var state by mutableStateOf(SettingsState())
private set
Expand Down Expand Up @@ -69,6 +73,10 @@
}
}

fun flushLogs(): Deferred<Unit> = viewModelScope.async {
logFileWriter.forceFlush()

Check warning on line 77 in app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/settings/SettingsViewModel.kt#L76-L77

Added lines #L76 - L77 were not covered by tests
}

private suspend fun fetchSelfUser() {
viewModelScope.launch {
val self =
Expand Down
Loading
Loading