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 @@ -43,6 +43,7 @@ class DebugScreenComposeTest {
onFlushLogs = { CompletableDeferred(Unit) },
debugDataOptionsContent = {},
dangerOptionsContent = {},
onShareLogsViaWire = {},
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ package com.wire.android.mapper
import com.wire.android.R
import com.wire.android.ui.home.conversations.findUser
import com.wire.android.ui.home.conversations.model.UIMessageContent
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.ConversationStartedWithMembers
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.FederationMemberRemoved
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.MemberAdded
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.MemberFailedToAdd
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.MemberJoined
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.MemberLeft
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.MemberRemoved
import com.wire.android.ui.home.conversations.model.UIMessageContent.SystemMessage.TeamMemberRemoved
import com.wire.android.ui.home.conversations.selfdeletion.SelfDeletionMapper.toSelfDeletionDuration
import com.wire.android.util.formatFullDateShortTime
import com.wire.android.util.orDefault
Expand Down Expand Up @@ -245,9 +253,9 @@ class SystemMessageContentMapper @Inject constructor(
return when (content) {
is Added ->
if (isAuthorSelfAction) {
UIMessageContent.SystemMessage.MemberJoined(author = authorName, isSelfTriggered = isSelfTriggered)
MemberJoined(author = authorName, isSelfTriggered = isSelfTriggered)
} else {
UIMessageContent.SystemMessage.MemberAdded(
MemberAdded(
author = authorName,
memberNames = memberNameList,
isSelfTriggered = isSelfTriggered
Expand All @@ -256,20 +264,20 @@ class SystemMessageContentMapper @Inject constructor(

is Removed ->
if (isAuthorSelfAction) {
UIMessageContent.SystemMessage.MemberLeft(author = authorName, isSelfTriggered = isSelfTriggered)
MemberLeft(author = authorName, isSelfTriggered = isSelfTriggered)
} else {
UIMessageContent.SystemMessage.MemberRemoved(
MemberRemoved(
author = authorName,
memberNames = memberNameList,
isSelfTriggered = isSelfTriggered
)
}

is CreationAdded -> {
UIMessageContent.SystemMessage.ConversationStartedWithMembers(memberNames = memberNameList)
ConversationStartedWithMembers(memberNames = memberNameList)
}

is FailedToAdd -> UIMessageContent.SystemMessage.MemberFailedToAdd(
is FailedToAdd -> MemberFailedToAdd(
memberNames = memberNameList,
type = when (content.type) {
FailedToAdd.Type.Federation -> UIMessageContent.SystemMessage.MemberFailedToAdd.Type.Federation
Expand All @@ -279,11 +287,11 @@ class SystemMessageContentMapper @Inject constructor(
}
)

is MemberChange.FederationRemoved -> UIMessageContent.SystemMessage.FederationMemberRemoved(
is MemberChange.FederationRemoved -> FederationMemberRemoved(
memberNames = memberNameList
)

is MemberChange.RemovedFromTeam -> UIMessageContent.SystemMessage.TeamMemberRemoved(
is MemberChange.RemovedFromTeam -> TeamMemberRemoved(
author = authorName,
memberNames = memberNameList
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.ramcosta.composedestinations.spec.Direction
import com.wire.android.BuildConfig
import com.wire.android.R
import com.wire.android.util.EmailComposer
import com.wire.android.util.externalShareChooserIntent
import com.wire.android.util.getDeviceIdString
import com.wire.android.util.getGitBuildId
import com.wire.android.util.sha256
Expand Down Expand Up @@ -100,7 +101,7 @@ object GiveFeedbackDestination : IntentDirection {
)
)
intent.selector = Intent(Intent.ACTION_SENDTO).setData(Uri.parse("mailto:"))
return Intent.createChooser(intent, context.getString(R.string.send_feedback_choose_email))
return context.externalShareChooserIntent(intent, context.getString(R.string.send_feedback_choose_email))
}

override val route: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
*/
package com.wire.android.ui

import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.wire.android.datastore.UserDataStoreProvider
import com.wire.android.di.ApplicationContext
import com.wire.android.di.CurrentAccount
import com.wire.android.di.KaliumCoreLogic
import com.wire.android.ui.analytics.AnalyticsConfiguration
Expand Down Expand Up @@ -51,6 +53,7 @@

@Suppress("LongParameterList")
class MiscViewModelFactory @Inject constructor(
@ApplicationContext private val context: Context,

Check warning on line 56 in app/src/main/kotlin/com/wire/android/ui/MiscViewModelFactory.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/MiscViewModelFactory.kt#L56

Added line #L56 was not covered by tests
private val analyticsEnabled: AnalyticsConfiguration,
private val selfServerConfig: Lazy<SelfServerConfigUseCase>,
private val observeSyncState: ObserveSyncStateUseCase,
Expand Down Expand Up @@ -110,6 +113,7 @@
)

fun importMediaAuthenticatedViewModel() = ImportMediaAuthenticatedViewModel(
context = context,

Check warning on line 116 in app/src/main/kotlin/com/wire/android/ui/MiscViewModelFactory.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/MiscViewModelFactory.kt#L116

Added line #L116 was not covered by tests
getSelf = getSelf,
getConversationsPaginated = getConversationsPaginated,
handleUriAsset = handleUriAsset,
Expand Down
20 changes: 18 additions & 2 deletions app/src/main/kotlin/com/wire/android/ui/WireActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import android.view.WindowManager
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -153,12 +154,14 @@ import com.wire.android.ui.userprofile.self.LocalSelfUserProfileLogoutAction
import com.wire.android.ui.userprofile.self.dialog.LogoutOptionsDialog
import com.wire.android.ui.userprofile.self.dialog.LogoutOptionsDialogState
import com.wire.android.util.CurrentScreenManager
import com.wire.android.ui.sharing.hasTrustedWireShareCaller
import com.wire.android.util.LocalSyncStateObserver
import com.wire.android.util.ShakeDetector
import com.wire.android.util.SwitchAccountObserver
import com.wire.android.util.SyncStateObserver
import com.wire.android.util.debug.FeatureVisibilityFlags
import com.wire.android.util.debug.LocalFeatureVisibilityFlags
import com.wire.android.util.getProviderAuthority
import com.wire.android.util.launchUpdateTheApp
import com.wire.kalium.logic.data.user.UserId
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -287,10 +290,19 @@ class WireActivity : BaseActivity() {
handleSynchronizeExternalData(intent)
return
}
setIntent(intent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
setIntentWithCurrentCaller(intent)
} else {
setIntent(intent)
}
handleNewIntent(intent)
}

@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
private fun setIntentWithCurrentCaller(intent: Intent) {
setIntent(intent, getCurrentCaller())
}

private fun handleNewIntent(intent: Intent, savedInstanceState: Bundle? = null) = lifecycleScope.launch {
newIntents.send(intent to savedInstanceState)
}
Expand Down Expand Up @@ -1183,7 +1195,11 @@ class WireActivity : BaseActivity() {
} else {
val handled = viewModel.handleIntentsThatAreNotDeepLinks(intent)
if (!handled) {
viewModel.handleDeepLink(intent)
viewModel.handleDeepLink(
intent = intent,
providerAuthority = getProviderAuthority(),
hasTrustedWireShareCaller = hasTrustedWireShareCaller()
)
intent.putExtra(HANDLED_DEEPLINK_FLAG, true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.ramcosta.composedestinations.generated.app.destinations.WelcomeScreenDestination
import com.wire.android.ui.authentication.login.LoginPasswordPath
import com.wire.android.ui.newauthentication.login.NewLoginViewModel
import com.wire.android.ui.sharing.ImportMediaNavArgs
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -86,10 +87,10 @@

private fun openImportMediaScreen(navigator: Navigator) {
navigator.navigate(
NavigationCommand(
ImportMediaScreenDestination,
BackStackMode.UPDATE_EXISTED
)
NavigationCommand(
ImportMediaScreenDestination(ImportMediaNavArgs(arrayListOf())),
BackStackMode.UPDATE_EXISTED

Check warning on line 92 in app/src/main/kotlin/com/wire/android/ui/WireActivityActionsHandler.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/WireActivityActionsHandler.kt#L90-L92

Added lines #L90 - L92 were not covered by tests
)
)
}

Expand Down
37 changes: 35 additions & 2 deletions app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ import com.wire.android.ui.common.dialogs.CustomServerDetailsDialogState
import com.wire.android.ui.common.dialogs.CustomServerDialogState
import com.wire.android.ui.common.dialogs.CustomServerNoNetworkDialogState
import com.wire.android.ui.joinConversation.JoinConversationViaCodeState
import com.wire.android.ui.sharing.sharingUris
import com.wire.android.ui.sharing.shouldRejectSharingIntent
import com.wire.android.ui.theme.Accent
import com.wire.android.ui.theme.ThemeOption
import com.wire.android.util.CurrentScreen
Expand Down Expand Up @@ -110,6 +112,8 @@ import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.InputStream
import java.io.InputStreamReader
import dev.zacsweers.metro.Inject
Expand Down Expand Up @@ -413,7 +417,11 @@ class WireActivityViewModel @Inject constructor(
}

@Suppress("ComplexMethod")
fun handleDeepLink(intent: Intent?) {
fun handleDeepLink(
intent: Intent?,
providerAuthority: String? = null,
hasTrustedWireShareCaller: Boolean = false
) {
viewModelScope.launch(dispatchers.io()) {
when (val result = deepLinkProcessor.value.invoke(intent?.data, intent?.action)) {
DeepLinkResult.AuthorizationNeeded -> sendAction(OnAuthorizationNeeded)
Expand Down Expand Up @@ -442,7 +450,16 @@ class WireActivityViewModel @Inject constructor(
is DeepLinkResult.OpenConversation -> sendAction(OpenConversation(result))
is DeepLinkResult.OpenOtherUserProfile -> onOpenUserProfileDeepLink(result)

DeepLinkResult.SharingIntent -> sendAction(OnShowImportMediaScreen)
DeepLinkResult.SharingIntent -> {
val shouldRejectSharingIntent = providerAuthority != null &&
intent?.shouldRejectSharingIntent(providerAuthority, hasTrustedWireShareCaller) == true
if (shouldRejectSharingIntent) {
logRejectedSharingIntent(intent, providerAuthority, hasTrustedWireShareCaller)
sendAction(ShowToast(R.string.public_share_ignored_wire_internal_files))
return@launch
}
sendAction(OnShowImportMediaScreen)
}
DeepLinkResult.Unknown -> {
sendAction(OnUnknownDeepLink)
appLogger.e("unknown deeplink result $result")
Expand All @@ -451,6 +468,22 @@ class WireActivityViewModel @Inject constructor(
}
}

private fun logRejectedSharingIntent(
intent: Intent?,
providerAuthority: String?,
hasTrustedWireShareCaller: Boolean
) {
val logMap = mapOf(
"event" to "public_share_rejected",
"reason" to "wire_file_provider_uri",
"action" to intent?.action.orEmpty(),
"providerAuthority" to providerAuthority.orEmpty(),
"hasTrustedWireShareCaller" to hasTrustedWireShareCaller.toString(),
"uriCount" to (intent?.sharingUris()?.size ?: 0).toString()
)
appLogger.w("Rejected public share intent: ${Json.encodeToString(logMap)}")
}

// Returns whether an intent was handled, or if there was nothing to do
@Suppress("ReturnCount")
suspend fun handleIntentsThatAreNotDeepLinks(intent: Intent?): Boolean {
Expand Down
30 changes: 28 additions & 2 deletions app/src/main/kotlin/com/wire/android/ui/debug/DebugScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.wire.android.ui.debug

import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.widget.Toast
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
Expand Down Expand Up @@ -51,10 +52,12 @@ import com.wire.android.ui.common.topappbar.NavigationIconType
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.ramcosta.composedestinations.generated.app.destinations.ConversationCryptoStatsScreenDestination
import com.ramcosta.composedestinations.generated.app.destinations.DebugFeatureFlagsScreenDestination
import com.ramcosta.composedestinations.generated.app.destinations.ImportMediaScreenDestination
import com.wire.android.ui.common.rowitem.SectionHeader
import com.wire.android.ui.home.settings.SettingsItem
import com.wire.android.ui.home.settings.backup.BackupAndRestoreDialog
import com.wire.android.ui.home.settings.backup.rememberBackUpAndRestoreStateHolder
import com.wire.android.ui.sharing.ImportMediaNavArgs
import com.wire.android.ui.theme.WireTheme
import com.wire.android.util.AppNameUtil
import com.wire.android.util.logging.LogShareLauncher
Expand Down Expand Up @@ -94,6 +97,13 @@ fun DebugScreen(
dangerOptionsContent = {
DangerOptions(exportObfuscatedCopyViewModel = exportObfuscatedCopyViewModel)
},
onShareLogsViaWire = { uri ->
navigator.navigate(
NavigationCommand(
ImportMediaScreenDestination(ImportMediaNavArgs(arrayListOf(uri)))
)
)
},
)
}

Expand All @@ -105,6 +115,7 @@ internal fun UserDebugContent(
onDatabaseLoggerEnabledChanged: (Boolean) -> Unit,
onDeleteLogs: () -> Unit,
onFlushLogs: () -> Deferred<Unit>,
onShareLogsViaWire: (Uri) -> Unit,
debugDataOptionsContent: @Composable (DebugContentState) -> Unit,
dangerOptionsContent: @Composable () -> Unit,
) {
Expand All @@ -131,7 +142,8 @@ internal fun UserDebugContent(
isLoggingEnabled = isLoggingEnabled,
onLoggingEnabledChange = onLoggingEnabledChange,
onDeleteLogs = onDeleteLogs,
onShareLogs = { debugContentState.shareLogs(onFlushLogs) },
onShareLogsExternally = { debugContentState.shareLogsExternally(onFlushLogs) },
onShareLogsViaWire = { debugContentState.shareLogsViaWire(onFlushLogs, onShareLogsViaWire) },
isDBLoggerEnabled = state.isDBLoggingEnabled,
onDBLoggerEnabledChange = onDatabaseLoggerEnabledChanged,
isPrivateBuild = BuildConfig.PRIVATE_BUILD,
Expand Down Expand Up @@ -230,7 +242,7 @@ data class DebugContentState(
).show()
}

fun shareLogs(onFlushLogs: () -> Deferred<Unit>) {
fun shareLogsExternally(onFlushLogs: () -> Deferred<Unit>) {
val dir = File(logPath).parentFile
if (dir != null && dir.exists()) {
logShareLauncher.shareLogs(dir) {
Expand All @@ -239,6 +251,19 @@ data class DebugContentState(
}
}
}

fun shareLogsViaWire(onFlushLogs: () -> Deferred<Unit>, onShareUri: (Uri) -> Unit) {
val dir = File(logPath).parentFile
if (dir != null && dir.exists()) {
logShareLauncher.shareLogsViaWire(
logsDirectory = dir,
onShareUri = onShareUri
) {
// Flush any buffered logs before sharing to ensure completeness.
onFlushLogs().await()
}
}
}
}

@Preview(heightDp = 1400)
Expand All @@ -253,6 +278,7 @@ internal fun PreviewUserDebugContent() = WireTheme {
onLoggingEnabledChange = {},
onDeleteLogs = {},
onFlushLogs = { CompletableDeferred(Unit) },
onShareLogsViaWire = {},
onDatabaseLoggerEnabledChanged = {},
debugDataOptionsContent = {
DebugDataOptions(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.wire.android.ui.debug

import com.ramcosta.composedestinations.generated.app.destinations.ImportMediaScreenDestination
import com.wire.android.navigation.annotation.app.WireRootDestination
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -26,11 +27,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.wire.android.R
import com.wire.android.navigation.NavigationCommand
import com.wire.android.navigation.Navigator
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.topappbar.NavigationIconType
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.wire.android.ui.sharing.ImportMediaNavArgs

@WireRootDestination
@Composable
Expand Down Expand Up @@ -63,7 +66,16 @@ fun LogManagementScreen(
isLoggingEnabled = state.isLoggingEnabled,
onLoggingEnabledChange = viewModel::setLoggingEnabledState,
onDeleteLogs = viewModel::deleteLogs,
onShareLogs = { contentState.shareLogs(viewModel::flushLogs) },
onShareLogsExternally = { contentState.shareLogsExternally(viewModel::flushLogs) },
onShareLogsViaWire = {
contentState.shareLogsViaWire(viewModel::flushLogs) { uri ->
navigator.navigate(
NavigationCommand(
ImportMediaScreenDestination(ImportMediaNavArgs(arrayListOf(uri)))
)
)
}
},
isDBLoggerEnabled = false,
onDBLoggerEnabledChange = {},
isPrivateBuild = false
Expand Down
Loading
Loading