Merge branch 'develop' into feature/fga/power_level

This commit is contained in:
ganfra 2023-07-18 22:54:33 +02:00
commit 1d2fd52ce6
188 changed files with 813 additions and 628 deletions

View file

@ -76,9 +76,9 @@ fun Context.getApplicationLabel(packageName: String): String {
/**
* Return true it the user has enabled the do not disturb mode.
*/
fun isDoNotDisturbModeOn(context: Context): Boolean {
fun Context.isDoNotDisturbModeOn(): Boolean {
// We cannot use NotificationManagerCompat here.
val setting = context.getSystemService<NotificationManager>()!!.currentInterruptionFilter
val setting = getSystemService<NotificationManager>()!!.currentInterruptionFilter
return setting == NotificationManager.INTERRUPTION_FILTER_NONE ||
setting == NotificationManager.INTERRUPTION_FILTER_ALARMS
@ -92,10 +92,10 @@ fun isDoNotDisturbModeOn(context: Context): Boolean {
* will return false and the notification privacy will fallback to "LOW_DETAIL".
*/
@SuppressLint("BatteryLife")
fun requestDisablingBatteryOptimization(activity: Activity, activityResultLauncher: ActivityResultLauncher<Intent>) {
fun Context.requestDisablingBatteryOptimization(activityResultLauncher: ActivityResultLauncher<Intent>) {
val intent = Intent()
intent.action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
intent.data = Uri.parse("package:" + activity.packageName)
intent.data = Uri.parse("package:$packageName")
activityResultLauncher.launch(intent)
}
@ -106,50 +106,48 @@ fun requestDisablingBatteryOptimization(activity: Activity, activityResultLaunch
/**
* Copy a text to the clipboard, and display a Toast when done.
*
* @param context the context
* @receiver the context
* @param text the text to copy
* @param toastMessage content of the toast message as a String resource. Null for no toast
*/
fun copyToClipboard(
context: Context,
fun Context.copyToClipboard(
text: CharSequence,
toastMessage: String? = null
) {
CopyToClipboardUseCase(context).execute(text)
toastMessage?.let { context.toast(it) }
CopyToClipboardUseCase(this).execute(text)
toastMessage?.let { toast(it) }
}
/**
* Shows notification settings for the current app.
* In android O will directly opens the notification settings, in lower version it will show the App settings
*/
fun startNotificationSettingsIntent(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
fun Context.startNotificationSettingsIntent(activityResultLauncher: ActivityResultLauncher<Intent>) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
} else {
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.data = Uri.fromParts("package", context.packageName, null)
intent.data = Uri.fromParts("package", packageName, null)
}
activityResultLauncher.launch(intent)
}
fun openAppSettingsPage(
activity: Activity,
noActivityFoundMessage: String = activity.getString(R.string.error_no_compatible_app_found),
fun Context.openAppSettingsPage(
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
try {
activity.startActivity(
startActivity(
Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.fromParts("package", activity.packageName, null)
data = Uri.fromParts("package", packageName, null)
}
)
} catch (activityNotFoundException: ActivityNotFoundException) {
activity.toast(noActivityFoundMessage)
toast(noActivityFoundMessage)
}
}
@ -157,52 +155,49 @@ fun openAppSettingsPage(
* Shows notification system settings for the given channel id.
*/
@TargetApi(Build.VERSION_CODES.O)
fun startNotificationChannelSettingsIntent(activity: Activity, channelID: String) {
fun Activity.startNotificationChannelSettingsIntent(channelID: String) {
if (!supportNotificationChannels()) return
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
putExtra(Settings.EXTRA_CHANNEL_ID, channelID)
}
activity.startActivity(intent)
startActivity(intent)
}
fun startAddGoogleAccountIntent(
context: Context,
fun Context.startAddGoogleAccountIntent(
activityResultLauncher: ActivityResultLauncher<Intent>,
noActivityFoundMessage: String = context.getString(R.string.error_no_compatible_app_found),
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Settings.ACTION_ADD_ACCOUNT)
intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf("com.google"))
try {
activityResultLauncher.launch(intent)
} catch (activityNotFoundException: ActivityNotFoundException) {
context.toast(noActivityFoundMessage)
toast(noActivityFoundMessage)
}
}
@RequiresApi(Build.VERSION_CODES.O)
fun startInstallFromSourceIntent(
context: Context,
fun Context.startInstallFromSourceIntent(
activityResultLauncher: ActivityResultLauncher<Intent>,
noActivityFoundMessage: String = context.getString(R.string.error_no_compatible_app_found),
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
.setData(Uri.parse(String.format("package:%s", context.packageName)))
.setData(Uri.parse(String.format("package:%s", packageName)))
try {
activityResultLauncher.launch(intent)
} catch (activityNotFoundException: ActivityNotFoundException) {
context.toast(noActivityFoundMessage)
toast(noActivityFoundMessage)
}
}
fun startSharePlainTextIntent(
context: Context,
fun Context.startSharePlainTextIntent(
activityResultLauncher: ActivityResultLauncher<Intent>?,
chooserTitle: String?,
text: String,
subject: String? = null,
extraTitle: String? = null,
noActivityFoundMessage: String = context.getString(R.string.error_no_compatible_app_found),
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val share = Intent(Intent.ACTION_SEND)
share.type = "text/plain"
@ -220,17 +215,16 @@ fun startSharePlainTextIntent(
if (activityResultLauncher != null) {
activityResultLauncher.launch(intent)
} else {
context.startActivity(intent)
startActivity(intent)
}
} catch (activityNotFoundException: ActivityNotFoundException) {
context.toast(noActivityFoundMessage)
toast(noActivityFoundMessage)
}
}
fun startImportTextFromFileIntent(
context: Context,
fun Context.startImportTextFromFileIntent(
activityResultLauncher: ActivityResultLauncher<Intent>,
noActivityFoundMessage: String = context.getString(R.string.error_no_compatible_app_found),
noActivityFoundMessage: String = getString(R.string.error_no_compatible_app_found),
) {
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "text/plain"
@ -238,7 +232,7 @@ fun startImportTextFromFileIntent(
try {
activityResultLauncher.launch(intent)
} catch (activityNotFoundException: ActivityNotFoundException) {
context.toast(noActivityFoundMessage)
toast(noActivityFoundMessage)
}
}

View file

@ -38,6 +38,7 @@ object MimeTypes {
const val Audio = "audio/*"
const val Ogg = "audio/ogg"
const val Mp3 = "audio/mp3"
const val PlainText = "text/plain"

View file

@ -37,8 +37,7 @@ class InviteFriendsUseCase @Inject constructor(
permalinkResult.fold(
onSuccess = { permalink ->
val appName = buildMeta.applicationName
startSharePlainTextIntent(
context = activity,
activity.startSharePlainTextIntent(
activityResultLauncher = null,
chooserTitle = stringProvider.getString(CommonStrings.action_invite_friends),
text = stringProvider.getString(CommonStrings.invite_friends_text, appName, permalink),

View file

@ -53,82 +53,72 @@ import io.element.android.libraries.theme.ElementTheme
fun ElementLogoAtom(
size: ElementLogoAtomSize,
modifier: Modifier = Modifier,
darkTheme: Boolean = isSystemInDarkTheme(),
) {
val outerSize = when (size) {
ElementLogoAtomSize.Large -> 158.dp
ElementLogoAtomSize.Medium -> 120.dp
}
val logoSize = when (size) {
ElementLogoAtomSize.Large -> 110.dp
ElementLogoAtomSize.Medium -> 83.5.dp
}
val cornerRadius = when(size) {
ElementLogoAtomSize.Large -> 44.dp
ElementLogoAtomSize.Medium -> 33.dp
}
val borderWidth = when (size) {
ElementLogoAtomSize.Large -> 1.dp
ElementLogoAtomSize.Medium -> 0.38.dp
}
val blur = if (isSystemInDarkTheme()) {
160.dp
} else {
24.dp
}
val blur = if (darkTheme) 160.dp else 24.dp
//box-shadow: 0px 6.075949668884277px 24.30379867553711px 0px #1B1D2280;
val shadowColor = if (isSystemInDarkTheme()) {
Color.Black.copy(alpha = 0.4f)
} else {
Color(0x401B1D22)
}
val backgroundColor = if (isSystemInDarkTheme()) Color.White.copy(alpha = 0.2f) else Color.White.copy(alpha = 0.4f)
val borderColor = if (isSystemInDarkTheme()) Color.White.copy(alpha = 0.8f) else Color.White.copy(alpha = 0.4f)
val shadowColor = if (darkTheme) size.shadowColorDark else size.shadowColorLight
val backgroundColor = if (darkTheme) Color.White.copy(alpha = 0.2f) else Color.White.copy(alpha = 0.4f)
val borderColor = if (darkTheme) Color.White.copy(alpha = 0.8f) else Color.White.copy(alpha = 0.4f)
Box(
modifier = modifier
.size(outerSize)
.border(borderWidth, borderColor, RoundedCornerShape(cornerRadius)),
.size(size.outerSize)
.border(size.borderWidth, borderColor, RoundedCornerShape(size.cornerRadius)),
contentAlignment = Alignment.Center,
) {
Box(
Modifier
.size(outerSize)
.size(size.outerSize)
.shapeShadow(
color = shadowColor,
cornerRadius = cornerRadius,
blurRadius = 32.dp,
cornerRadius = size.cornerRadius,
blurRadius = size.shadowRadius,
offsetY = 8.dp,
)
)
Box(
Modifier
.clip(RoundedCornerShape(cornerRadius))
.size(outerSize)
.clip(RoundedCornerShape(size.cornerRadius))
.size(size.outerSize)
.background(backgroundColor)
.blur(blur)
)
Image(
modifier = Modifier.size(logoSize),
modifier = Modifier.size(size.logoSize),
painter = painterResource(id = R.drawable.element_logo),
contentDescription = null
)
}
}
enum class ElementLogoAtomSize {
Medium,
Large
}
sealed class ElementLogoAtomSize(
val outerSize: Dp,
val logoSize: Dp,
val cornerRadius: Dp,
val borderWidth: Dp,
val shadowColorDark: Color,
val shadowColorLight: Color,
val shadowRadius: Dp,
) {
object Medium : ElementLogoAtomSize(
outerSize = 120.dp,
logoSize = 83.5.dp,
cornerRadius = 33.dp,
borderWidth = 0.38.dp,
shadowColorDark = Color.Black.copy(alpha = 0.4f),
shadowColorLight = Color(0x401B1D22),
shadowRadius = 32.dp,
)
@Composable
@DayNightPreviews
internal fun ElementLogoAtomPreview() {
ElementPreview {
Box(
Modifier
.size(170.dp)
.background(ElementTheme.colors.bgSubtlePrimary))
ElementLogoAtom(ElementLogoAtomSize.Large)
}
object Large : ElementLogoAtomSize(
outerSize = 158.dp,
logoSize = 110.dp,
cornerRadius = 44.dp,
borderWidth = 0.5.dp,
shadowColorDark = Color.Black,
shadowColorLight = Color(0x801B1D22),
shadowRadius = 60.dp,
)
}
fun Modifier.shapeShadow(
@ -168,3 +158,29 @@ fun Modifier.shapeShadow(
}
}
)
@Composable
@DayNightPreviews
internal fun ElementLogoAtomMediumPreview() {
ContentToPreview(ElementLogoAtomSize.Medium)
}
@Composable
@DayNightPreviews
internal fun ElementLogoAtomLargePreview() {
ContentToPreview(ElementLogoAtomSize.Large)
}
@Composable
private fun ContentToPreview(elementLogoAtomSize: ElementLogoAtomSize) {
ElementPreview {
Box(
Modifier
.size(elementLogoAtomSize.outerSize + elementLogoAtomSize.shadowRadius * 2)
.background(ElementTheme.colors.bgSubtlePrimary),
contentAlignment = Alignment.Center
) {
ElementLogoAtom(elementLogoAtomSize)
}
}
}

View file

@ -31,6 +31,7 @@ import io.element.android.libraries.designsystem.atomic.atoms.InfoListItemMolecu
import io.element.android.libraries.designsystem.atomic.atoms.InfoListItemPosition
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
import kotlinx.collections.immutable.ImmutableList
@Composable
@ -54,7 +55,13 @@ fun InfoListOrganism(
else -> InfoListItemPosition.Middle
}
InfoListItemMolecule(
message = { Text(item.message, style = textStyle) },
message = {
Text(
text = item.message,
style = textStyle,
color = ElementTheme.colors.textPrimary,
)
},
icon = {
if (item.iconId != null) {
Icon(resourceId = item.iconId, contentDescription = null, tint = iconTint)

View file

@ -28,11 +28,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.element.android.libraries.designsystem.R
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
import io.element.android.libraries.designsystem.preview.DayNightPreviews
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.theme.ElementTheme
@ -52,10 +51,6 @@ fun OnBoardingPage(
footer: @Composable () -> Unit = {},
content: @Composable () -> Unit = {},
) {
// Note: having a night variant of R.drawable.onboarding_bg in the folder `drawable-night` is working
// at runtime, but is not in Android Studio Preview. So I prefer to handle this manually.
val isLight = ElementTheme.colors.isLight
val bgDrawableRes = if (isLight) R.drawable.onboarding_bg_light else R.drawable.onboarding_bg_dark
Box(
modifier = modifier
.fillMaxSize()
@ -64,7 +59,7 @@ fun OnBoardingPage(
Image(
modifier = Modifier
.fillMaxSize(),
painter = painterResource(id = bgDrawableRes),
painter = painterResource(id = R.drawable.onboarding_bg),
contentScale = ContentScale.Crop,
contentDescription = null,
)
@ -92,18 +87,9 @@ fun OnBoardingPage(
}
}
@Preview
@DayNightPreviews
@Composable
internal fun OnBoardingPageLightPreview() =
ElementPreviewLight { ContentToPreview() }
@Preview
@Composable
internal fun OnBoardingPageDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@Composable
private fun ContentToPreview() {
internal fun OnBoardingPagePreview() = ElementPreview {
OnBoardingPage(
content = {
Box(

View file

@ -20,14 +20,17 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@ -37,21 +40,32 @@ import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.ui.strings.CommonStrings
import timber.log.Timber
@Composable
fun ProgressDialog(
modifier: Modifier = Modifier,
text: String? = null,
type: ProgressDialogType = ProgressDialogType.Indeterminate,
onDismiss: () -> Unit = {},
isCancellable: Boolean = false,
onDismissRequest: () -> Unit = {},
) {
DisposableEffect(Unit) {
onDispose {
Timber.v("OnDispose progressDialog")
}
}
Dialog(
onDismissRequest = onDismiss,
onDismissRequest = onDismissRequest,
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
) {
ProgressDialogContent(
modifier = modifier,
text = text,
isCancellable = isCancellable,
onCancelClicked = onDismissRequest,
progressIndicator = {
when (type) {
is ProgressDialogType.Indeterminate -> {
@ -81,6 +95,8 @@ sealed interface ProgressDialogType {
private fun ProgressDialogContent(
modifier: Modifier = Modifier,
text: String? = null,
isCancellable: Boolean = false,
onCancelClicked: () -> Unit = {},
progressIndicator: @Composable () -> Unit = {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.primary
@ -107,6 +123,17 @@ private fun ProgressDialogContent(
color = MaterialTheme.colorScheme.primary,
)
}
if (isCancellable) {
Spacer(modifier = Modifier.height(24.dp))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.BottomEnd
) {
TextButton(onClick = onCancelClicked) {
Text(stringResource(id = CommonStrings.action_cancel))
}
}
}
}
}
}
@ -118,6 +145,6 @@ internal fun ProgressDialogPreview() = ElementThemedPreview { ContentToPreview()
@Composable
private fun ContentToPreview() {
DialogPreview {
ProgressDialogContent(text = "test dialog content")
ProgressDialogContent(text = "test dialog content", isCancellable = true)
}
}

View file

Before

Width:  |  Height:  |  Size: 388 KiB

After

Width:  |  Height:  |  Size: 388 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 223 KiB

After

Width:  |  Height:  |  Size: 223 KiB

Before After
Before After

View file

@ -21,5 +21,5 @@ import java.time.Duration
data class AudioInfo(
val duration: Duration?,
val size: Long?,
val mimeType: String?,
val mimetype: String?,
)

View file

@ -95,9 +95,7 @@ interface MatrixRoom : Closeable {
suspend fun leave(): Result<Unit>
suspend fun acceptInvitation(): Result<Unit>
suspend fun rejectInvitation(): Result<Unit>
suspend fun join(): Result<Unit>
suspend fun inviteUserById(id: UserId): Result<Unit>

View file

@ -22,11 +22,11 @@ import org.matrix.rustcomponents.sdk.AudioInfo as RustAudioInfo
fun RustAudioInfo.map(): AudioInfo = AudioInfo(
duration = duration,
size = size?.toLong(),
mimeType = mimetype
mimetype = mimetype
)
fun AudioInfo.map(): RustAudioInfo = RustAudioInfo(
duration = duration,
size = size?.toULong(),
mimetype = mimeType,
mimetype = mimetype,
)

View file

@ -256,15 +256,9 @@ class RustMatrixRoom(
}
}
override suspend fun acceptInvitation(): Result<Unit> = withContext(roomDispatcher) {
override suspend fun join(): Result<Unit> = withContext(roomDispatcher) {
runCatching {
innerRoom.acceptInvitation()
}
}
override suspend fun rejectInvitation(): Result<Unit> = withContext(roomDispatcher) {
runCatching {
innerRoom.rejectInvitation()
innerRoom.join()
}
}

View file

@ -63,8 +63,7 @@ class FakeMatrixRoom(
private var userDisplayNameResult = Result.success<String?>(null)
private var userAvatarUrlResult = Result.success<String?>(null)
private var updateMembersResult: Result<Unit> = Result.success(Unit)
private var acceptInviteResult = Result.success(Unit)
private var rejectInviteResult = Result.success(Unit)
private var joinRoomResult = Result.success(Unit)
private var inviteUserResult = Result.success(Unit)
private var canInviteResult = Result.success(true)
private val canSendStateResults = mutableMapOf<StateEventType, Result<Boolean>>()
@ -101,11 +100,6 @@ class FakeMatrixRoom(
var sendLocationCount: Int = 0
private set
var isInviteAccepted: Boolean = false
private set
var isInviteRejected: Boolean = false
private set
var invitedUserId: UserId? = null
private set
@ -196,16 +190,11 @@ class FakeMatrixRoom(
return Result.success(Unit)
}
override suspend fun leave(): Result<Unit> = leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
override suspend fun leave(): Result<Unit> =
leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
override suspend fun acceptInvitation(): Result<Unit> {
isInviteAccepted = true
return acceptInviteResult
}
override suspend fun rejectInvitation(): Result<Unit> {
isInviteRejected = true
return rejectInviteResult
override suspend fun join(): Result<Unit> {
return joinRoomResult
}
override suspend fun inviteUserById(id: UserId): Result<Unit> = simulateLongTask {
@ -316,12 +305,8 @@ class FakeMatrixRoom(
userAvatarUrlResult = avatarUrl
}
fun givenAcceptInviteResult(result: Result<Unit>) {
acceptInviteResult = result
}
fun givenRejectInviteResult(result: Result<Unit>) {
rejectInviteResult = result
fun givenJoinRoomResult(result: Result<Unit>) {
joinRoomResult = result
}
fun givenInviteUserResult(result: Result<Unit>) {

View file

@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Attachment
import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material.icons.outlined.VideoCameraBack
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@ -44,9 +45,9 @@ fun AttachmentThumbnail(
thumbnailSize: Long = 32L,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
) {
if (info.mediaSource != null) {
if (info.thumbnailSource != null) {
val mediaRequestData = MediaRequestData(
source = info.mediaSource,
source = info.thumbnailSource,
kind = MediaRequestData.Kind.Thumbnail(thumbnailSize),
)
BlurHashAsyncImage(
@ -68,6 +69,12 @@ fun AttachmentThumbnail(
contentDescription = info.textContent,
)
}
AttachmentThumbnailType.Audio -> {
Icon(
imageVector = Icons.Outlined.GraphicEq,
contentDescription = info.textContent,
)
}
AttachmentThumbnailType.File -> {
Icon(
imageVector = Icons.Outlined.Attachment,
@ -88,13 +95,13 @@ fun AttachmentThumbnail(
@Parcelize
enum class AttachmentThumbnailType: Parcelable {
Image, Video, File, Location
Image, Video, File, Audio, Location
}
@Parcelize
data class AttachmentThumbnailInfo(
val mediaSource: MediaSource?,
val textContent: String?,
val type: AttachmentThumbnailType?,
val blurHash: String?,
val type: AttachmentThumbnailType,
val thumbnailSource: MediaSource? = null,
val textContent: String? = null,
val blurHash: String? = null,
): Parcelable

View file

@ -46,36 +46,43 @@ class MediaSender @Inject constructor(
}
private suspend fun MatrixRoom.sendMedia(
info: MediaUploadInfo,
uploadInfo: MediaUploadInfo,
progressCallback: ProgressCallback?
): Result<Unit> {
return when (info) {
return when (uploadInfo) {
is MediaUploadInfo.Image -> {
sendImage(
file = info.file,
thumbnailFile = info.thumbnailFile,
imageInfo = info.info,
file = uploadInfo.file,
thumbnailFile = uploadInfo.thumbnailFile,
imageInfo = uploadInfo.imageInfo,
progressCallback = progressCallback
)
}
is MediaUploadInfo.Video -> {
sendVideo(
file = info.file,
thumbnailFile = info.thumbnailFile,
videoInfo = info.info,
file = uploadInfo.file,
thumbnailFile = uploadInfo.thumbnailFile,
videoInfo = uploadInfo.videoInfo,
progressCallback = progressCallback
)
}
is MediaUploadInfo.Audio -> {
sendAudio(
file = uploadInfo.file,
audioInfo = uploadInfo.audioInfo,
progressCallback = progressCallback
)
}
is MediaUploadInfo.AnyFile -> {
sendFile(
file = info.file,
fileInfo = info.info,
file = uploadInfo.file,
fileInfo = uploadInfo.fileInfo,
progressCallback = progressCallback
)
}
else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $info"))
else -> Result.failure(IllegalStateException("Unexpected MediaUploadInfo format: $uploadInfo"))
}
}
}

View file

@ -26,8 +26,8 @@ sealed interface MediaUploadInfo {
val file: File
data class Image(override val file: File, val info: ImageInfo, val thumbnailFile: File) : MediaUploadInfo
data class Video(override val file: File, val info: VideoInfo, val thumbnailFile: File) : MediaUploadInfo
data class Audio(override val file: File, val info: AudioInfo) : MediaUploadInfo
data class AnyFile(override val file: File, val info: FileInfo) : MediaUploadInfo
data class Image(override val file: File, val imageInfo: ImageInfo, val thumbnailFile: File) : MediaUploadInfo
data class Video(override val file: File, val videoInfo: VideoInfo, val thumbnailFile: File) : MediaUploadInfo
data class Audio(override val file: File, val audioInfo: AudioInfo) : MediaUploadInfo
data class AnyFile(override val file: File, val fileInfo: FileInfo) : MediaUploadInfo
}

View file

@ -133,7 +133,7 @@ class AndroidMediaPreProcessor @Inject constructor(
removeSensitiveImageMetadata(compressionResult.file)
return MediaUploadInfo.Image(
file = compressionResult.file,
info = imageInfo,
imageInfo = imageInfo,
thumbnailFile = thumbnailResult.file
)
}
@ -156,7 +156,7 @@ class AndroidMediaPreProcessor @Inject constructor(
removeSensitiveImageMetadata(file)
return MediaUploadInfo.Image(
file = file,
info = imageInfo,
imageInfo = imageInfo,
thumbnailFile = thumbnailResult.file
)
}
@ -184,7 +184,7 @@ class AndroidMediaPreProcessor @Inject constructor(
val videoInfo = extractVideoMetadata(resultFile, mimeType, thumbnailInfo)
return MediaUploadInfo.Video(
file = resultFile,
info = videoInfo,
videoInfo = videoInfo,
thumbnailFile = thumbnailInfo.file
)
}
@ -196,7 +196,7 @@ class AndroidMediaPreProcessor @Inject constructor(
val info = AudioInfo(
duration = extractDuration(),
size = file.length(),
mimeType = mimeType,
mimetype = mimeType,
)
MediaUploadInfo.Audio(file, info)

View file

@ -165,15 +165,15 @@ class NotificationChannels @Inject constructor(
private fun supportNotificationChannels() = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
fun openSystemSettingsForSilentCategory(activity: Activity) {
startNotificationChannelSettingsIntent(activity, SILENT_NOTIFICATION_CHANNEL_ID)
activity.startNotificationChannelSettingsIntent(SILENT_NOTIFICATION_CHANNEL_ID)
}
fun openSystemSettingsForNoisyCategory(activity: Activity) {
startNotificationChannelSettingsIntent(activity, NOISY_NOTIFICATION_CHANNEL_ID)
activity.startNotificationChannelSettingsIntent(NOISY_NOTIFICATION_CHANNEL_ID)
}
fun openSystemSettingsForCallCategory(activity: Activity) {
startNotificationChannelSettingsIntent(activity, CALL_NOTIFICATION_CHANNEL_ID)
activity.startNotificationChannelSettingsIntent(CALL_NOTIFICATION_CHANNEL_ID)
}
}
}

View file

@ -483,7 +483,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice",
eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = MediaSource("https://domain.com/image.jpg"),
thumbnailSource = MediaSource("https://domain.com/image.jpg"),
textContent = "image.jpg",
type = AttachmentThumbnailType.Image,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
@ -501,7 +501,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice",
eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = MediaSource("https://domain.com/video.mp4"),
thumbnailSource = MediaSource("https://domain.com/video.mp4"),
textContent = "video.mp4",
type = AttachmentThumbnailType.Video,
blurHash = "TQF5:I_NtRE4kXt7Z#MwkCIARPjr",
@ -519,7 +519,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice",
eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = null,
thumbnailSource = null,
textContent = "logs.txt",
type = AttachmentThumbnailType.File,
blurHash = null,
@ -537,7 +537,7 @@ fun TextComposerReplyPreview() = ElementPreview {
senderName = "Alice",
eventId = EventId("$1234"),
attachmentThumbnailInfo = AttachmentThumbnailInfo(
mediaSource = null,
thumbnailSource = null,
textContent = null,
type = AttachmentThumbnailType.Location,
blurHash = null,