Skip to content

Commit

Permalink
Merge pull request #166 from ouchadam/feature/image-gallery
Browse files Browse the repository at this point in the history
Image attachments and gallery
  • Loading branch information
ouchadam committed Sep 29, 2022
2 parents 861d65b + ff69156 commit 0283073
Show file tree
Hide file tree
Showing 20 changed files with 593 additions and 22 deletions.
12 changes: 7 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.dapk.st">
package="app.dapk.st">

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

<application
android:name="app.dapk.st.SmallTalkApplication"
Expand All @@ -17,13 +19,13 @@
android:exported="true"
android:targetActivity="app.dapk.st.home.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
android:resource="@xml/shortcuts"/>

</activity-alias>

Expand Down
2 changes: 2 additions & 0 deletions app/src/main/kotlin/app/dapk/st/SmallTalkApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import app.dapk.st.graph.AppModule
import app.dapk.st.home.HomeModule
import app.dapk.st.login.LoginModule
import app.dapk.st.messenger.MessengerModule
import app.dapk.st.messenger.gallery.ImageGalleryModule
import app.dapk.st.notifications.NotificationsModule
import app.dapk.st.profile.ProfileModule
import app.dapk.st.push.PushModule
Expand Down Expand Up @@ -81,6 +82,7 @@ class SmallTalkApplication : Application(), ModuleProvider {
TaskRunnerModule::class -> appModule.domainModules.taskRunnerModule
CoreAndroidModule::class -> appModule.coreAndroidModule
ShareEntryModule::class -> featureModules.shareEntryModule
ImageGalleryModule::class -> featureModules.imageGalleryModule
else -> throw IllegalArgumentException("Unknown: $klass")
} as T
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/app/dapk/st/graph/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import app.dapk.st.matrix.sync.internal.request.ApiToDeviceEvent
import app.dapk.st.matrix.sync.internal.room.MessageDecrypter
import app.dapk.st.messenger.MessengerActivity
import app.dapk.st.messenger.MessengerModule
import app.dapk.st.messenger.gallery.ImageGalleryModule
import app.dapk.st.navigator.IntentFactory
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.notifications.MatrixPushHandler
Expand Down Expand Up @@ -217,6 +218,10 @@ internal class FeatureModules internal constructor(
ShareEntryModule(matrixModules.sync, matrixModules.room)
}

val imageGalleryModule by unsafeLazy {
ImageGalleryModule(context.contentResolver, coroutineDispatchers)
}

val pushModule by unsafeLazy {
domainModules.pushModule
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package app.dapk.st.core

import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.design.components.SmallTalkTheme
import app.dapk.st.design.components.ThemeConfig
import app.dapk.st.navigator.navigator
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import androidx.activity.compose.setContent as _setContent

abstract class DapkActivity : ComponentActivity(), EffectScope {
Expand All @@ -27,7 +31,6 @@ abstract class DapkActivity : ComponentActivity(), EffectScope {
super.onCreate(savedInstanceState)
this.themeConfig = ThemeConfig(themeStore.isMaterialYouEnabled())


window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
}
Expand Down Expand Up @@ -58,10 +61,40 @@ abstract class DapkActivity : ComponentActivity(), EffectScope {
}
}

@Suppress("OVERRIDE_DEPRECATION")
override fun onBackPressed() {
if (needsBackLeakWorkaround && !onBackPressedDispatcher.hasEnabledCallbacks()) {
finishAfterTransition()
} else
} else {
super.onBackPressed()
}
}

protected suspend fun ensurePermission(permission: String): PermissionResult {
return when {
checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED -> PermissionResult.Granted

shouldShowRequestPermissionRationale(permission) -> PermissionResult.ShowRational

else -> {
val isGranted = suspendCancellableCoroutine { continuation ->
val callback: (result: Boolean) -> Unit = { result -> continuation.resume(result) }
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission(), callback)
launcher.launch(permission)
continuation.invokeOnCancellation { launcher.unregister() }
}

when (isGranted) {
true -> PermissionResult.Granted
false -> PermissionResult.Denied
}
}
}
}
}

sealed interface PermissionResult {
object Granted : PermissionResult
object ShowRational : PermissionResult
object Denied : PermissionResult
}
1 change: 1 addition & 0 deletions features/home/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dependencies {
implementation project(':domains:store')
implementation project(":core")
implementation project(":design-library")
implementation Dependencies.mavenCentral.coil
}
6 changes: 3 additions & 3 deletions features/home/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="app.dapk.st.home">

<application>
<activity android:name="app.dapk.st.home.MainActivity"/>
</application>
<application>
<activity android:name="app.dapk.st.home.MainActivity"/>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class MainActivity : DapkActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

homeViewModel.events.onEach {
when (it) {
HomeEvent.Relaunch -> recreate()
Expand Down
1 change: 1 addition & 0 deletions features/messenger/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
android:windowSoftInputMode="adjustResize"/>

<activity android:name=".roomsettings.RoomSettingsActivity"/>
<activity android:name=".gallery.ImageGalleryActivity"/>

</application>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import android.widget.Toast
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import app.dapk.st.core.DapkActivity
import app.dapk.st.core.*
import app.dapk.st.core.extensions.unsafeLazy
import app.dapk.st.core.module
import app.dapk.st.core.viewModel
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.messenger.gallery.GetImageFromGallery
import app.dapk.st.navigator.MessageAttachment
import kotlinx.parcelize.Parcelize

Expand Down Expand Up @@ -51,10 +51,25 @@ class MessengerActivity : DapkActivity() {
super.onCreate(savedInstanceState)
val payload = readPayload<MessagerActivityPayload>()
val factory = module.decryptingFetcherFactory(RoomId(payload.roomId))

val galleryLauncher = registerForActivityResult(GetImageFromGallery()) {
it?.let { uri ->
viewModel.post(
MessengerAction.ComposerImageUpdate(
MessageAttachment(
AndroidUri(it.toString()),
MimeType.Image,
)
)
)
}
}


setContent {
Surface(Modifier.fillMaxSize()) {
CompositionLocalProvider(LocalDecyptingFetcherFactory provides factory) {
MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator)
MessengerScreen(RoomId(payload.roomId), payload.attachments, viewModel, navigator, galleryLauncher)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package app.dapk.st.messenger

import android.content.res.Configuration
import androidx.activity.result.ActivityResultLauncher
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.CircleShape
Expand All @@ -13,6 +15,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Image
import androidx.compose.material.icons.filled.Send
import androidx.compose.material3.*
import androidx.compose.runtime.*
Expand All @@ -33,24 +36,32 @@ import app.dapk.st.core.Lce
import app.dapk.st.core.LifecycleEffect
import app.dapk.st.core.StartObserving
import app.dapk.st.core.components.CenteredLoading
import app.dapk.st.core.extensions.takeIfContent
import app.dapk.st.design.components.*
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.UserId
import app.dapk.st.matrix.sync.MessageMeta
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.RoomEvent.Message
import app.dapk.st.matrix.sync.RoomState
import app.dapk.st.messenger.gallery.ImageGalleryActivityPayload
import app.dapk.st.navigator.MessageAttachment
import app.dapk.st.navigator.Navigator
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import kotlinx.coroutines.launch

@Composable
internal fun MessengerScreen(roomId: RoomId, attachments: List<MessageAttachment>?, viewModel: MessengerViewModel, navigator: Navigator) {
internal fun MessengerScreen(
roomId: RoomId,
attachments: List<MessageAttachment>?,
viewModel: MessengerViewModel,
navigator: Navigator,
galleryLauncher: ActivityResultLauncher<ImageGalleryActivityPayload>
) {
val state = viewModel.state

viewModel.ObserveEvents()
viewModel.ObserveEvents(galleryLauncher)
LifecycleEffect(
onStart = { viewModel.post(MessengerAction.OnMessengerVisible(roomId, attachments)) },
onStop = { viewModel.post(MessengerAction.OnMessengerGone) }
Expand All @@ -74,6 +85,7 @@ internal fun MessengerScreen(roomId: RoomId, attachments: List<MessageAttachment
state.composerState,
onTextChange = { viewModel.post(MessengerAction.ComposerTextUpdate(it)) },
onSend = { viewModel.post(MessengerAction.ComposerSendText) },
onAttach = { viewModel.startAttachment() }
)
}

Expand All @@ -89,10 +101,16 @@ internal fun MessengerScreen(roomId: RoomId, attachments: List<MessageAttachment
}

@Composable
private fun MessengerViewModel.ObserveEvents() {
private fun MessengerViewModel.ObserveEvents(galleryLauncher: ActivityResultLauncher<ImageGalleryActivityPayload>) {
StartObserving {
this@ObserveEvents.events.launch {
// TODO()
when (it) {
MessengerEvent.SelectImageAttachment -> {
state.roomState.takeIfContent()?.let {
galleryLauncher.launch(ImageGalleryActivityPayload(it.roomState.roomOverview.roomName ?: ""))
}
}
}
}
}
}
Expand Down Expand Up @@ -553,7 +571,7 @@ private fun RowScope.SendStatus(message: RoomEvent) {
}

@Composable
private fun TextComposer(state: ComposerState.Text, onTextChange: (String) -> Unit, onSend: () -> Unit) {
private fun TextComposer(state: ComposerState.Text, onTextChange: (String) -> Unit, onSend: () -> Unit, onAttach: () -> Unit) {
Row(
Modifier
.fillMaxWidth()
Expand All @@ -579,7 +597,16 @@ private fun TextComposer(state: ComposerState.Text, onTextChange: (String) -> Un
onValueChange = { onTextChange(it) },
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
textStyle = LocalTextStyle.current.copy(color = SmallTalkTheme.extendedColors.onOthersBubble),
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences, autoCorrect = true)
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Sentences, autoCorrect = true),
decorationBox = {
Box {
Icon(
modifier = Modifier.align(Alignment.CenterEnd).clickable { onAttach() },
imageVector = Icons.Filled.Image,
contentDescription = "",
)
}
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ data class MessengerScreenState(
val composerState: ComposerState,
)

sealed interface MessengerEvent
sealed interface MessengerEvent {
object SelectImageAttachment : MessengerEvent
}

sealed interface ComposerState {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal class MessengerViewModel(
is MessengerAction.ComposerTextUpdate -> updateState { copy(composerState = ComposerState.Text(action.newValue)) }
MessengerAction.ComposerSendText -> sendMessage()
MessengerAction.ComposerClear -> updateState { copy(composerState = ComposerState.Text("")) }
is MessengerAction.ComposerImageUpdate -> updateState { copy(composerState = ComposerState.Attachments(listOf(action.newValue))) }
}
}

Expand Down Expand Up @@ -100,6 +101,7 @@ internal class MessengerViewModel(
}
}
}

is ComposerState.Attachments -> {
val copy = composerState.copy()
updateState { copy(composerState = ComposerState.Text("")) }
Expand All @@ -123,6 +125,12 @@ internal class MessengerViewModel(
}
}

fun startAttachment() {
viewModelScope.launch {
_events.emit(MessengerEvent.SelectImageAttachment)
}
}

}

private fun MessengerState.latestMessageEventFromOthers(self: UserId) = this.roomState.events
Expand All @@ -133,6 +141,7 @@ private fun MessengerState.latestMessageEventFromOthers(self: UserId) = this.roo

sealed interface MessengerAction {
data class ComposerTextUpdate(val newValue: String) : MessengerAction
data class ComposerImageUpdate(val newValue: MessageAttachment) : MessengerAction
object ComposerSendText : MessengerAction
object ComposerClear : MessengerAction
data class OnMessengerVisible(val roomId: RoomId, val attachments: List<MessageAttachment>?) : MessengerAction
Expand Down
Loading

0 comments on commit 0283073

Please sign in to comment.