Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Commit

Permalink
* Added IP address distinction for IPv4 and IPv6 (thanks, IvBaranov!)
Browse files Browse the repository at this point in the history
* Added @nullable and @nonnull annotations to BonjourService methods
* Improved documentation on BonjourService methods
* Implemented the Builder pattern for BonjourService, to keep BonjourDiscovery implementations clean
  • Loading branch information
Marcel Schnelle committed Sep 14, 2015
1 parent d0dfc96 commit ea6e7d2
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 76 deletions.
16 changes: 10 additions & 6 deletions example/src/main/java/rxbonjour/example/BonjourVH.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package rxbonjour.example;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.TextView;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Iterator;

import butterknife.Bind;
Expand Down Expand Up @@ -34,14 +37,15 @@ protected BonjourVH(LayoutInflater inflater, ViewGroup parent) {
}

@Override protected void onBindItem(BonjourService item) {
Context context = tvName.getContext();
tvName.setText(item.getName());
tvType.setText(item.getType());
if (item.getmHostv4() != null) {
tvHostPortV4.setText(item.getmHostv4() + ":" + item.getPort());
}
if (item.getmHostv6() != null) {
tvHostPortV6.setText(item.getmHostv6() + ":" + item.getPort());
}

// Display host address information
Inet4Address inet4Address = item.getV4Host();
Inet6Address inet6Address = item.getV6Host();
tvHostPortV4.setText(inet4Address != null ? context.getString(R.string.format_host_address_v4, inet4Address, item.getPort()) : "");
tvHostPortV6.setText(inet6Address != null ? context.getString(R.string.format_host_address_v6, inet6Address, item.getPort()) : "");

// Display TXT records, if any could be resolved
int txtRecordCount = item.getTxtRecordCount();
Expand Down
2 changes: 2 additions & 0 deletions example/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@
<string name="hint_type">Enter Service Type (e.g. \'_http._tcp\')…</string>
<string name="button_apply">Apply</string>
<string name="toast_invalidtype">\'%s\' is not a valid Bonjour type!</string>
<string name="format_host_address_v4">IPv4: %1$s:%2$s</string>
<string name="format_host_address_v6">IPv6: %1$s:%2$s</string>
</resources>
3 changes: 3 additions & 0 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ android {
}

dependencies {
// Support annotations
compile 'com.android.support:support-annotations:23.0.1'

// Reactive extensions
compile 'io.reactivex:rxandroid:1.0.1'

Expand Down
30 changes: 8 additions & 22 deletions lib/src/main/java/rxbonjour/internal/JBBonjourDiscovery.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Build;
import android.os.Bundle;

import java.lang.ref.WeakReference;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import rx.Subscriber;
Expand Down Expand Up @@ -62,32 +57,23 @@ public JBBonjourDiscovery() {
* @return A BonjourEvent containing the necessary information
*/
@TargetApi(LOLLIPOP) private BonjourEvent newBonjourEvent(BonjourEvent.Type type, NsdServiceInfo serviceInfo) {
// Construct a new BonjourService
BonjourService.Builder serviceBuilder = new BonjourService.Builder(serviceInfo.getServiceName(), serviceInfo.getServiceType());

// Prepare TXT record Bundle (on Lollipop and up)
Bundle txtRecords;
if (Build.VERSION.SDK_INT >= LOLLIPOP) {
Map<String, byte[]> attributes = serviceInfo.getAttributes();
txtRecords = new Bundle(attributes.size());
for (String key : attributes.keySet()) {
txtRecords.putString(key, new String(attributes.get(key), Charset.forName("UTF-8")));
serviceBuilder.addTxtRecord(key, new String(attributes.get(key), Charset.forName("UTF-8")));
}

} else {
txtRecords = new Bundle(0);
}

InetAddress host = serviceInfo.getHost();
Inet4Address hostv4 = null;
Inet6Address hostv6 = null;
if (host instanceof Inet4Address) {
hostv4 = (Inet4Address) host;
} else if (host instanceof Inet6Address) {
hostv6 = (Inet6Address) host;
}

// Add host address and port
serviceBuilder.addAddress(serviceInfo.getHost());
serviceBuilder.setPort(serviceInfo.getPort());

// Create and return an event wrapping the BonjourService
BonjourService service = new BonjourService(serviceInfo.getServiceName(), serviceInfo.getServiceType(), hostv4, hostv6, serviceInfo.getPort(), txtRecords);
return new BonjourEvent(type, service);
return new BonjourEvent(type, serviceBuilder.build());
}

/* Begin overrides */
Expand Down
36 changes: 13 additions & 23 deletions lib/src/main/java/rxbonjour/internal/SupportBonjourDiscovery.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@

import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.Bundle;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Enumeration;

import java.util.HashMap;
import java.util.Map;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
Expand Down Expand Up @@ -65,32 +60,27 @@ public SupportBonjourDiscovery() {
* @return A BonjourEvent containing the necessary information
*/
private BonjourEvent newBonjourEvent(BonjourEvent.Type type, ServiceEvent event) {
// Access the event's ServiceInfo and obtain a suitable IP address
// Construct a new BonjourService
ServiceInfo info = event.getInfo();
InetAddress[] addresses = info.getInetAddresses();
Inet4Address inet4Address = null;
Inet6Address inet6Address = null;
for (InetAddress a : addresses) {
if (a != null) {
if (a instanceof Inet4Address) {
inet4Address = (Inet4Address) a;
} else if (a instanceof Inet6Address) {
inet6Address = (Inet6Address) a;
}
}
}
BonjourService.Builder serviceBuilder = new BonjourService.Builder(event.getName(), event.getType());

// Prepare TXT record Bundle
Enumeration<String> keys = info.getPropertyNames();
Bundle txtRecords = new Bundle();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
txtRecords.putString(key, info.getPropertyString(key));
serviceBuilder.addTxtRecord(key, info.getPropertyString(key));
}

// Add non-null host addresses and port
InetAddress[] addresses = info.getInetAddresses();
for (InetAddress address : addresses) {
if (address == null) continue;
serviceBuilder.addAddress(address);
}
serviceBuilder.setPort(info.getPort());

// Create the service object and wrap it in an event
BonjourService service = new BonjourService(event.getName(), event.getType(), inet4Address, inet6Address, info.getPort(), txtRecords);
return new BonjourEvent(type, service);
// Create and return an event wrapping the BonjourService
return new BonjourEvent(type, serviceBuilder.build());
}

/**
Expand Down
147 changes: 122 additions & 25 deletions lib/src/main/java/rxbonjour/model/BonjourService.java
Original file line number Diff line number Diff line change
@@ -1,81 +1,131 @@
package rxbonjour.model;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Map;

/**
* Resolved Bonjour service detected within the device's local network.
*
* @author marcel
*/
public class BonjourService {

private String mName;
private String mType;
private Inet4Address mHostv4;
private Inet6Address mHostv6;
private Inet4Address mV4Host;
private Inet6Address mV6Host;
private int mPort;
private Bundle mTxtRecords;

public BonjourService(String name, String type, Inet4Address hostv4, Inet6Address hostv6, int port, Bundle txtRecords) {
private BonjourService(String name, String type, Inet4Address v4Host, Inet6Address v6Host, int port, Bundle txtRecords) {
mName = name;
mType = type;
mHostv4 = hostv4;
mHostv6 = hostv6;
mV4Host = v4Host;
mV6Host = v6Host;
mPort = port;
mTxtRecords = txtRecords;
}

public String getName() {
/**
* @return The service's display name, e.g. 'Office Printer'
*/
public @NonNull String getName() {
return mName;
}

public String getType() {
/**
* @return The service's type, e.g. '_http._tcp.local.'
*/
public @NonNull String getType() {
return mType;
}

@Deprecated
public InetAddress getHost() {
if (mHostv4 != null) {
return mHostv4;
} else {
return mHostv6;
}
/**
* Obtains the host address of the service.
* For services with both an IPv4 <strong>and</strong> an IPv6 address, the former address takes precedence over the latter,
* so that it always favors the v4 address over the v6 one.
* <p/>
* If you need to access specific addresses, consider using {@link #getV4Host()} and {@link #getV6Host()}, respectively.
*
* @return A host address of the service
*/
public @NonNull InetAddress getHost() {
return (mV4Host != null) ? mV4Host : mV6Host;
}

public Inet4Address getmHostv4() {
return mHostv4;
/**
* @return The IPv4 host address of the service, or null if it doesn't provide any
*/
public @Nullable Inet4Address getV4Host() {
return mV4Host;
}

public Inet6Address getmHostv6() {
return mHostv6;
/**
* @return The IPv6 host address of the service, or null if it doesn't provide any
*/
public @Nullable Inet6Address getV6Host() {
return mV6Host;
}

/**
* @return The port on which the service is being broadcast
*/
public int getPort() {
return mPort;
}

/**
* @return The number of TXT records associated with the service
*/
public int getTxtRecordCount() {
return mTxtRecords.size();
}

public Bundle getTxtRecords() {
/**
* Returns a Bundle containing all TXT records associated with the service, stored as &lt;String, String&gt; key-value pairs.
* <p/>
* If the service doesn't have any TXT records, or none could be resolved, this returns an empty Bundle
*
* @return A Bundle containing the service's TXT records
*/
public @NonNull Bundle getTxtRecords() {
return mTxtRecords;
}

/**
* Returns the specific TXT record with the provided key, falling back to the default value if this TXT record doesn't exist.
*
* @param key Key of the TXT record
* @param defaultValue Value to return if the TXT record isn't contained in the service's records
* @return The associated value for the provided key, or the default value if absent
*/
public String getTxtRecord(String key, String defaultValue) {
String value = mTxtRecords.getString(key);
return value != null ? value : defaultValue;
}

/**
* Returns the specific TXT record with the provided key, or null if no such mapping exists.
* This method is a shorthand for <pre>getTxtRecord(key, null)</pre>
*
* @param key Key of the TXT record
* @return The associated value for the provided key, or null if absent
*/
public @Nullable String getTxtRecord(String key) {
return getTxtRecord(key, null);
}

@Override public String toString() {
return "BonjourService{" +
"mName='" + mName + '\'' +
", mType='" + mType + '\'' +
", mHostv4=" + mHostv4 +
", mHostv6=" + mHostv6 +
", mV4Host=" + mV4Host +
", mV6Host=" + mV6Host +
", mPort=" + mPort +
'}';
}
Expand All @@ -85,16 +135,63 @@ public String getTxtRecord(String key, String defaultValue) {
if (!(o instanceof BonjourService)) return false;

BonjourService that = (BonjourService) o;
return mName.equals(that.getName());

if (mPort != that.mPort) return false;
if (!mName.equals(that.mName)) return false;
if (!mType.equals(that.mType)) return false;
if (mV4Host != null ? !mV4Host.equals(that.mV4Host) : that.mV4Host != null) return false;
return !(mV6Host != null ? !mV6Host.equals(that.mV6Host) : that.mV6Host != null);
}

@Override public int hashCode() {
int result = mName != null ? mName.hashCode() : 0;
result = 31 * result + (mType != null ? mType.hashCode() : 0);
result = 31 * result + (mHostv4 != null ? mHostv4.hashCode() : 0);
result = 31 * result + (mHostv6 != null ? mHostv6.hashCode() : 0);
result = 31 * result + (mV4Host != null ? mV4Host.hashCode() : 0);
result = 31 * result + (mV6Host != null ? mV6Host.hashCode() : 0);
result = 31 * result + mPort;
return result;
}

/* Begin static */

public static final class Builder {

private String mName;
private String mType;
private Inet4Address mHostv4;
private Inet6Address mHostv6;
private int mPort;
private Bundle mTxtRecords;

public Builder(String name, String type) {
mName = name;
mType = type;
}

public Builder addAddress(InetAddress address) {
if (address instanceof Inet4Address) {
mHostv4 = (Inet4Address) address;

} else if (address instanceof Inet6Address) {
mHostv6 = (Inet6Address) address;
}
return this;
}

public Builder setPort(int port) {
mPort = port;
return this;
}

public Builder addTxtRecord(String key, String value) {
if (mTxtRecords == null) mTxtRecords = new Bundle();
mTxtRecords.putString(key, value);
return this;
}

public BonjourService build() {
if (mTxtRecords == null) mTxtRecords = new Bundle(0);
return new BonjourService(mName, mType, mHostv4, mHostv6, mPort, mTxtRecords);
}
}
}

0 comments on commit ea6e7d2

Please sign in to comment.