Skip to content

Commit abf31ea

Browse files
authored
Merge pull request #171 from tobexyz/feat/issue164
issue #164 fixed touch control in image viewer and some minor cleaning
2 parents f52e7b8 + f35bd14 commit abf31ea

21 files changed

Lines changed: 383 additions & 61 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ tobexyz
1515
alt="Get it on F-Droid"
1616
height="80">](https://f-droid.org/packages/de.yaacc/)
1717

18-
<img src="./docs/screenshots/4.2.x/browse_servers.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.2.x/browse_image_folder.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.2.x/browse_receiver.png" alt= “” width="30%" height="30%">
19-
<img src="./docs/screenshots/4.2.x/music_player.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.2.x/playlist_fully_editable.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.2.x/image_player_show_menu.png" alt= “” width="30%" height="30%">
18+
<img src="./docs/screenshots/4.4.x/browse_servers.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.4.x/browse_image_folder.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.4.x/browse_receiver.png" alt= “” width="30%" height="30%">
19+
<img src="./docs/screenshots/4.2.x/music_player.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.4.x/playlist_fully_editable.png" alt= “” width="30%" height="30%"> <img src="./docs/screenshots/4.4.x/image_player_show_menu.png" alt= “” width="30%" height="30%">
2020

2121
## Description
2222

yaacc/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
buildscript {
22

33
dependencies {
4-
classpath 'com.android.tools.build:gradle:8.12.2'
4+
classpath 'com.android.tools.build:gradle:8.13.0'
55

66
}
77
}

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,54 @@ public BrowseContentItemAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
160160
.inflate(R.layout.browse_content_item, parent, false);
161161
ContentListClickListener bItemClickListener = new ContentListClickListener(upnpClient, contentListFragment, contentList, this);
162162
view.setOnClickListener(bItemClickListener);
163+
view.setOnKeyListener((v, keyCode, event) -> {
164+
if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) return false;
165+
int position = contentList.getChildAdapterPosition(v);
166+
167+
if (position == RecyclerView.NO_POSITION) return false;
168+
BrowseContentItemAdapter.ViewHolder holder = null;
169+
switch (keyCode) {
170+
case android.view.KeyEvent.KEYCODE_DPAD_CENTER:
171+
case android.view.KeyEvent.KEYCODE_ENTER:
172+
// Trigger normal click
173+
v.performClick();
174+
return true;
175+
case android.view.KeyEvent.KEYCODE_DPAD_RIGHT:
176+
// Focus first visible action button
177+
holder = (BrowseContentItemAdapter.ViewHolder) contentList.getChildViewHolder(v);
178+
if (holder.playlistAdd.getVisibility() == View.VISIBLE) {
179+
holder.playlistAdd.requestFocus();
180+
return true;
181+
}
182+
if (holder.download.getVisibility() == View.VISIBLE) {
183+
holder.download.requestFocus();
184+
return true;
185+
}
186+
if (holder.play.getVisibility() == View.VISIBLE) {
187+
holder.play.requestFocus();
188+
return true;
189+
}
190+
return false;
191+
case android.view.KeyEvent.KEYCODE_DPAD_LEFT:
192+
holder = (BrowseContentItemAdapter.ViewHolder) contentList.getChildViewHolder(v);
193+
View focus = v.findFocus();
194+
if (holder.playAll.hasFocus()) {
195+
holder.play.requestFocus();
196+
return true;
197+
}
198+
if (holder.play.hasFocus() && holder.download.getVisibility() == View.VISIBLE) {
199+
holder.download.requestFocus();
200+
return true;
201+
}
202+
if (holder.download.hasFocus() && holder.playlistAdd.getVisibility() == View.VISIBLE) {
203+
holder.playlistAdd.requestFocus();
204+
return true;
205+
}
206+
// Let parent handle; if we are on first column maybe switch tabs later
207+
return true;
208+
}
209+
return false;
210+
});
163211
return new BrowseContentItemAdapter.ViewHolder(view);
164212
}
165213

@@ -377,6 +425,25 @@ public ViewHolder(View itemView) {
377425
playAll = itemView.findViewById(R.id.browseContentItemPlayAll);
378426
download = itemView.findViewById(R.id.browseContentItemDownload);
379427
playlistAdd = itemView.findViewById(R.id.browseContentItemPlaylistAdd);
428+
// Ensure buttons are reachable via DPAD when focused from row
429+
View.OnKeyListener actionKeyListener = (v, keyCode, event) -> {
430+
if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) return false;
431+
switch (keyCode) {
432+
case android.view.KeyEvent.KEYCODE_DPAD_LEFT:
433+
// Move focus back to row root
434+
itemView.requestFocus();
435+
return true;
436+
case android.view.KeyEvent.KEYCODE_DPAD_DOWN:
437+
case android.view.KeyEvent.KEYCODE_DPAD_UP:
438+
// Let RecyclerView handle vertical navigation
439+
return false;
440+
}
441+
return false;
442+
};
443+
play.setOnKeyListener(actionKeyListener);
444+
playAll.setOnKeyListener(actionKeyListener);
445+
download.setOnKeyListener(actionKeyListener);
446+
playlistAdd.setOnKeyListener(actionKeyListener);
380447
}
381448
}
382449
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,30 @@ public ViewHolder onCreateViewHolder(ViewGroup parent,
9090
View view = LayoutInflater.from(parent.getContext())
9191
.inflate(R.layout.browse_device_item, parent, false);
9292
view.setOnClickListener(new ServerListClickListener(deviceList, this, upnpClient, context));
93+
view.setOnKeyListener((v, keyCode, event) -> {
94+
if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) return false;
95+
int position = deviceList.getChildAdapterPosition(v);
96+
if (position == RecyclerView.NO_POSITION) return false;
97+
switch (keyCode) {
98+
case android.view.KeyEvent.KEYCODE_DPAD_CENTER:
99+
case android.view.KeyEvent.KEYCODE_ENTER:
100+
// Trigger normal click
101+
v.performClick();
102+
return true;
103+
case android.view.KeyEvent.KEYCODE_DPAD_RIGHT:
104+
// Focus first visible action button
105+
BrowseDeviceAdapter.ViewHolder holder = (BrowseDeviceAdapter.ViewHolder) deviceList.getChildViewHolder(v);
106+
if (holder.configButton.getVisibility() == View.VISIBLE) {
107+
holder.configButton.requestFocus();
108+
return true;
109+
}
110+
return false;
111+
case android.view.KeyEvent.KEYCODE_DPAD_LEFT:
112+
// Let parent handle; if we are on first column maybe switch tabs later
113+
return false;
114+
}
115+
return false;
116+
});
93117
return new ViewHolder(view, context);
94118
}
95119

@@ -99,6 +123,8 @@ public void onBindViewHolder(final ViewHolder holder, final int listPosition) {
99123
if (device instanceof RemoteDevice) {
100124
holder.scanButton.setVisibility(View.GONE);
101125
holder.scanButtonLabel.setVisibility(View.GONE);
126+
holder.configButton.setVisibility(View.GONE);
127+
holder.scanButton.setFocusable(false);
102128
if (device.hasIcons()) {
103129
Icon[] icons = device.getIcons();
104130
for (Icon icon : icons) {
@@ -117,10 +143,12 @@ public void onBindViewHolder(final ViewHolder holder, final int listPosition) {
117143
} else if (device instanceof LocalDevice) {
118144
//We know our icon
119145
holder.scanButton.setVisibility(View.VISIBLE);
146+
holder.scanButton.setFocusable(true);
120147
holder.scanButton.setImageDrawable(ThemeHelper.tintDrawable(context.getResources().getDrawable(R.drawable.ic_baseline_refresh_48, context.getTheme()), context.getTheme()));
121148
holder.scanButtonLabel.setVisibility(View.VISIBLE);
122149
holder.icon.setImageResource(R.drawable.yaacc48_24_png);
123150
holder.configButton.setVisibility(View.VISIBLE);
151+
holder.configButton.setFocusable(true);
124152
holder.configButton.setImageDrawable(ThemeHelper.tintDrawable(context.getResources().getDrawable(R.drawable.ic_baseline_settings_32, context.getTheme()), context.getTheme()));
125153
}
126154

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,28 @@ public BrowseReceiverDeviceAdapter.ViewHolder onCreateViewHolder(ViewGroup paren
8888
int viewType) {
8989
View view = LayoutInflater.from(parent.getContext())
9090
.inflate(R.layout.browse_receiver_device_item, parent, false);
91-
91+
view.setOnKeyListener((v, keyCode, event) -> {
92+
if (event.getAction() != android.view.KeyEvent.ACTION_DOWN) return false;
93+
94+
int position = devicesListView.getChildAdapterPosition(v);
95+
if (position == RecyclerView.NO_POSITION) return false;
96+
switch (keyCode) {
97+
case android.view.KeyEvent.KEYCODE_DPAD_CENTER:
98+
case android.view.KeyEvent.KEYCODE_ENTER:
99+
// Trigger normal click
100+
v.performClick();
101+
return true;
102+
case android.view.KeyEvent.KEYCODE_DPAD_RIGHT:
103+
// Focus first visible action button
104+
BrowseReceiverDeviceAdapter.ViewHolder holder = (BrowseReceiverDeviceAdapter.ViewHolder) devicesListView.getChildViewHolder(v);
105+
holder.checkBox.requestFocus();
106+
return true;
107+
case android.view.KeyEvent.KEYCODE_DPAD_LEFT:
108+
// Let parent handle; if we are on first column maybe switch tabs later
109+
return false;
110+
}
111+
return false;
112+
});
92113
return new ViewHolder(view);
93114
}
94115

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ public void onClick(View itemView) {
8383
task.execute(ContentItemPlayTask.PLAY_ALL);
8484
}
8585

86-
8786
}
8887
}
8988
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package de.yaacc.browser;
1919

20+
import android.graphics.drawable.Drawable;
2021
import android.os.Bundle;
2122
import android.util.Log;
2223
import android.view.LayoutInflater;
@@ -47,6 +48,7 @@
4748
import de.yaacc.upnp.UpnpClient;
4849
import de.yaacc.upnp.UpnpClientListener;
4950
import de.yaacc.upnp.callback.contentdirectory.ContentDirectoryBrowseResult;
51+
import de.yaacc.util.ThemeHelper;
5052

5153
/**
5254
* Activity for browsing devices and folders. Represents the entrypoint for the whole application.
@@ -91,6 +93,8 @@ public void run() {
9193
private void init(Bundle savedInstanceState, View contentlistView) {
9294
upnpClient = ((Yaacc) requireActivity().getApplicationContext()).getUpnpClient();
9395
backButton = contentlistView.findViewById(R.id.contentListBackButton);
96+
Drawable icon = ThemeHelper.tintDrawable(getResources().getDrawable(R.drawable.ic_baseline_arrow_back_24, getContext().getTheme()), getContext().getTheme());
97+
backButton.setImageDrawable(icon);
9498
backButton.setOnClickListener((v) -> {
9599
onBackPressed();
96100
});
@@ -100,6 +104,9 @@ private void init(Bundle savedInstanceState, View contentlistView) {
100104
topSeperator = contentlistView.findViewById(R.id.contentListTopSeperator);
101105
contentList = contentlistView.findViewById(R.id.contentList);
102106
contentList.setLayoutManager(new LinearLayoutManager(getActivity()));
107+
contentList.setFocusable(true);
108+
contentList.setFocusableInTouchMode(false); // Good for D-Pad primary interaction
109+
contentList.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
103110
upnpClient.addUpnpClientListener(this);
104111
if (getActivity() != null) {
105112
getActivity().runOnUiThread(() -> {
@@ -239,8 +246,6 @@ private void initBrowsItemAdapter(RecyclerView itemList) {
239246
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
240247
super.onScrolled(recyclerView, dx, dy);
241248
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
242-
243-
244249
if (linearLayoutManager != null && linearLayoutManager.findLastCompletelyVisibleItemPosition() == bItemAdapter.getItemCount() - 1) {
245250
if (getActivity() != null) {
246251
getActivity().runOnUiThread(() -> {
@@ -249,8 +254,6 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
249254
});
250255
}
251256
}
252-
253-
254257
}
255258
});
256259
}
@@ -372,9 +375,9 @@ public void playAllChildsOfParentFrom(DIDLObject item) {
372375
return;
373376
}
374377
ContentDirectoryBrowseResult result = upnpClient.browseSync(new Position(0, item.getParentID(), upnpClient.getProviderDevice().getIdentity().getUdn().getIdentifierString(), item.getTitle()));
375-
if (result == null || (result.getResult() != null && result.getResult().getItems().size() == 0)) {
378+
if (result == null || (result.getResult() != null && result.getResult().getItems().isEmpty())) {
376379
Log.d(getClass().getName(), "Browse result of parent no direct items found...");
377-
if (result != null && result.getResult() != null && result.getResult().getContainers().size() > 0) {
380+
if (result != null && result.getResult() != null && !result.getResult().getContainers().isEmpty()) {
378381
play(upnpClient.initializePlayers(upnpClient.toItemList(result.getResult())));
379382
} else {
380383
play(upnpClient.initializePlayers(item));

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ private void init(Bundle savedInstanceState, View view) {
5151
upnpClient = ((Yaacc) requireActivity().getApplicationContext()).getUpnpClient();
5252
contentList = view.findViewById(R.id.playerList);
5353
contentList.setLayoutManager(new LinearLayoutManager(getActivity()));
54+
contentList.setFocusable(true);
55+
contentList.setFocusableInTouchMode(false); // Good for D-Pad primary interaction
56+
contentList.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
5457
upnpClient.addUpnpClientListener(this);
5558
Thread thread = new Thread(this::populatePlayerList);
5659
thread.start();

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
*/
1818
package de.yaacc.browser;
1919

20-
import android.content.SharedPreferences;
2120
import android.graphics.drawable.Drawable;
2221
import android.os.Bundle;
2322
import android.util.Log;
@@ -28,7 +27,6 @@
2827
import android.widget.Toast;
2928

3029
import androidx.fragment.app.Fragment;
31-
import androidx.preference.PreferenceManager;
3230
import androidx.recyclerview.widget.LinearLayoutManager;
3331
import androidx.recyclerview.widget.RecyclerView;
3432

@@ -50,7 +48,6 @@
5048
*/
5149
public class ReceiverListFragment extends Fragment implements
5250
UpnpClientListener, OnBackPressedListener {
53-
private static final String RECEIVER_LIST_NAVIGATOR = "RECEIVER_LIST_NAVIGATOR";
5451
protected RecyclerView contentList;
5552
private UpnpClient upnpClient = null;
5653
private BrowseReceiverDeviceAdapter bDeviceAdapter;
@@ -74,6 +71,9 @@ private void init(Bundle savedInstanceState, View view) {
7471
upnpClient = ((Yaacc) getActivity().getApplicationContext()).getUpnpClient();
7572
contentList = view.findViewById(R.id.receiverList);
7673
contentList.setLayoutManager(new LinearLayoutManager(getActivity()));
74+
contentList.setFocusable(true);
75+
contentList.setFocusableInTouchMode(false); // Good for D-Pad primary interaction
76+
contentList.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
7777
upnpClient.addUpnpClientListener(this);
7878
ImageButton refresh = view.findViewById(R.id.receiverListRefreshButton);
7979
Drawable icon = ThemeHelper.tintDrawable(getResources().getDrawable(R.drawable.ic_baseline_refresh_32, getContext().getTheme()), getContext().getTheme());
@@ -92,16 +92,6 @@ private void init(Bundle savedInstanceState, View view) {
9292
thread.start();
9393
}
9494

95-
/**
96-
* load app preferences
97-
*
98-
* @return app preferences
99-
*/
100-
private SharedPreferences getPreferences() {
101-
return PreferenceManager
102-
.getDefaultSharedPreferences(getActivity().getApplicationContext());
103-
104-
}
10595

10696
@Override
10797
public void onCreate(Bundle savedInstanceState) {

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ private void populateDeviceList() {
106106
} else {
107107
bDeviceAdapter.setDevices(new LinkedList<>(upnpClient.getDevicesProvidingContentDirectoryService()));
108108
}
109-
110109
});
111110

112111
}
@@ -180,6 +179,9 @@ private void init(Bundle savedInstanceState, View view) {
180179
// Define where to show the folder contents for media
181180
contentList = view.findViewById(R.id.serverList);
182181
contentList.setLayoutManager(new LinearLayoutManager(getActivity()));
182+
contentList.setFocusable(true);
183+
contentList.setFocusableInTouchMode(false); // Good for D-Pad primary interaction
184+
contentList.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
183185
ImageButton refresh = view.findViewById(R.id.serverListRefreshButton);
184186
Drawable icon = ThemeHelper.tintDrawable(getResources().getDrawable(R.drawable.ic_baseline_refresh_32, getContext().getTheme()), getContext().getTheme());
185187
refresh.setImageDrawable(icon);
@@ -224,7 +226,7 @@ private void init(Bundle savedInstanceState, View view) {
224226
((Yaacc) getContext().getApplicationContext()).stopShutdownTimer();
225227
}
226228
});
227-
ImageView shutdowwnSettingsImageView = view.findViewById(R.id.serverListSetShutdownTimer);
229+
ImageButton shutdowwnSettingsImageView = view.findViewById(R.id.serverListSetShutdownTimer);
228230
icon = ThemeHelper.tintDrawable(getResources().getDrawable(R.drawable.ic_baseline_settings_32, getContext().getTheme()), getContext().getTheme());
229231
shutdowwnSettingsImageView.setImageDrawable(icon);
230232
shutdowwnSettingsImageView.setOnClickListener(v -> {

0 commit comments

Comments
 (0)