From 2ee36549a3a7f29a55caa70cb3edc23ba2593a70 Mon Sep 17 00:00:00 2001
From: tobexyz <2400680-tobexyz@users.noreply.gitlab.com>
Date: Sat, 21 Feb 2026 23:36:45 +0100
Subject: [PATCH 01/12] chore: README.md
---
README.md | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 1c332492..3b844874 100644
--- a/README.md
+++ b/README.md
@@ -15,8 +15,8 @@ tobexyz
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/de.yaacc/)
-
-
+
+
## Description
@@ -29,10 +29,15 @@ in order to play media from your device on any UPnP renderer in your network.
* UPnP/DLNA Server - share files of your device in the network
* UPnP/DLNA Client - receive media from other devices on your device
* UPnP/DLNA Controller - control media renderer in the network
+* Live streaming - stream system audio and screen to network devices (Android 10+)
+* SAF support - browse and share files from external storage, USB drives, SD cards
+* Lock screen controls - hardware volume buttons and media controls
+* Receiver controls - play/pause/stop directly from receiver tab with status display
* Control multiple media renderer
* Allow download files to the device
* Allow sharing of URLs and sending them to the current media renderers
* Use your device as an proxy if your media renderer can't process https media URLs
+* Battery optimized with smart WiFi lock and foreground service management
## Configuration
From 202536d90a58180560928cafe89a03d44339e492 Mon Sep 17 00:00:00 2001
From: Tobe <40026159+tobexyz@users.noreply.github.com>
Date: Mon, 2 Mar 2026 22:10:02 +0100
Subject: [PATCH 02/12] added stats to README.md (#206)
added stats to README.md
---
README.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/README.md b/README.md
index 3b844874..54a89c1a 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,13 @@ tobexyz
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/de.yaacc/)
+
+
+
+
+)
+
+
From 7a118d96fb0d3cdd337ad3d6cfe1bcfe3024b577 Mon Sep 17 00:00:00 2001
From: tobexyz <2400680-tobexyz@users.noreply.gitlab.com>
Date: Wed, 18 Mar 2026 22:27:00 +0100
Subject: [PATCH 03/12] issue #207 devices not shown up
---
.gitignore | 1 +
.../de/yaacc/browser/ServerListFragment.java | 4 ++++
.../upnp/server/YaaccUpnpServerService.java | 19 +++++++++++++++++++
3 files changed, 24 insertions(+)
diff --git a/.gitignore b/.gitignore
index ed65b2ea..12f3f8f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+reports
.kiro
doxygen
.bundle
diff --git a/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java b/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
index 14ef02b1..cb3c50a0 100644
--- a/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
+++ b/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
@@ -192,6 +192,10 @@ public void receiverDeviceAdded(Device, ?, ?> device) {
@Override
public void onResume() {
super.onResume();
+ // Trigger UPnP discovery to find new devices (fixes Android 9 compatibility)
+ if (upnpClient != null && upnpClient.isInitialized()) {
+ upnpClient.searchDevices();
+ }
//refresh device list
Thread thread = new Thread(this::populateDeviceList);
thread.start();
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
index 3e9c14fb..ab925548 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
@@ -229,6 +229,25 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}
// App is active when service starts
networkDeviceListener.setAppInForeground(true);
+
+ // Trigger UPnP discovery when service starts (fixes Android 9 compatibility)
+ YaaccLogger.d(getClass().getName(), "Triggering UPnP discovery on service start");
+ if (networkDeviceListener.isInitalized()) {
+ new Thread(() -> {
+ try {
+ Thread.sleep(1000); // Wait a bit for network to stabilize
+ UpnpClient client = ((Yaacc) getApplicationContext()).getUpnpClient();
+ if (client != null && client.isInitialized()) {
+ client.searchDevices();
+ YaaccLogger.d(getClass().getName(), "UPnP discovery triggered");
+ }
+ } catch (Exception e) {
+ YaaccLogger.e(getClass().getName(), "Error triggering UPnP discovery", e);
+ }
+ }).start();
+ } else {
+ YaaccLogger.w(getClass().getName(), "NetworkDeviceListener not initialized, discovery will be triggered when network becomes available");
+ }
locaDeviceUuid = preferences.getString(getApplicationContext().getString(R.string.settings_local_device_uuid_key), null);
if (locaDeviceUuid == null) {
From 9fa39613edf74a6599393e6e7420be0ed3c89d7e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 19 Mar 2026 18:11:31 +0100
Subject: [PATCH 04/12] chore(deps-dev): bump json from 2.18.1 to 2.19.2 in
/docs (#208)
Bumps [json](https://github.com/ruby/json) from 2.18.1 to 2.19.2.
- [Release notes](https://github.com/ruby/json/releases)
- [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md)
- [Commits](https://github.com/ruby/json/compare/v2.18.1...v2.19.2)
---
updated-dependencies:
- dependency-name: json
dependency-version: 2.19.2
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
docs/Gemfile.lock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index eb2d0726..abc84e09 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -207,7 +207,7 @@ GEM
gemoji (>= 3, < 5)
html-pipeline (~> 2.2)
jekyll (>= 3.0, < 5.0)
- json (2.18.1)
+ json (2.19.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
From 7251613ff7551d5a3341038312efdec051b9f6ce Mon Sep 17 00:00:00 2001
From: tobexyz <2400680-tobexyz@users.noreply.gitlab.com>
Date: Thu, 19 Mar 2026 19:43:46 +0100
Subject: [PATCH 05/12] issue #207 added interface infromation in notification;
renamed interface filter; added tun interface to blacklist
---
.../de/yaacc/upnp/server/YaaccUpnpServerService.java | 12 ++++++++++++
.../de/yaacc/util/InterfaceResolutionHelper.java | 2 +-
yaacc/src/main/res/values/setting_strings.xml | 6 +++---
yaacc/src/main/res/xml/preference.xml | 8 ++++----
4 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
index ab925548..3e8f7b53 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
@@ -351,6 +351,18 @@ private void showNotification() {
statusBuilder.append("⚠ HTTP");
}
+ // Network interface info
+ if (networkDeviceListener != null && networkDeviceListener.isInitalized()) {
+ try {
+ de.yaacc.util.InterfaceResolutionHelper.InterfaceHolder iface = de.yaacc.util.InterfaceResolutionHelper.getNetworkInterface(this);
+ if (iface.inetAddress != null) {
+ statusBuilder.append(" | ").append(iface.networkInterface.getName()).append(":").append(iface.inetAddress.getHostAddress());
+ }
+ } catch (Exception e) {
+ YaaccLogger.d(getClass().getName(), "Failed to get network interface info", e);
+ }
+ }
+
// Server/Renderer status
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean serverEnabled = preferences.getBoolean(getString(R.string.settings_local_server_chkbx), false);
diff --git a/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java b/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
index c73afcc3..9f98f2e7 100644
--- a/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
+++ b/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
@@ -103,7 +103,7 @@ public static String[] getIfAndIpAddress(Context context) {
public static InterfaceHolder getNetworkInterface(Context context) {
InterfaceHolder result = new InterfaceHolder();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
- List interfaces = new ArrayList<>(List.of(preferences.getString(context.getString(R.string.settings_local_server_if_filter_key), "lo,dummy,rmnet,ccmni").split(",")));
+ List interfaces = new ArrayList<>(List.of(preferences.getString(context.getString(R.string.settings_upnp_if_filter_key), "lo,dummy,rmnet,ccmni,tun").split(",")));
interfaces.remove(""); //remove empty string, if there, otherwise we got into trouble finding an network interface in code below
try {
for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {
diff --git a/yaacc/src/main/res/values/setting_strings.xml b/yaacc/src/main/res/values/setting_strings.xml
index cbf977f9..aeeddbeb 100644
--- a/yaacc/src/main/res/values/setting_strings.xml
+++ b/yaacc/src/main/res/values/setting_strings.xml
@@ -69,7 +69,7 @@
audio_chkbx
video_chkbx
image_chkbx
- local server configuration
+ UPnP server and networking
local server state
local server is started
local server is stopped
@@ -141,8 +141,8 @@
Change tabs on touch
Change tabs on swipe and touch
Swipe on browse
- settings_local_server_if_filter_key
- black listed interfaces for ip address retrieval
+ settings_upnp_if_filter_key
+ blacklisted interfaces for UPnP network interface discovery
settings_media_paths_pref_key
settings_local_server_shutdown_timer_chkbx
settings_saf_tree_uris_pref_key
diff --git a/yaacc/src/main/res/xml/preference.xml b/yaacc/src/main/res/xml/preference.xml
index fd154c06..b1c16812 100644
--- a/yaacc/src/main/res/xml/preference.xml
+++ b/yaacc/src/main/res/xml/preference.xml
@@ -111,7 +111,7 @@
android:key="@string/image_viewer_settings_duration_key"
android:title="@string/image_viewer_settings_duration" />
-
+
+ android:key="@string/settings_upnp_if_filter_key"
+ android:title="@string/settings_upnp_if_filter_names"
+ android:defaultValue="lo,dummy,rmnet,ccmni,tun" />
Date: Thu, 26 Mar 2026 21:55:00 +0100
Subject: [PATCH 06/12] chore(deps-dev): bump activesupport from 7.1.3.2 to
7.2.3.1 in /docs (#209)
Bumps [activesupport](https://github.com/rails/rails) from 7.1.3.2 to 7.2.3.1.
- [Release notes](https://github.com/rails/rails/releases)
- [Changelog](https://github.com/rails/rails/blob/v8.1.2.1/activesupport/CHANGELOG.md)
- [Commits](https://github.com/rails/rails/compare/v7.1.3.2...v7.2.3.1)
---
updated-dependencies:
- dependency-name: activesupport
dependency-version: 7.2.3.1
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
docs/Gemfile.lock | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index abc84e09..c9ae5d1d 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -1,31 +1,34 @@
GEM
remote: https://rubygems.org/
specs:
- activesupport (7.1.3.2)
+ activesupport (7.2.3.1)
base64
+ benchmark (>= 0.3)
bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
+ concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
+ logger (>= 1.4.2)
+ minitest (>= 5.1, < 6)
+ securerandom (>= 0.3)
+ tzinfo (~> 2.0, >= 2.0.5)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
- base64 (0.2.0)
- bigdecimal (3.1.6)
+ base64 (0.3.0)
+ benchmark (0.5.0)
+ bigdecimal (4.0.1)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
colorator (1.1.0)
commonmarker (0.23.10)
- concurrent-ruby (1.2.3)
- connection_pool (2.4.1)
+ concurrent-ruby (1.3.6)
+ connection_pool (3.0.2)
dnsruby (1.70.0)
simpleidn (~> 0.2.1)
- drb (2.2.1)
+ drb (2.2.3)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
@@ -97,7 +100,7 @@ GEM
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.8.0)
- i18n (1.14.4)
+ i18n (1.14.8)
concurrent-ruby (~> 1.0)
jekyll (3.9.5)
addressable (~> 2.4)
@@ -222,8 +225,7 @@ GEM
jekyll (>= 3.5, < 5.0)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
- minitest (5.22.2)
- mutex_m (0.2.0)
+ minitest (5.27.0)
net-http (0.9.1)
uri (>= 0.11.1)
nokogiri (1.19.1-x86_64-linux-gnu)
@@ -250,6 +252,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
+ securerandom (0.4.1)
simpleidn (0.2.1)
unf (~> 0.1.4)
terminal-table (1.8.0)
From 78a4710ca3420100b7a9349230470cd2518a9273 Mon Sep 17 00:00:00 2001
From: tobexyz <40026159+tobexyz@users.noreply.github.com>
Date: Tue, 31 Mar 2026 22:00:09 +0200
Subject: [PATCH 07/12] issue #207 translation
---
.../de/yaacc/browser/ServerListFragment.java | 46 +++++++++----------
.../upnp/server/YaaccUpnpServerService.java | 13 +++---
.../main/res/values-de/setting_strings.xml | 2 +
yaacc/src/main/res/values-de/strings.xml | 2 +
.../main/res/values-es/setting_strings.xml | 2 +
yaacc/src/main/res/values-es/strings.xml | 2 +
.../main/res/values-fr/setting_strings.xml | 2 +
yaacc/src/main/res/values-fr/strings.xml | 2 +
.../main/res/values-nl/setting_strings.xml | 2 +
yaacc/src/main/res/values-nl/strings.xml | 2 +
.../main/res/values-pt/setting_strings.xml | 2 +
yaacc/src/main/res/values-pt/strings.xml | 2 +
.../main/res/values-zh/setting_strings.xml | 2 +
yaacc/src/main/res/values-zh/strings.xml | 2 +
yaacc/src/main/res/values/strings.xml | 2 +
15 files changed, 56 insertions(+), 29 deletions(-)
diff --git a/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java b/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
index cb3c50a0..464f5484 100644
--- a/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
+++ b/yaacc/src/main/java/de/yaacc/browser/ServerListFragment.java
@@ -22,7 +22,6 @@
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import de.yaacc.util.YaaccLogger;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -53,6 +52,7 @@
import de.yaacc.util.FormatHelper;
import de.yaacc.util.ShutdownTimerListener;
import de.yaacc.util.ThemeHelper;
+import de.yaacc.util.YaaccLogger;
/**
* Activity for browsing devices and folders. Represents the entrypoint for the whole application.
@@ -81,7 +81,7 @@ private SharedPreferences getPreferences() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+
// Register callback for MediaProjection stop events
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
de.yaacc.upnp.server.media.MediaProjectionHelper.setStopCallback(() -> {
@@ -90,17 +90,17 @@ public void onCreate(Bundle savedInstanceState) {
getActivity().runOnUiThread(() -> {
android.content.SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(requireContext());
prefs.edit()
- .putBoolean(getString(R.string.settings_local_server_serve_system_audio_chkbx), false)
- .putBoolean(getString(R.string.settings_local_server_serve_screen_cast_chkbx), false)
- .apply();
-
+ .putBoolean(getString(R.string.settings_local_server_serve_system_audio_chkbx), false)
+ .putBoolean(getString(R.string.settings_local_server_serve_screen_cast_chkbx), false)
+ .apply();
+
de.yaacc.browser.BrowseDeviceAdapter.setAudioStreaming(false);
de.yaacc.browser.BrowseDeviceAdapter.setVideoStreaming(false);
-
+
if (bDeviceAdapter != null) {
bDeviceAdapter.notifyDataSetChanged();
}
-
+
YaaccLogger.i(getClass().getName(), "Streaming disabled - MediaProjection stopped");
});
}
@@ -192,7 +192,7 @@ public void receiverDeviceAdded(Device, ?, ?> device) {
@Override
public void onResume() {
super.onResume();
- // Trigger UPnP discovery to find new devices (fixes Android 9 compatibility)
+ // Trigger UPnP discovery to find new devices
if (upnpClient != null && upnpClient.isInitialized()) {
upnpClient.searchDevices();
}
@@ -349,21 +349,21 @@ public void setShutdownTimerRemainingTime(String s) {
public void onTick(long millisUntilFinished) {
setShutdownTimerRemainingTime(FormatHelper.parseMillisToTimeStringTo(millisUntilFinished));
}
-
+
@Override
public void onActivityResult(int requestCode, int resultCode, android.content.Intent data) {
super.onActivityResult(requestCode, resultCode, data);
-
+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q &&
- requestCode == de.yaacc.upnp.server.media.MediaProjectionHelper.REQUEST_CODE_MEDIA_PROJECTION) {
-
+ requestCode == de.yaacc.upnp.server.media.MediaProjectionHelper.REQUEST_CODE_MEDIA_PROJECTION) {
+
if (de.yaacc.upnp.server.media.MediaProjectionHelper.handlePermissionResult(requireContext(), resultCode, data)) {
// Permission granted - enable only the button that requested it
YaaccLogger.i(getClass().getName(), "MediaProjection permission granted");
-
+
android.content.SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(requireContext());
android.content.SharedPreferences.Editor editor = prefs.edit();
-
+
// Enable only the button that requested permission
if (de.yaacc.browser.BrowseDeviceAdapter.isPendingAudioRequest()) {
editor.putBoolean(getString(R.string.settings_local_server_serve_system_audio_chkbx), true);
@@ -374,10 +374,10 @@ public void onActivityResult(int requestCode, int resultCode, android.content.In
de.yaacc.browser.BrowseDeviceAdapter.setVideoStreaming(true);
}
editor.apply();
-
+
// Clear pending flags
de.yaacc.browser.BrowseDeviceAdapter.clearPendingRequests();
-
+
// Refresh the adapter to update button states
if (bDeviceAdapter != null) {
bDeviceAdapter.notifyDataSetChanged();
@@ -385,15 +385,15 @@ public void onActivityResult(int requestCode, int resultCode, android.content.In
} else {
// Permission denied - disable both and clear pending
YaaccLogger.w(getClass().getName(), "MediaProjection permission denied");
-
+
de.yaacc.browser.BrowseDeviceAdapter.clearPendingRequests();
-
+
android.content.SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(requireContext());
prefs.edit()
- .putBoolean(getString(R.string.settings_local_server_serve_system_audio_chkbx), false)
- .putBoolean(getString(R.string.settings_local_server_serve_screen_cast_chkbx), false)
- .apply();
-
+ .putBoolean(getString(R.string.settings_local_server_serve_system_audio_chkbx), false)
+ .putBoolean(getString(R.string.settings_local_server_serve_screen_cast_chkbx), false)
+ .apply();
+
// Update adapter state
if (bDeviceAdapter != null) {
de.yaacc.browser.BrowseDeviceAdapter.setAudioStreaming(false);
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
index 3e8f7b53..b4f19ca6 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
@@ -97,6 +97,7 @@
import de.yaacc.upnp.server.media.SystemAudioCaptureService;
import de.yaacc.upnp.server.renderingcontrol.YaaccAudioRenderingControlService;
import de.yaacc.util.InterfaceResolutionHelper;
+import de.yaacc.util.InterfaceResolutionHelper.InterfaceHolder;
import de.yaacc.util.NotificationId;
import de.yaacc.util.SAFCacheManager;
import de.yaacc.util.YaaccLogger;
@@ -229,8 +230,8 @@ public int onStartCommand(Intent intent, int flags, int startId) {
}
// App is active when service starts
networkDeviceListener.setAppInForeground(true);
-
- // Trigger UPnP discovery when service starts (fixes Android 9 compatibility)
+
+ // Trigger UPnP discovery when service starts
YaaccLogger.d(getClass().getName(), "Triggering UPnP discovery on service start");
if (networkDeviceListener.isInitalized()) {
new Thread(() -> {
@@ -354,8 +355,8 @@ private void showNotification() {
// Network interface info
if (networkDeviceListener != null && networkDeviceListener.isInitalized()) {
try {
- de.yaacc.util.InterfaceResolutionHelper.InterfaceHolder iface = de.yaacc.util.InterfaceResolutionHelper.getNetworkInterface(this);
- if (iface.inetAddress != null) {
+ InterfaceHolder iface = InterfaceResolutionHelper.getNetworkInterface(this);
+ if (iface.inetAddress != null && iface.networkInterface != null) {
statusBuilder.append(" | ").append(iface.networkInterface.getName()).append(":").append(iface.inetAddress.getHostAddress());
}
} catch (Exception e) {
@@ -596,7 +597,7 @@ private void createUpnpDevice() {
// Register BOTH devices as separate top-level devices
registry.addDevice(serverDevice);
registry.addDevice(rendererDevice);
-
+
localDevice = serverDevice; // Track server device for reference
} else {
// Single device type
@@ -1290,7 +1291,7 @@ private void startCombinedCapture() {
return;
}
- android.media.projection.MediaProjection projection = MediaProjectionHelper.getMediaProjection();
+ MediaProjection projection = MediaProjectionHelper.getMediaProjection();
if (projection == null) {
YaaccLogger.w(getClass().getName(), "Cannot start combined capture: no MediaProjection");
diff --git a/yaacc/src/main/res/values-de/setting_strings.xml b/yaacc/src/main/res/values-de/setting_strings.xml
index 28a6c783..653e87cc 100644
--- a/yaacc/src/main/res/values-de/setting_strings.xml
+++ b/yaacc/src/main/res/values-de/setting_strings.xml
@@ -42,6 +42,7 @@
Standardanzeigedauer
Pausendauer
Lokale Serverkonfiguration
+ UPnP-Server und Netzwerk
Lokaler Serverstatus
Lokaler server ist gestartet
Lokaler server ist gestoppt
@@ -111,4 +112,5 @@
Screencast-Stream
Die Bildschirmübertragung ist deaktiviert.
Bildschirmübertragung ist aktiviert
+ Sperrliste für die UPnP-Netzwerkschnittstellenerkennung
diff --git a/yaacc/src/main/res/values-de/strings.xml b/yaacc/src/main/res/values-de/strings.xml
index 4492b950..7cc47949 100644
--- a/yaacc/src/main/res/values-de/strings.xml
+++ b/yaacc/src/main/res/values-de/strings.xml
@@ -38,6 +38,7 @@
Während des Überfliegens ist ein Fehler aufgetreten
Es ist folgender Fehler aufgetreten:
Kein Empfänger gefunden
+ Es ist kein Empfängergerät ausgewählt. Bitte wählen Sie zuerst einen Empfänger im Reiter „Empfänger“ aus.
Keine Aktivität zum Abspielen vom Typ \'%1$s\' gefunden
Dauer der Bildanzeige
Keine gültigen Uri-Informationen zum Anzeigen gefunden
@@ -45,6 +46,7 @@
stoppe
pausiere
vorheriges
+ Bild
nächstes
Beenden
Albumbild
diff --git a/yaacc/src/main/res/values-es/setting_strings.xml b/yaacc/src/main/res/values-es/setting_strings.xml
index e322879e..dbd0b2f8 100644
--- a/yaacc/src/main/res/values-es/setting_strings.xml
+++ b/yaacc/src/main/res/values-es/setting_strings.xml
@@ -57,6 +57,7 @@
Configure la cantidad de subprocesos utilizados para cargar contenido desde el servidor UPnP. Más subprocesos provocan una carga más rápida pero aumentan la posibilidad de errores. Los cambios entran en vigor después del reinicio de yaacc.
no se encontró contenido para navegar
configuración del servidor local
+ Servidor UPnP y redes
estado del servidor local
se inicia el servidor local
el servidor local está detenido
@@ -111,4 +112,5 @@
transmisión de pantalla
La transmisión de pantalla está deshabilitada
La transmisión de pantalla está habilitada
+ Interfaces incluidas en la lista negra para la detección de interfaces de red UPnP
\ No newline at end of file
diff --git a/yaacc/src/main/res/values-es/strings.xml b/yaacc/src/main/res/values-es/strings.xml
index 48ab852b..ce92ee96 100644
--- a/yaacc/src/main/res/values-es/strings.xml
+++ b/yaacc/src/main/res/values-es/strings.xml
@@ -38,6 +38,7 @@
Ocurrió un error desconocido mientras navegaba
Se produjo el siguiente error:
No se encontró ningún dispositivo receptor
+ No se ha seleccionado ningún dispositivo receptor. Seleccione primero un receptor en la pestaña Receptor.
No se encontró actividad para jugar tipo MIME \'%1$s\'
Duración de la estancia de la foto
Configuración de YAACC
@@ -47,6 +48,7 @@
detener
pausa
Previo
+ Imagen
próximo
Salida
reproductor de música yaac
diff --git a/yaacc/src/main/res/values-fr/setting_strings.xml b/yaacc/src/main/res/values-fr/setting_strings.xml
index a73c6ee3..101519a5 100644
--- a/yaacc/src/main/res/values-fr/setting_strings.xml
+++ b/yaacc/src/main/res/values-fr/setting_strings.xml
@@ -57,6 +57,7 @@
Configurez le nombre de threads utilisés pour charger le contenu à partir du serveur UPnP. Plus de threads entraînent un chargement plus rapide mais augmentent le risque d\'erreurs. Les modifications prennent effet après le redémarrage de yaacc.
aucun contenu trouvé pour la navigation
configuration du serveur local
+ Serveur UPnP et réseau
état du serveur local
le serveur local est démarré
le serveur local est arrêté
@@ -111,4 +112,5 @@
flux de diffusion d\'écran
La diffusion d\'écran est désactivée.
La diffusion d\'écran est activée
+ interfaces mises sur liste noire pour la découverte d\'interfaces réseau UPnP
\ No newline at end of file
diff --git a/yaacc/src/main/res/values-fr/strings.xml b/yaacc/src/main/res/values-fr/strings.xml
index e38d7096..90b7f016 100644
--- a/yaacc/src/main/res/values-fr/strings.xml
+++ b/yaacc/src/main/res/values-fr/strings.xml
@@ -38,6 +38,7 @@
Une erreur inconnue s\'est produite lors de la navigation
L\'erreur suivante s\'est produite :
Aucun appareil récepteur trouvé
+ Aucun périphérique récepteur n\'est sélectionné. Veuillez d\'abord sélectionner un récepteur dans l\'onglet Récepteur.
Aucune activité trouvée pour la lecture du type MIME \'%1$s\'
Durée du séjour photo
Paramètres YAACC
@@ -47,6 +48,7 @@
arrêt
pause
précédent
+ Image
suivant
Sortie
Lecteur de musique YAACC
diff --git a/yaacc/src/main/res/values-nl/setting_strings.xml b/yaacc/src/main/res/values-nl/setting_strings.xml
index 7cd24ee3..40b6522a 100644
--- a/yaacc/src/main/res/values-nl/setting_strings.xml
+++ b/yaacc/src/main/res/values-nl/setting_strings.xml
@@ -57,6 +57,7 @@
Configureer het aantal threads dat wordt gebruikt voor het laden van inhoud van de UPnP-server. Meer threads zorgen voor sneller laden, maar vergroten de kans op fouten. Wijzigingen zijn van kracht na het opnieuw opstarten van yaacc.
geen inhoud gevonden om te browsen
lokale serverconfiguratie
+ UPnP-server en netwerken
lokale serverstatus
lokale server wordt gestart
lokale server is gestopt
@@ -111,4 +112,5 @@
schermcaststream
Schermcaststream is uitgeschakeld
Schermcaststream is ingeschakeld
+ Interfaces die op de zwarte lijst staan voor UPnP-netwerkinterfacedetectie
\ No newline at end of file
diff --git a/yaacc/src/main/res/values-nl/strings.xml b/yaacc/src/main/res/values-nl/strings.xml
index 0d591ccb..e5438286 100644
--- a/yaacc/src/main/res/values-nl/strings.xml
+++ b/yaacc/src/main/res/values-nl/strings.xml
@@ -38,6 +38,7 @@
Er is een onbekende fout opgetreden tijdens het browsen
De volgende fout is opgetreden:
Geen ontvangstapparaat gevonden
+ Er is geen ontvanger geselecteerd. Selecteer eerst een ontvanger in het tabblad Ontvanger.
Geen activiteit gevonden voor het spelen van mimetype \'%1$s\'
Duur van het beeldverblijf
YAACC-instellingen
@@ -47,6 +48,7 @@
stop
pauze
vorig
+ Afbeelding
volgende
Uitgang
YAACC-muziekspeler
diff --git a/yaacc/src/main/res/values-pt/setting_strings.xml b/yaacc/src/main/res/values-pt/setting_strings.xml
index 00361bd8..38e4e056 100644
--- a/yaacc/src/main/res/values-pt/setting_strings.xml
+++ b/yaacc/src/main/res/values-pt/setting_strings.xml
@@ -57,6 +57,7 @@
Configure o número de threads usados para carregar conteúdo do servidor UPnP. Mais threads causam um carregamento mais rápido, mas aumentam a possibilidade de erros. As alterações entram em vigor após a reinicialização do yaacc.
nenhum conteúdo encontrado para navegar
configuração do servidor local
+ Servidor UPnP e redes
estado do servidor local
o servidor local está iniciado
o servidor local está parado
@@ -111,4 +112,5 @@
fluxo de transmissão do ecrã
A transmissão de vídeo do ecrã está desativada.
A transmissão de vídeo do ecrã está ativada.
+ Interfaces bloqueadas para descoberta de interfaces de rede UPnP
\ No newline at end of file
diff --git a/yaacc/src/main/res/values-pt/strings.xml b/yaacc/src/main/res/values-pt/strings.xml
index c28d82f2..3e4e50e5 100644
--- a/yaacc/src/main/res/values-pt/strings.xml
+++ b/yaacc/src/main/res/values-pt/strings.xml
@@ -39,6 +39,7 @@
Ocorreu um erro desconhecido durante a navegação
Ocorreu o seguinte erro:
Nenhum dispositivo receptor encontrado
+ Nenhum dispositivo recetor selecionado. Selecione primeiro um recetor no separador Receptor.
Nenhuma atividade encontrada para jogar o tipo MIME \'%1$s\'
Duração da permanência da imagem
Configurações YAACC
@@ -48,6 +49,7 @@
parar
pausa
anterior
+ Imagem
próximo
Saída
Leitor de música YAACC
diff --git a/yaacc/src/main/res/values-zh/setting_strings.xml b/yaacc/src/main/res/values-zh/setting_strings.xml
index ee476729..94e743f2 100644
--- a/yaacc/src/main/res/values-zh/setting_strings.xml
+++ b/yaacc/src/main/res/values-zh/setting_strings.xml
@@ -57,6 +57,7 @@
配置用于加载来自 UPnP 服务器内容的线程数。线程数多加载速度快,但增加错误的可能性。重启 yaacc 后更改生效。
未找到浏览内容
本地服务器配置
+ UPnP 服务器和网络
本地服务器状态
已启动本地服务器
本地服务器已停止
@@ -111,4 +112,5 @@
屏幕投射流
屏幕投射流已禁用
屏幕投射流已启用
+ UPnP 網路介面發現的黑名單接口
diff --git a/yaacc/src/main/res/values-zh/strings.xml b/yaacc/src/main/res/values-zh/strings.xml
index b1407c0c..c9640df1 100644
--- a/yaacc/src/main/res/values-zh/strings.xml
+++ b/yaacc/src/main/res/values-zh/strings.xml
@@ -38,6 +38,7 @@
浏览时发生未知错误
出现下列错误:
未找到接收设备
+ 未選擇接收器設備。請先在「接收器」標籤中選擇接收器。
未找到播放 mimetype \'%1$s\' 的活动
图片停留时长
YAACC 设置
@@ -47,6 +48,7 @@
停止
暂停
上一个
+ 图像
下一个
退出
YAACC 音乐播放器
diff --git a/yaacc/src/main/res/values/strings.xml b/yaacc/src/main/res/values/strings.xml
index bf464c57..34c05b7b 100644
--- a/yaacc/src/main/res/values/strings.xml
+++ b/yaacc/src/main/res/values/strings.xml
@@ -133,4 +133,6 @@
PAUSED
STOPPED
NO MEDIA
+ interfaces blacklisted for obtaining IP addresses
+ local server configuration
From 342ba327ca682813e29022a344ebab32497ba153 Mon Sep 17 00:00:00 2001
From: tobexyz <40026159+tobexyz@users.noreply.github.com>
Date: Tue, 31 Mar 2026 22:14:56 +0200
Subject: [PATCH 08/12] issue #205 prevent npe if no network if is found
---
.../java/de/yaacc/util/InterfaceResolutionHelper.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java b/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
index 9f98f2e7..6f4bd5db 100644
--- a/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
+++ b/yaacc/src/main/java/de/yaacc/util/InterfaceResolutionHelper.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.SharedPreferences;
-import de.yaacc.util.YaaccLogger;
import androidx.preference.PreferenceManager;
@@ -91,8 +90,13 @@ public static String[] getIfAndIpAddress(Context context) {
String hostAddress = null;
String[] result = new String[2];
InterfaceHolder useableInterface = getNetworkInterface(context);
+ if (useableInterface.inetAddress == null || useableInterface.networkInterface == null) {
+ // maybe wifi is off we have to use the loopback device
+ result[0] = "0.0.0.0";
+ result[1] = "lo";
+ return result;
+ }
hostAddress = useableInterface.inetAddress.getHostAddress();
-
// maybe wifi is off we have to use the loopback device
hostAddress = hostAddress == null ? "0.0.0.0" : hostAddress;
result[0] = hostAddress;
From fc478c9a33980d9858e3b0e8aeb7ce28a3596283 Mon Sep 17 00:00:00 2001
From: tobexyz <40026159+tobexyz@users.noreply.github.com>
Date: Tue, 31 Mar 2026 22:50:28 +0200
Subject: [PATCH 09/12] issue #210 fixed NPEs if no usable network interface
found
---
.../de/yaacc/upnp/registry/LocalItems.java | 9 ++++-
.../upnp/server/NetworkDeviceListener.java | 40 ++++++++++++-------
.../upnp/server/YaaccUpnpServerService.java | 17 ++++----
3 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/yaacc/src/main/java/de/yaacc/upnp/registry/LocalItems.java b/yaacc/src/main/java/de/yaacc/upnp/registry/LocalItems.java
index 4d2f942f..c77ac635 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/registry/LocalItems.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/registry/LocalItems.java
@@ -52,6 +52,7 @@
import java.util.concurrent.Executors;
import de.yaacc.upnp.protocol.SendingAsync;
+import de.yaacc.upnp.protocol.UpnpProtocolHandler;
import de.yaacc.util.YaaccLogger;
/**
@@ -321,12 +322,18 @@ public void run() {
} catch (InterruptedException ex) {
YaaccLogger.e(getClass().getName(), "Background execution interrupted: " + ex.getMessage());
}
- registry.getUpnpProtocolHandler().createSendingNotificationAlive(localDevice).run();
+ UpnpProtocolHandler handler = registry.getUpnpProtocolHandler();
+ if (handler != null) {
+ handler.createSendingNotificationAlive(localDevice).run();
+ }
}
});
}
protected void advertiseByebye(final LocalDevice localDevice, boolean asynchronous) {
+ if (registry.getUpnpProtocolHandler() == null) {
+ return;
+ }
final SendingAsync prot = registry.getUpnpProtocolHandler().createSendingNotificationByebye(localDevice);
if (asynchronous) {
registry.executeAsyncProtocol(prot);
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/NetworkDeviceListener.java b/yaacc/src/main/java/de/yaacc/upnp/server/NetworkDeviceListener.java
index 7fdd0baa..78af8d21 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/NetworkDeviceListener.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/NetworkDeviceListener.java
@@ -29,18 +29,18 @@
import androidx.preference.PreferenceManager;
import de.yaacc.R;
-import de.yaacc.util.YaaccLogger;
-
import de.yaacc.upnp.protocol.UpnpProtocolHandler;
import de.yaacc.upnp.registry.Registry;
import de.yaacc.upnp.server.http.HttpRequestSender;
import de.yaacc.upnp.server.udp.MulticastReceiver;
import de.yaacc.upnp.server.udp.UdpTransiver;
+import de.yaacc.util.YaaccLogger;
public class NetworkDeviceListener {
private final Context context;
private final WifiManager wifiManager;
private final Registry registry;
+ private final YaaccUpnpServerService service;
private HttpRequestSender httpRequestSender;
private WifiManager.MulticastLock multicastLock;
private WifiManager.WifiLock wifiLock;
@@ -54,7 +54,8 @@ public class NetworkDeviceListener {
private UpnpProtocolHandler upnpProtocolHandler;
- public NetworkDeviceListener(Context context, Registry registry) throws IllegalStateException {
+ public NetworkDeviceListener(Context context, Registry registry, YaaccUpnpServerService service) throws IllegalStateException {
+ this.service = service;
this.context = context;
this.registry = registry;
this.wifiManager = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE));
@@ -80,6 +81,15 @@ public void onAvailable(@NonNull Network network) {
YaaccLogger.d(getClass().getName(), String.format("Network disabled %s", currentNetwork));
}
currentNetwork = network;
+ if (multicastReceiver == null) {
+ multicastReceiver = new MulticastReceiver();
+ }
+ if (udpTransiver == null) {
+ udpTransiver = new UdpTransiver();
+ }
+ if (httpRequestSender == null) {
+ httpRequestSender = new HttpRequestSender();
+ }
enable();
YaaccLogger.d(getClass().getName(), String.format("Network enabled %s", currentNetwork));
}
@@ -113,6 +123,7 @@ public void enable() {
multicastReceiver.execute();
udpTransiver.init(context, upnpProtocolHandler);
udpTransiver.execute();
+ service.updateNotification();
}
public void disable() {
@@ -126,6 +137,7 @@ public void disable() {
upnpProtocolHandler = null;
multicastReceiver.cancel();
udpTransiver.cancel();
+ service.updateNotification();
}
private boolean isWifi() {
@@ -197,24 +209,24 @@ public boolean isWifiLockHeld() {
private boolean shouldHoldWifiLock() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-
+
// Hold lock if server enabled
boolean serverEnabled = prefs.getBoolean(context.getString(R.string.settings_local_server_chkbx), false);
-
+
// Hold lock if renderer enabled
boolean rendererEnabled = prefs.getBoolean(context.getString(R.string.settings_local_server_receiver_chkbx), false);
-
+
// Hold lock if app in foreground (for discovery)
boolean needsForDiscovery = isAppInForeground;
-
+
boolean shouldHold = serverEnabled || rendererEnabled || needsForDiscovery;
-
- YaaccLogger.d(getClass().getName(),
- "WiFi lock decision: server=" + serverEnabled +
- ", renderer=" + rendererEnabled +
- ", foreground=" + isAppInForeground +
- " -> " + (shouldHold ? "HOLD" : "RELEASE"));
-
+
+ YaaccLogger.d(getClass().getName(),
+ "WiFi lock decision: server=" + serverEnabled +
+ ", renderer=" + rendererEnabled +
+ ", foreground=" + isAppInForeground +
+ " -> " + (shouldHold ? "HOLD" : "RELEASE"));
+
return shouldHold;
}
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
index b4f19ca6..9f6f024e 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
@@ -97,7 +97,6 @@
import de.yaacc.upnp.server.media.SystemAudioCaptureService;
import de.yaacc.upnp.server.renderingcontrol.YaaccAudioRenderingControlService;
import de.yaacc.util.InterfaceResolutionHelper;
-import de.yaacc.util.InterfaceResolutionHelper.InterfaceHolder;
import de.yaacc.util.NotificationId;
import de.yaacc.util.SAFCacheManager;
import de.yaacc.util.YaaccLogger;
@@ -225,7 +224,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
registry = new RegistryImpl();
}
if (networkDeviceListener == null) {
- networkDeviceListener = new NetworkDeviceListener(getApplicationContext(), registry);
+ networkDeviceListener = new NetworkDeviceListener(getApplicationContext(), registry, this);
registry.setUpnpProtocolHandler(networkDeviceListener.getUpnpProtocolHandler());
}
// App is active when service starts
@@ -325,8 +324,8 @@ public void onTaskRemoved(Intent rootIntent) {
YaaccLogger.d(getClass().getName(), "Task removed - app backgrounded");
if (networkDeviceListener != null) {
networkDeviceListener.setAppInForeground(false);
- updateNotification(); // WiFi lock may have changed
}
+ updateNotification(); // WiFi lock may have changed
}
/**
@@ -355,13 +354,17 @@ private void showNotification() {
// Network interface info
if (networkDeviceListener != null && networkDeviceListener.isInitalized()) {
try {
- InterfaceHolder iface = InterfaceResolutionHelper.getNetworkInterface(this);
- if (iface.inetAddress != null && iface.networkInterface != null) {
- statusBuilder.append(" | ").append(iface.networkInterface.getName()).append(":").append(iface.inetAddress.getHostAddress());
+ String[] iface = InterfaceResolutionHelper.getIfAndIpAddress(this);
+ if (!"0.0.0.0".equals(iface[0])) {
+ statusBuilder.append(" | ").append(iface[1]).append(":").append(iface[0]);
+ } else {
+ statusBuilder.append(" | No usable network interface found");
}
} catch (Exception e) {
YaaccLogger.d(getClass().getName(), "Failed to get network interface info", e);
}
+ } else {
+ statusBuilder.append(" | No usable network interface found");
}
// Server/Renderer status
@@ -411,7 +414,7 @@ private void showNotification() {
/**
* Update notification with current server status.
*/
- private void updateNotification() {
+ public void updateNotification() {
showNotification();
}
From 3177584e6c256ee9900e48d9b4674cfdb4990149 Mon Sep 17 00:00:00 2001
From: tobexyz <40026159+tobexyz@users.noreply.github.com>
Date: Sun, 5 Apr 2026 13:08:43 +0200
Subject: [PATCH 10/12] issue #205 changed upnp device consturction if server
and renderer is active. Added HEAD to the allowed upnp request methods
---
.../upnp/protocol/UpnpProtocolHandler.java | 3 +-
.../protocol/sync/ReceivingRetrieval.java | 7 +++
.../upnp/server/YaaccUpnpServerService.java | 62 ++++++++++---------
.../yaacc/upnp/server/udp/UdpTransiver.java | 10 ++-
.../cling/model/message/UpnpRequest.java | 7 ++-
5 files changed, 49 insertions(+), 40 deletions(-)
diff --git a/yaacc/src/main/java/de/yaacc/upnp/protocol/UpnpProtocolHandler.java b/yaacc/src/main/java/de/yaacc/upnp/protocol/UpnpProtocolHandler.java
index f26f3f42..bcf35d57 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/protocol/UpnpProtocolHandler.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/protocol/UpnpProtocolHandler.java
@@ -162,7 +162,8 @@ public Namespace getNamespace() {
public ReceivingSync createReceivingSync(StreamRequestMessage message) throws ProtocolCreationException {
YaaccLogger.v(getClass().getName(), "Creating protocol for incoming synchronous: " + message);
- if (message.getOperation().getMethod().equals(UpnpRequest.Method.GET)) {
+ if (message.getOperation().getMethod().equals(UpnpRequest.Method.GET) ||
+ message.getOperation().getMethod().equals(UpnpRequest.Method.HEAD)) {
return createReceivingRetrieval(message);
diff --git a/yaacc/src/main/java/de/yaacc/upnp/protocol/sync/ReceivingRetrieval.java b/yaacc/src/main/java/de/yaacc/upnp/protocol/sync/ReceivingRetrieval.java
index 784f2804..cf5f3ac5 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/protocol/sync/ReceivingRetrieval.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/protocol/sync/ReceivingRetrieval.java
@@ -101,6 +101,13 @@ protected StreamResponseMessage executeSync() throws IOException {
}
}
+ // HEAD requests don't need content, just return headers
+ if (getInputMessage().getOperation().getMethod().equals(org.fourthline.cling.model.message.UpnpRequest.Method.HEAD)) {
+ StreamResponseMessage response = new StreamResponseMessage(UpnpResponse.Status.OK);
+ response.getHeaders().add(UpnpHeader.Type.SERVER, new ServerHeader());
+ return response;
+ }
+
return createResponse(requestedURI, foundResource);
}
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
index 9f6f024e..ed3c9ef9 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java
@@ -440,6 +440,20 @@ private void initialize() {
YaaccLogger.i(getClass().getName(), "initialize() called");
+ // Wait for NetworkDeviceListener to be initialized
+ if (!networkDeviceListener.isInitalized()) {
+ YaaccLogger.w(getClass().getName(), "NetworkDeviceListener not initialized, waiting...");
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ if (!networkDeviceListener.isInitalized()) {
+ YaaccLogger.e(getClass().getName(), "NetworkDeviceListener still not initialized, aborting");
+ return;
+ }
+ }
+
// Try to create HTTP server with retries
boolean serverStarted = false;
for (int attempt = 1; attempt <= HTTP_SERVER_MAX_RETRIES && !serverStarted; attempt++) {
@@ -535,6 +549,7 @@ private void createUpnpDevice() {
if (localDevice != null && registry.getDevices().contains(localDevice)) {
YaaccLogger.d(this.getClass().getName(), "Removing old device before creating new one");
registry.removeDevice(localDevice);
+
}
try {
@@ -563,45 +578,32 @@ private void createUpnpDevice() {
DeviceIdentity identity = new DeviceIdentity(new UDN(locaDeviceUuid));
- // If both server and renderer are enabled, create TWO separate devices
+ // If both server and renderer are enabled, create nested device structure
if (serverEnabled && providerEnabled && rendererEnabled) {
- // Create MediaServer device
- DeviceDetails serverDetails = new DeviceDetails(
- getLocalServerName() + " - Server",
- new ManufacturerDetails("yaacc.de", "https://www.yaacc.de"),
- new ModelDetails(getLocalServerName() + " - UpnP Server", "Free Android UPnP/DLNA, GNU GPL", versionName),
- URI.create("http://" + InterfaceResolutionHelper.getIpAddress(getApplicationContext()) + ":" + PORT)
- );
-
- LocalDevice serverDevice = new LocalDevice(
- new DeviceIdentity(new UDN(locaDeviceUuid + "-server")),
- new UDADeviceType("MediaServer"),
- serverDetails,
- createDeviceIcons(),
- createMediaServerServices()
- );
-
- // Create MediaRenderer device
- DeviceDetails rendererDetails = new DeviceDetails(
- getLocalServerName() + " - Renderer",
- new ManufacturerDetails("yaacc.de", "https://www.yaacc.de"),
- new ModelDetails(getLocalServerName() + " - UpnP Renderer", "Free Android UPnP/DLNA, GNU GPL", versionName),
- URI.create("http://" + InterfaceResolutionHelper.getIpAddress(getApplicationContext()) + ":" + PORT)
- );
-
+ // Create MediaServer as root with embedded MediaRenderer
LocalDevice rendererDevice = new LocalDevice(
new DeviceIdentity(new UDN(locaDeviceUuid + "-renderer")),
new UDADeviceType("MediaRenderer"),
- rendererDetails,
+ yaaccDetails,
createDeviceIcons(),
createMediaRendererServices()
);
- // Register BOTH devices as separate top-level devices
- registry.addDevice(serverDevice);
- registry.addDevice(rendererDevice);
+ List> serverServices = new ArrayList<>();
+ serverServices.addAll(Arrays.asList(createCoreServices()));
+ serverServices.addAll(Arrays.asList(createMediaServerServices()));
- localDevice = serverDevice; // Track server device for reference
+ LocalDevice serverDevice = new LocalDevice(
+ identity,
+ new UDADeviceType("MediaServer"),
+ yaaccDetails,
+ createDeviceIcons(),
+ serverServices.toArray(new LocalService>[0]),
+ new LocalDevice[]{rendererDevice}
+ );
+
+ registry.addDevice(serverDevice);
+ localDevice = serverDevice;
} else {
// Single device type
if (serverEnabled && providerEnabled) {
diff --git a/yaacc/src/main/java/de/yaacc/upnp/server/udp/UdpTransiver.java b/yaacc/src/main/java/de/yaacc/upnp/server/udp/UdpTransiver.java
index edd4e270..b982a199 100644
--- a/yaacc/src/main/java/de/yaacc/upnp/server/udp/UdpTransiver.java
+++ b/yaacc/src/main/java/de/yaacc/upnp/server/udp/UdpTransiver.java
@@ -1,9 +1,7 @@
package de.yaacc.upnp.server.udp;
import android.content.Context;
-import de.yaacc.util.YaaccLogger;
-import de.yaacc.util.Exceptions;
import org.fourthline.cling.model.UnsupportedDataException;
import org.fourthline.cling.model.message.IncomingDatagramMessage;
import org.fourthline.cling.model.message.OutgoingDatagramMessage;
@@ -18,7 +16,9 @@
import de.yaacc.upnp.protocol.ProtocolCreationException;
import de.yaacc.upnp.protocol.ReceivingAsync;
import de.yaacc.upnp.protocol.UpnpProtocolHandler;
+import de.yaacc.util.Exceptions;
import de.yaacc.util.InterfaceResolutionHelper;
+import de.yaacc.util.YaaccLogger;
public class UdpTransiver {
@@ -46,15 +46,13 @@ public void init(Context context, UpnpProtocolHandler protocolHandler) {
private void initSocket() {
InterfaceResolutionHelper.InterfaceHolder usableInterface = InterfaceResolutionHelper.getNetworkInterface(context);
try {
-
- // TODO: UPNP VIOLATION: The spec does not prohibit using the 1900 port here again, however, the
- // Netgear ReadyNAS miniDLNA implementation will no longer answer if it has to send search response
- // back via UDP unicast to port 1900... so we use an ephemeral port
YaaccLogger.v(getClass().getName(), "Creating bound socket (for datagram input/output) on: " + usableInterface.inetAddress);
InetSocketAddress localAddress = new InetSocketAddress(usableInterface.inetAddress, 0);
socket = new MulticastSocket(localAddress);
socket.setTimeToLive(TTL);
socket.setReceiveBufferSize(262144); // Keep a backlog of incoming datagrams if we are not fast enough
+ socket.setReuseAddress(true);
+
YaaccLogger.v(getClass().getName(), "Socket created and bound to: " + socket.getLocalSocketAddress() + " on interface: " + usableInterface.networkInterface.getDisplayName());
} catch (Exception ex) {
throw new IllegalStateException("Could not initialize " + getClass().getSimpleName() + ": " + ex);
diff --git a/yaacc/src/main/java/org/fourthline/cling/model/message/UpnpRequest.java b/yaacc/src/main/java/org/fourthline/cling/model/message/UpnpRequest.java
index 248752e2..8b53cb8c 100644
--- a/yaacc/src/main/java/org/fourthline/cling/model/message/UpnpRequest.java
+++ b/yaacc/src/main/java/org/fourthline/cling/model/message/UpnpRequest.java
@@ -16,11 +16,11 @@
package org.fourthline.cling.model.message;
import java.net.URI;
-import java.net.URL;
import java.net.URISyntaxException;
+import java.net.URL;
import java.util.HashMap;
-import java.util.Map;
import java.util.Locale;
+import java.util.Map;
/**
* A request message, with a method (GET, POST, NOTIFY, etc).
@@ -32,6 +32,7 @@ public class UpnpRequest extends UpnpOperation {
public static enum Method {
GET("GET"),
+ HEAD("HEAD"),
POST("POST"),
NOTIFY("NOTIFY"),
MSEARCH("M-SEARCH"),
@@ -57,7 +58,7 @@ public String getHttpName() {
public static Method getByHttpName(String httpName) {
if (httpName == null) return UNKNOWN;
- Method m = byName.get(httpName.toUpperCase(Locale.ROOT));
+ Method m = byName.get(httpName.toUpperCase(Locale.ROOT));
return m != null ? m : UNKNOWN;
}
}
From 7a08b8c6cf499f5aca5cfc6b95b78d09fa71393d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 8 Apr 2026 07:40:41 +0000
Subject: [PATCH 11/12] chore(deps): bump addressable from 2.8.6 to 2.9.0 in
/docs
Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.8.6 to 2.9.0.
- [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.8.6...addressable-2.9.0)
---
updated-dependencies:
- dependency-name: addressable
dependency-version: 2.9.0
dependency-type: indirect
...
Signed-off-by: dependabot[bot]
---
docs/Gemfile.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index c9ae5d1d..8acb38da 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -13,8 +13,8 @@ GEM
minitest (>= 5.1, < 6)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
- addressable (2.8.6)
- public_suffix (>= 2.0.2, < 6.0)
+ addressable (2.9.0)
+ public_suffix (>= 2.0.2, < 8.0)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.0.1)
@@ -235,7 +235,7 @@ GEM
sawyer (~> 0.9)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
- public_suffix (5.0.4)
+ public_suffix (5.1.1)
racc (1.8.1)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
From 2ae80dda7b96140dd48b10abe5fa67eb8286a6ec Mon Sep 17 00:00:00 2001
From: tobexyz <40026159+tobexyz@users.noreply.github.com>
Date: Thu, 9 Apr 2026 21:05:46 +0200
Subject: [PATCH 12/12] chore: prepare release
---
fastlane/metadata/android/en-US/changelogs/50001.txt | 3 +++
yaacc/src/main/AndroidManifest.xml | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
create mode 100644 fastlane/metadata/android/en-US/changelogs/50001.txt
diff --git a/fastlane/metadata/android/en-US/changelogs/50001.txt b/fastlane/metadata/android/en-US/changelogs/50001.txt
new file mode 100644
index 00000000..f56e11fe
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/50001.txt
@@ -0,0 +1,3 @@
+Version 5.0.1
+- fixed crash on startup if no network device is found
+- fixed problems with device lookup
diff --git a/yaacc/src/main/AndroidManifest.xml b/yaacc/src/main/AndroidManifest.xml
index c0a75e7a..2e01afec 100644
--- a/yaacc/src/main/AndroidManifest.xml
+++ b/yaacc/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
+ android:versionCode="50001"
+ android:versionName="5.0.1">