├── src └── main │ ├── res │ └── values │ │ └── strings.xml │ ├── java │ └── com │ │ └── abemart │ │ └── wroup │ │ ├── common │ │ ├── listeners │ │ │ ├── ServiceDisconnectedListener.java │ │ │ ├── PeerConnectedListener.java │ │ │ ├── ClientConnectedListener.java │ │ │ ├── ServiceConnectedListener.java │ │ │ ├── ClientDisconnectedListener.java │ │ │ ├── DataReceivedListener.java │ │ │ ├── ServiceRegisteredListener.java │ │ │ └── ServiceDiscoveredListener.java │ │ ├── messages │ │ │ ├── RegistrationMessageContent.java │ │ │ ├── DisconnectionMessageContent.java │ │ │ ├── RegisteredDevicesMessageContent.java │ │ │ └── MessageWrapper.java │ │ ├── WroupServiceDevice.java │ │ ├── WiFiP2PError.java │ │ ├── WroupDevice.java │ │ ├── WiFiP2PInstance.java │ │ ├── WiFiDirectBroadcastReceiver.java │ │ └── direct │ │ │ └── WiFiDirectUtils.java │ │ ├── service │ │ └── WroupService.java │ │ └── client │ │ └── WroupClient.java │ └── AndroidManifest.xml ├── .gitignore ├── proguard-rules.pro ├── install.gradle ├── bintray.gradle └── README.md /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Wroup 3 | 4 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ServiceDisconnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | public interface ServiceDisconnectedListener { 5 | 6 | void onServerDisconnectedListener(); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/PeerConnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import android.net.wifi.p2p.WifiP2pInfo; 5 | 6 | public interface PeerConnectedListener { 7 | 8 | void onPeerConnected(WifiP2pInfo wifiP2pInfo); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ClientConnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public interface ClientConnectedListener { 7 | 8 | void onClientConnected(WroupDevice wroupDevice); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ServiceConnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public interface ServiceConnectedListener { 7 | 8 | void onServiceConnected(WroupDevice serviceDevice); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ClientDisconnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public interface ClientDisconnectedListener { 7 | 8 | void onClientDisconnected(WroupDevice wroupDevice); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/DataReceivedListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.messages.MessageWrapper; 5 | 6 | public interface DataReceivedListener { 7 | 8 | void onDataReceived(MessageWrapper messageWrapper); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ServiceRegisteredListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.WiFiP2PError; 5 | 6 | public interface ServiceRegisteredListener { 7 | 8 | void onSuccessServiceRegistered(); 9 | 10 | void onErrorServiceRegistered(WiFiP2PError wiFiP2PError); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/messages/RegistrationMessageContent.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.messages; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public class RegistrationMessageContent { 7 | 8 | private WroupDevice wroupDevice; 9 | 10 | public WroupDevice getWroupDevice() { 11 | return wroupDevice; 12 | } 13 | 14 | public void setWroupDevice(WroupDevice wroupDevice) { 15 | this.wroupDevice = wroupDevice; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/messages/DisconnectionMessageContent.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.messages; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public class DisconnectionMessageContent { 7 | 8 | private WroupDevice wroupDevice; 9 | 10 | 11 | public void setWroupDevice(WroupDevice wroupDevice) { 12 | this.wroupDevice = wroupDevice; 13 | } 14 | 15 | public WroupDevice getWroupDevice() { 16 | return wroupDevice; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/messages/RegisteredDevicesMessageContent.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.messages; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | import java.util.List; 7 | 8 | public class RegisteredDevicesMessageContent { 9 | 10 | private List devicesRegistered; 11 | 12 | public List getDevicesRegistered() { 13 | return devicesRegistered; 14 | } 15 | 16 | public void setDevicesRegistered(List devicesRegistered) { 17 | this.devicesRegistered = devicesRegistered; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/listeners/ServiceDiscoveredListener.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.listeners; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | import com.abemart.wroup.common.WiFiP2PError; 6 | import com.abemart.wroup.common.WroupServiceDevice; 7 | import com.abemart.wroup.service.WroupService; 8 | 9 | import java.util.List; 10 | 11 | public interface ServiceDiscoveredListener { 12 | 13 | void onNewServiceDeviceDiscovered(WroupServiceDevice serviceDevice); 14 | 15 | void onFinishServiceDeviceDiscovered(List serviceDevices); 16 | 17 | void onError(WiFiP2PError wiFiP2PError); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/WroupServiceDevice.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common; 2 | 3 | 4 | import android.net.wifi.p2p.WifiP2pDevice; 5 | 6 | import java.util.Map; 7 | 8 | public class WroupServiceDevice extends WroupDevice { 9 | 10 | 11 | private Map txtRecordMap; 12 | 13 | public WroupServiceDevice(WifiP2pDevice wifiP2pDevice) { 14 | super(wifiP2pDevice); 15 | } 16 | 17 | public Map getTxtRecordMap() { 18 | return txtRecordMap; 19 | } 20 | 21 | public void setTxtRecordMap(Map txtRecordMap) { 22 | this.txtRecordMap = txtRecordMap; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/WiFiP2PError.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common; 2 | 3 | 4 | public enum WiFiP2PError { 5 | 6 | ERROR(0), P2P_NOT_SUPPORTED(1), BUSSY(2); 7 | 8 | private int reason; 9 | 10 | WiFiP2PError(int reason) { 11 | this.reason = reason; 12 | } 13 | 14 | public int getReason() { 15 | return reason; 16 | } 17 | 18 | public static WiFiP2PError fromReason(int reason) { 19 | for (WiFiP2PError wiFiP2PError : WiFiP2PError.values()) { 20 | if (reason == wiFiP2PError.reason) { 21 | return wiFiP2PError; 22 | } 23 | } 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | 11 | # Built application files 12 | /*/build/ 13 | 14 | # Crashlytics configuations 15 | com_crashlytics_export_strings.xml 16 | 17 | # Local configuration file (sdk path, etc) 18 | local.properties 19 | 20 | # Gradle generated files 21 | .gradle/ 22 | 23 | # Signing files 24 | .signing/ 25 | 26 | # User-specific configurations 27 | .idea/libraries/ 28 | .idea/workspace.xml 29 | .idea/tasks.xml 30 | .idea/.name 31 | .idea/compiler.xml 32 | .idea/copyright/profiles_settings.xml 33 | .idea/encodings.xml 34 | .idea/misc.xml 35 | .idea/modules.xml 36 | .idea/scopes/scope_settings.xml 37 | .idea/vcs.xml 38 | *.iml 39 | 40 | # OS-specific files 41 | .DS_Store 42 | .DS_Store? 43 | ._* -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Abel/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /install.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | 3 | group = 'com.abemart.wroup' 4 | 5 | install { 6 | repositories.mavenInstaller { 7 | pom { 8 | project { 9 | packaging 'aar' 10 | groupId 'com.abemart.wroup' 11 | artifactId 'wroup' 12 | 13 | name 'Wroup' 14 | description 'Android WiFi P2P Library' 15 | url 'https://github.com/ble180/Wroup' 16 | 17 | licenses { 18 | license { 19 | name 'MIT' 20 | url 'https://opensource.org/licenses/MIT' 21 | } 22 | } 23 | developers { 24 | developer { 25 | id 'abemart' //YOUR ID 26 | name 'Abel Martinez' //YOUR NAME 27 | email 'ble18059@gmail.com' //YOUR EMAIL 28 | } 29 | } 30 | scm { 31 | connection 'https://github.com/ble180/Wroup.git' 32 | developerConnection 'https://github.com/ble180/Wroup.git' 33 | url 'https://github.com/ble180/Wroup.git' 34 | } 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/messages/MessageWrapper.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.messages; 2 | 3 | 4 | import com.abemart.wroup.common.WroupDevice; 5 | 6 | public class MessageWrapper { 7 | 8 | public enum MessageType { 9 | NORMAL, CONNECTION_MESSAGE, DISCONNECTION_MESSAGE, REGISTERED_DEVICES; 10 | } 11 | 12 | private String message; 13 | private MessageType messageType; 14 | private WroupDevice wroupDevice; 15 | 16 | 17 | public void setWroupDevice(WroupDevice wroupDevice) { 18 | this.wroupDevice = wroupDevice; 19 | } 20 | 21 | public WroupDevice getWroupDevice() { 22 | return wroupDevice; 23 | } 24 | 25 | public String getMessage() { 26 | return message; 27 | } 28 | 29 | public void setMessage(String message) { 30 | this.message = message; 31 | } 32 | 33 | public MessageType getMessageType() { 34 | return messageType; 35 | } 36 | 37 | public void setMessageType(MessageType messageType) { 38 | this.messageType = messageType; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "MessageWrapper{" + 44 | "message='" + message + '\'' + 45 | ", messageType=" + messageType + 46 | ", wroupDevice=" + wroupDevice + 47 | '}'; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | 3 | version = '0.9' 4 | 5 | task sourcesJar(type: Jar) { 6 | from android.sourceSets.main.java.srcDirs 7 | classifier = 'sources' 8 | } 9 | 10 | task javadoc(type: Javadoc) { 11 | source = android.sourceSets.main.java.srcDirs 12 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 13 | } 14 | 15 | task javadocJar(type: Jar, dependsOn: javadoc) { 16 | classifier = 'javadoc' 17 | from javadoc.destinationDir 18 | } 19 | 20 | afterEvaluate { 21 | javadoc.classpath += files(android.libraryVariants.collect { variant -> 22 | variant.javaCompile.classpath.files 23 | }) 24 | } 25 | 26 | artifacts { 27 | archives javadocJar 28 | archives sourcesJar 29 | } 30 | 31 | // Bintray 32 | Properties properties = new Properties() 33 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 34 | 35 | bintray { 36 | user = properties.getProperty("bintray.user") 37 | key = properties.getProperty("bintray.apikey") 38 | 39 | configurations = ['archives'] 40 | pkg { 41 | repo = 'Wroup' 42 | name = 'wroup' 43 | desc = 'Android WiFi P2P Library' 44 | websiteUrl = 'https://github.com/ble180/Wroup' 45 | vcsUrl = 'https://github.com/ble180/Wroup.git' 46 | licenses = ["MIT"] 47 | publish = true 48 | publicDownloadNumbers = true 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/WroupDevice.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common; 2 | 3 | 4 | import android.net.wifi.p2p.WifiP2pDevice; 5 | 6 | 7 | public class WroupDevice { 8 | 9 | private String deviceName; 10 | private String deviceMac; 11 | private String deviceServerSocketIP; 12 | private int deviceServerSocketPort; 13 | 14 | private String customName; 15 | 16 | public WroupDevice() { 17 | 18 | } 19 | 20 | public WroupDevice(WifiP2pDevice device) { 21 | this.deviceName = device.deviceName; 22 | this.deviceMac = device.deviceAddress; 23 | } 24 | 25 | public String getDeviceName() { 26 | return deviceName; 27 | } 28 | 29 | public void setDeviceName(String deviceName) { 30 | this.deviceName = deviceName; 31 | } 32 | 33 | public String getDeviceMac() { 34 | return deviceMac; 35 | } 36 | 37 | public void setDeviceMac(String deviceMac) { 38 | this.deviceMac = deviceMac; 39 | } 40 | 41 | public String getDeviceServerSocketIP() { 42 | return deviceServerSocketIP; 43 | } 44 | 45 | public void setDeviceServerSocketIP(String deviceServerSocketIP) { 46 | this.deviceServerSocketIP = deviceServerSocketIP; 47 | } 48 | 49 | public int getDeviceServerSocketPort() { 50 | return deviceServerSocketPort; 51 | } 52 | 53 | public void setDeviceServerSocketPort(int deviceServerSocketPort) { 54 | this.deviceServerSocketPort = deviceServerSocketPort; 55 | } 56 | 57 | public String getCustomName() { 58 | return customName; 59 | } 60 | 61 | public void setCustomName(String customName) { 62 | this.customName = customName; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return new StringBuilder().append("WroupDevice[deviceName=").append(deviceName).append("][deviceMac=").append(deviceMac).append("][deviceServerSocketIP=").append(deviceServerSocketIP).append("][deviceServerSocketPort=").append(deviceServerSocketPort).append("]").toString(); 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) return true; 73 | if (o == null || getClass() != o.getClass()) return false; 74 | 75 | WroupDevice that = (WroupDevice) o; 76 | 77 | if (deviceName != null ? !deviceName.equals(that.deviceName) : that.deviceName != null) 78 | return false; 79 | return deviceMac != null ? deviceMac.equals(that.deviceMac) : that.deviceMac == null; 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | int result = deviceName != null ? deviceName.hashCode() : 0; 85 | result = 31 * result + (deviceMac != null ? deviceMac.hashCode() : 0); 86 | return result; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/WiFiP2PInstance.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common; 2 | 3 | 4 | import android.content.Context; 5 | import android.net.wifi.p2p.WifiP2pInfo; 6 | import android.net.wifi.p2p.WifiP2pManager; 7 | import android.util.Log; 8 | 9 | import com.abemart.wroup.common.listeners.PeerConnectedListener; 10 | import com.abemart.wroup.common.listeners.ServiceDisconnectedListener; 11 | 12 | 13 | public class WiFiP2PInstance implements WifiP2pManager.ConnectionInfoListener { 14 | 15 | private static final String TAG = WiFiP2PInstance.class.getSimpleName(); 16 | 17 | private static WiFiP2PInstance instance; 18 | 19 | private WifiP2pManager wifiP2pManager; 20 | private WifiP2pManager.Channel channel; 21 | private WiFiDirectBroadcastReceiver broadcastReceiver; 22 | 23 | private WroupDevice thisDevice; 24 | 25 | private PeerConnectedListener peerConnectedListener; 26 | private ServiceDisconnectedListener serviceDisconnectedListener; 27 | 28 | private WiFiP2PInstance() { 29 | } 30 | 31 | private WiFiP2PInstance(Context context) { 32 | this(); 33 | 34 | wifiP2pManager = (WifiP2pManager) context.getSystemService(Context.WIFI_P2P_SERVICE); 35 | channel = wifiP2pManager.initialize(context, context.getMainLooper(), null); 36 | broadcastReceiver = new WiFiDirectBroadcastReceiver(this); 37 | } 38 | 39 | 40 | public static WiFiP2PInstance getInstance(Context context) { 41 | if (instance == null) { 42 | instance = new WiFiP2PInstance(context); 43 | } 44 | 45 | return instance; 46 | } 47 | 48 | public WifiP2pManager getWifiP2pManager() { 49 | return wifiP2pManager; 50 | } 51 | 52 | public WifiP2pManager.Channel getChannel() { 53 | return channel; 54 | } 55 | 56 | public WiFiDirectBroadcastReceiver getBroadcastReceiver() { 57 | return broadcastReceiver; 58 | } 59 | 60 | public void setThisDevice(WroupDevice thisDevice) { 61 | this.thisDevice = thisDevice; 62 | } 63 | 64 | public WroupDevice getThisDevice() { 65 | return thisDevice; 66 | } 67 | 68 | public void setPeerConnectedListener(PeerConnectedListener peerConnectedListener) { 69 | this.peerConnectedListener = peerConnectedListener; 70 | } 71 | 72 | public void setServerDisconnectedListener(ServiceDisconnectedListener serviceDisconnectedListener) { 73 | this.serviceDisconnectedListener = serviceDisconnectedListener; 74 | } 75 | 76 | public void startPeerDiscovering() { 77 | wifiP2pManager.discoverPeers(channel, new WifiP2pManager.ActionListener() { 78 | @Override 79 | public void onSuccess() { 80 | Log.i(TAG, "Peers discovering initialized"); 81 | } 82 | 83 | @Override 84 | public void onFailure(int reason) { 85 | Log.e(TAG, "Error initiating peer disconvering. Reason: " + reason); 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | public void onConnectionInfoAvailable(WifiP2pInfo info) { 92 | if (peerConnectedListener != null) { 93 | peerConnectedListener.onPeerConnected(info); 94 | } 95 | } 96 | 97 | public void onServerDeviceDisconnected() { 98 | if (serviceDisconnectedListener != null) { 99 | serviceDisconnectedListener.onServerDisconnectedListener(); 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/WiFiDirectBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.NetworkInfo; 7 | import android.net.wifi.p2p.WifiP2pDevice; 8 | import android.net.wifi.p2p.WifiP2pDeviceList; 9 | import android.net.wifi.p2p.WifiP2pManager; 10 | import android.util.Log; 11 | 12 | 13 | public class WiFiDirectBroadcastReceiver extends BroadcastReceiver { 14 | 15 | private static final String TAG = WiFiDirectBroadcastReceiver.class.getName(); 16 | 17 | private WiFiP2PInstance wiFiP2PInstance; 18 | 19 | public WiFiDirectBroadcastReceiver(WiFiP2PInstance wiFiP2PInstance) { 20 | this.wiFiP2PInstance = wiFiP2PInstance; 21 | } 22 | 23 | @Override 24 | public void onReceive(Context context, Intent intent) { 25 | String action = intent.getAction(); 26 | 27 | if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { 28 | 29 | int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); 30 | if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { 31 | Log.i(TAG, "WiFi P2P is active"); 32 | } else { 33 | Log.i(TAG, "WiFi P2P isn't active"); 34 | } 35 | 36 | } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { 37 | 38 | Log.d(TAG, "New peers detected. Requesting peers list..."); 39 | 40 | if (wiFiP2PInstance != null) { 41 | wiFiP2PInstance.getWifiP2pManager().requestPeers(wiFiP2PInstance.getChannel(), new WifiP2pManager.PeerListListener() { 42 | 43 | @Override 44 | public void onPeersAvailable(WifiP2pDeviceList peers) { 45 | if (!peers.getDeviceList().isEmpty()) { 46 | Log.d(TAG, "Peers detected:"); 47 | 48 | for (WifiP2pDevice device : peers.getDeviceList()) { 49 | Log.d(TAG, "\tDevice Name: " + device.deviceName); 50 | Log.d(TAG, "\tDevice Address: " + device.deviceAddress); 51 | } 52 | } else { 53 | Log.d(TAG, "No peers detected"); 54 | } 55 | } 56 | }); 57 | } 58 | 59 | } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { 60 | 61 | NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); 62 | if (networkInfo.isConnected()) { 63 | Log.d(TAG, "New device is connected"); 64 | wiFiP2PInstance.getWifiP2pManager().requestConnectionInfo(wiFiP2PInstance.getChannel(), wiFiP2PInstance); 65 | } else { 66 | Log.d(TAG, "The server device has been disconnected"); 67 | wiFiP2PInstance.onServerDeviceDisconnected(); 68 | } 69 | 70 | } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { 71 | 72 | WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE); 73 | Log.d(TAG, "This device name: " + device.deviceName); 74 | Log.d(TAG, "This device address: " + device.deviceAddress); 75 | 76 | if (wiFiP2PInstance.getThisDevice() == null) { 77 | wiFiP2PInstance.setThisDevice(new WroupDevice(device)); 78 | } 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/common/direct/WiFiDirectUtils.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.common.direct; 2 | 3 | 4 | import android.net.wifi.p2p.WifiP2pGroup; 5 | import android.net.wifi.p2p.WifiP2pManager; 6 | import android.util.Log; 7 | 8 | import com.abemart.wroup.common.WiFiP2PError; 9 | import com.abemart.wroup.common.WiFiP2PInstance; 10 | 11 | public class WiFiDirectUtils { 12 | 13 | private static final String TAG = WiFiDirectUtils.class.getSimpleName(); 14 | 15 | public static void clearServiceRequest(WiFiP2PInstance wiFiP2PInstance) { 16 | wiFiP2PInstance.getWifiP2pManager().clearServiceRequests(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 17 | 18 | @Override 19 | public void onSuccess() { 20 | Log.d(TAG, "Success clearing service request"); 21 | } 22 | 23 | @Override 24 | public void onFailure(int reason) { 25 | Log.e(TAG, "Error clearing service request: " + reason); 26 | } 27 | 28 | }); 29 | } 30 | 31 | public static void clearLocalServices(WiFiP2PInstance wiFiP2PInstance) { 32 | wiFiP2PInstance.getWifiP2pManager().clearLocalServices(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 33 | 34 | @Override 35 | public void onSuccess() { 36 | Log.d(TAG, "Local services cleared"); 37 | } 38 | 39 | @Override 40 | public void onFailure(int reason) { 41 | Log.e(TAG, "Error clearing local services: " + WiFiP2PError.fromReason(reason)); 42 | } 43 | 44 | }); 45 | } 46 | 47 | public static void cancelConnect(WiFiP2PInstance wiFiP2PInstance) { 48 | wiFiP2PInstance.getWifiP2pManager().cancelConnect(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 49 | 50 | @Override 51 | public void onSuccess() { 52 | Log.d(TAG, "Connect canceled successfully"); 53 | } 54 | 55 | @Override 56 | public void onFailure(int reason) { 57 | Log.e(TAG, "Error canceling connect: " + WiFiP2PError.fromReason(reason)); 58 | } 59 | 60 | }); 61 | } 62 | 63 | public static void removeGroup(final WiFiP2PInstance wiFiP2PInstance) { 64 | wiFiP2PInstance.getWifiP2pManager().requestGroupInfo(wiFiP2PInstance.getChannel(), new WifiP2pManager.GroupInfoListener() { 65 | @Override 66 | public void onGroupInfoAvailable(final WifiP2pGroup group) { 67 | wiFiP2PInstance.getWifiP2pManager().removeGroup(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 68 | @Override 69 | public void onSuccess() { 70 | Log.i(TAG, "Group removed: " + group.getNetworkName()); 71 | } 72 | 73 | @Override 74 | public void onFailure(int reason) { 75 | Log.e(TAG, "Fail disconnecting from group. Reason: " + WiFiP2PError.fromReason(reason)); 76 | } 77 | }); 78 | } 79 | }); 80 | } 81 | 82 | public static void stopPeerDiscovering(WiFiP2PInstance wiFiP2PInstance) { 83 | wiFiP2PInstance.getWifiP2pManager().stopPeerDiscovery(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 84 | 85 | @Override 86 | public void onSuccess() { 87 | Log.d(TAG, "Peer disconvering stopped"); 88 | } 89 | 90 | @Override 91 | public void onFailure(int reason) { 92 | Log.e(TAG, "Error stopping peer discovering: " + WiFiP2PError.fromReason(reason)); 93 | } 94 | 95 | }); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wroup 2 | Wroup is a WiFi P2P Android Library, or also know as WiFi Direct. This library adds a layer to the official WiFi Direct API to ease the use of this. The name of the library come from join Group + WiFi with the result of Wroup since the purpose of this library is creating WiFi P2P groups and allow the communication between the peers connected. 3 | 4 | 5 | ### Table of contents 6 | * [Installation](#installation) 7 | * [Usage](#usage) 8 | * [Getting Started](#getting-started) 9 | * [Introduction](#introduction) 10 | * [Server device](#server-device) 11 | * [Client device](#client-device) 12 | * [Sending messages](#sending-messages) 13 | * [Example App](#example-app) 14 | * [Contributing](#contributing) 15 | * [License](#license) 16 | 17 | ## Installation 18 | You only need to add the Wroup Library as dependency in your gradle file. The library is located in JCenter repositories, so you maybe need to add the JCenter repository to your gradle configuration: 19 | ```groovy 20 | repositories { 21 | jcenter() 22 | } 23 | 24 | compile 'com.abemart.wroup:wroup:0.9' 25 | ``` 26 | 27 | ## Usage 28 | ### Getting started 29 | Once that you have imported the Wroup dependency in your app, the first step is register the ```WiFiDirectBroadcastReceiver``` in your main activity as follows: 30 | ```java 31 | ... 32 | 33 | private WiFiDirectBroadcastReceiver wiFiDirectBroadcastReceiver; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | 40 | wiFiDirectBroadcastReceiver = WiFiP2PInstance.getInstance(this).getBroadcastReceiver(); 41 | ... 42 | } 43 | 44 | @Override 45 | protected void onResume() { 46 | super.onResume(); 47 | 48 | IntentFilter intentFilter = new IntentFilter(); 49 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); 50 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); 51 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); 52 | intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); 53 | registerReceiver(wiFiDirectBroadcastReceiver, intentFilter); 54 | } 55 | 56 | @Override 57 | protected void onPause() { 58 | super.onPause(); 59 | unregisterReceiver(wiFiDirectBroadcastReceiver); 60 | } 61 | 62 | ... 63 | ``` 64 | 65 | Many events in the official WiFi Direct API are received by this BroadcastReceiver because all the methods in the API are asynchronous. 66 | 67 | ### Introduction 68 | We distinguish two types of devices: 69 | * Server devices: When a group is created in WiFi Direct, there is a device that it's the group owner. In the Wroup library the Server Device is who creates the group and the group owner. Also it manages the interaction in the group. For example, if a new client has been connected to the group the server device sends to all the clients already connected this new connection. With this approach all the clients can know all the peers connected. 70 | * Client devices: Clients devices can discover nearby groups and connecting to them. After a client is connected to the group, it can send messages to any of the peers connected. 71 | 72 | ### Server Device 73 | To create a Service device you must obtain the ```WroupService``` instance and register a group in the local WiFi network. 74 | ```java 75 | ... 76 | 77 | WroupService wroupService = WroupService.getInstance(getApplicationContext()); 78 | wroupService.registerService("Group Name", new ServiceRegisteredListener() { 79 | 80 | @Override 81 | public void onSuccessServiceRegistered() { 82 | ... 83 | } 84 | 85 | @Override 86 | public void onErrorServiceRegistered(WiFiP2PError wiFiP2PError) { 87 | Toast.makeText(getApplicationContext(), "Error creating group", Toast.LENGTH_SHORT).show(); 88 | } 89 | 90 | }); 91 | 92 | ``` 93 | 94 | Then you can implement a series of listener to know group changes (connections and disconnections): 95 | 96 | ```java 97 | wroupService.setClientConnectedListener(new ClientConnectedListener() { 98 | @Override 99 | public void onClientConnected(WroupDevice wroupDevice) { 100 | // New client connected to the group 101 | }); 102 | }); 103 | 104 | wroupService.setClientDisconnected(new ClientDisconnectedListener() { 105 | @Override 106 | public void onClientDisconnected(WroupDevice wroupDevice) { 107 | // Client disconnected from the group 108 | } 109 | }) 110 | 111 | ``` 112 | 113 | 114 | ### Client Device 115 | Multiple client devices can be connected to the same group. The client device can discover new groups registered in the same local network and connecting to them, to find those nearby groups you have to do the following: 116 | ```java 117 | wroupClient = WroupClient.getInstance(getApplicationContext()); 118 | wroupClient.discoverServices(5000L, new ServiceDiscoveredListener() { 119 | 120 | @Override 121 | public void onNewServiceDeviceDiscovered(WroupServiceDevice serviceDevice) { 122 | // New service discover 123 | } 124 | 125 | @Override 126 | public void onFinishServiceDeviceDiscovered(List serviceDevices) { 127 | // The list of services discovered in the time indicated 128 | } 129 | 130 | @Override 131 | public void onError(WiFiP2PError wiFiP2PError) { 132 | // An error occurred during the searching 133 | } 134 | }); 135 | ``` 136 | 137 | At the same as ```WroupService``` you can registered the listeners: ```ClientConnectedListener``` and ```ClientDisconnectedListener```. 138 | 139 | ### Sending Messages 140 | Both ```WroupService``` and ```WroupClient``` can send messages to all the clients connected to the group. The object to send is a ```MessageWrapper``` that contains the sender device, a type and the message in String format. There are four types of messages: 141 | * NORMAL: The normal type is which you must to use. The rest of them are message types to manage the state of group between clients and server. 142 | * CONNECTION_MESSAGE: A connection message is sent when a new client is connected to the group. 143 | * DISCONNECTION_MESSAGE: A disconnection message is sent when a client is disconnected from the group. 144 | * REGISTERED_DEVICES: This message is sent by the server to the client when it has been connected to the group. The content of the message is the clients already connected to the group, with this approach the client can know in every moment the devices connected. 145 | 146 | You can send a message as follow: 147 | ```java 148 | MessageWrapper message = new MessageWrapper(); 149 | message.setMessage("This is a message to all clients"); 150 | message.setMessageType(MessageWrapper.MessageType.NORMAL); 151 | 152 | wroupClient.sendMessageToAllClients(message); 153 | ``` 154 | 155 | To receive the messages you have to implement the ```DataReceivedListener``` and set to the ```WroupClient``` or ```WroupServer``` instance: 156 | ```java 157 | @Override 158 | public void onDataReceived(MessageWrapper messageWrapper) { 159 | // New message received 160 | } 161 | ``` 162 | 163 | Messages that are not of ```NORMAL``` type are excluded from this listener. 164 | 165 | ### Cleaning the instances 166 | To disconnect from a group (client) or delete a group (server) you must call to: 167 | #### Server 168 | ```java 169 | wroupService.disconnect(); 170 | ``` 171 | #### Client 172 | ```java 173 | wroupClient.disconnect(); 174 | ``` 175 | 176 | 177 | ## Example App 178 | If you have see the library in action you can dowload the [Wroup-Example](https://github.com/ble180/Wroup-Example) project. It's a chat application with you can create a group (server) and other devices (clients) can be join to the group and have a conversation. 179 | 180 | ## Contributing 181 | Feel free to submit issues, requests, or fork the project. 182 | 183 | ## License 184 | (MIT) 185 | 186 | ``` 187 | Copyright (c) 2015 Peak Digital LLC 188 | 189 | Permission is hereby granted, free of charge, to any person obtaining a copy 190 | of this software and associated documentation files (the "Software"), to deal 191 | in the Software without restriction, including without limitation the rights 192 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 193 | copies of the Software, and to permit persons to whom the Software is 194 | furnished to do so, subject to the following conditions: 195 | 196 | The above copyright notice and this permission notice shall be included in all 197 | copies or substantial portions of the Software. 198 | 199 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 200 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 201 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 202 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 203 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 204 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 205 | SOFTWA -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/service/WroupService.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.service; 2 | 3 | 4 | import android.content.Context; 5 | import android.net.wifi.p2p.WifiP2pGroup; 6 | import android.net.wifi.p2p.WifiP2pInfo; 7 | import android.net.wifi.p2p.WifiP2pManager; 8 | import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; 9 | import android.os.AsyncTask; 10 | import android.util.Log; 11 | 12 | import com.abemart.wroup.common.WiFiP2PError; 13 | import com.abemart.wroup.common.WiFiP2PInstance; 14 | import com.abemart.wroup.common.WroupDevice; 15 | import com.abemart.wroup.common.direct.WiFiDirectUtils; 16 | import com.abemart.wroup.common.listeners.ClientConnectedListener; 17 | import com.abemart.wroup.common.listeners.ClientDisconnectedListener; 18 | import com.abemart.wroup.common.listeners.DataReceivedListener; 19 | import com.abemart.wroup.common.listeners.PeerConnectedListener; 20 | import com.abemart.wroup.common.listeners.ServiceRegisteredListener; 21 | import com.abemart.wroup.common.messages.DisconnectionMessageContent; 22 | import com.abemart.wroup.common.messages.MessageWrapper; 23 | import com.abemart.wroup.common.messages.RegisteredDevicesMessageContent; 24 | import com.abemart.wroup.common.messages.RegistrationMessageContent; 25 | import com.google.gson.Gson; 26 | 27 | import org.apache.commons.io.IOUtils; 28 | 29 | import java.io.IOException; 30 | import java.io.OutputStream; 31 | import java.net.InetAddress; 32 | import java.net.InetSocketAddress; 33 | import java.net.ServerSocket; 34 | import java.net.Socket; 35 | import java.util.ArrayList; 36 | import java.util.HashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | 40 | /** 41 | * Singleton class acting as a "server" device. 42 | *

43 | * With Wroup Library you can register a service in the current local network to be discovered by 44 | * other devices. When a service is registered a WiFi P2P Group is created, we know it as Wroup ;) 45 | *

46 | * WroupService is the group owner and it manages the group changes (connections and 47 | * disconnections). When a new client is connected/disconnected the service device notify to the 48 | * other devices connected. 49 | *

50 | * To register a service you must do the following: 51 | *

 52 |  * {@code
 53 |  *
 54 |  * wiFiP2PService = WroupService.getInstance(getApplicationContext());
 55 |  * wiFiP2PService.registerService(groupName, new ServiceRegisteredListener() {
 56 |  *
 57 |  *  public void onSuccessServiceRegistered() {
 58 |  *      Log.i(TAG, "Wroup created. Waiting for client connections...");
 59 |  *  }
 60 |  *
 61 |  *  public void onErrorServiceRegistered(WiFiP2PError wiFiP2PError) {
 62 |  *      Log.e(TAG, "Error creating group");
 63 |  *  }
 64 |  *
 65 |  * });
 66 |  * }
 67 |  * 
68 | */ 69 | public class WroupService implements PeerConnectedListener { 70 | 71 | 72 | private static final String TAG = WroupService.class.getSimpleName(); 73 | 74 | private static final String SERVICE_TYPE = "_wroup._tcp"; 75 | public static final String SERVICE_PORT_PROPERTY = "SERVICE_PORT"; 76 | public static final Integer SERVICE_PORT_VALUE = 9999; 77 | public static final String SERVICE_NAME_PROPERTY = "SERVICE_NAME"; 78 | public static final String SERVICE_NAME_VALUE = "WROUP"; 79 | public static final String SERVICE_GROUP_NAME = "GROUP_NAME"; 80 | 81 | private static WroupService instance; 82 | 83 | private DataReceivedListener dataReceivedListener; 84 | private ClientConnectedListener clientConnectedListener; 85 | private ClientDisconnectedListener clientDisconnectedListener; 86 | private Map clientsConnected = new HashMap<>(); 87 | private WiFiP2PInstance wiFiP2PInstance; 88 | 89 | private ServerSocket serverSocket; 90 | private Boolean groupAlreadyCreated = false; 91 | 92 | private WroupService(Context context) { 93 | wiFiP2PInstance = WiFiP2PInstance.getInstance(context); 94 | wiFiP2PInstance.setPeerConnectedListener(this); 95 | } 96 | 97 | /** 98 | * Return the WroupService instance. If the instance doesn't exist yet, it's 99 | * created and returned. 100 | * 101 | * @param context The application context. 102 | * @return The actual WroupService instance. 103 | */ 104 | public static WroupService getInstance(Context context) { 105 | if (instance == null) { 106 | instance = new WroupService(context); 107 | } 108 | return instance; 109 | } 110 | 111 | /** 112 | * Start a Wroup service registration in the actual local network with the name indicated in 113 | * the arguments. When te service is registered the method 114 | * {@link ServiceRegisteredListener#onSuccessServiceRegistered()} is called. 115 | * 116 | * @param groupName The name of the group that want to be created. 117 | * @param serviceRegisteredListener The ServiceRegisteredListener to notify 118 | * registration changes. 119 | */ 120 | public void registerService(String groupName, ServiceRegisteredListener serviceRegisteredListener) { 121 | registerService(groupName, null, serviceRegisteredListener); 122 | } 123 | 124 | /** 125 | * Start a Wroup service registration in the actual local network with the name indicated in 126 | * the arguments. When te service is registered the method 127 | * {@link ServiceRegisteredListener#onSuccessServiceRegistered()} is called. 128 | * 129 | * @param groupName The name of the group that want to be created. 130 | * @param customProperties A Map of custom properties which will be registered with the 131 | * service. This properties can be accessed by the client devices 132 | * when the service is discovered. 133 | * @param serviceRegisteredListener The ServiceRegisteredListener to notify 134 | * registration changes. 135 | */ 136 | public void registerService(String groupName, Map customProperties, final ServiceRegisteredListener serviceRegisteredListener) { 137 | 138 | // We need to start peer discovering because otherwise the clients cannot found the service 139 | wiFiP2PInstance.startPeerDiscovering(); 140 | 141 | Map record = new HashMap<>(); 142 | record.put(SERVICE_PORT_PROPERTY, SERVICE_PORT_VALUE.toString()); 143 | record.put(SERVICE_NAME_PROPERTY, SERVICE_NAME_VALUE); 144 | record.put(SERVICE_GROUP_NAME, groupName); 145 | 146 | // Insert the custom properties to the record Map 147 | if (customProperties != null) { 148 | for (Map.Entry entry : customProperties.entrySet()) { 149 | record.put(entry.getKey(), entry.getValue()); 150 | } 151 | } 152 | 153 | WifiP2pDnsSdServiceInfo serviceInfo = WifiP2pDnsSdServiceInfo.newInstance(groupName, SERVICE_TYPE, record); 154 | 155 | wiFiP2PInstance.getWifiP2pManager().clearLocalServices(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 156 | 157 | @Override 158 | public void onSuccess() { 159 | Log.d(TAG, "Success clearing local services"); 160 | } 161 | 162 | @Override 163 | public void onFailure(int reason) { 164 | Log.e(TAG, "Error clearing local services: " + reason); 165 | } 166 | }); 167 | 168 | wiFiP2PInstance.getWifiP2pManager().addLocalService(wiFiP2PInstance.getChannel(), serviceInfo, new WifiP2pManager.ActionListener() { 169 | 170 | @Override 171 | public void onSuccess() { 172 | Log.d(TAG, "Service registered"); 173 | serviceRegisteredListener.onSuccessServiceRegistered(); 174 | 175 | // Create the group to the clients can connect to it 176 | removeAndCreateGroup(); 177 | 178 | // Create the socket that will accept request 179 | createServerSocket(); 180 | } 181 | 182 | @Override 183 | public void onFailure(int reason) { 184 | WiFiP2PError wiFiP2PError = WiFiP2PError.fromReason(reason); 185 | if (wiFiP2PError != null) { 186 | Log.e(TAG, "Failure registering the service. Reason: " + wiFiP2PError.name()); 187 | serviceRegisteredListener.onErrorServiceRegistered(wiFiP2PError); 188 | } 189 | } 190 | 191 | }); 192 | } 193 | 194 | /** 195 | * Remove the group created. Before the disconnection, the server sends a message to all 196 | * clients connected to notify the disconnection. 197 | */ 198 | public void disconnect() { 199 | if (serverSocket != null) { 200 | try { 201 | serverSocket.close(); 202 | Log.i(TAG, "ServerSocket closed"); 203 | } catch (IOException e) { 204 | Log.e(TAG, "Error closing the serverSocket"); 205 | } 206 | } 207 | 208 | groupAlreadyCreated = false; 209 | serverSocket = null; 210 | clientsConnected.clear(); 211 | 212 | WiFiDirectUtils.removeGroup(wiFiP2PInstance); 213 | WiFiDirectUtils.clearLocalServices(wiFiP2PInstance); 214 | WiFiDirectUtils.stopPeerDiscovering(wiFiP2PInstance); 215 | } 216 | 217 | /** 218 | * Set the listener to know when data is received from the client devices connected to the group. 219 | * 220 | * @param dataReceivedListener The DataReceivedListener to notify data entries. 221 | */ 222 | public void setDataReceivedListener(DataReceivedListener dataReceivedListener) { 223 | this.dataReceivedListener = dataReceivedListener; 224 | } 225 | 226 | /** 227 | * Set the listener to know when a client has been disconnected from the group. 228 | * 229 | * @param clientDisconnectedListener The ClientDisconnectedListener to notify 230 | * client disconnections. 231 | */ 232 | public void setClientDisconnectedListener(ClientDisconnectedListener clientDisconnectedListener) { 233 | this.clientDisconnectedListener = clientDisconnectedListener; 234 | } 235 | 236 | /** 237 | * Set the listener to know when a new client is registered in the group. 238 | * 239 | * @param clientConnectedListener The ClientConnectedListener to notify new 240 | * connections in the group. 241 | */ 242 | public void setClientConnectedListener(ClientConnectedListener clientConnectedListener) { 243 | this.clientConnectedListener = clientConnectedListener; 244 | } 245 | 246 | @Override 247 | public void onPeerConnected(WifiP2pInfo wifiP2pInfo) { 248 | Log.i(TAG, "OnPeerConnected..."); 249 | 250 | if (wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner) { 251 | Log.i(TAG, "I am the group owner"); 252 | Log.i(TAG, "My addess is: " + wifiP2pInfo.groupOwnerAddress.getHostAddress()); 253 | } 254 | } 255 | 256 | /** 257 | * Send a message to all the devices connected to the group. 258 | * 259 | * @param message The message to be sent. 260 | */ 261 | public void sendMessageToAllClients(final MessageWrapper message) { 262 | for (WroupDevice clientDevice : clientsConnected.values()) { 263 | sendMessage(clientDevice, message); 264 | } 265 | } 266 | 267 | /** 268 | * Send a message to the desired device who it's connected in the group. 269 | * 270 | * @param device The receiver of the message. 271 | * @param message The message to be sent. 272 | */ 273 | public void sendMessage(final WroupDevice device, MessageWrapper message) { 274 | // Set the actual device to the message 275 | message.setWroupDevice(wiFiP2PInstance.getThisDevice()); 276 | 277 | new AsyncTask() { 278 | @Override 279 | protected Void doInBackground(MessageWrapper... params) { 280 | if (device != null && device.getDeviceServerSocketIP() != null) { 281 | try { 282 | Socket socket = new Socket(); 283 | socket.bind(null); 284 | 285 | InetSocketAddress hostAddres = new InetSocketAddress(device.getDeviceServerSocketIP(), device.getDeviceServerSocketPort()); 286 | socket.connect(hostAddres, 2000); 287 | 288 | Gson gson = new Gson(); 289 | String messageJson = gson.toJson(params[0]); 290 | 291 | OutputStream outputStream = socket.getOutputStream(); 292 | outputStream.write(messageJson.getBytes(), 0, messageJson.getBytes().length); 293 | 294 | Log.d(TAG, "Sending data: " + params[0]); 295 | 296 | socket.close(); 297 | outputStream.close(); 298 | } catch (IOException e) { 299 | Log.e(TAG, "Error creating client socket: " + e.getMessage()); 300 | } 301 | } 302 | 303 | return null; 304 | } 305 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message); 306 | } 307 | 308 | private void createServerSocket() { 309 | if (serverSocket == null) { 310 | new AsyncTask() { 311 | 312 | @Override 313 | protected Void doInBackground(Void... params) { 314 | 315 | try { 316 | serverSocket = new ServerSocket(SERVICE_PORT_VALUE); 317 | Log.i(TAG, "Server socket created. Accepting requests..."); 318 | 319 | while (true) { 320 | Socket socket = serverSocket.accept(); 321 | 322 | String dataReceived = IOUtils.toString(socket.getInputStream()); 323 | Log.i(TAG, "Data received: " + dataReceived); 324 | Log.i(TAG, "From IP: " + socket.getInetAddress().getHostAddress()); 325 | 326 | Gson gson = new Gson(); 327 | MessageWrapper messageWrapper = gson.fromJson(dataReceived, MessageWrapper.class); 328 | onMessageReceived(messageWrapper, socket.getInetAddress()); 329 | } 330 | } catch (IOException e) { 331 | Log.e(TAG, "Error creating/closing server socket: " + e.getMessage()); 332 | } 333 | 334 | return null; 335 | } 336 | 337 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 338 | } 339 | } 340 | 341 | 342 | private void removeAndCreateGroup() { 343 | wiFiP2PInstance.getWifiP2pManager().requestGroupInfo(wiFiP2PInstance.getChannel(), new WifiP2pManager.GroupInfoListener() { 344 | 345 | @Override 346 | public void onGroupInfoAvailable(final WifiP2pGroup group) { 347 | if (group != null) { 348 | wiFiP2PInstance.getWifiP2pManager().removeGroup(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 349 | @Override 350 | public void onSuccess() { 351 | Log.d(TAG, "Group deleted"); 352 | Log.d(TAG, "\tNetwordk Name: " + group.getNetworkName()); 353 | Log.d(TAG, "\tInterface: " + group.getInterface()); 354 | Log.d(TAG, "\tPassword: " + group.getPassphrase()); 355 | Log.d(TAG, "\tOwner Name: " + group.getOwner().deviceName); 356 | Log.d(TAG, "\tOwner Address: " + group.getOwner().deviceAddress); 357 | Log.d(TAG, "\tClient list size: " + group.getClientList().size()); 358 | 359 | groupAlreadyCreated = false; 360 | 361 | // Now we can create the group 362 | createGroup(); 363 | } 364 | 365 | @Override 366 | public void onFailure(int reason) { 367 | Log.e(TAG, "Error deleting group"); 368 | } 369 | }); 370 | } else { 371 | createGroup(); 372 | } 373 | } 374 | }); 375 | } 376 | 377 | private void createGroup() { 378 | if (!groupAlreadyCreated) { 379 | wiFiP2PInstance.getWifiP2pManager().createGroup(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 380 | 381 | @Override 382 | public void onSuccess() { 383 | Log.i(TAG, "Group created!"); 384 | groupAlreadyCreated = true; 385 | } 386 | 387 | @Override 388 | public void onFailure(int reason) { 389 | Log.e(TAG, "Error creating group. Reason: " + WiFiP2PError.fromReason(reason)); 390 | } 391 | }); 392 | } 393 | } 394 | 395 | private void onMessageReceived(MessageWrapper messageWrapper, InetAddress fromAddress) { 396 | if (messageWrapper.getMessageType().equals(MessageWrapper.MessageType.CONNECTION_MESSAGE)) { 397 | Gson gson = new Gson(); 398 | 399 | String messageContentStr = messageWrapper.getMessage(); 400 | RegistrationMessageContent registrationMessageContent = gson.fromJson(messageContentStr, RegistrationMessageContent.class); 401 | WroupDevice client = registrationMessageContent.getWroupDevice(); 402 | client.setDeviceServerSocketIP(fromAddress.getHostAddress()); 403 | clientsConnected.put(client.getDeviceMac(), client); 404 | 405 | Log.d(TAG, "New client registered:"); 406 | Log.d(TAG, "\tDevice name: " + client.getDeviceName()); 407 | Log.d(TAG, "\tDecive mac: " + client.getDeviceMac()); 408 | Log.d(TAG, "\tDevice IP: " + client.getDeviceServerSocketIP()); 409 | Log.d(TAG, "\tDevice ServerSocket port: " + client.getDeviceServerSocketPort()); 410 | 411 | // Sending to all clients that new client is connected 412 | for (WroupDevice device : clientsConnected.values()) { 413 | if (!client.getDeviceMac().equals(device.getDeviceMac())) { 414 | sendConnectionMessage(device, client); 415 | } else { 416 | sendRegisteredDevicesMessage(device); 417 | } 418 | } 419 | 420 | if (clientConnectedListener != null) { 421 | clientConnectedListener.onClientConnected(client); 422 | } 423 | } else if (messageWrapper.getMessageType().equals(MessageWrapper.MessageType.DISCONNECTION_MESSAGE)) { 424 | Gson gson = new Gson(); 425 | 426 | String messageContentStr = messageWrapper.getMessage(); 427 | DisconnectionMessageContent disconnectionMessageContent = gson.fromJson(messageContentStr, DisconnectionMessageContent.class); 428 | WroupDevice client = disconnectionMessageContent.getWroupDevice(); 429 | clientsConnected.remove(client.getDeviceMac()); 430 | 431 | Log.d(TAG, "Client disconnected:"); 432 | Log.d(TAG, "\tDevice name: " + client.getDeviceName()); 433 | Log.d(TAG, "\tDecive mac: " + client.getDeviceMac()); 434 | Log.d(TAG, "\tDevice IP: " + client.getDeviceServerSocketIP()); 435 | Log.d(TAG, "\tDevice ServerSocket port: " + client.getDeviceServerSocketPort()); 436 | 437 | // Sending to all clients that a client is disconnected now 438 | for (WroupDevice device : clientsConnected.values()) { 439 | if (!client.getDeviceMac().equals(device.getDeviceMac())) { 440 | sendDisconnectionMessage(device, client); 441 | } 442 | } 443 | 444 | if (clientDisconnectedListener != null) { 445 | clientDisconnectedListener.onClientDisconnected(client); 446 | } 447 | } else { 448 | if (dataReceivedListener != null) { 449 | dataReceivedListener.onDataReceived(messageWrapper); 450 | } 451 | } 452 | } 453 | 454 | private void sendConnectionMessage(WroupDevice deviceToSend, WroupDevice deviceConnected) { 455 | RegistrationMessageContent content = new RegistrationMessageContent(); 456 | content.setWroupDevice(deviceConnected); 457 | 458 | Gson gson = new Gson(); 459 | 460 | MessageWrapper messageWrapper = new MessageWrapper(); 461 | messageWrapper.setMessageType(MessageWrapper.MessageType.CONNECTION_MESSAGE); 462 | messageWrapper.setMessage(gson.toJson(content)); 463 | 464 | sendMessage(deviceToSend, messageWrapper); 465 | } 466 | 467 | private void sendDisconnectionMessage(WroupDevice deviceToSend, WroupDevice deviceDisconnected) { 468 | DisconnectionMessageContent content = new DisconnectionMessageContent(); 469 | content.setWroupDevice(deviceDisconnected); 470 | 471 | Gson gson = new Gson(); 472 | 473 | MessageWrapper disconnectionMessage = new MessageWrapper(); 474 | disconnectionMessage.setMessageType(MessageWrapper.MessageType.DISCONNECTION_MESSAGE); 475 | disconnectionMessage.setMessage(gson.toJson(content)); 476 | 477 | sendMessage(deviceToSend, disconnectionMessage); 478 | } 479 | 480 | private void sendRegisteredDevicesMessage(WroupDevice deviceToSend) { 481 | List devicesConnected = new ArrayList<>(); 482 | for (WroupDevice device : clientsConnected.values()) { 483 | if (!device.getDeviceMac().equals(deviceToSend.getDeviceMac())) { 484 | devicesConnected.add(device); 485 | } 486 | } 487 | 488 | RegisteredDevicesMessageContent content = new RegisteredDevicesMessageContent(); 489 | content.setDevicesRegistered(devicesConnected); 490 | 491 | Gson gson = new Gson(); 492 | 493 | MessageWrapper messageWrapper = new MessageWrapper(); 494 | messageWrapper.setMessageType(MessageWrapper.MessageType.REGISTERED_DEVICES); 495 | messageWrapper.setMessage(gson.toJson(content)); 496 | 497 | sendMessage(deviceToSend, messageWrapper); 498 | } 499 | 500 | } 501 | -------------------------------------------------------------------------------- /src/main/java/com/abemart/wroup/client/WroupClient.java: -------------------------------------------------------------------------------- 1 | package com.abemart.wroup.client; 2 | 3 | 4 | import android.content.Context; 5 | import android.net.wifi.p2p.WifiP2pConfig; 6 | import android.net.wifi.p2p.WifiP2pDevice; 7 | import android.net.wifi.p2p.WifiP2pInfo; 8 | import android.net.wifi.p2p.WifiP2pManager; 9 | import android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener; 10 | import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener; 11 | import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest; 12 | import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; 13 | import android.os.AsyncTask; 14 | import android.os.Handler; 15 | import android.util.Log; 16 | 17 | import com.abemart.wroup.common.WiFiP2PError; 18 | import com.abemart.wroup.common.WiFiP2PInstance; 19 | import com.abemart.wroup.common.WroupDevice; 20 | import com.abemart.wroup.common.WroupServiceDevice; 21 | import com.abemart.wroup.common.direct.WiFiDirectUtils; 22 | import com.abemart.wroup.common.listeners.ClientDisconnectedListener; 23 | import com.abemart.wroup.common.listeners.ClientConnectedListener; 24 | import com.abemart.wroup.common.listeners.DataReceivedListener; 25 | import com.abemart.wroup.common.listeners.PeerConnectedListener; 26 | import com.abemart.wroup.common.listeners.ServiceConnectedListener; 27 | import com.abemart.wroup.common.listeners.ServiceDisconnectedListener; 28 | import com.abemart.wroup.common.listeners.ServiceDiscoveredListener; 29 | import com.abemart.wroup.common.messages.DisconnectionMessageContent; 30 | import com.abemart.wroup.common.messages.MessageWrapper; 31 | import com.abemart.wroup.common.messages.RegisteredDevicesMessageContent; 32 | import com.abemart.wroup.common.messages.RegistrationMessageContent; 33 | import com.abemart.wroup.service.WroupService; 34 | import com.google.gson.Gson; 35 | 36 | import org.apache.commons.io.IOUtils; 37 | 38 | import java.io.IOException; 39 | import java.io.OutputStream; 40 | import java.net.InetSocketAddress; 41 | import java.net.ServerSocket; 42 | import java.net.Socket; 43 | import java.util.ArrayList; 44 | import java.util.Collection; 45 | import java.util.HashMap; 46 | import java.util.List; 47 | import java.util.Map; 48 | 49 | /** 50 | * Singleton class acting as a client device. 51 | *

52 | * Wroup Library will allow you to create a "Server" device and multiple "Client" devices. The 53 | * {@link WroupService} can register a service which could be discover by the multiple client 54 | * devices. The client will search the Wroup services registered in the local network and could 55 | * connect to any ot them. 56 | *

57 | * WroupClient only discover Wroup services, can exist multiple services registered with WiFi-P2P in 58 | * the same local network but only a WroupClient instance will found services registered by a 59 | * WroupService device. 60 | *

61 | * To discover the Wroup services registered you only need to do the following: 62 | *

 63 |  * {@code wiFiP2PClient = WroupClient.getInstance(getApplicationContext());
 64 |  * wiFiP2PClient.discoverServices(5000L, new ServiceDiscoveredListener() {
 65 |  *
 66 |  *  public void onNewServiceDeviceDiscovered(WroupServiceDevice serviceDevice) {
 67 |  *      Log.i(TAG, "New service found:");
 68 |  *      Log.i(TAG, "\tName: " + serviceDevice.getDeviceName());
 69 |  *  }
 70 |  *
 71 |  *  public void onFinishServiceDeviceDiscovered(List serviceDevices) {
 72 |  *      Log.i(TAG, "Found '" + serviceDevices.size() + "' services");
 73 |  *  }
 74 |  *
 75 |  *  public void onError(WiFiP2PError wiFiP2PError) {
 76 |  *      Toast.makeText(getApplicationContext(), "Error searching groups: " + wiFiP2PError, Toast.LENGTH_LONG).show();
 77 |  *  }
 78 |  * });
 79 |  * }
 80 |  * 
81 | * Once that you have the desired service to which connect you must call to 82 | * {@link #connectToService(WroupServiceDevice, ServiceConnectedListener)} passing as argument the 83 | * appropiate {@link WroupServiceDevice} obtained in the discoverServices() call. 84 | */ 85 | public class WroupClient implements PeerConnectedListener, ServiceDisconnectedListener { 86 | 87 | private static final String TAG = WroupClient.class.getSimpleName(); 88 | 89 | private static WroupClient instance; 90 | 91 | private List serviceDevices = new ArrayList<>(); 92 | 93 | private DnsSdTxtRecordListener dnsSdTxtRecordListener; 94 | private DnsSdServiceResponseListener dnsSdServiceResponseListener; 95 | private ServiceConnectedListener serviceConnectedListener; 96 | private DataReceivedListener dataReceivedListener; 97 | private ServiceDisconnectedListener serviceDisconnectedListener; 98 | private ClientConnectedListener clientConnectedListener; 99 | private ClientDisconnectedListener clientDisconnectedListener; 100 | 101 | private ServerSocket serverSocket; 102 | 103 | private WiFiP2PInstance wiFiP2PInstance; 104 | private WroupDevice serviceDevice; 105 | private Map clientsConnected; 106 | private Boolean isRegistered = false; 107 | 108 | private WroupClient(Context context) { 109 | wiFiP2PInstance = WiFiP2PInstance.getInstance(context); 110 | wiFiP2PInstance.setPeerConnectedListener(this); 111 | wiFiP2PInstance.setServerDisconnectedListener(this); 112 | this.clientsConnected = new HashMap<>(); 113 | } 114 | 115 | /** 116 | * Return the WroupClient instance. If the instance doesn't exist yet, it's created and returned. 117 | * 118 | * @param context The application context. 119 | * @return The actual WroupClient instance. 120 | */ 121 | public static WroupClient getInstance(Context context) { 122 | if (instance == null && context != null) { 123 | instance = new WroupClient(context); 124 | } 125 | return instance; 126 | } 127 | 128 | /** 129 | * Start to discover Wroup services registered in the current local network. 130 | *

131 | * Before you start to discover services you must to register the WiFiDirectBroadcastReceiver 132 | * in the onResume() method of your activity. 133 | * 134 | * @param discoveringTimeInMillis The time in milliseconds to search for registered Wroup services. 135 | * @param serviceDiscoveredListener The listener to notify changes of the services found by the client. 136 | * @see com.abemart.wroup.common.WiFiDirectBroadcastReceiver 137 | */ 138 | public void discoverServices(Long discoveringTimeInMillis, final ServiceDiscoveredListener serviceDiscoveredListener) { 139 | serviceDevices.clear(); 140 | 141 | // We need to start discovering peers to activate the service search 142 | wiFiP2PInstance.startPeerDiscovering(); 143 | 144 | setupDnsListeners(wiFiP2PInstance, serviceDiscoveredListener); 145 | WiFiDirectUtils.clearServiceRequest(wiFiP2PInstance); 146 | 147 | WifiP2pServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance(); 148 | wiFiP2PInstance.getWifiP2pManager().addServiceRequest(wiFiP2PInstance.getChannel(), serviceRequest, new WifiP2pManager.ActionListener() { 149 | 150 | @Override 151 | public void onSuccess() { 152 | Log.d(TAG, "Success adding service request"); 153 | } 154 | 155 | @Override 156 | public void onFailure(int reason) { 157 | WiFiP2PError wiFiP2PError = WiFiP2PError.fromReason(reason); 158 | Log.e(TAG, "Error adding service request. Reason: " + WiFiP2PError.fromReason(reason)); 159 | serviceDiscoveredListener.onError(wiFiP2PError); 160 | } 161 | 162 | }); 163 | 164 | wiFiP2PInstance.getWifiP2pManager().discoverServices(wiFiP2PInstance.getChannel(), new WifiP2pManager.ActionListener() { 165 | 166 | @Override 167 | public void onSuccess() { 168 | Log.d(TAG, "Success initiating disconvering services"); 169 | } 170 | 171 | @Override 172 | public void onFailure(int reason) { 173 | WiFiP2PError wiFiP2PError = WiFiP2PError.fromReason(reason); 174 | if (wiFiP2PError != null) { 175 | Log.e(TAG, "Error discovering services. Reason: " + wiFiP2PError.name()); 176 | serviceDiscoveredListener.onError(wiFiP2PError); 177 | } 178 | } 179 | 180 | }); 181 | 182 | Handler handler = new Handler(); 183 | handler.postDelayed(new Runnable() { 184 | @Override 185 | public void run() { 186 | serviceDiscoveredListener.onFinishServiceDeviceDiscovered(serviceDevices); 187 | } 188 | }, discoveringTimeInMillis); 189 | } 190 | 191 | /** 192 | * Start the connection with the WroupServiceDevice passed by argument. When the 193 | * connection is stablished with the device service the {@link ServiceConnectedListener#onServiceConnected(WroupDevice)} 194 | * method is called. 195 | *

196 | * When the client is connected to the service, it's connected to the WiFi Direct Group created 197 | * by the service device. Once the client belongs to the "Wroup" (group), it can know when a new 198 | * client is connected or disconnected from it. 199 | * 200 | * @param serviceDevice The WroupServiceDevice with you want to connect. 201 | * @param serviceConnectedListener The listener to know when the client device is connected to 202 | * the desired service. 203 | */ 204 | public void connectToService(final WroupServiceDevice serviceDevice, ServiceConnectedListener serviceConnectedListener) { 205 | this.serviceDevice = serviceDevice; 206 | this.serviceConnectedListener = serviceConnectedListener; 207 | 208 | WifiP2pConfig wifiP2pConfig = new WifiP2pConfig(); 209 | wifiP2pConfig.deviceAddress = serviceDevice.getDeviceMac(); 210 | 211 | wiFiP2PInstance.getWifiP2pManager().connect(wiFiP2PInstance.getChannel(), wifiP2pConfig, new WifiP2pManager.ActionListener() { 212 | @Override 213 | public void onSuccess() { 214 | Log.i(TAG, "Initiated connection to device: "); 215 | Log.i(TAG, "\tDevice name: " + serviceDevice.getDeviceName()); 216 | Log.i(TAG, "\tDevice address: " + serviceDevice.getDeviceMac()); 217 | } 218 | 219 | @Override 220 | public void onFailure(int reason) { 221 | Log.e(TAG, "Fail initiation connection. Reason: " + WiFiP2PError.fromReason(reason)); 222 | } 223 | }); 224 | } 225 | 226 | /** 227 | * Set the listener to know when data is received from the service device or other client devices 228 | * connected to the same group. 229 | * 230 | * @param dataReceivedListener The DataReceivedListener to notify data entries. 231 | */ 232 | public void setDataReceivedListener(DataReceivedListener dataReceivedListener) { 233 | this.dataReceivedListener = dataReceivedListener; 234 | } 235 | 236 | /** 237 | * Set the listener to notify when the service device has been disconnected. 238 | * 239 | * @param serviceDisconnectedListener The ServiceDisconnectedListener to notify 240 | * service device disconnections. 241 | */ 242 | public void setServerDisconnetedListener(ServiceDisconnectedListener serviceDisconnectedListener) { 243 | this.serviceDisconnectedListener = serviceDisconnectedListener; 244 | } 245 | 246 | /** 247 | * Set the listener to know when a new client is registered in the actual group. 248 | * 249 | * @param clientConnectedListener The ClientConnectedListener to notify new 250 | * connections in the group. 251 | */ 252 | public void setClientConnectedListener(ClientConnectedListener clientConnectedListener) { 253 | this.clientConnectedListener = clientConnectedListener; 254 | } 255 | 256 | /** 257 | * Set the listener to know when a client has been disconnected from the group. 258 | * 259 | * @param clientDisconnectedListener The ClientDisconnectedListener to notify 260 | * client disconnections. 261 | */ 262 | public void setClientDisconnectedListener(ClientDisconnectedListener clientDisconnectedListener) { 263 | this.clientDisconnectedListener = clientDisconnectedListener; 264 | } 265 | 266 | @Override 267 | public void onPeerConnected(WifiP2pInfo wifiP2pInfo) { 268 | Log.i(TAG, "OnPeerConnected..."); 269 | 270 | if (wifiP2pInfo.groupFormed && wifiP2pInfo.isGroupOwner) { 271 | Log.e(TAG, "I shouldn't be the group owner, I'am a client!"); 272 | } 273 | 274 | if (wifiP2pInfo.groupFormed && serviceDevice != null && !isRegistered) { 275 | serviceDevice.setDeviceServerSocketIP(wifiP2pInfo.groupOwnerAddress.getHostAddress()); 276 | Log.i(TAG, "The Server Address is: " + wifiP2pInfo.groupOwnerAddress.getHostAddress()); 277 | 278 | // We are connected to the server. Create a server socket to receive messages 279 | createServerSocket(); 280 | 281 | // FIXME - Change this into a server socket creation listener or similar 282 | // Wait 2 seconds for the server socket creation 283 | Handler handler = new Handler(); 284 | handler.postDelayed(new Runnable() { 285 | public void run() { 286 | // We send the negotiation message to the server 287 | sendServerRegistrationMessage(); 288 | if (serviceConnectedListener != null) { 289 | serviceConnectedListener.onServiceConnected(serviceDevice); 290 | } 291 | 292 | isRegistered = true; 293 | } 294 | }, 2000); 295 | } 296 | } 297 | 298 | @Override 299 | public void onServerDisconnectedListener() { 300 | // If the server is disconnected the client is cleared 301 | disconnect(); 302 | 303 | if (serviceDisconnectedListener != null) { 304 | serviceDisconnectedListener.onServerDisconnectedListener(); 305 | } 306 | } 307 | 308 | /** 309 | * Send a message to the service device. 310 | * 311 | * @param message The message to be sent. 312 | */ 313 | public void sendMessageToServer(MessageWrapper message) { 314 | sendMessage(serviceDevice, message); 315 | } 316 | 317 | /** 318 | * Send a message to all the devices connected to the group, including the service device. 319 | * 320 | * @param message The message to be sent. 321 | */ 322 | public void sendMessageToAllClients(MessageWrapper message) { 323 | sendMessageToServer(message); 324 | 325 | for (WroupDevice device : clientsConnected.values()) { 326 | if (!device.getDeviceMac().equals(wiFiP2PInstance.getThisDevice().getDeviceMac())) { 327 | sendMessage(device, message); 328 | } 329 | } 330 | } 331 | 332 | /** 333 | * Send a message to the desired device who it's connected in the group. 334 | * 335 | * @param device The receiver of the message. 336 | * @param message The message to be sent. 337 | */ 338 | public void sendMessage(final WroupDevice device, MessageWrapper message) { 339 | // Set the actual device to the message 340 | message.setWroupDevice(wiFiP2PInstance.getThisDevice()); 341 | 342 | new AsyncTask() { 343 | @Override 344 | protected Void doInBackground(MessageWrapper... params) { 345 | if (device != null && device.getDeviceServerSocketIP() != null) { 346 | try { 347 | Socket socket = new Socket(); 348 | socket.bind(null); 349 | 350 | InetSocketAddress hostAddres = new InetSocketAddress(device.getDeviceServerSocketIP(), device.getDeviceServerSocketPort()); 351 | socket.connect(hostAddres, 2000); 352 | 353 | Gson gson = new Gson(); 354 | String messageJson = gson.toJson(params[0]); 355 | 356 | OutputStream outputStream = socket.getOutputStream(); 357 | outputStream.write(messageJson.getBytes(), 0, messageJson.getBytes().length); 358 | 359 | Log.d(TAG, "Sending data: " + params[0]); 360 | 361 | socket.close(); 362 | outputStream.close(); 363 | } catch (IOException e) { 364 | Log.e(TAG, "Error creating client socket: " + e.getMessage()); 365 | } 366 | } 367 | 368 | return null; 369 | } 370 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message); 371 | } 372 | 373 | /** 374 | * Disconnect from the actual group connected. Before the disconnection, the client sends a 375 | * message to the service device to notify the disconnection. 376 | */ 377 | public void disconnect() { 378 | if (serverSocket != null) { 379 | try { 380 | serverSocket.close(); 381 | Log.i(TAG, "ServerSocket closed"); 382 | } catch (IOException e) { 383 | Log.e(TAG, "Error closing the serverSocket"); 384 | } 385 | } 386 | 387 | sendDisconnectionMessage(); 388 | 389 | // FIXME - Change this into a message sent it listener 390 | // Wait 2 seconds to disconnection message was sent 391 | Handler handler = new Handler(); 392 | handler.postDelayed(new Runnable() { 393 | public void run() { 394 | WiFiDirectUtils.clearServiceRequest(wiFiP2PInstance); 395 | WiFiDirectUtils.stopPeerDiscovering(wiFiP2PInstance); 396 | WiFiDirectUtils.removeGroup(wiFiP2PInstance); 397 | 398 | serverSocket = null; 399 | isRegistered = false; 400 | clientsConnected.clear(); 401 | } 402 | }, 2000); 403 | } 404 | 405 | /** 406 | * Obtain the devices connected to the actual group. 407 | * 408 | * @return the devices connected to the actual group. 409 | */ 410 | public Collection getClientsConnected() { 411 | return clientsConnected.values(); 412 | } 413 | 414 | private void setupDnsListeners(WiFiP2PInstance wiFiP2PInstance, ServiceDiscoveredListener serviceDiscoveredListener) { 415 | if (dnsSdTxtRecordListener == null || dnsSdServiceResponseListener == null) { 416 | dnsSdTxtRecordListener = getTxtRecordListener(serviceDiscoveredListener); 417 | dnsSdServiceResponseListener = getServiceResponseListener(); 418 | 419 | wiFiP2PInstance.getWifiP2pManager().setDnsSdResponseListeners(wiFiP2PInstance.getChannel(), dnsSdServiceResponseListener, dnsSdTxtRecordListener); 420 | } 421 | } 422 | 423 | private DnsSdTxtRecordListener getTxtRecordListener(final ServiceDiscoveredListener serviceDiscoveredListener) { 424 | return new DnsSdTxtRecordListener() { 425 | 426 | @Override 427 | public void onDnsSdTxtRecordAvailable(String fullDomainName, Map txtRecordMap, WifiP2pDevice device) { 428 | 429 | if (txtRecordMap.containsKey(WroupService.SERVICE_NAME_PROPERTY) && txtRecordMap.get(WroupService.SERVICE_NAME_PROPERTY).equalsIgnoreCase(WroupService.SERVICE_NAME_VALUE)) { 430 | Integer servicePort = Integer.valueOf(txtRecordMap.get(WroupService.SERVICE_PORT_PROPERTY)); 431 | WroupServiceDevice serviceDevice = new WroupServiceDevice(device); 432 | serviceDevice.setDeviceServerSocketPort(servicePort); 433 | serviceDevice.setTxtRecordMap(txtRecordMap); 434 | 435 | if (!serviceDevices.contains(serviceDevice)) { 436 | Log.i(TAG, "Found a new Wroup service: "); 437 | Log.i(TAG, "\tDomain Name: " + fullDomainName); 438 | Log.i(TAG, "\tDevice Name: " + device.deviceName); 439 | Log.i(TAG, "\tDevice Address: " + device.deviceAddress); 440 | Log.i(TAG, "\tServer socket Port: " + serviceDevice.getDeviceServerSocketPort()); 441 | 442 | serviceDevices.add(serviceDevice); 443 | serviceDiscoveredListener.onNewServiceDeviceDiscovered(serviceDevice); 444 | } 445 | } else { 446 | Log.d(TAG, "Found a new service: "); 447 | Log.d(TAG, "\tDomain Name: " + fullDomainName); 448 | Log.d(TAG, "\tDevice Name: " + device.deviceName); 449 | Log.d(TAG, "\tDevice Address: " + device.deviceAddress); 450 | } 451 | } 452 | }; 453 | } 454 | 455 | private DnsSdServiceResponseListener getServiceResponseListener() { 456 | return new DnsSdServiceResponseListener() { 457 | 458 | @Override 459 | public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice srcDevice) { 460 | 461 | } 462 | }; 463 | } 464 | 465 | private void createServerSocket() { 466 | if (serverSocket == null) { 467 | new AsyncTask() { 468 | 469 | @Override 470 | protected Void doInBackground(Void... params) { 471 | 472 | try { 473 | serverSocket = new ServerSocket(0); 474 | 475 | int port = serverSocket.getLocalPort(); 476 | wiFiP2PInstance.getThisDevice().setDeviceServerSocketPort(port); 477 | 478 | Log.i(TAG, "Client ServerSocket created. Accepting requests..."); 479 | Log.i(TAG, "\tPort: " + port); 480 | 481 | while (true) { 482 | Socket socket = serverSocket.accept(); 483 | 484 | String dataReceived = IOUtils.toString(socket.getInputStream()); 485 | Log.i(TAG, "Data received: " + dataReceived); 486 | Log.i(TAG, "From IP: " + socket.getInetAddress().getHostAddress()); 487 | 488 | Gson gson = new Gson(); 489 | MessageWrapper messageWrapper = gson.fromJson(dataReceived, MessageWrapper.class); 490 | onMessageReceived(messageWrapper); 491 | } 492 | } catch (IOException e) { 493 | Log.e(TAG, "Error creating/closing client ServerSocket: " + e.getMessage()); 494 | } 495 | 496 | return null; 497 | } 498 | 499 | }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 500 | } 501 | } 502 | 503 | private void onMessageReceived(MessageWrapper messageWrapper) { 504 | if (MessageWrapper.MessageType.CONNECTION_MESSAGE.equals(messageWrapper.getMessageType())) { 505 | Gson gson = new Gson(); 506 | 507 | String messageContentStr = messageWrapper.getMessage(); 508 | RegistrationMessageContent registrationMessageContent = gson.fromJson(messageContentStr, RegistrationMessageContent.class); 509 | WroupDevice device = registrationMessageContent.getWroupDevice(); 510 | clientsConnected.put(device.getDeviceMac(), device); 511 | 512 | if (clientConnectedListener != null) { 513 | clientConnectedListener.onClientConnected(device); 514 | } 515 | 516 | Log.d(TAG, "New client connected to the group:"); 517 | Log.d(TAG, "\tDevice name: " + device.getDeviceName()); 518 | Log.d(TAG, "\tDecive mac: " + device.getDeviceMac()); 519 | Log.d(TAG, "\tDevice IP: " + device.getDeviceServerSocketIP()); 520 | Log.d(TAG, "\tDevice ServerSocket port: " + device.getDeviceServerSocketPort()); 521 | } else if (MessageWrapper.MessageType.DISCONNECTION_MESSAGE.equals(messageWrapper.getMessageType())) { 522 | Gson gson = new Gson(); 523 | 524 | String messageContentStr = messageWrapper.getMessage(); 525 | DisconnectionMessageContent disconnectionMessageContent = gson.fromJson(messageContentStr, DisconnectionMessageContent.class); 526 | WroupDevice device = disconnectionMessageContent.getWroupDevice(); 527 | clientsConnected.remove(device.getDeviceMac()); 528 | 529 | if (clientDisconnectedListener != null) { 530 | clientDisconnectedListener.onClientDisconnected(device); 531 | } 532 | 533 | Log.d(TAG, "Client disconnected from the group:"); 534 | Log.d(TAG, "\tDevice name: " + device.getDeviceName()); 535 | Log.d(TAG, "\tDecive mac: " + device.getDeviceMac()); 536 | Log.d(TAG, "\tDevice IP: " + device.getDeviceServerSocketIP()); 537 | Log.d(TAG, "\tDevice ServerSocket port: " + device.getDeviceServerSocketPort()); 538 | } else if (MessageWrapper.MessageType.REGISTERED_DEVICES.equals(messageWrapper.getMessageType())) { 539 | Gson gson = new Gson(); 540 | 541 | String messageContentStr = messageWrapper.getMessage(); 542 | RegisteredDevicesMessageContent registeredDevicesMessageContent = gson.fromJson(messageContentStr, RegisteredDevicesMessageContent.class); 543 | List devicesConnected = registeredDevicesMessageContent.getDevicesRegistered(); 544 | 545 | for (WroupDevice device : devicesConnected) { 546 | clientsConnected.put(device.getDeviceMac(), device); 547 | Log.d(TAG, "Client already connected to the group:"); 548 | Log.d(TAG, "\tDevice name: " + device.getDeviceName()); 549 | Log.d(TAG, "\tDecive mac: " + device.getDeviceMac()); 550 | Log.d(TAG, "\tDevice IP: " + device.getDeviceServerSocketIP()); 551 | Log.d(TAG, "\tDevice ServerSocket port: " + device.getDeviceServerSocketPort()); 552 | } 553 | } else { 554 | if (dataReceivedListener != null) { 555 | dataReceivedListener.onDataReceived(messageWrapper); 556 | } 557 | } 558 | } 559 | 560 | private void sendServerRegistrationMessage() { 561 | RegistrationMessageContent content = new RegistrationMessageContent(); 562 | content.setWroupDevice(wiFiP2PInstance.getThisDevice()); 563 | 564 | Gson gson = new Gson(); 565 | 566 | MessageWrapper negotiationMessage = new MessageWrapper(); 567 | negotiationMessage.setMessageType(MessageWrapper.MessageType.CONNECTION_MESSAGE); 568 | negotiationMessage.setMessage(gson.toJson(content)); 569 | 570 | sendMessageToServer(negotiationMessage); 571 | } 572 | 573 | private void sendDisconnectionMessage() { 574 | DisconnectionMessageContent content = new DisconnectionMessageContent(); 575 | content.setWroupDevice(wiFiP2PInstance.getThisDevice()); 576 | 577 | Gson gson = new Gson(); 578 | 579 | MessageWrapper disconnectionMessage = new MessageWrapper(); 580 | disconnectionMessage.setMessageType(MessageWrapper.MessageType.DISCONNECTION_MESSAGE); 581 | disconnectionMessage.setMessage(gson.toJson(content)); 582 | 583 | sendMessageToServer(disconnectionMessage); 584 | } 585 | 586 | } 587 | --------------------------------------------------------------------------------