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

feat: Show e2ei certificate in device details screen - PART 2 (WPB-3214) #2332

Merged
merged 4 commits into from
Oct 25, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import com.wire.android.ui.common.dimensions
import com.wire.android.ui.theme.wireDimensions
import com.wire.android.ui.theme.wireTypography

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WireCenterAlignedTopAppBar(
title: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.settings.devices.e2ei

import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.wire.android.R
import com.wire.android.ui.common.bottomsheet.MenuBottomSheetItem
import com.wire.android.ui.common.bottomsheet.MenuItemIcon
import com.wire.android.ui.common.bottomsheet.MenuModalSheetContent
import com.wire.android.ui.common.bottomsheet.MenuModalSheetHeader
import com.wire.android.ui.common.bottomsheet.WireModalSheetLayout
import com.wire.android.ui.common.bottomsheet.WireModalSheetState

@Composable
fun E2eiCertificateDetailsBottomSheet(
sheetState: WireModalSheetState,
onCopyToClipboard: () -> Unit,
onDownload: () -> Unit,
) {
val coroutineScope = rememberCoroutineScope()

WireModalSheetLayout(sheetState = sheetState, coroutineScope = coroutineScope) {
MenuModalSheetContent(
header = MenuModalSheetHeader.Gone,
menuItems = buildList {
add {
CreateCertificateSheetItem(
title = stringResource(R.string.e2ei_certificate_details_copy_to_clipboard),
icon = R.drawable.ic_copy,
onClicked = onCopyToClipboard,
enabled = true
)
}
add {
CreateCertificateSheetItem(
title = stringResource(R.string.e2ei_certificate_details_download),
icon = R.drawable.ic_download,
onClicked = onDownload,
enabled = true
)
}
}
)
}
}

@Composable
private fun CreateCertificateSheetItem(
title: String,
icon: Int,
onClicked: () -> Unit,
enabled: Boolean = true,
) {
MenuBottomSheetItem(
title = title,
onItemClick = onClicked,
icon = {
MenuItemIcon(
id = icon,
contentDescription = "",
)
},
enabled = enabled
)
}

@Preview
@Composable
fun PreviewE2eiCertificateDetailsBottomSheet() {
E2eiCertificateDetailsBottomSheet(
sheetState = WireModalSheetState(),
onCopyToClipboard = { },
onDownload = { }
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.settings.devices.e2ei

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootNavGraph
import com.wire.android.R
import com.wire.android.navigation.Navigator
import com.wire.android.navigation.style.PopUpNavigationAnimation
import com.wire.android.ui.common.button.WireSecondaryIconButton
import com.wire.android.ui.common.colorsScheme
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.snackbar.LocalSnackbarHostState
import com.wire.android.ui.common.topappbar.NavigationIconType
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
import com.wire.android.util.copyLinkToClipboard
import com.wire.android.util.createPemFile
import com.wire.android.util.saveFileToDownloadsFolder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okio.Path.Companion.toOkioPath

@RootNavGraph
@Destination(
navArgsDelegate = E2eiCertificateDetailsScreenNavArgs::class,
style = PopUpNavigationAnimation::class,
)
@Composable
fun E2eiCertificateDetailsScreen(
e2eiCertificateDetailsViewModel: E2eiCertificateDetailsViewModel = hiltViewModel(),
navigator: Navigator
) {
val snackbarHostState = LocalSnackbarHostState.current
val scope = rememberCoroutineScope()
val context = LocalContext.current

WireScaffold(
topBar = {
WireCenterAlignedTopAppBar(
onNavigationPressed = navigator::navigateBack,
title = stringResource(R.string.e2ei_certificate_details_screen_title),
navigationIconType = NavigationIconType.Back,
actions = {
WireSecondaryIconButton(
onButtonClicked = {
e2eiCertificateDetailsViewModel.state.wireModalSheetState.show()
},
iconResource = R.drawable.ic_more,
contentDescription = R.string.content_description_more_options
)
}
)
}
) {
val clipboardManager = LocalClipboardManager.current

with(e2eiCertificateDetailsViewModel) {
val copiedToClipboardString =
stringResource(id = R.string.e2ei_certificate_details_certificate_copied_to_clipboard)
val downloadedString = stringResource(id = R.string.media_gallery_on_image_downloaded)

E2eiCertificateDetailsContent(
padding = it,
certificateString = getCertificate()
)
E2eiCertificateDetailsBottomSheet(
sheetState = state.wireModalSheetState,
onCopyToClipboard = {
clipboardManager.copyLinkToClipboard(getCertificate())
scope.launch {
state.wireModalSheetState.hide()
snackbarHostState.showSnackbar(copiedToClipboardString)
}
},
onDownload = {
scope.launch {
withContext(Dispatchers.IO) {
createPemFile(CERTIFICATE_FILE_NAME, getCertificate()).also {
saveFileToDownloadsFolder(
context = context,
assetName = CERTIFICATE_FILE_NAME,
assetDataPath = it.toPath().toOkioPath(),
assetDataSize = it.length()
)
}
}
state.wireModalSheetState.hide()
snackbarHostState.showSnackbar(downloadedString)
}
}
)
}
}
}

@Composable
fun E2eiCertificateDetailsContent(
padding: PaddingValues,
certificateString: String
) {
val textStyle = TextStyle(
textAlign = TextAlign.Justify,
fontSize = 12.sp,
lineHeight = 18.sp,
color = colorsScheme().onBackground
)
val scroll = rememberScrollState(0)
Text(
modifier = Modifier
.verticalScroll(scroll)
.padding(
top = padding.calculateTopPadding() + dimensions().spacing16x,
start = padding.calculateStartPadding(LayoutDirection.Ltr) + dimensions().spacing16x,
end = padding.calculateEndPadding(LayoutDirection.Ltr) + dimensions().spacing16x,
bottom = padding.calculateBottomPadding()
),
text = certificateString,
style = textStyle
)
}

const val CERTIFICATE_FILE_NAME = "certificate.pem"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.settings.devices.e2ei

data class E2eiCertificateDetailsScreenNavArgs(
val certificateString: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui.settings.devices.e2ei

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import com.wire.android.ui.common.bottomsheet.WireModalSheetState
import com.wire.android.ui.navArgs
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@HiltViewModel
class E2eiCertificateDetailsViewModel @Inject constructor(

Check warning on line 31 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L31

Added line #L31 was not covered by tests
savedStateHandle: SavedStateHandle,
) : ViewModel() {

Check warning on line 33 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L33

Added line #L33 was not covered by tests

var state: E2eiCertificateDetailsState by mutableStateOf(E2eiCertificateDetailsState())

Check warning on line 35 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L35

Added line #L35 was not covered by tests
private set

private val e2eiCertificateDetailsScreenNavArgs: E2eiCertificateDetailsScreenNavArgs =
savedStateHandle.navArgs()

Check warning on line 39 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L39

Added line #L39 was not covered by tests

fun getCertificate() = e2eiCertificateDetailsScreenNavArgs.certificateString

Check warning on line 41 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L41

Added line #L41 was not covered by tests
}

data class E2eiCertificateDetailsState(
val wireModalSheetState: WireModalSheetState = WireModalSheetState()
)

Check warning on line 46 in app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/e2ei/E2eiCertificateDetailsViewModel.kt#L44-L46

Added lines #L44 - L46 were not covered by tests
4 changes: 2 additions & 2 deletions app/src/main/kotlin/com/wire/android/util/ClipboardCopier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ package com.wire.android.util
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.text.AnnotatedString

fun ClipboardManager.copyLinkToClipboard(link: String) {
setText(AnnotatedString(link))
fun ClipboardManager.copyLinkToClipboard(text: String) {
setText(AnnotatedString(text))
}
17 changes: 17 additions & 0 deletions app/src/main/kotlin/com/wire/android/util/FileUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.data.asset.isAudioMimeType
import com.wire.kalium.logic.util.buildFileName
import com.wire.kalium.logic.util.splitFileExtensionAndCopyCounter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okio.Path
import java.io.File
Expand Down Expand Up @@ -112,6 +113,22 @@ fun getTempWritableAttachmentUri(context: Context, attachmentPath: Path): Uri {
return FileProvider.getUriForFile(context, context.getProviderAuthority(), file)
}

suspend fun createPemFile(
pathname: String,
content: String
): File {
return withContext(Dispatchers.IO) {
return@withContext File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS
),
pathname
).apply {
writeText(content)
}
}
}

private fun Context.saveFileDataToDownloadsFolder(assetName: String, downloadedDataPath: Path, fileSize: Long): Uri? {
val resolver = contentResolver
val mimeType = Uri.parse(downloadedDataPath.toString()).getMimeType(this@saveFileDataToDownloadsFolder)
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1239,4 +1239,9 @@
<string name="biometrics_prompt_dialog_title">Authenticate with biometrics</string>
<string name="biometrics_prompt_dialog_subtitle">To unlock Wire</string>
<string name="biometrics_use_passcode_button">Use passcode</string>
<!-- e2ei-->
<string name="e2ei_certificate_details_screen_title">Certificate Details</string>
<string name="e2ei_certificate_details_copy_to_clipboard">Copy to Clipboard</string>
<string name="e2ei_certificate_details_download">Download</string>
<string name="e2ei_certificate_details_certificate_copied_to_clipboard">Certificate copied to clipboard</string>
</resources>