-
Notifications
You must be signed in to change notification settings - Fork 895
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2564 from quran/initial_audiobar_work
Add initial AudioBar Composables
- Loading branch information
Showing
15 changed files
with
910 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
plugins { | ||
id("quran.android.library.compose") | ||
} | ||
|
||
android.namespace = "com.quran.mobile.feature.audiobar" | ||
|
||
dependencies { | ||
implementation(project(":common:audio")) | ||
implementation(project(":common:ui:core")) | ||
|
||
// compose | ||
implementation(libs.compose.animation) | ||
implementation(libs.compose.foundation) | ||
implementation(libs.compose.material) | ||
implementation(libs.compose.material3) | ||
implementation(libs.compose.material.icons) | ||
implementation(libs.compose.ui) | ||
implementation(libs.compose.ui.tooling.preview) | ||
debugImplementation(libs.compose.ui.tooling) | ||
|
||
// circuit | ||
implementation(libs.circuit.foundation) | ||
|
||
// coroutines | ||
implementation(libs.kotlinx.coroutines.core) | ||
implementation(libs.kotlinx.coroutines.android) | ||
} |
15 changes: 15 additions & 0 deletions
15
feature/audiobar/src/main/kotlin/com/quran/mobile/feature/audiobar/AudioBarPresenter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.quran.mobile.feature.audiobar | ||
|
||
import androidx.compose.runtime.Composable | ||
import com.quran.labs.androidquran.common.audio.repository.AudioStatusRepository | ||
import com.slack.circuit.runtime.presenter.Presenter | ||
|
||
class AudioBarPresenter( | ||
private val audioStatusRepository: AudioStatusRepository | ||
) : Presenter<AudioBarState> { | ||
|
||
@Composable | ||
override fun present(): AudioBarState { | ||
TODO("Not yet implemented") | ||
} | ||
} |
120 changes: 120 additions & 0 deletions
120
feature/audiobar/src/main/kotlin/com/quran/mobile/feature/audiobar/AudioBarState.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package com.quran.mobile.feature.audiobar | ||
|
||
import com.slack.circuit.runtime.CircuitUiEvent | ||
import com.slack.circuit.runtime.CircuitUiState | ||
|
||
sealed class AudioBarState : CircuitUiState { | ||
sealed class ActivePlayback : AudioBarState() { | ||
abstract val repeat: Int | ||
abstract val speed: Float | ||
abstract val eventSink: (AudioBarEvent.CommonPlaybackEvent) -> Unit | ||
} | ||
|
||
data class Playing( | ||
override val repeat: Int, | ||
override val speed: Float, | ||
override val eventSink: (AudioBarEvent.CommonPlaybackEvent) -> Unit, | ||
val playbackEventSink: (AudioBarEvent.PlayingPlaybackEvent) -> Unit | ||
) : ActivePlayback() | ||
|
||
data class Paused( | ||
override val repeat: Int, | ||
override val speed: Float, | ||
override val eventSink: (AudioBarEvent.CommonPlaybackEvent) -> Unit, | ||
val pausedEventSink: (AudioBarEvent.PausedPlaybackEvent) -> Unit | ||
) : ActivePlayback() | ||
|
||
data class Stopped( | ||
val qariName: String, | ||
val enableRecording: Boolean, | ||
val eventSink: (AudioBarEvent.StoppedPlaybackEvent) -> Unit | ||
) : AudioBarState() | ||
|
||
data class Loading( | ||
val progress: Int, | ||
val message: String, | ||
val eventSink: (AudioBarEvent.CancelablePlaybackEvent) -> Unit | ||
) : AudioBarState() | ||
|
||
data class Error( | ||
val message: String, | ||
val eventSink: (AudioBarEvent.CancelablePlaybackEvent) -> Unit | ||
) : AudioBarState() | ||
|
||
data class Prompt( | ||
val message: String, | ||
val eventSink: (AudioBarEvent.PromptEvent) -> Unit | ||
) : AudioBarState() | ||
|
||
sealed class RecitationState(val isRecitationActive: Boolean) : AudioBarState() { | ||
abstract val eventSink: (AudioBarEvent.CommonRecordingEvent) -> Unit | ||
} | ||
|
||
data class RecitationListening( | ||
override val eventSink: (AudioBarEvent.CommonRecordingEvent) -> Unit, | ||
val listeningEventSink: (AudioBarEvent.RecitationListeningEvent) -> Unit | ||
) : RecitationState(true) | ||
|
||
data class RecitationPlaying( | ||
override val eventSink: (AudioBarEvent.CommonRecordingEvent) -> Unit, | ||
val playingEventSink: (AudioBarEvent.RecitationPlayingEvent) -> Unit | ||
) : RecitationState(false) | ||
|
||
data class RecitationStopped( | ||
override val eventSink: (AudioBarEvent.CommonRecordingEvent) -> Unit, | ||
val stoppedEventSink: (AudioBarEvent.RecitationStoppedEvent) -> Unit | ||
) : RecitationState(false) | ||
} | ||
|
||
sealed class AudioBarEvent : CircuitUiEvent { | ||
sealed class CommonPlaybackEvent : AudioBarEvent() { | ||
data object Stop : CommonPlaybackEvent() | ||
data object Rewind : CommonPlaybackEvent() | ||
data object FastForward : CommonPlaybackEvent() | ||
data class SetSpeed(val speed: Float) : CommonPlaybackEvent() | ||
data class SetRepeat(val repeat: Int) : CommonPlaybackEvent() | ||
} | ||
|
||
sealed class PlayingPlaybackEvent : AudioBarEvent() { | ||
data object Pause : PlayingPlaybackEvent() | ||
} | ||
|
||
sealed class PausedPlaybackEvent : AudioBarEvent() { | ||
data object Play : PausedPlaybackEvent() | ||
} | ||
|
||
sealed class CancelablePlaybackEvent : AudioBarEvent() { | ||
data object Cancel : CancelablePlaybackEvent() | ||
} | ||
|
||
sealed class StoppedPlaybackEvent : AudioBarEvent() { | ||
data object ChangeQari : StoppedPlaybackEvent() | ||
data object Play : StoppedPlaybackEvent() | ||
data object Record : StoppedPlaybackEvent() | ||
} | ||
|
||
sealed class PromptEvent : AudioBarEvent() { | ||
data object Cancel : PromptEvent() | ||
data object Acknowledge : PromptEvent() | ||
} | ||
|
||
sealed class CommonRecordingEvent : AudioBarEvent() { | ||
data object Recitation : CommonRecordingEvent() | ||
data object RecitationLongPress : CommonRecordingEvent() | ||
data object Transcript : CommonRecordingEvent() | ||
} | ||
|
||
sealed class RecitationListeningEvent : AudioBarEvent() { | ||
data object HideVerses : RecitationListeningEvent() | ||
} | ||
|
||
sealed class RecitationPlayingEvent : AudioBarEvent() { | ||
data object EndSession : RecitationPlayingEvent() | ||
data object PauseRecitation : RecitationPlayingEvent() | ||
} | ||
|
||
sealed class RecitationStoppedEvent : AudioBarEvent() { | ||
data object EndSession : RecitationStoppedEvent() | ||
data object PlayRecitation : RecitationStoppedEvent() | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
feature/audiobar/src/main/kotlin/com/quran/mobile/feature/audiobar/ui/AudioBar.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package com.quran.mobile.feature.audiobar.ui | ||
|
||
import android.content.res.Configuration | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.requiredWidthIn | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.tooling.preview.Preview | ||
import androidx.compose.ui.unit.dp | ||
import com.quran.labs.androidquran.common.ui.core.QuranTheme | ||
import com.quran.mobile.feature.audiobar.AudioBarState | ||
|
||
@Composable | ||
fun AudioBar(audioBarState: AudioBarState) { | ||
val modifier = Modifier | ||
.requiredWidthIn(max = 360.dp) | ||
.fillMaxWidth() | ||
.height(56.dp) | ||
|
||
when (audioBarState) { | ||
is AudioBarState.Paused -> PausedAudioBar(state = audioBarState, modifier = modifier) | ||
is AudioBarState.Playing -> PlayingAudioBar(state = audioBarState, modifier = modifier) | ||
is AudioBarState.Error -> ErrorAudioBar(state = audioBarState, modifier = modifier) | ||
is AudioBarState.Loading -> LoadingAudioBar(state = audioBarState, modifier = modifier) | ||
is AudioBarState.Prompt -> PromptingAudioBar(state = audioBarState, modifier = modifier) | ||
is AudioBarState.RecitationListening -> RecitationListeningAudioBar( | ||
state = audioBarState, | ||
modifier = modifier | ||
) | ||
|
||
is AudioBarState.RecitationPlaying -> RecitationPlayingAudioBar( | ||
state = audioBarState, | ||
modifier = modifier | ||
) | ||
|
||
is AudioBarState.RecitationStopped -> RecitationStoppedAudioBar( | ||
state = audioBarState, | ||
modifier = modifier | ||
) | ||
|
||
is AudioBarState.Stopped -> StoppedAudioBar(state = audioBarState, modifier = modifier) | ||
} | ||
} | ||
|
||
@Preview | ||
@Preview("arabic", locale = "ar") | ||
@Preview("dark theme", uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
fun AudioBarStoppedPreview() { | ||
QuranTheme { | ||
Surface { | ||
AudioBar(audioBarState = AudioBarState.Stopped( | ||
qariName = "Abdul Basit", | ||
enableRecording = false, | ||
eventSink = {} | ||
)) | ||
} | ||
} | ||
} | ||
|
||
@Preview | ||
@Preview("arabic", locale = "ar") | ||
@Preview("dark theme", uiMode = Configuration.UI_MODE_NIGHT_YES) | ||
@Composable | ||
fun AudioBarPlayingPreview() { | ||
QuranTheme { | ||
Surface { | ||
AudioBar(audioBarState = AudioBarState.Playing( | ||
repeat = 1, | ||
speed = 1.5f, | ||
eventSink = {}, | ||
playbackEventSink = {} | ||
)) | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
feature/audiobar/src/main/kotlin/com/quran/mobile/feature/audiobar/ui/ErrorAudioBar.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.quran.mobile.feature.audiobar.ui | ||
|
||
import androidx.compose.foundation.layout.IntrinsicSize | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.fillMaxHeight | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.material.icons.filled.Close | ||
import androidx.compose.material3.Divider | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.IconButton | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
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 com.quran.labs.androidquran.common.ui.core.QuranIcons | ||
import com.quran.labs.androidquran.common.ui.core.QuranTheme | ||
import com.quran.mobile.feature.audiobar.AudioBarEvent | ||
import com.quran.mobile.feature.audiobar.AudioBarState | ||
|
||
@Composable | ||
fun ErrorAudioBar(state: AudioBarState.Error, modifier: Modifier = Modifier) { | ||
val sink = state.eventSink | ||
Row( | ||
verticalAlignment = Alignment.CenterVertically, | ||
modifier = modifier.height(IntrinsicSize.Min) | ||
) { | ||
IconButton(onClick = { sink(AudioBarEvent.CancelablePlaybackEvent.Cancel) }) { | ||
Icon(QuranIcons.Close, contentDescription = stringResource(id = android.R.string.cancel)) | ||
} | ||
|
||
Divider( | ||
modifier = Modifier | ||
.fillMaxHeight() | ||
.width(Dp.Hairline) | ||
) | ||
|
||
Text(text = state.message) | ||
} | ||
} | ||
|
||
@Preview | ||
@Composable | ||
fun ErrorAudioBarPreview() { | ||
QuranTheme { | ||
ErrorAudioBar( | ||
state = AudioBarState.Error( | ||
message = "Error message", | ||
eventSink = {} | ||
) | ||
) | ||
} | ||
} |
Oops, something went wrong.