Skip to content

Commit 5f99bb2

Browse files
committed
Restored Add File buttons
- Disabled remove file button while removing - Always keep add file buttons enabled
1 parent 6ad568c commit 5f99bb2

File tree

7 files changed

+109
-94
lines changed

7 files changed

+109
-94
lines changed

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/browsing/files/details/FileDetailsView.kt

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.heightIn
2424
import androidx.compose.foundation.layout.padding
2525
import androidx.compose.foundation.layout.requiredHeight
2626
import androidx.compose.foundation.layout.requiredWidth
27-
import androidx.compose.foundation.layout.size
2827
import androidx.compose.foundation.layout.systemBars
2928
import androidx.compose.foundation.layout.width
3029
import androidx.compose.foundation.lazy.LazyColumn
@@ -113,53 +112,56 @@ private val viewPadding = viewPaddingUnit
113112
private val maxMenuHeight = topMenuHeight + rowPadding
114113

115114
@Composable
116-
private fun PlayableFileMenu(
115+
private fun PlayLastButton(
117116
fileDetailsState: FileDetailsState,
118-
mediaStylePalette: MediaStylePalette,
119-
playableFileDetailsState: PlayableFileDetailsState?,
117+
modifier: Modifier = Modifier,
120118
) {
121-
val modifier = Modifier.requiredWidth(topMenuIconWidth)
122-
val isLoading by fileDetailsState.isLoading.subscribeAsState()
123-
124-
LabelledRefreshButton(
125-
onClick = {
126-
fileDetailsState.promiseLoadedActiveFile()
127-
},
128-
modifier = modifier,
129-
enabled = !isLoading,
130-
)
131-
132119
val addFileToPlaybackLabel = stringResource(id = R.string.btn_add_file_to_playback)
133120
ColumnMenuIcon(
134121
onClick = { fileDetailsState.addToNowPlaying() },
135-
icon = {
136-
Image(
137-
painter = painterResource(id = R.drawable.playlist_plus),
138-
colorFilter = ColorFilter.tint(mediaStylePalette.secondaryTextColor),
139-
contentDescription = addFileToPlaybackLabel,
140-
modifier = Modifier.size(topMenuIconSize),
141-
)
142-
},
122+
iconPainter = painterResource(id = R.drawable.playlist_plus),
123+
contentDescription = addFileToPlaybackLabel,
143124
label = addFileToPlaybackLabel,
144125
labelMaxLines = 1,
145126
modifier = modifier,
146127
)
128+
}
147129

130+
@Composable
131+
private fun PlayNextButton(
132+
fileDetailsState: FileDetailsState,
133+
modifier: Modifier = Modifier,
134+
) {
148135
val playFileNextPlaybackLabel = stringResource(id = R.string.btn_play_file_next)
149136
ColumnMenuIcon(
150137
onClick = { fileDetailsState.playNext() },
151-
icon = {
152-
Image(
153-
painter = painterResource(id = R.drawable.playlist_inner_plus),
154-
colorFilter = ColorFilter.tint(mediaStylePalette.secondaryTextColor),
155-
contentDescription = playFileNextPlaybackLabel,
156-
modifier = Modifier.size(topMenuIconSize),
157-
)
158-
},
138+
iconPainter = painterResource(id = R.drawable.playlist_inner_plus),
139+
contentDescription = playFileNextPlaybackLabel,
159140
label = playFileNextPlaybackLabel,
160141
labelMaxLines = 1,
161142
modifier = modifier,
162143
)
144+
}
145+
146+
@Composable
147+
private fun PlayableFileMenu(
148+
fileDetailsState: FileDetailsState,
149+
playableFileDetailsState: PlayableFileDetailsState?,
150+
) {
151+
val modifier = Modifier.requiredWidth(topMenuIconWidth)
152+
val isLoading by fileDetailsState.isLoading.subscribeAsState()
153+
154+
LabelledRefreshButton(
155+
onClick = {
156+
fileDetailsState.promiseLoadedActiveFile()
157+
},
158+
modifier = modifier,
159+
enabled = !isLoading,
160+
)
161+
162+
PlayLastButton(fileDetailsState, modifier)
163+
164+
PlayNextButton(fileDetailsState, modifier)
163165

164166
if (playableFileDetailsState != null) {
165167
val playLabel = stringResource(id = R.string.btn_play)
@@ -179,7 +181,7 @@ private fun PlayableFileMenu(
179181
private fun NowPlayingFileMenu(
180182
fileDetailsState: FileDetailsState,
181183
nowPlayingFileDetailsState: NowPlayingFileDetailsState,
182-
playableFileDetailsState: PlayableFileDetailsState?,
184+
playableFileDetailsState: PlayableFileDetailsState,
183185
) {
184186
val modifier = Modifier.requiredWidth(topMenuIconWidth)
185187
val isLoading by fileDetailsState.isLoading.subscribeAsState()
@@ -193,6 +195,7 @@ private fun NowPlayingFileMenu(
193195
)
194196

195197
val isInPosition by nowPlayingFileDetailsState.isInPosition.subscribeAsState()
198+
val isRemoving by nowPlayingFileDetailsState.isRemoving.subscribeAsState()
196199
val removeFileLabel = stringResource(id = R.string.btn_remove_file)
197200
ColumnMenuIcon(
198201
onClick = { nowPlayingFileDetailsState.removeFile() },
@@ -201,21 +204,23 @@ private fun NowPlayingFileMenu(
201204
label = removeFileLabel,
202205
labelMaxLines = 1,
203206
modifier = modifier,
204-
enabled = !isLoading && isInPosition
207+
enabled = !isLoading && !isRemoving && isInPosition
205208
)
206209

207-
if (playableFileDetailsState != null) {
208-
val playLabel = stringResource(id = R.string.skip_to)
209-
ColumnMenuIcon(
210-
onClick = { playableFileDetailsState.play() },
211-
iconPainter = painterResource(id = R.drawable.av_next_white),
212-
contentDescription = playLabel,
213-
label = playLabel,
214-
labelMaxLines = 1,
215-
modifier = Modifier.requiredWidth(topMenuIconWidth),
216-
enabled = isInPosition
217-
)
218-
}
210+
val playLabel = stringResource(id = R.string.skip_to)
211+
ColumnMenuIcon(
212+
onClick = { playableFileDetailsState.play() },
213+
iconPainter = painterResource(id = R.drawable.av_next_white),
214+
contentDescription = playLabel,
215+
label = playLabel,
216+
labelMaxLines = 1,
217+
modifier = Modifier.requiredWidth(topMenuIconWidth),
218+
enabled = isInPosition
219+
)
220+
221+
PlayLastButton(fileDetailsState, modifier)
222+
223+
PlayNextButton(fileDetailsState, modifier)
219224
}
220225

221226
@Composable
@@ -913,7 +918,6 @@ fun FileDetailsView(
913918
menuIcons = {
914919
PlayableFileMenu(
915920
viewModel,
916-
coverArtColorState,
917921
playableFileDetailsState
918922
)
919923
}
@@ -925,7 +929,6 @@ fun FileDetailsView(
925929
menuIcons = {
926930
PlayableFileMenu(
927931
viewModel,
928-
coverArtColorState,
929932
playableFileDetailsState,
930933
)
931934
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.lasthopesoftware.bluewater.client.browsing.files.details
22

33
import com.lasthopesoftware.bluewater.shared.observables.InteractionState
4+
import com.namehillsoftware.handoff.promises.Promise
45

56
interface NowPlayingFileDetailsState {
67
val isInPosition: InteractionState<Boolean>
7-
fun removeFile()
8+
fun removeFile(): Promise<Unit>
9+
val isRemoving: InteractionState<Boolean>
810
}

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/browsing/files/details/NowPlayingFileDetailsViewModel.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ class NowPlayingFileDetailsViewModel(
4242
AutoCloseableManager().also(::addCloseable)
4343
}
4444

45+
private val mutableIsRemoving = MutableInteractionState(false)
4546
private val mutableIsLoading = MutableInteractionState(false)
4647
private val mutableIsInPosition = MutableInteractionState(false)
4748

49+
override val isRemoving = mutableIsRemoving.asInteractionState()
4850
override val isInPosition = mutableIsInPosition.asInteractionState()
4951
override val isLoading = autoCloseableManager.manage(LiftedInteractionState(
5052
Observable.combineLatest(listOf(mutableIsLoading.mapNotNull(), fileDetailsState.isLoading.mapNotNull())) { source -> source.any { loading -> loading as Boolean } },
@@ -66,11 +68,14 @@ class NowPlayingFileDetailsViewModel(
6668
.must(this)
6769
}
6870

69-
override fun removeFile() {
70-
val libraryId = activeLibraryId ?: return
71-
val positionedFile = activePositionedFile ?: return
71+
override fun removeFile(): Promise<Unit> {
72+
val libraryId = activeLibraryId ?: return Unit.toPromise()
73+
val positionedFile = activePositionedFile ?: return Unit.toPromise()
7274

73-
controlPlayback.removeFromPlaylistAtPosition(libraryId, positionedFile.playlistPosition)
75+
mutableIsRemoving.value = true
76+
return controlPlayback
77+
.removeFromPlaylistAtPosition(libraryId, positionedFile.playlistPosition)
78+
.must { _ -> mutableIsRemoving.value = false }
7479
}
7580

7681
override fun play() {

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/playback/service/ControlPlaybackService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ interface ControlPlaybackService {
3030

3131
fun addAfterNowPlayingFile(libraryId: LibraryId, serviceFile: ServiceFile)
3232

33-
fun removeFromPlaylistAtPosition(libraryId: LibraryId, position: Int)
33+
fun removeFromPlaylistAtPosition(libraryId: LibraryId, position: Int): Promise<Unit>
3434

3535
fun setRepeating(libraryId: LibraryId)
3636

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/playback/service/PlaybackService.kt

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,13 @@ import java.util.concurrent.TimeoutException
238238
)
239239
}
240240

241-
fun removeFileAtPositionFromPlaylist(context: Context, libraryId: LibraryId, filePosition: Int) {
242-
context.safelyStartService(
243-
getNewSelfIntent(
244-
context,
245-
PlaybackEngineAction.RemoveFileAtPosition(libraryId, filePosition)
246-
)
247-
)
248-
}
241+
fun removeFileAtPositionFromPlaylist(context: Context, libraryId: LibraryId, filePosition: Int): Promise<Unit> =
242+
context.promiseBoundService<PlaybackService>()
243+
.eventually { h ->
244+
h.service
245+
.removeFileAtPosition(libraryId, filePosition)
246+
.must { _ -> h.close() }
247+
}
249248

250249
fun moveFile(context: Context, libraryId: LibraryId, filePosition: Int, newPosition: Int) {
251250
context.safelyStartService(
@@ -669,25 +668,6 @@ import java.util.concurrent.TimeoutException
669668
}
670669
}
671670
}
672-
is PlaybackEngineAction.RemoveFileAtPosition -> {
673-
val (libraryId, filePosition) = playbackEngineAction
674-
675-
restorePlaybackServices(libraryId)
676-
.eventually { it.playlistFiles.removeFileAtPosition(filePosition) }
677-
.then { _ ->
678-
applicationMessageBus.sendMessage(LibraryPlaybackMessage.PlaylistChanged(libraryId))
679-
}
680-
.eventually {
681-
mainLoopHandlerExecutor.preparePromise {
682-
Toast.makeText(
683-
this,
684-
getText(R.string.lbl_song_removed_from_now_playing),
685-
Toast.LENGTH_SHORT
686-
).show()
687-
}
688-
}
689-
.unitResponse()
690-
}
691671
is PlaybackEngineAction.MoveFile -> {
692672
val (libraryId, filePosition, newPosition) = playbackEngineAction
693673

@@ -833,6 +813,23 @@ import java.util.concurrent.TimeoutException
833813

834814
override fun onBind(intent: Intent): IBinder? = binder
835815

816+
private fun removeFileAtPosition(libraryId: LibraryId, playlistPosition: Int) =
817+
restorePlaybackServices(libraryId)
818+
.eventually { it.playlistFiles.removeFileAtPosition(playlistPosition) }
819+
.then { _ ->
820+
applicationMessageBus.sendMessage(LibraryPlaybackMessage.PlaylistChanged(libraryId))
821+
}
822+
.eventually {
823+
mainLoopHandlerExecutor.preparePromise {
824+
Toast.makeText(
825+
this,
826+
getText(R.string.lbl_song_removed_from_now_playing),
827+
Toast.LENGTH_SHORT
828+
).show()
829+
}
830+
}
831+
.unitResponse()
832+
836833
private fun startNewPlaylist(libraryId: LibraryId, playlist: List<ServiceFile>, playlistPosition: Int): Promise<Unit> {
837834
activeLibraryId = libraryId
838835
val playbackState = promisedPlaybackServices.value
@@ -1092,12 +1089,6 @@ import java.util.concurrent.TimeoutException
10921089
override val requestCode = 9
10931090
}
10941091

1095-
@Parcelize
1096-
data class RemoveFileAtPosition(override val libraryId: LibraryId, val position: Int) : PlaybackEngineAction {
1097-
@IgnoredOnParcel
1098-
override val requestCode = 10
1099-
}
1100-
11011092
@Parcelize
11021093
data class MoveFile(override val libraryId: LibraryId, val from: Int, val to: Int) : PlaybackEngineAction {
11031094
@IgnoredOnParcel

projectBlueWater/src/main/java/com/lasthopesoftware/bluewater/client/playback/service/PlaybackServiceController.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ class PlaybackServiceController(private val context: Context) : ControlPlaybackS
4242
PlaybackService.addAfterNowPlayingFile(context, libraryId, serviceFile)
4343
}
4444

45-
override fun removeFromPlaylistAtPosition(libraryId: LibraryId, position: Int) {
45+
override fun removeFromPlaylistAtPosition(libraryId: LibraryId, position: Int): Promise<Unit> =
4646
PlaybackService.removeFileAtPositionFromPlaylist(context, libraryId, position)
47-
}
4847

4948
override fun setRepeating(libraryId: LibraryId) = PlaybackService.setRepeating(context, libraryId)
5049

projectBlueWater/src/test/java/com/lasthopesoftware/bluewater/client/browsing/files/details/GivenAFile/When removing the file from now playing.kt

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class `When removing the file from now playing` {
3434
every { removeFromPlaylistAtPosition(any(), any()) } answers {
3535
removedLibraryId = firstArg()
3636
removedPosition = lastArg()
37+
Unit.toPromise()
3738
}
3839
},
3940
mockk {
@@ -62,17 +63,20 @@ class `When removing the file from now playing` {
6263
}
6364

6465
private var isLoadingStates = mutableListOf<Boolean>()
66+
private var isRemovingStates = mutableListOf<Boolean>()
6567
private var isInPositionStates = mutableListOf<Boolean>()
6668

6769
@BeforeAll
6870
fun act() {
6971
viewModel.isLoading.mapNotNull().subscribe(isLoadingStates::add).toCloseable().use {
70-
viewModel.isInPosition.mapNotNull().subscribe(isInPositionStates::add).toCloseable().use {
71-
viewModel
72-
.load(LibraryId(libraryId), PositionedFile(501, ServiceFile(serviceFileId)))
73-
.toExpiringFuture()
74-
.get()
75-
viewModel.removeFile()
72+
viewModel.isRemoving.mapNotNull().subscribe(isRemovingStates::add).toCloseable().use {
73+
viewModel.isInPosition.mapNotNull().subscribe(isInPositionStates::add).toCloseable().use {
74+
viewModel
75+
.load(LibraryId(libraryId), PositionedFile(501, ServiceFile(serviceFileId)))
76+
.toExpiringFuture()
77+
.get()
78+
viewModel.removeFile().toExpiringFuture().get()
79+
}
7680
}
7781
}
7882
}
@@ -82,6 +86,17 @@ class `When removing the file from now playing` {
8286
assertThat(isLoadingStates).isEqualTo(listOf(true))
8387
}
8488

89+
@Test
90+
fun `then is removing changes correctly`() {
91+
assertThat(isRemovingStates).isEqualTo(
92+
listOf(
93+
false,
94+
true,
95+
false,
96+
)
97+
)
98+
}
99+
85100
@Test
86101
fun `then the file is in position changes correctly`() {
87102
assertThat(isInPositionStates).isEqualTo(listOf(false))

0 commit comments

Comments
 (0)