Skip to content

Commit 953e9e4

Browse files
committed
feat: allow usage of local mobil hotpot #218
1 parent e35b41b commit 953e9e4

3 files changed

Lines changed: 153 additions & 36 deletions

File tree

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import android.media.MediaMetadataRetriever;
2929
import android.net.Uri;
3030
import android.os.IBinder;
31-
import de.yaacc.util.YaaccLogger;
3231
import android.webkit.MimeTypeMap;
3332

3433
import androidx.annotation.NonNull;
@@ -57,10 +56,7 @@
5756
import org.fourthline.cling.model.types.UDAServiceId;
5857
import org.fourthline.cling.model.types.UDAServiceType;
5958
import org.fourthline.cling.model.types.UDN;
60-
import de.yaacc.upnp.registry.Registry;
61-
import de.yaacc.upnp.registry.RegistryListener;
6259
import org.fourthline.cling.support.contentdirectory.DIDLParser;
63-
import de.yaacc.upnp.callback.contentdirectory.Browse.Status;
6460
import org.fourthline.cling.support.model.BrowseFlag;
6561
import org.fourthline.cling.support.model.DIDLContent;
6662
import org.fourthline.cling.support.model.DIDLObject;
@@ -73,10 +69,6 @@
7369
import org.fourthline.cling.support.model.item.Item;
7470
import org.fourthline.cling.support.model.item.MusicTrack;
7571
import org.fourthline.cling.support.model.item.VideoItem;
76-
import de.yaacc.upnp.callback.renderingcontrol.GetMute;
77-
import de.yaacc.upnp.callback.renderingcontrol.GetVolume;
78-
import de.yaacc.upnp.callback.renderingcontrol.SetMute;
79-
import de.yaacc.upnp.callback.renderingcontrol.SetVolume;
8072
import org.seamless.util.MimeType;
8173

8274
import java.io.IOException;
@@ -106,15 +98,23 @@
10698
import de.yaacc.player.Player;
10799
import de.yaacc.player.PlayerService;
108100
import de.yaacc.player.YaaccMediaRouteProvider;
101+
import de.yaacc.upnp.callback.contentdirectory.Browse.Status;
109102
import de.yaacc.upnp.callback.contentdirectory.ContentDirectoryBrowseActionCallback;
110103
import de.yaacc.upnp.callback.contentdirectory.ContentDirectoryBrowseResult;
104+
import de.yaacc.upnp.callback.renderingcontrol.GetMute;
105+
import de.yaacc.upnp.callback.renderingcontrol.GetVolume;
106+
import de.yaacc.upnp.callback.renderingcontrol.SetMute;
107+
import de.yaacc.upnp.callback.renderingcontrol.SetVolume;
111108
import de.yaacc.upnp.protocol.async.SendingSearch;
109+
import de.yaacc.upnp.registry.Registry;
110+
import de.yaacc.upnp.registry.RegistryListener;
112111
import de.yaacc.upnp.server.YaaccUpnpServerService;
113112
import de.yaacc.upnp.server.avtransport.AvTransport;
114113
import de.yaacc.util.FileDownloader;
115114
import de.yaacc.util.FormatHelper;
116115
import de.yaacc.util.InterfaceResolutionHelper;
117116
import de.yaacc.util.Watchdog;
117+
import de.yaacc.util.YaaccLogger;
118118

119119
/**
120120
* A client facade to the upnp lookup and access framework. This class provides
@@ -157,12 +157,12 @@ public boolean initialize(Context context) {
157157
if (context != null) {
158158
this.context = context;
159159
this.preferences = PreferenceManager.getDefaultSharedPreferences(context);
160-
160+
161161
// Initialize MediaRouteProvider for UPnP devices
162162
mediaRouteProvider = new YaaccMediaRouteProvider(context, this);
163163
androidx.mediarouter.media.MediaRouter.getInstance(context)
164-
.addProvider(mediaRouteProvider);
165-
164+
.addProvider(mediaRouteProvider);
165+
166166
// FIXME check if this is right: Context.BIND_AUTO_CREATE kills the
167167
// service after closing the activity
168168
return context.bindService(new Intent(context, YaaccUpnpServerService.class), this, Context.BIND_AUTO_CREATE);
@@ -498,7 +498,7 @@ public void addUpnpClientListener(UpnpClientListener listener) {
498498
return getLocalDummyDevice();
499499
}
500500
if (isInitialized()) {
501-
return getRegistry().getDevice(new UDN(identifier), true);
501+
return getRegistry().getDevice(new UDN(identifier), false);
502502
}
503503
return null;
504504
}

yaacc/src/main/java/de/yaacc/upnp/server/NetworkDeviceListener.java

Lines changed: 126 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
*/
1919
package de.yaacc.upnp.server;
2020

21+
import android.content.BroadcastReceiver;
2122
import android.content.Context;
23+
import android.content.Intent;
24+
import android.content.IntentFilter;
2225
import android.content.SharedPreferences;
2326
import android.net.ConnectivityManager;
2427
import android.net.Network;
@@ -53,18 +56,34 @@ public class NetworkDeviceListener {
5356
private UdpTransiver udpTransiver;
5457
private UpnpProtocolHandler upnpProtocolHandler;
5558

59+
private final Object lock = new Object();
60+
private boolean isHotspotEnabled = false;
61+
private BroadcastReceiver networkStateReceiver;
62+
63+
private boolean isHotspotEnabled() {
64+
try {
65+
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
66+
java.lang.reflect.Method method = wifiManager.getClass().getDeclaredMethod("isWifiApEnabled");
67+
return (Boolean) method.invoke(wifiManager);
68+
} catch (Exception e) {
69+
YaaccLogger.w(getClass().getName(), "Failed to check hotspot state", e);
70+
return false;
71+
}
72+
}
5673

5774
public NetworkDeviceListener(Context context, Registry registry, YaaccUpnpServerService service) throws IllegalStateException {
5875
this.service = service;
5976
this.context = context;
6077
this.registry = registry;
6178
this.wifiManager = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE));
79+
// Check hotspot state on startup
80+
isHotspotEnabled = isHotspotEnabled();
6281
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
6382
if (!isCellular()) {
6483
currentNetwork = connectivityManager.getActiveNetwork();
6584

6685
}
67-
if (currentNetwork != null) {
86+
if (currentNetwork != null || isWifiOrHotspot()) {
6887
multicastReceiver = new MulticastReceiver();
6988
udpTransiver = new UdpTransiver();
7089
httpRequestSender = new HttpRequestSender();
@@ -107,37 +126,74 @@ public void onLost(@NonNull Network network) {
107126
}
108127
}
109128
});
129+
130+
// Register for WiFi and access point state changes
131+
networkStateReceiver = new BroadcastReceiver() {
132+
@Override
133+
public void onReceive(Context context, Intent intent) {
134+
String action = intent.getAction();
135+
if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
136+
YaaccLogger.d(getClass().getName(), "WiFi state changed");
137+
onNetworkStateChange();
138+
} else if ("android.net.wifi.WIFI_AP_STATE_CHANGED".equals(action)) {
139+
int apState = intent.getIntExtra("wifi_state", 0);
140+
isHotspotEnabled = (apState == 13); // WIFI_AP_STATE_ENABLED
141+
YaaccLogger.d(getClass().getName(), "Access point state changed: " + (isHotspotEnabled ? "enabled" : "disabled"));
142+
onNetworkStateChange();
143+
}
144+
}
145+
};
146+
147+
IntentFilter filter = new IntentFilter();
148+
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
149+
filter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
150+
context.registerReceiver(networkStateReceiver, filter);
110151
}
111152

112153
public void enable() {
113-
YaaccLogger.v(getClass().getName(), "in android router enable");
114-
115-
// Enable multicast on the WiFi network interface,
116-
// requires android.permission.CHANGE_WIFI_MULTICAST_STATE
117-
if (isWifi()) {
118-
setWiFiMulticastLock(true);
119-
setWifiLock(true);
154+
synchronized (lock) {
155+
YaaccLogger.v(getClass().getName(), "in android router enable");
156+
157+
// Enable multicast on the WiFi network interface,
158+
// requires android.permission.CHANGE_WIFI_MULTICAST_STATE
159+
if (isWifiOrHotspot()) {
160+
setWiFiMulticastLock(true);
161+
setWifiLock(true);
162+
}
163+
if (multicastReceiver == null) multicastReceiver = new MulticastReceiver();
164+
if (udpTransiver == null) udpTransiver = new UdpTransiver();
165+
if (httpRequestSender == null) httpRequestSender = new HttpRequestSender();
166+
upnpProtocolHandler = new UpnpProtocolHandler(context, registry, udpTransiver, multicastReceiver, httpRequestSender);
167+
multicastReceiver.init(context, upnpProtocolHandler);
168+
multicastReceiver.execute();
169+
udpTransiver.init(context, upnpProtocolHandler);
170+
udpTransiver.execute();
171+
service.updateNotification();
120172
}
121-
upnpProtocolHandler = new UpnpProtocolHandler(context, registry, udpTransiver, multicastReceiver, httpRequestSender);
122-
multicastReceiver.init(context, upnpProtocolHandler);
123-
multicastReceiver.execute();
124-
udpTransiver.init(context, upnpProtocolHandler);
125-
udpTransiver.execute();
126-
service.updateNotification();
127173
}
128174

129175
public void disable() {
130-
YaaccLogger.v(getClass().getName(), "in android router disable");
131-
// Disable multicast on WiFi network interface,
132-
// requires android.permission.CHANGE_WIFI_MULTICAST_STATE
133-
if (isWifi()) {
134-
setWiFiMulticastLock(false);
135-
setWifiLock(false);
176+
synchronized (lock) {
177+
YaaccLogger.v(getClass().getName(), "in android router disable");
178+
// Disable multicast on WiFi network interface,
179+
// requires android.permission.CHANGE_WIFI_MULTICAST_STATE
180+
if (isWifiOrHotspot()) {
181+
setWiFiMulticastLock(false);
182+
setWifiLock(false);
183+
}
184+
if (upnpProtocolHandler != null) {
185+
upnpProtocolHandler = null;
186+
}
187+
if (multicastReceiver != null) {
188+
multicastReceiver.cancel();
189+
multicastReceiver = null;
190+
}
191+
if (udpTransiver != null) {
192+
udpTransiver.cancel();
193+
udpTransiver = null;
194+
}
195+
service.updateNotification();
136196
}
137-
upnpProtocolHandler = null;
138-
multicastReceiver.cancel();
139-
udpTransiver.cancel();
140-
service.updateNotification();
141197
}
142198

143199
private boolean isWifi() {
@@ -148,6 +204,10 @@ private boolean isWifi() {
148204
return connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork()).hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
149205
}
150206

207+
private boolean isWifiOrHotspot() {
208+
return isWifi() || isHotspotEnabled;
209+
}
210+
151211
private boolean isCellular() {
152212
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
153213
if (connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork()) == null) {
@@ -256,4 +316,46 @@ public UpnpProtocolHandler getUpnpProtocolHandler() {
256316
public boolean isInitalized() {
257317
return udpTransiver != null && multicastReceiver != null && upnpProtocolHandler != null;
258318
}
319+
320+
private void onNetworkStateChange() {
321+
YaaccLogger.d(getClass().getName(), "Network state change detected");
322+
synchronized (lock) {
323+
if (isWifiOrHotspot()) {
324+
if (!isInitalized()) {
325+
YaaccLogger.d(getClass().getName(), "Reinitializing network components");
326+
if (currentNetwork == null) {
327+
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
328+
currentNetwork = connectivityManager.getActiveNetwork();
329+
}
330+
if (currentNetwork != null) {
331+
if (multicastReceiver == null) multicastReceiver = new MulticastReceiver();
332+
if (udpTransiver == null) udpTransiver = new UdpTransiver();
333+
if (httpRequestSender == null) httpRequestSender = new HttpRequestSender();
334+
enable();
335+
// Notify service to restart UPnP device
336+
if (service != null) {
337+
service.onNetworkStateChange();
338+
}
339+
}
340+
}
341+
} else {
342+
YaaccLogger.d(getClass().getName(), "Not on WiFi or hotspot, disabling");
343+
disable();
344+
// Notify service to stop UPnP device
345+
if (service != null) {
346+
service.onNetworkStateChange();
347+
}
348+
}
349+
}
350+
}
351+
352+
public void cleanup() {
353+
if (networkStateReceiver != null) {
354+
try {
355+
context.unregisterReceiver(networkStateReceiver);
356+
} catch (IllegalArgumentException e) {
357+
YaaccLogger.w(getClass().getName(), "Receiver not registered");
358+
}
359+
}
360+
}
259361
}

yaacc/src/main/java/de/yaacc/upnp/server/YaaccUpnpServerService.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package de.yaacc.upnp.server;
2020

21+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
2122
import android.app.NotificationManager;
2223
import android.app.PendingIntent;
2324
import android.app.Service;
@@ -1197,6 +1198,20 @@ public Registry getRegistry() {
11971198
return registry;
11981199
}
11991200

1201+
1202+
public void onNetworkStateChange() {
1203+
YaaccLogger.d(getClass().getName(), "Network state change - restarting UPnP device");
1204+
if (isInitialized()) {
1205+
// Remove old device and create new one
1206+
if (localDevice != null) {
1207+
registry.removeDevice(localDevice);
1208+
localDevice = null;
1209+
}
1210+
createUpnpDevice();
1211+
updateNotification();
1212+
}
1213+
}
1214+
12001215
public boolean isInitialized() {
12011216
return registry != null && networkDeviceListener.isInitalized();
12021217
}

0 commit comments

Comments
 (0)