@@ -588,6 +588,27 @@ private synchronized void performRemove(@NotNull Playlist4ApiProto.Rem rem) {
588588 }
589589 }
590590
591+ /**
592+ * Performs the given move operation. This also makes sure that {@link TracksKeeper#tracks} is in a non-shuffled state,
593+ * even if {@link StateWrapper#isShufflingContext()} may return {@code true}. Context will be therefore reshuffled.
594+ */
595+ private synchronized void performMove (@ NotNull Playlist4ApiProto .Mov mov ) {
596+ boolean wasShuffled = false ;
597+ if (isShufflingContext ()) {
598+ wasShuffled = true ;
599+ tracksKeeper .toggleShuffle (false );
600+ }
601+
602+ try {
603+ if (mov .hasFromIndex () && mov .hasToIndex () && mov .hasLength ())
604+ tracksKeeper .moveTracks (mov .getFromIndex (), mov .getToIndex (), mov .getLength ());
605+ else
606+ throw new IllegalArgumentException (TextFormat .shortDebugString (mov ));
607+ } finally {
608+ if (wasShuffled ) tracksKeeper .toggleShuffle (true );
609+ }
610+ }
611+
591612 @ Override
592613 public void onMessage (@ NotNull String uri , @ NotNull Map <String , String > headers , @ NotNull byte [] payload ) throws IOException {
593614 if (uri .startsWith ("hm://playlist/" )) {
@@ -603,6 +624,8 @@ public void onMessage(@NotNull String uri, @NotNull Map<String, String> headers,
603624 performRemove (op .getRem ());
604625 break ;
605626 case MOV :
627+ performMove (op .getMov ());
628+ break ;
606629 case UPDATE_ITEM_ATTRIBUTES :
607630 case UPDATE_LIST_ATTRIBUTES :
608631 LOGGER .warn ("Unsupported operation: " + TextFormat .shortDebugString (op ));
@@ -1247,6 +1270,41 @@ void removeTracks(int from, int length) {
12471270 }
12481271 }
12491272
1273+ /**
1274+ * Moves tracks in the current state. {@link TracksKeeper#tracks} MUST be in a
1275+ * non-shuffled state.
1276+ */
1277+ void moveTracks (int from , int to , int length ) {
1278+ if (from == to )
1279+ return ; // nothing to do
1280+
1281+ for (int counter = length ; counter > 0 ; counter --) {
1282+ ContextTrack toMove = tracks .remove (from );
1283+
1284+ // To index shifts by one if to > from
1285+ int newTo = to - (to > from ? 1 : 0 );
1286+
1287+ tracks .add (newTo , toMove );
1288+
1289+ // Fix up the current track index
1290+ int curr = getCurrentTrackIndex ();
1291+ if (from < curr && newTo >= curr )
1292+ shiftCurrentTrackIndex (-1 );
1293+ else if (from > curr && newTo <= curr )
1294+ shiftCurrentTrackIndex (1 );
1295+ else if (from == curr )
1296+ shiftCurrentTrackIndex (newTo - curr );
1297+
1298+ // Set up for next iteration
1299+ if (from > to ) {
1300+ from ++;
1301+ to ++;
1302+ }
1303+ }
1304+
1305+ updatePrevNextTracks ();
1306+ }
1307+
12501308 synchronized void updateMetadataFor (int index , @ NotNull String key , @ NotNull String value ) {
12511309 ContextTrack .Builder builder = tracks .get (index ).toBuilder ();
12521310 builder .putMetadata (key , value );
0 commit comments