Created
March 22, 2016 15:21
-
-
Save YisroelForta/bd57ed34a43485b46037 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.appletv; | |
import android.annotation.TargetApi; | |
import android.app.Service; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.net.nsd.NsdManager; | |
import android.net.nsd.NsdServiceInfo; | |
import android.os.Binder; | |
import android.os.Build; | |
import android.os.IBinder; | |
import android.support.annotation.Nullable; | |
import android.util.Log; | |
import java.io.DataOutputStream; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.net.Socket; | |
import java.nio.ByteBuffer; | |
import java.nio.ByteOrder; | |
import java.nio.charset.Charset; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Locale; | |
//NsdManager was added in API 16 | |
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) | |
public class AppleTVClientService extends Service { | |
private static final String TAG = "apple_tv"; | |
private static final String SERVICE_NAME = "_name._tcp."; | |
private static final int INT_BYTE_COUNT = Integer.SIZE/Byte.SIZE; | |
private static final int LONG_BYTE_COUNT = Long.SIZE/Byte.SIZE; | |
private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN; | |
private final IBinder = new LocalBinder(); | |
private NsdManager mNsdManager; | |
private NsdManager.DiscoveryListener mDiscoveryListener; | |
private NsdManager.ResolveListener mResolveListener; | |
private List<NsdServiceInfo> services; | |
private Callbacks callbacks; | |
@Nullable | |
@Override | |
public IBinder onBind(Intent intent) { | |
return ; | |
} | |
public class LocalBinder extends Binder { | |
public AppleTVClientService getServiceInstance(){ | |
return AppleTVClientService.this; | |
} | |
} | |
public interface Callbacks { | |
void onTVFound(String name); | |
void onCommunicationComplete(boolean successful); | |
void onCommunicationFailed(String failReason); | |
} | |
public void setCallbacks(Callbacks callbacks) { | |
this.callbacks = callbacks; | |
} | |
public void updateUIThread() { | |
if (callbacks != null && services.size() > 0) { | |
for (NsdServiceInfo service : services) { | |
callbacks.onTVFound(service.getServiceName()); | |
} | |
} | |
} | |
@Override | |
public void onCreate() { | |
services = new ArrayList<>(); | |
mNsdManager = (NsdManager)getSystemService(Context.NSD_SERVICE); | |
initializeDiscoveryListener(); | |
initializeResolveListener(); | |
mNsdManager.discoverServices(SERVICE_NAME, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); | |
} | |
public void resolveWithHostAndCommunicate() { | |
//resolve with all available hosts | |
for (NsdServiceInfo service : services) { | |
mNsdManager.resolveService(service, mResolveListener); | |
} | |
} | |
public void initializeDiscoveryListener() { | |
mDiscoveryListener = new NsdManager.DiscoveryListener() { | |
@Override | |
public void onDiscoveryStarted(String regType) { | |
Log.d(TAG, "Service discovery started"); | |
} | |
@Override | |
public void onServiceFound(NsdServiceInfo service) { | |
Log.d(TAG, "Service found: "+service); | |
services.add(service); | |
if (callbacks != null) { | |
callbacks.onTVFound(service.getServiceName()); | |
} | |
} | |
@Override | |
public void onServiceLost(NsdServiceInfo service) { | |
Log.d(TAG, "service lost: " + service); | |
services.remove(service); | |
} | |
@Override | |
public void onDiscoveryStopped(String serviceType) { | |
Log.i(TAG, "Discovery stopped: " + serviceType); | |
} | |
@Override | |
public void onStartDiscoveryFailed(String serviceType, int errorCode) { | |
Log.d(TAG, "Discovery failed: Error code:" + errorCode); | |
mNsdManager.stopServiceDiscovery(this); | |
AppleTVClientService.this.stopSelf(); | |
} | |
@Override | |
public void onStopDiscoveryFailed(String serviceType, int errorCode) { | |
Log.d(TAG, "Discovery failed: Error code:" + errorCode); | |
mNsdManager.stopServiceDiscovery(this); | |
onDestroy(); | |
} | |
}; | |
} | |
public void initializeResolveListener() { | |
mResolveListener = new NsdManager.ResolveListener() { | |
@Override | |
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { | |
Log.d(TAG, "Resolve failed " + errorCode); | |
if (callbacks != null) { | |
callbacks.onCommunicationFailed("Resolve failed " + errorCode); | |
} | |
onDestroy(); | |
} | |
@Override | |
public void onServiceResolved(NsdServiceInfo serviceInfo) { | |
Log.d(TAG, "Resolve Succeeded " + serviceInfo); | |
communicateWithTV(serviceInfo); | |
} | |
}; | |
} | |
private void communicateWithTV(NsdServiceInfo serviceInfo) { | |
try { | |
Socket socket = new Socket(serviceInfo.getHost(), serviceInfo.getPort()); | |
int headerLength = getHeaderLength(socket); | |
// TODO: determine string to send to Apple TV | |
String stringToSend = ... | |
byte[] bytesToSend = getBytesToSend(stringToSend); | |
sendBytes(socket, bytesToSend); | |
boolean succeeded = getSucceeded(socket); | |
if (callbacks != null) { | |
callbacks.onCommunicationComplete(succeeded); | |
} | |
this.stopSelf(); | |
} | |
catch (IOException e) { | |
e.printStackTrace(); | |
Log.e(TAG, e.getMessage()); | |
if (callbacks != null) { | |
callbacks.onCommunicationFailed(e.getLocalizedMessage()); | |
} | |
onDestroy(); | |
} | |
} | |
private byte[] getBytesToSend(String stringToEncrypt) { | |
// TODO: given a string to send, determine bytes to send (optionally performing encryption, etc.) | |
byte[] bytesToSend = ... | |
return bytesToSend; | |
} | |
private void sendBytes(Socket socket, byte[] bytesToSend) throws IOException { | |
OutputStream os = socket.getOutputStream(); | |
DataOutputStream dos = new DataOutputStream(os); | |
int bytesToSendLength = bytesToSend.length; | |
byte[] bytesToSendLengthBytes = ByteBuffer.allocate(LONG_BYTE_COUNT).order(BYTE_ORDER).putLong(bytesToSendLength).array(); | |
dos.write(bytesToSendLengthBytes); | |
dos.write(bytesToSend); | |
} | |
private boolean getSucceeded(Socket socket) throws IOException { | |
byte[] headerBytes = new byte[LONG_BYTE_COUNT]; | |
InputStream inputStream = socket.getInputStream(); | |
while (true) { | |
int i = inputStream.read(headerBytes); | |
if (LONG_BYTE_COUNT == i || i == -1) { | |
break; | |
} | |
} | |
ByteBuffer headerByteBuffer = ByteBuffer.wrap(headerBytes); | |
headerByteBuffer.order(BYTE_ORDER); | |
long responseSize = headerByteBuffer.getLong(); | |
byte[] responseBytes = new byte[(int) responseSize]; | |
while (true) { | |
int i = inputStream.read(responseBytes); | |
if (responseSize == i || i == -1) { | |
break; | |
} | |
} | |
ByteBuffer responseByteBuffer = ByteBuffer.wrap(responseBytes); | |
responseByteBuffer.order(BYTE_ORDER); | |
int response = responseByteBuffer.getShort(); | |
return (response == 1); | |
} | |
@Override | |
public void onDestroy() { | |
tearDown(); | |
super.onDestroy(); | |
} | |
private void tearDown() { | |
try { | |
if (mNsdManager != null && mDiscoveryListener != null) { | |
mNsdManager.stopServiceDiscovery(mDiscoveryListener); | |
} | |
} | |
catch (IllegalArgumentException e) { | |
e.printStackTrace(); | |
Log.e(TAG, e.getMessage()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment