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

[PBE-4800] Support Voice Recording in Compose #5404

Merged
merged 87 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
8f7d710
WIP - draw voice recording attachment
kanat Sep 3, 2024
dfb55bb
WIP - make drag work properly
kanat Sep 4, 2024
38a1520
WIP - fix WaveformTrack
kanat Sep 4, 2024
9a8ef78
WIP - use progress to draw Thumb position
kanat Sep 4, 2024
8cfd5c9
WIP - sync currentProgress with progress
kanat Sep 4, 2024
a57a417
WIP - expose drag callbacks
kanat Sep 4, 2024
987127f
WIP - allow replay after completion
kanat Sep 4, 2024
4dd3d22
WIP - fix seekTo logic
kanat Sep 4, 2024
d4f33b0
WIP - fix track color names
kanat Sep 4, 2024
ce98e4c
WIP - add recording content
kanat Sep 5, 2024
5a67624
Merge branch 'develop' into compose/voice-recording
kanat Sep 6, 2024
d75a47f
WIP - introduce attachment factory types
kanat Sep 6, 2024
f775630
WIP - make WaveformSlider more customizable
kanat Sep 6, 2024
0272591
WIP - utilize WaveformSlider in composer
kanat Sep 6, 2024
b5caa0a
WIP - clean up DefaultMessageComposerRecordingContent
kanat Sep 6, 2024
0a0b385
WIP - recompose up DefaultMessageComposerRecordingContent
kanat Sep 6, 2024
3d0b7dc
WIP - add recording STC controls
kanat Sep 6, 2024
7dcbd03
Merge remote-tracking branch 'origin/develop' into compose/voice-reco…
kanat Sep 9, 2024
9db0f5b
WIP - pass the offset changes
kanat Sep 9, 2024
50dfa44
WIP - move mic popup
kanat Sep 9, 2024
3a7e8e9
Merge remote-tracking branch 'origin/develop' into compose/voice-reco…
kanat Sep 10, 2024
b8c82bd
WIP - lock recording
kanat Sep 10, 2024
33ffeb0
WIP - slide to cancel recording
kanat Sep 10, 2024
cf17e45
WIP - remove isRecordingVisible state
kanat Sep 10, 2024
3e0b6e0
WIP - start recording, link vm
kanat Sep 10, 2024
6101a0b
WIP - display timer
kanat Sep 10, 2024
d669a99
WIP - fix waveform slider start position
kanat Sep 11, 2024
7887784
WIP - remove background colors
kanat Sep 11, 2024
9ffb22a
WIP - theming
kanat Sep 11, 2024
08757a3
WIP - theme RecordingControlButtons
kanat Sep 11, 2024
6625397
WIP - margin RecordingSlideToCancelIndicator
kanat Sep 11, 2024
758f435
WIP - theme RecordingContent
kanat Sep 11, 2024
a97ff15
WIP - utilize ComponentSize and ComponentPadding
kanat Sep 11, 2024
6d6f2d0
WIP - utilize AudioRecordingFloatingIconStyle
kanat Sep 11, 2024
e64d8d7
WIP - theme DefaultMessageComposerRecordingContent
kanat Sep 11, 2024
1939613
WIP - restrict movable area
kanat Sep 11, 2024
4717ef9
WIP - support recording progress dragging
kanat Sep 11, 2024
5a47205
WIP - replace pause icon
kanat Sep 11, 2024
ce7d545
WIP - implement Hold To Record popup
kanat Sep 11, 2024
7b5ebc0
Merge remote-tracking branch 'origin/develop' into compose/voice-reco…
kanat Sep 12, 2024
f0a3ce4
WIP - fix touch gestures logic
kanat Sep 12, 2024
e0eff9a
WIP - add AudioRecordingHoldToRecordTheme
kanat Sep 13, 2024
05f1c2a
WIP - imple permission handling
kanat Sep 13, 2024
586cf62
WIP - support ripple effect
kanat Sep 13, 2024
098de99
WIP - remember updated state
kanat Sep 13, 2024
6b30332
WIP - add attachment preview content
kanat Sep 16, 2024
4224ba3
WIP - show progress for a current playing attachment
kanat Sep 16, 2024
5b7c265
WIP - undo stream_ui_audio_record_player.xml changes
kanat Sep 16, 2024
d3cd054
WIP - fix edge cases in attachment preview player
kanat Sep 16, 2024
8637635
WIP - file can handle any
kanat Sep 16, 2024
b9d5b8c
WIP - delete StatefulStreamMediaRecorder
kanat Sep 16, 2024
168a990
WIP - reorganize AudioRecordingTheme
kanat Sep 16, 2024
1bcf1f4
fix spotless
kanat Sep 16, 2024
daa7209
fix detekt
kanat Sep 16, 2024
eef18f9
api dump
kanat Sep 16, 2024
2ce907e
remove logs from recording components
kanat Sep 16, 2024
b3a8f18
add AudioRecordingAttachmentTheme
kanat Sep 17, 2024
040a8b6
add CHANGELOG
kanat Sep 17, 2024
bdbee2a
stop playing when cancelling recording
kanat Sep 17, 2024
0fbe1cc
fix ktlint
kanat Sep 17, 2024
b3c9cec
fix unit tests
kanat Sep 17, 2024
833b1e7
fix spotless
kanat Sep 17, 2024
8249731
fix unit tests
kanat Sep 17, 2024
eb26443
change public API
kanat Sep 17, 2024
d579324
add docs
kanat Sep 17, 2024
460f28c
spotless, api dump
kanat Sep 17, 2024
b989dc3
fix detekt
kanat Sep 17, 2024
a24e807
fix seekTo crash
kanat Sep 17, 2024
bc50e3c
fix ktlint
kanat Sep 17, 2024
54c52fb
disable debugging logs
kanat Sep 17, 2024
2130a48
Merge remote-tracking branch 'origin/develop' into compose/voice-reco…
kanat Sep 18, 2024
bb57107
address PR comments
kanat Sep 18, 2024
f011d58
add AudioRecordingActions
kanat Sep 18, 2024
e02c483
address PR comments
kanat Sep 18, 2024
0180cf0
add AudioRecordingAttachmentPreviewTheme and extract reusable components
kanat Sep 18, 2024
a757387
extract SpeedButton
kanat Sep 18, 2024
6dab600
fix imports
kanat Sep 18, 2024
91b6c86
extract AudioRecordAttachmentContentItemBase
kanat Sep 18, 2024
7a85ae1
add & utilize WaveformSliderLayoutStyle
kanat Sep 18, 2024
f765943
fix spotless, detekt, api_dump
kanat Sep 18, 2024
0338cd8
fix docs
kanat Sep 18, 2024
8325f57
fix the docs
kanat Sep 18, 2024
7150157
fix api dump
kanat Sep 18, 2024
e3dc9ab
fix mic button being stuck
kanat Sep 18, 2024
e3479c3
reuse recordingActions in components
kanat Sep 18, 2024
2a5c342
remove logs
kanat Sep 18, 2024
5d8d716
fix detekt
kanat Sep 18, 2024
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
9 changes: 0 additions & 9 deletions .idea/inspectionProfiles/ktlint.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
### ✅ Added
- Add a default theme for `MediaPreviewActivity` and `MediaGalleryPreviewActivity`. [#5391](https://github.com/GetStream/stream-chat-android/pull/5391)
- Added typing indicator on `ChannelList`. [#5399](https://github.com/GetStream/stream-chat-android/pull/5399)
- Added support for Audio Recording feature. [#5404](https://github.com/GetStream/stream-chat-android/pull/5404)

### ⚠️ Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ object Versions {
internal const val ANDROIDX_ANNOTATIONS = "1.6.0"
internal const val ANDROIDX_APPCOMPAT = "1.6.1"
internal const val ANDROIDX_COMPOSE = "1.6.8"
internal const val ANDROIDX_COMPOSE_CONSTRAINT_LAYOUT = "1.0.1"
internal const val ANDROIDX_COMPOSE_MATERIAL3 = "1.2.1"
internal const val ANDROIDX_CORE_TEST = "2.2.0"
internal const val ANDROIDX_FRAGMENT = "1.6.1"
internal const val ANDROIDX_KTX = "1.10.1"
internal const val ANDROIDX_LIFECYCLE = "2.6.1"
internal const val ANDROIDX_LIFECYCLE = "2.8.5"
internal const val ANDROIDX_NAVIGATION = "2.5.3"
internal const val ANDROIDX_PREFERENCES = "1.2.1"
internal const val ANDROIDX_RECYCLERVIEW = "1.3.1"
Expand Down Expand Up @@ -121,6 +122,7 @@ object Dependencies {
const val composeCompilerPlugin = "org.jetbrains.kotlin:compose-compiler-gradle-plugin:${Versions.KOTLIN}"
const val composeRuntime = "androidx.compose.runtime:runtime:${Versions.ANDROIDX_COMPOSE}"
const val composeUi = "androidx.compose.ui:ui:${Versions.ANDROIDX_COMPOSE}"
const val composeConstraintLayout = "androidx.constraintlayout:constraintlayout-compose:${Versions.ANDROIDX_COMPOSE_CONSTRAINT_LAYOUT}"
const val composeUiTest = "androidx.compose.ui:ui-test-junit4:${Versions.ANDROIDX_COMPOSE}"
const val composeUiTestManifest = "androidx.compose.ui:ui-test-manifest:${Versions.ANDROIDX_COMPOSE}"
const val composeUiTooling = "androidx.compose.ui:ui-tooling:${Versions.ANDROIDX_COMPOSE}"
Expand All @@ -139,6 +141,7 @@ object Dependencies {
const val composeLandscapistPlaceholder = "com.github.skydoves:landscapist-placeholder:${Versions.LANDSCAPIST}"
const val composeLandscapistAnimation = "com.github.skydoves:landscapist-animation:${Versions.LANDSCAPIST}"
const val composeLifecycle = "androidx.lifecycle:lifecycle-runtime-compose:${Versions.ANDROIDX_LIFECYCLE}"
const val composeAndroidLifecycle = "androidx.lifecycle:lifecycle-runtime-compose-android:${Versions.ANDROIDX_LIFECYCLE}"
const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINES}"
const val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINES}"
const val coroutinesTest = "org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.COROUTINES}"
Expand Down
228 changes: 228 additions & 0 deletions docusaurus/docs/Android/compose/guides/enabling-audio-recording.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Voice Recording

## Introduction

The Compose UI Components Chat SDK provides the flexibility to customize the visual presentation of audio recording. You can personalize the way audio recording is displayed and make it more unique according to your preferences and requirements.

## Enabling Audio Recording

`MessageComposer` in Compose UI Components serves as the container where audio recording functionality is rendered.
It provides the necessary components and elements to handle the recording process and display relevant user interface elements related to audio recording.

Let's display enabled audio recording button by setting `AudioRecordingTheme.enabled` to `true` in our `MessageComposerTheme`:

```kotlin
ChatTheme(
messageComposerTheme = MessageComposerTheme.defaultTheme().let {
it.copy(
audioRecording = it.audioRecording.copy(
enabled = true,
),
)
},
) {
// Your UI
}
```

This will show the microphone button in the `MessageComposer` next to the send button.

### Send and Record buttons visibility

If you want to show the send button only when there's text in the input, you can do that by
setting `AudioRecordingTheme.showRecordButtonOverSend` to `true` in
the `MessageComposerTheme`. This way, the send button will only be visible when there's
something typed in the composer.

```kotlin
ChatTheme(
messageComposerTheme = MessageComposerTheme.defaultTheme().let {
it.copy(
audioRecording = it.audioRecording.copy(
enabled = true,
showRecordButtonOverSend = true,
),
)
},
) {
// Your UI
}
```

:::note
Only certain attributes were used here, you can find the rest in
the source code [here](https://github.com/GetStream/stream-chat-android/blob/main/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/messages/composer/AudioRecordingTheme.kt).
:::

## Customization

### Customize `MessageComposer`

You can customize the audio recording theming by overriding `MessageComposerTheme.audioRecording` in `ChatTheme`.

```kotlin
ChatTheme(
messageComposerTheme = MessageComposerTheme.defaultTheme().let {
it.copy(
audioRecording = it.audioRecording.copy(
// Customize the audio recording theme here
),
)
},
) {
// Your UI
}
```

The `MessageComposerTheme` contains the `AudioRecordingTheme` and `AttachmentsPreviewTheme`:

```kotlin
public data class MessageComposerTheme(
//...
val audioRecording: AudioRecordingTheme,
val attachmentsPreview: AttachmentsPreviewTheme,
//...
)
```

The `AudioRecordingTheme` contains the following attributes:

```kotlin
public data class AudioRecordingTheme(
/** Whether the audio recording feature is enabled. */
val enabled: Boolean,

/** Sends the recording on "Complete" button click. If false, attaches it for manual sending. */
val sendOnComplete: Boolean,

/** Whether to show the record button over the send button. */
val showRecordButtonOverSend: Boolean,

/** The style for the record button. */
val recordButton: IconContainerStyle,

/** The theme for the floating icons. */
val floatingIcons: AudioRecordingFloatingIconsTheme,

/** The theme for the slide to cancel component. */
val slideToCancel: AudioRecordingSlideToCancelTheme,

/** The theme for the audio recording playback component. */
val playback: AudioRecordingPlaybackTheme,

/** The theme for the audio recording controls component. */
val controls: AudioRecordingControlsTheme,

/** The theme for the hold to record component. */
val holdToRecord: AudioRecordingHoldToRecordTheme,

/** The theme for the permission rationale component. */
val permissionRationale: AudioRecordingPermissionRationaleTheme,
)
```

The `AttachmentsPreviewTheme` contains the `AudioRecordingAttachmentPreviewTheme`:

```kotlin
public data class AttachmentsPreviewTheme(
//...
val audioRecording: AudioRecordingAttachmentPreviewTheme,
//...
)
```

And `AudioRecordingAttachmentPreviewTheme` contains the following attributes:

```kotlin
public data class AudioRecordingAttachmentPreviewTheme(

/** The size of the audio recording attachment preview. */
public val size: ComponentSize,

/** The padding for the audio recording attachment preview. */
public val padding: ComponentPadding,

/** The style for the play button. */
public val playButton: IconContainerStyle,

/** The style for the pause button. */
public val pauseButton: IconContainerStyle,

/** The style for the timer component. */
public val timerStyle: TextContainerStyle,

/** The style for the waveform slider. */
public val waveformSliderStyle: WaveformSliderLayoutStyle,
)
```

### Customize `MessageList`

To customize the audio recording attachment in the `MessageList`, you can override `MessageTheme.audioRecording` in `ChatTheme`.

```kotlin
ChatTheme(
ownMessageTheme = MessageTheme.defaultOwnTheme().let {
it.copy(
audioRecording = AudioRecordingAttachmentTheme.defaultOwnTheme().copy(
// Customize the audio recording attachment theme here
),
)
},
otherMessageTheme = MessageTheme.defaultOtherTheme().let {
it.copy(
audioRecording = AudioRecordingAttachmentTheme.defaultOtherTheme().copy(
// Customize the audio recording attachment theme here
),
)
},
) {
// Your UI
}
```

The `MessageTheme` contains the `AudioRecordingAttachmentTheme`:

```kotlin
public data class MessageTheme(
//...
val audioRecording: AudioRecordingAttachmentTheme,
//...
)
```

And `AudioRecordingAttachmentTheme` contains the following attributes:

```kotlin
public data class AudioRecordingAttachmentTheme(
/** The size of the audio recording attachment. */
public val size: ComponentSize,

/** The padding for the audio recording attachment. */
public val padding: ComponentPadding,

/** The style for the play button. */
public val playButton: IconContainerStyle,

/** The style for the pause button. */
public val pauseButton: IconContainerStyle,

/** The style for the timer component. */
public val timerStyle: TextContainerStyle,

/** The style for the waveform slider. */
public val waveformSliderStyle: WaveformSliderLayoutStyle,

/** The width of the tail container which holds the speed button and the content type icon. */
public val tailWidth: Dp,

/** The style for the speed button. */
public val speedButton: TextContainerStyle,

/** The style for the content type icon. */
public val contentTypeIcon: IconStyle,
)
```
1 change: 1 addition & 0 deletions docusaurus/sidebars-android.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ module.exports = {
],
},
"compose/guides/custom-translations",
"compose/guides/enabling-audio-recording",
"compose/guides/customizing-image-and-video-previews",
"compose/guides/implementing-own-capabilities",
]
Expand Down
16 changes: 16 additions & 0 deletions stream-chat-android-client/api/stream-chat-android-client.api
Original file line number Diff line number Diff line change
Expand Up @@ -578,14 +578,30 @@ public final class io/getstream/chat/android/client/api/models/WatchChannelReque
}

public final class io/getstream/chat/android/client/audio/NativeMediaPlayer$Companion {
public static final field MEDIA_ERROR_ALREADY_EXISTS I
public static final field MEDIA_ERROR_BAD_INDEX I
public static final field MEDIA_ERROR_BAD_TYPE I
public static final field MEDIA_ERROR_BAD_VALUE I
public static final field MEDIA_ERROR_DEAD_OBJECT I
public static final field MEDIA_ERROR_FAILED_TRANSACTION I
public static final field MEDIA_ERROR_INVALID_OPERATION I
public static final field MEDIA_ERROR_IO I
public static final field MEDIA_ERROR_JPARKS_BROKE_IT I
public static final field MEDIA_ERROR_MALFORMED I
public static final field MEDIA_ERROR_NAME_NOT_FOUND I
public static final field MEDIA_ERROR_NOT_ENOUGH_DATA I
public static final field MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK I
public static final field MEDIA_ERROR_NO_INIT I
public static final field MEDIA_ERROR_NO_MEMORY I
public static final field MEDIA_ERROR_OK I
public static final field MEDIA_ERROR_PERMISSION_DENIED I
public static final field MEDIA_ERROR_SERVER_DIED I
public static final field MEDIA_ERROR_SYSTEM I
public static final field MEDIA_ERROR_TIMED_OUT I
public static final field MEDIA_ERROR_UNKNOWN I
public static final field MEDIA_ERROR_UNKNOWN_ERROR I
public static final field MEDIA_ERROR_UNSUPPORTED I
public static final field MEDIA_ERROR_WOULD_BLOCK I
}

public final class io/getstream/chat/android/client/audio/WaveformExtractorKt {
Expand Down
1 change: 1 addition & 0 deletions stream-chat-android-client/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComplexCondition:NativeMediaPlayerMock.kt$NativeMediaPlayerMock$_state != NativeMediaPlayerState.PREPARED &amp;&amp; _state != NativeMediaPlayerState.PAUSED &amp;&amp; _state != NativeMediaPlayerState.STARTED &amp;&amp; _state != NativeMediaPlayerState.PLAYBACK_COMPLETED</ID>
<ID>ForbiddenComment:MessageUtils.kt$// TODO: type should be a sealed/class or enum at the client level</ID>
<ID>FunctionOnlyReturningConstant:StringExtensionsKtTest.kt$StringExtensionsKtTest.Companion$fun createStreamCdnImageLinkWithoutDimensionParameters()</ID>
<ID>LongMethod:ChatClient.kt$ChatClient$private suspend fun handleEvent(event: ChatEvent)</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2852,6 +2852,11 @@ internal constructor(
return runCatching { userStateService.state.userOrError() }.getOrNull()
}

@InternalStreamChatApi
public fun getCurrentOrStoredUserId(): String? {
return getCurrentUser()?.id ?: getStoredUser()?.id
}

public fun getCurrentToken(): String? {
return runCatching {
when (userStateService.state) {
Expand Down
Loading
Loading