Skip to content

Commit 9f6ba5e

Browse files
committed
issue #164 allow keyboard navigation on playlist. bugfix retrieving items on play all
1 parent abf31ea commit 9f6ba5e

5 files changed

Lines changed: 99 additions & 31 deletions

File tree

yaacc/src/main/java/de/yaacc/browser/ContentListFragment.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,15 +376,13 @@ public void playAllChildsOfParentFrom(DIDLObject item) {
376376
}
377377
ContentDirectoryBrowseResult result = upnpClient.browseSync(new Position(0, item.getParentID(), upnpClient.getProviderDevice().getIdentity().getUdn().getIdentifierString(), item.getTitle()));
378378
if (result == null || (result.getResult() != null && result.getResult().getItems().isEmpty())) {
379-
Log.d(getClass().getName(), "Browse result of parent no direct items found...");
380379
if (result != null && result.getResult() != null && !result.getResult().getContainers().isEmpty()) {
381-
play(upnpClient.initializePlayers(upnpClient.toItemList(result.getResult())));
380+
play(upnpClient.initializePlayers(upnpClient.toItemList(result.getResult(), 3)));
382381
} else {
383382
play(upnpClient.initializePlayers(item));
384383
}
385384
} else {
386385
List<Item> items = result.getResult() == null ? new ArrayList<>() : result.getResult().getItems();
387-
Log.d(getClass().getName(), "Browse result items: " + items.size());
388386
int index = items.indexOf(item);
389387
if (index > 0) {
390388
//sort selected item to the beginning

yaacc/src/main/java/de/yaacc/player/PlaylistDialogFragment.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434
import java.beans.PropertyChangeEvent;
3535
import java.beans.PropertyChangeListener;
36-
import java.util.Collections;
3736

3837
import de.yaacc.R;
3938
import de.yaacc.Yaacc;
@@ -104,14 +103,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
104103
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
105104
int fromPosition = viewHolder.getAdapterPosition();
106105
int toPosition = target.getAdapterPosition();
107-
if (player.isPlaying() && (viewHolder.getAdapterPosition() <= player.getCurrentItemIndex()
108-
|| target.getAdapterPosition() <= player.getCurrentItemIndex())) {
109-
//do not allow to drag current playing item
110-
return false;
111-
}
112-
Collections.swap(player.getItems(), fromPosition, toPosition);
113-
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
114-
return true;
106+
return playlistItemAdapter.moveItem(fromPosition, toPosition);
115107
}
116108

117109
@Override

yaacc/src/main/java/de/yaacc/player/PlaylistItemAdapter.java

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import android.content.Context;
44
import android.graphics.Typeface;
5+
import android.util.Log;
6+
import android.view.KeyEvent;
57
import android.view.LayoutInflater;
68
import android.view.View;
79
import android.view.ViewGroup;
@@ -13,6 +15,7 @@
1315
import androidx.recyclerview.widget.RecyclerView;
1416

1517
import java.util.ArrayList;
18+
import java.util.Collections;
1619
import java.util.List;
1720

1821
import de.yaacc.R;
@@ -24,6 +27,7 @@ public class PlaylistItemAdapter extends RecyclerView.Adapter<PlaylistItemAdapte
2427
private final Context context;
2528
private final Player player;
2629
private RecyclerView listView;
30+
private int selectedForMovePosition = -1; // -1 indicates no item is selected for move
2731

2832
public PlaylistItemAdapter(Context ctx, RecyclerView listView, Player player) {
2933
super();
@@ -64,30 +68,93 @@ public void onBindViewHolder(final PlaylistItemAdapter.ViewHolder holder, final
6468
if (player.isPlaying() && listPosition <= player.getCurrentItemIndex()) {
6569
holder.dragIcon.setImageDrawable(ThemeHelper.tintDrawable(context.getResources().getDrawable(R.drawable.ic_baseline_lock_32, context.getTheme()), context.getTheme()));
6670
holder.deleteIcon.setVisibility(View.GONE);
71+
holder.dragIcon.setFocusable(false); // Cannot move locked items
6772
} else {
6873
holder.dragIcon.setImageDrawable(ThemeHelper.tintDrawable(context.getResources().getDrawable(R.drawable.ic_baseline_drag_indicator_32, context.getTheme()), context.getTheme()));
6974
holder.deleteIcon.setVisibility(View.VISIBLE);
75+
holder.dragIcon.setFocusable(true);
7076
}
7177
holder.deleteIcon.setOnClickListener(l -> removeItem(listPosition));
7278
if (player.isPlaying() && player.getCurrentItemIndex() == listPosition) {
7379
holder.name.setTypeface(null, Typeface.BOLD);
7480
holder.name.setText(item.getTitle() + " ▶");
81+
} else {
82+
holder.name.setTypeface(null, Typeface.NORMAL); // Ensure non-playing items are not bold
7583
}
84+
85+
if (listPosition == selectedForMovePosition) {
86+
// Highlight the selected item (e.g., change background color or add a border)
87+
Log.d(getClass().getName(), "Item selected for keyboard move: " + item.getTitle());
88+
holder.itemView.setBackgroundColor(context.getResources().getColor(R.color.design_default_color_secondary));
89+
} else {
90+
holder.itemView.setBackgroundColor(context.getResources().getColor(android.R.color.transparent));
91+
}
92+
93+
holder.dragIcon.setOnKeyListener((v, keyCode, event) -> {
94+
if (event.getAction() == KeyEvent.ACTION_DOWN) {
95+
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
96+
if (selectedForMovePosition == holder.getAdapterPosition()) { // Use holder.getAdapterPosition() for safety
97+
// Deselect item
98+
int previouslySelected = selectedForMovePosition;
99+
selectedForMovePosition = -1;
100+
notifyItemChanged(previouslySelected); // To remove highlight
101+
return true;
102+
} else if (selectedForMovePosition == -1 && !(player.isPlaying() && holder.getAdapterPosition() <= player.getCurrentItemIndex())) {
103+
// Select item for move
104+
selectedForMovePosition = holder.getAdapterPosition();
105+
notifyItemChanged(selectedForMovePosition); // To add highlight
106+
return true;
107+
}
108+
} else if (selectedForMovePosition == holder.getAdapterPosition()) {
109+
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
110+
moveItem(selectedForMovePosition, selectedForMovePosition - 1);
111+
return true;
112+
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
113+
moveItem(selectedForMovePosition, selectedForMovePosition + 1);
114+
return true;
115+
}
116+
}
117+
}
118+
return false;
119+
});
76120
}
77121

78-
private void removeItem(int listPosition) {
79-
if (player.getItems().size() > listPosition && listPosition > 0) {
80-
player.getItems().remove(listPosition);
81-
setItems(player.getItems(), listPosition);
122+
public boolean moveItem(int fromPosition, int toPosition) {
123+
if (player.isPlaying() && (fromPosition <= player.getCurrentItemIndex() || toPosition <= player.getCurrentItemIndex())) {
124+
return false;
82125
}
126+
if (fromPosition < 0 || fromPosition >= items.size() || toPosition < 0 || toPosition >= items.size()) {
127+
return false;
128+
}
129+
130+
Collections.swap(player.getItems(), fromPosition, toPosition);
131+
Collections.swap(items, fromPosition, toPosition);
132+
notifyItemMoved(fromPosition, toPosition);
133+
134+
// Update selectedForMovePosition if the moved item was the one selected for keyboard move
135+
if (selectedForMovePosition == fromPosition) {
136+
selectedForMovePosition = toPosition;
137+
}
138+
// No notifyItemChanged(fromPosition) or notifyItemChanged(toPosition) here
139+
140+
if (listView != null) {
141+
listView.scrollToPosition(toPosition);
142+
}
143+
return true;
83144
}
84145

85-
public void setItems(List<PlayableItem> items, int removedPosition) {
86-
this.items.clear();
87-
this.items.addAll(items);
88-
notifyItemRemoved(removedPosition);
89-
int itemChangedCount = this.items.size() - removedPosition;
90-
notifyItemRangeChanged(removedPosition, itemChangedCount);
146+
147+
private void removeItem(int listPosition) {
148+
if (player.getItems().size() > listPosition && listPosition >= 0 && !(player.isPlaying() && listPosition <= player.getCurrentItemIndex())) {
149+
player.getItems().remove(listPosition);
150+
// Update local items list to reflect removal before notifying adapter
151+
PlayableItem removedItem = items.remove(listPosition);
152+
notifyItemRemoved(listPosition);
153+
// notifyItemRangeChanged is important if item positions change relative to others
154+
if (listPosition < items.size()) {
155+
notifyItemRangeChanged(listPosition, items.size() - listPosition);
156+
}
157+
}
91158
}
92159

93160
static class ViewHolder extends RecyclerView.ViewHolder {
@@ -99,6 +166,7 @@ static class ViewHolder extends RecyclerView.ViewHolder {
99166
public ViewHolder(@NonNull View itemView) {
100167
super(itemView);
101168
dragIcon = itemView.findViewById(R.id.playlistItemDragIcon);
169+
dragIcon.setFocusable(true); // Make the drag icon focusable
102170
deleteIcon = itemView.findViewById(R.id.playlistItemDeleteIcon);
103171
name = itemView.findViewById(R.id.playlistItemName);
104172
}

yaacc/src/main/java/de/yaacc/upnp/UpnpClient.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,15 @@ public ContentDirectoryBrowseResult browseSync(Device<?, ?, ?> device, String ob
613613
getControlPoint().execute(actionCallback);
614614
while (actionCallback.getStatus() == Status.LOADING && actionCallback.getUpnpFailure() == null) {
615615
//FIXME implement maybe async model?
616+
try {
617+
TimeUnit.MILLISECONDS.sleep(100);
618+
} catch (InterruptedException e) {
619+
Log.e(getClass().getName(), "InterruptedException", e);
620+
}
621+
}
622+
if (actionCallback.getUpnpFailure() != null) {
623+
Log.e(getClass().getName(), "UPnP failure: " + actionCallback.getUpnpFailure());
616624
}
617-
618625
}
619626

620627
if (preferences.getBoolean(getContext().getString(R.string.settings_browse_thumbnails_coverlookup_chkbx), false)) {
@@ -688,7 +695,7 @@ public void searchDevices() {
688695
* @return the player
689696
*/
690697
public List<Player> initializePlayers(DIDLObject didlObject) {
691-
return initializePlayers(toItemList(didlObject));
698+
return initializePlayers(toItemList(didlObject, 3));
692699
}
693700

694701
/**
@@ -879,14 +886,17 @@ public List<Player> getCurrentPlayers(AvTransport transport) {
879886
* @param didlContent the content
880887
* @return all items included in the content
881888
**/
882-
public List<Item> toItemList(DIDLContent didlContent) {
889+
public List<Item> toItemList(DIDLContent didlContent, int depth) {
883890
List<Item> items = new ArrayList<>();
884891
if (didlContent == null) {
885892
return items;
886893
}
887894
items.addAll(didlContent.getItems());
895+
if (depth == 0) {
896+
return items;
897+
}
888898
for (Container c : didlContent.getContainers()) {
889-
items.addAll(toItemList(c));
899+
items.addAll(toItemList(c, depth - 1));
890900
}
891901
return items;
892902
}
@@ -897,14 +907,14 @@ public List<Item> toItemList(DIDLContent didlContent) {
897907
* @param didlObject the content
898908
* @return the list of cling items
899909
*/
900-
public List<Item> toItemList(DIDLObject didlObject) {
910+
public List<Item> toItemList(DIDLObject didlObject, int depth) {
901911
List<Item> items = new ArrayList<>();
902-
if (didlObject instanceof Container) {
912+
if (didlObject instanceof Container && depth != 0) {
903913
DIDLContent content = loadContainer((Container) didlObject);
904914
if (content != null) {
905915
items.addAll(content.getItems());
906916
for (Container includedContainer : content.getContainers()) {
907-
items.addAll(toItemList(includedContainer));
917+
items.addAll(toItemList(includedContainer, depth - 1));
908918
}
909919
}
910920
} else if (didlObject instanceof Item) {
@@ -1510,7 +1520,7 @@ public DeviceDetails getDetails() {
15101520
}
15111521

15121522
public void addToPlaylist(DIDLObject item) {
1513-
List<Item> itemList = toItemList(item);
1523+
List<Item> itemList = toItemList(item, 3);
15141524

15151525
if (getCurrentPlayers().stream().noneMatch(p -> getReceiverDevices().stream()
15161526
.map(d -> d.getIdentity().getUdn().getIdentifierString())

yaacc/src/main/java/de/yaacc/util/FileDownloader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ protected Void doInBackground(DIDLObject... didlObjects) {
6969
}
7070
}
7171
createNotification(storageDir.getAbsolutePath());
72-
List<Item> items = upnpClient.toItemList(didlObjects[0]);
72+
List<Item> items = upnpClient.toItemList(didlObjects[0], 3);
7373
for (Item item : items) {
7474
PlayableItem playableItem = new PlayableItem(item, 0);
7575
String filename = playableItem.getTitle().replace(" ", "");

0 commit comments

Comments
 (0)