Merge "WiFi Direct Service Discovery sample" into jb-dev

This commit is contained in:
Anirudh Dewani
2012-07-03 12:29:41 -07:00
committed by Android (Google) Code Review
20 changed files with 1086 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := WiFiDirectServiceDiscovery
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
# Use the following include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.wifidirect.discovery"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="16" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Google Play filtering -->
<uses-feature android:name="android.hardware.wifi.direct" android:required="true"/>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".WiFiServiceDiscoveryActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,52 @@
<p>This is a demo application highlighting how to advertise and discover local services that are Wi-Fi peer to peer network capable with
the Wi-Fi Direct Service Discovery APIs. Service discovery on Wi-Fi direct allows applications to discover and enagage with peers that support a certain service.
As an example, a gaming application can find and associate with devices that support the game. This application allows you to chat with a peer after a succesful connection.</p>
<p>The source code for this demo app shows how to accomplish three key things
with Wi-Fi Direct Service Discovery APIs: Advertise services, discover services and connect to peers advertising such services</p>
<p>The application includes:<p>
<ul> <li><a
href="src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.html"><code>WiFiServiceDiscoveryActivity</code></a>
&mdash; the main <code>Activity</code> that contains two fragments to handle app's UI. It advertises and discovers services and also registers a broadcast receiver for Wi-Fi Direct related events.</li> <li><a
href="src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.html"><code>
WiFiDirectBroadcastReceiver</code></a> &mdash; a <code>BroadcastReceiver</code>
that listens for Wi-Fi Direct related events and passes them to
<code>WiFiServiceDiscoveryActivity</code> and it's fragments for neccesary action.</li> <li><a
href="src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.html"><code>WiFiDirectServicesList</code></a>
&mdash; a <code>ListFragment</code> that displays available services, peers and their status. </li>
<li><a href="src/com/example/android/wifidirect/discovery/WiFiChatFragment.html"><code>WiFiChatFragment</code></a>
&mdash; a <code>Fragment</code> that displays handles chat UI </li>
<li><a href="src/com/example/android/wifidirect/discovery/ChatManager.html"><code>ChatManager</code></a>
&mdash; a <code>Runnable</code> that continously performs Socket I/O.</li>
<li><a href="src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.html"><code>GroupOwnerSocketHandler</code></a>
&mdash; a <code>Thread</code> that implements a server side Socket handler and spawns a client socket per connection.</li> </ul>
<li><a href="src/com/example/android/wifidirect/discovery/ClientSocketHandler.html"><code>GroupOwnerSocketHandler</code></a>
&mdash; a <code>Thread</code> that implements a client side Socket handler.</li> </ul>
<p>If you are developing an application that uses the Wi-Fi Direct Service Discovery APIs, remember that the
feature is supported only on Android 4.1 (API level 16) and higher versions of
the platform. To ensure that your application can only
be installed on devices that are capable of supporting Wi-Fi Direct Service Discovery, remember to add the
following to the application's manifest before publishing to Google Play:</p>
<ul> <li><code>&lt;uses-sdk android:minSdkVersion="16" /&gt;</code>, which
indicates to Google Play and the platform that your application requires
Android 4.1 or higher. For more information, see <a
href="../../../guide/appendix/api-levels.html">API Levels</a> and the
documentation for the <a
href="../../../guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
element.</li> </ul> <p>To control how Google Play filters your application
from devices that do not support Wi-Fi Direct mode, remember to add the following to the
application's manifest <ul> <li><code>&lt;uses-feature
android:name="android.hardware.wifi.direct" /&gt;</code>, which tells Google
Play that your application uses the Wi-Fi Direct API. The declaration should include
an <code>android:required</code> attribute that indicates whether you want
Google Play to filter the application from devices that do not offer Wi-Fi Direct support. Other <code>&lt;uses-feature&gt;</code> declarations may also be
needed, depending on your implementation. For more information, see the
documentation for the <a
href="../../../guide/topics/manifest/uses-feature-element.html"><code>&lt;uses-feature&gt;</code></a>
element.</li> </ul>
<p>For more information about using the Wi-Fi Direct Service Discovery APIs, see the <a
href="../../../reference/android/net/wifi/p2p/package-summary.html"><code>android.net.wifi.p2p </a></code>
documentation. </p>
<img alt="" src="../images/WifiDirect.png" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_gravity="top"
android:gravity="top" >
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<LinearLayout
android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dip">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="@string/finding_service" >
</TextView>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginLeft="20dip"
style="@android:style/Widget.ProgressBar.Small" >
</ProgressBar>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="50dip"
android:transcriptMode="alwaysScroll" >
<!-- Preview: listitem=@android:layout/simple_list_item_1 -->
</ListView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dip"
android:layout_gravity="bottom" >
<EditText
android:id="@+id/txtChatLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="0.90"
android:focusableInTouchMode="true"
android:hint="@string/txt_hint"
android:visibility="visible" >
</EditText>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_weight="0.10"
android:text="@string/btn_send" />
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container_root"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="top"
android:orientation="vertical" />
<TextView
android:id="@+id/status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom" >
</TextView>
</FrameLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="finding_service">Finding local services</string>
<string name="btn_send">Send</string>
<string name="txt_hint">Type Message</string>
<string name="app_name">Wi-Fi Discovery</string>
</resources>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="boldText">
<item name="android:textStyle">bold|italic</item>
<item name="android:textColor">#99CC00</item>
</style>
<style name="normalText">
<item name="android:textStyle">normal</item>
<item name="android:textColor">#33B5E5</item>
</style>
</resources>

View File

@@ -0,0 +1,76 @@
package com.example.android.wifidirect.discovery;
import android.os.Handler;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* Handles reading and writing of messages with socket buffers. Uses a Handler
* to post messages to UI thread for UI updates.
*/
public class ChatManager implements Runnable {
private Socket socket = null;
private Handler handler;
public ChatManager(Socket socket, Handler handler) {
this.socket = socket;
this.handler = handler;
}
private InputStream iStream;
private OutputStream oStream;
private static final String TAG = "ChatHandler";
@Override
public void run() {
try {
iStream = socket.getInputStream();
oStream = socket.getOutputStream();
byte[] buffer = new byte[1024];
int bytes;
handler.obtainMessage(WiFiServiceDiscoveryActivity.MY_HANDLE, this)
.sendToTarget();
while (true) {
try {
// Read from the InputStream
bytes = iStream.read(buffer);
if (bytes == -1) {
break;
}
// Send the obtained bytes to the UI Activity
Log.d(TAG, "Rec:" + String.valueOf(buffer));
handler.obtainMessage(WiFiServiceDiscoveryActivity.MESSAGE_READ,
bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void write(byte[] buffer) {
try {
oStream.write(buffer);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
}

View File

@@ -0,0 +1,49 @@
package com.example.android.wifidirect.discovery;
import android.os.Handler;
import android.util.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
public class ClientSocketHandler extends Thread {
private static final String TAG = "ClientSocketHandler";
private Handler handler;
private ChatManager chat;
private InetAddress mAddress;
public ClientSocketHandler(Handler handler, InetAddress groupOwnerAddress) {
this.handler = handler;
this.mAddress = groupOwnerAddress;
}
@Override
public void run() {
Socket socket = new Socket();
try {
socket.bind(null);
socket.connect(new InetSocketAddress(mAddress.getHostAddress(),
WiFiServiceDiscoveryActivity.SERVER_PORT), 5000);
Log.d(TAG, "Launching the I/O handler");
chat = new ChatManager(socket, handler);
new Thread(chat).start();
} catch (IOException e) {
e.printStackTrace();
try {
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return;
}
}
public ChatManager getChat() {
return chat;
}
}

View File

@@ -0,0 +1,67 @@
package com.example.android.wifidirect.discovery;
import android.os.Handler;
import android.util.Log;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* The implementation of a ServerSocket handler. This is used by the wifi p2p
* group owner.
*/
public class GroupOwnerSocketHandler extends Thread {
ServerSocket socket = null;
private final int THREAD_COUNT = 10;
private Handler handler;
private static final String TAG = "GroupOwnerSocketHandler";
public GroupOwnerSocketHandler(Handler handler) throws IOException {
try {
socket = new ServerSocket(4545);
this.handler = handler;
Log.d("GroupOwnerSocketHandler", "Socket Started");
} catch (IOException e) {
e.printStackTrace();
pool.shutdownNow();
throw e;
}
}
/**
* A ThreadPool for client sockets.
*/
private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
THREAD_COUNT, THREAD_COUNT, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
@Override
public void run() {
while (true) {
try {
// A blocking operation. Initiate a ChatManager instance when
// there is a new connection
pool.execute(new ChatManager(socket.accept(), handler));
Log.d(TAG, "Launching the I/O handler");
} catch (IOException e) {
try {
if (socket != null && !socket.isClosed())
socket.close();
} catch (IOException ioe) {
}
e.printStackTrace();
pool.shutdownNow();
break;
}
}
}
}

View File

@@ -0,0 +1,109 @@
package com.example.android.wifidirect.discovery;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* This fragment handles chat related UI which includes a list view for messages
* and a message entry field with send button.
*/
public class WiFiChatFragment extends Fragment {
private View view;
private ChatManager chatManager;
private TextView chatLine;
private ListView listView;
ChatMessageAdapter adapter = null;
private List<String> items = new ArrayList<String>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_chat, container, false);
chatLine = (TextView) view.findViewById(R.id.txtChatLine);
listView = (ListView) view.findViewById(android.R.id.list);
adapter = new ChatMessageAdapter(getActivity(), android.R.id.text1,
items);
listView.setAdapter(adapter);
view.findViewById(R.id.button1).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View arg0) {
if (chatManager != null) {
chatManager.write(chatLine.getText().toString()
.getBytes());
pushMessage("Me: " + chatLine.getText().toString());
chatLine.setText("");
chatLine.clearFocus();
}
}
});
return view;
}
public interface MessageTarget {
public Handler getHandler();
}
public void setChatManager(ChatManager obj) {
chatManager = obj;
}
public void pushMessage(String readMessage) {
adapter.add(readMessage);
adapter.notifyDataSetChanged();
}
/**
* ArrayAdapter to manage chat messages.
*/
public class ChatMessageAdapter extends ArrayAdapter<String> {
List<String> messages = null;
public ChatMessageAdapter(Context context, int textViewResourceId,
List<String> items) {
super(context, textViewResourceId, items);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(android.R.layout.simple_list_item_1, null);
}
String message = items.get(position);
if (message != null && !message.isEmpty()) {
TextView nameText = (TextView) v
.findViewById(android.R.id.text1);
if (nameText != null) {
nameText.setText(message);
if (message.startsWith("Me: ")) {
nameText.setTextAppearance(getActivity(),
R.style.normalText);
} else {
nameText.setTextAppearance(getActivity(),
R.style.boldText);
}
}
}
return v;
}
}
}

View File

@@ -0,0 +1,91 @@
package com.example.android.wifidirect.discovery;
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkInfo;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
import android.util.Log;
/**
* A BroadcastReceiver that notifies of important wifi p2p events.
*/
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager manager;
private Channel channel;
private Activity activity;
/**
* @param manager WifiP2pManager system service
* @param channel Wifi p2p channel
* @param activity activity associated with the receiver
*/
public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel,
Activity activity) {
super();
this.manager = manager;
this.channel = channel;
this.activity = activity;
}
/*
* (non-Javadoc)
* @see android.content.BroadcastReceiver#onReceive(android.content.Context,
* android.content.Intent)
*/
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(WiFiServiceDiscoveryActivity.TAG, action);
if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
if (manager == null) {
return;
}
NetworkInfo networkInfo = (NetworkInfo) intent
.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// we are connected with the other device, request connection
// info to find group owner IP
Log.d(WiFiServiceDiscoveryActivity.TAG,
"Connected to p2p network. Requesting network details");
manager.requestConnectionInfo(channel,
(ConnectionInfoListener) activity);
} else {
// It's a disconnect
}
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
.equals(action)) {
WifiP2pDevice device = (WifiP2pDevice) intent
.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
Log.d(WiFiServiceDiscoveryActivity.TAG, "Device status -" + device.status);
}
}
}

View File

@@ -0,0 +1,107 @@
package com.example.android.wifidirect.discovery;
import android.app.ListFragment;
import android.content.Context;
import android.net.wifi.p2p.WifiP2pDevice;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* A simple ListFragment that shows the available services as published by the
* peers
*/
public class WiFiDirectServicesList extends ListFragment {
WiFiDevicesAdapter listAdapter = null;
interface DeviceClickListener {
public void connectP2p(WiFiP2pService wifiP2pService);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.devices_list, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listAdapter = new WiFiDevicesAdapter(this.getActivity(),
android.R.layout.simple_list_item_2, android.R.id.text1,
new ArrayList<WiFiP2pService>());
setListAdapter(listAdapter);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
((DeviceClickListener) getActivity()).connectP2p((WiFiP2pService) l
.getItemAtPosition(position));
((TextView) v.findViewById(android.R.id.text2)).setText("Connecting");
}
public class WiFiDevicesAdapter extends ArrayAdapter<WiFiP2pService> {
private List<WiFiP2pService> items;
public WiFiDevicesAdapter(Context context, int resource,
int textViewResourceId, List<WiFiP2pService> items) {
super(context, resource, textViewResourceId, items);
this.items = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(android.R.layout.simple_list_item_2, null);
}
WiFiP2pService service = items.get(position);
if (service != null) {
TextView nameText = (TextView) v
.findViewById(android.R.id.text1);
if (nameText != null) {
nameText.setText(service.device.deviceName + " - " + service.instanceName);
}
TextView statusText = (TextView) v
.findViewById(android.R.id.text2);
statusText.setText(getDeviceStatus(service.device.status));
}
return v;
}
}
public static String getDeviceStatus(int statusCode) {
switch (statusCode) {
case WifiP2pDevice.CONNECTED:
return "Connected";
case WifiP2pDevice.INVITED:
return "Invited";
case WifiP2pDevice.FAILED:
return "Failed";
case WifiP2pDevice.AVAILABLE:
return "Available";
case WifiP2pDevice.UNAVAILABLE:
return "Unavailable";
default:
return "Unknown";
}
}
}

View File

@@ -0,0 +1,13 @@
package com.example.android.wifidirect.discovery;
import android.net.wifi.p2p.WifiP2pDevice;
/**
* A structure to hold service information.
*/
public class WiFiP2pService {
WifiP2pDevice device;
String instanceName = null;
String serviceRegistrationType = null;
}

View File

@@ -0,0 +1,346 @@
package com.example.android.wifidirect.discovery;
import android.app.Activity;
import android.app.Fragment;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.net.wifi.WpsInfo;
import android.net.wifi.p2p.WifiP2pConfig;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager.ActionListener;
import android.net.wifi.p2p.WifiP2pManager.Channel;
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
import android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener;
import android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.example.android.wifidirect.discovery.WiFiChatFragment.MessageTarget;
import com.example.android.wifidirect.discovery.WiFiDirectServicesList.DeviceClickListener;
import com.example.android.wifidirect.discovery.WiFiDirectServicesList.WiFiDevicesAdapter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* The main activity for the sample. This activity registers a local service and
* perform discovery over Wi-Fi p2p network. It also hosts a couple of fragments
* to manage chat operations. When the app is launched, the device publishes a
* chat service and also tries to discover services published by other peers. On
* selecting a peer published service, the app initiates a Wi-Fi P2P (Direct)
* connection with the peer. On successful connection with a peer advertising
* the same service, the app opens up sockets to initiate a chat.
* {@code WiFiChatFragment} is then added to the the main activity which manages
* the interface and messaging needs for a chat session.
*/
public class WiFiServiceDiscoveryActivity extends Activity implements
DeviceClickListener, Handler.Callback, MessageTarget,
ConnectionInfoListener {
public static final String TAG = "wifidirectdemo";
// TXT RECORD properties
public static final String TXTRECORD_PROP_AVAILABLE = "available";
public static final String SERVICE_INSTANCE = "_wifidemotest";
public static final String SERVICE_REG_TYPE = "_presence._tcp";
public static final int MESSAGE_READ = 0x400 + 1;
public static final int MY_HANDLE = 0x400 + 2;
private WifiP2pManager manager;
static final int SERVER_PORT = 4545;
private final IntentFilter intentFilter = new IntentFilter();
private Channel channel;
private BroadcastReceiver receiver = null;
private WifiP2pDnsSdServiceRequest serviceRequest;
private Handler handler = new Handler(this);
private WiFiChatFragment chatFragment;
private WiFiDirectServicesList servicesList;
private TextView statusTxtView;
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
statusTxtView = (TextView) findViewById(R.id.status_text);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter
.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter
.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
channel = manager.initialize(this, getMainLooper(), null);
startRegistrationAndDiscovery();
servicesList = new WiFiDirectServicesList();
getFragmentManager().beginTransaction()
.add(R.id.container_root, servicesList, "services").commit();
}
@Override
protected void onRestart() {
Fragment frag = getFragmentManager().findFragmentByTag("services");
if (frag != null) {
getFragmentManager().beginTransaction().remove(frag).commit();
}
super.onRestart();
}
@Override
protected void onStop() {
if (manager != null && channel != null) {
manager.removeGroup(channel, new ActionListener() {
@Override
public void onFailure(int reasonCode) {
Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);
}
@Override
public void onSuccess() {
}
});
}
super.onStop();
}
/**
* Registers a local service and then initiates a service discovery
*/
private void startRegistrationAndDiscovery() {
Map<String, String> record = new HashMap<String, String>();
record.put(TXTRECORD_PROP_AVAILABLE, "visible");
WifiP2pDnsSdServiceInfo service = WifiP2pDnsSdServiceInfo.newInstance(
SERVICE_INSTANCE, SERVICE_REG_TYPE, record);
manager.addLocalService(channel, service, new ActionListener() {
@Override
public void onSuccess() {
appendStatus("Added Local Service");
}
@Override
public void onFailure(int error) {
appendStatus("Failed to add a service");
}
});
discoverService();
}
private void discoverService() {
/*
* Register listeners for DNS-SD services. These are callbacks invoked
* by the system when a service is actually discovered.
*/
manager.setDnsSdResponseListeners(channel,
new DnsSdServiceResponseListener() {
@Override
public void onDnsSdServiceAvailable(String instanceName,
String registrationType, WifiP2pDevice srcDevice) {
// A service has been discovered. Is this our app?
if (instanceName.equalsIgnoreCase(SERVICE_INSTANCE)) {
// update the UI and add the item the discovered
// device.
WiFiDirectServicesList fragment = (WiFiDirectServicesList) getFragmentManager()
.findFragmentByTag("services");
if (fragment != null) {
WiFiDevicesAdapter adapter = ((WiFiDevicesAdapter) fragment
.getListAdapter());
WiFiP2pService service = new WiFiP2pService();
service.device = srcDevice;
service.instanceName = instanceName;
service.serviceRegistrationType = registrationType;
adapter.add(service);
adapter.notifyDataSetChanged();
Log.d(TAG, "onBonjourServiceAvailable "
+ instanceName);
}
}
}
}, new DnsSdTxtRecordListener() {
/**
* A new TXT record is available. Pick up the advertised
* buddy name.
*/
@Override
public void onDnsSdTxtRecordAvailable(
String fullDomainName, Map<String, String> record,
WifiP2pDevice device) {
Log.d(TAG,
device.deviceName + " is "
+ record.get(TXTRECORD_PROP_AVAILABLE));
}
});
// After attaching listeners, create a service request and initiate
// discovery.
serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
manager.addServiceRequest(channel, serviceRequest,
new ActionListener() {
@Override
public void onSuccess() {
appendStatus("Added service discovery request");
}
@Override
public void onFailure(int arg0) {
appendStatus("Failed adding service discovery request");
}
});
manager.discoverServices(channel, new ActionListener() {
@Override
public void onSuccess() {
appendStatus("Service discovery initiated");
}
@Override
public void onFailure(int arg0) {
appendStatus("Service discovery failed");
}
});
}
@Override
public void connectP2p(WiFiP2pService service) {
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = service.device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
if (serviceRequest != null)
manager.removeServiceRequest(channel, serviceRequest,
new ActionListener() {
@Override
public void onSuccess() {
}
@Override
public void onFailure(int arg0) {
}
});
manager.connect(channel, config, new ActionListener() {
@Override
public void onSuccess() {
appendStatus("Connecting to service");
}
@Override
public void onFailure(int errorCode) {
appendStatus("Failed connecting to service");
}
});
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
Log.d(TAG, readMessage);
(chatFragment).pushMessage("Buddy: " + readMessage);
break;
case MY_HANDLE:
Object obj = msg.obj;
(chatFragment).setChatManager((ChatManager) obj);
}
return true;
}
@Override
public void onResume() {
super.onResume();
receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
registerReceiver(receiver, intentFilter);
}
@Override
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
@Override
public void onConnectionInfoAvailable(WifiP2pInfo p2pInfo) {
Thread handler = null;
/*
* The group owner accepts connections using a server socket and then spawns a
* client socket for every client. This is handled by {@code
* GroupOwnerSocketHandler}
*/
if (p2pInfo.isGroupOwner) {
Log.d(TAG, "Connected as group owner");
try {
handler = new GroupOwnerSocketHandler(
((MessageTarget) this).getHandler());
handler.start();
} catch (IOException e) {
Log.d(TAG,
"Failed to create a server thread - " + e.getMessage());
return;
}
} else {
Log.d(TAG, "Connected as peer");
handler = new ClientSocketHandler(
((MessageTarget) this).getHandler(),
p2pInfo.groupOwnerAddress);
handler.start();
}
chatFragment = new WiFiChatFragment();
getFragmentManager().beginTransaction()
.replace(R.id.container_root, chatFragment).commit();
statusTxtView.setVisibility(View.GONE);
}
public void appendStatus(String status) {
String current = statusTxtView.getText().toString();
statusTxtView.setText(current + "\n" + status);
}
}