WiFi Direct Service Discovery sample
Change-Id: Id5ac219cbf03a8a96203ed682f729df9bbb59fcd
diff --git a/samples/WiFiDirectServiceDiscovery/Android.mk b/samples/WiFiDirectServiceDiscovery/Android.mk
new file mode 100644
index 0000000..1682aa2
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/Android.mk
@@ -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))
diff --git a/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml b/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml
new file mode 100644
index 0000000..17e4fd5
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/AndroidManifest.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/_index.html b/samples/WiFiDirectServiceDiscovery/_index.html
new file mode 100644
index 0000000..58f5962
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/_index.html
@@ -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>
+ — 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> — 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>
+ — 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>
+ — 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>
+ — 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>
+ — 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>
+ — 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><uses-sdk android:minSdkVersion="16" /></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><uses-sdk></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><uses-feature
+ android:name="android.hardware.wifi.direct" /></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><uses-feature></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><uses-feature></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" />
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
new file mode 100644
index 0000000..d7f0af9
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml b/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml
new file mode 100644
index 0000000..7a6fba4
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/fragment_chat.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/main.xml b/samples/WiFiDirectServiceDiscovery/res/layout/main.xml
new file mode 100644
index 0000000..c1c1bf8
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/layout/main.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/values/strings.xml b/samples/WiFiDirectServiceDiscovery/res/values/strings.xml
new file mode 100644
index 0000000..fa6090f
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/strings.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/res/values/style.xml b/samples/WiFiDirectServiceDiscovery/res/values/style.xml
new file mode 100644
index 0000000..3742271
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/res/values/style.xml
@@ -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>
\ No newline at end of file
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java
new file mode 100644
index 0000000..d537f10
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ChatManager.java
@@ -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);
+ }
+ }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java
new file mode 100644
index 0000000..a182fd8
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/ClientSocketHandler.java
@@ -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;
+ }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java
new file mode 100644
index 0000000..483d817
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/GroupOwnerSocketHandler.java
@@ -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;
+ }
+ }
+ }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java
new file mode 100644
index 0000000..e4a1858
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiChatFragment.java
@@ -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;
+ }
+ }
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java
new file mode 100644
index 0000000..d606dba
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectBroadcastReceiver.java
@@ -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);
+
+ }
+ }
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java
new file mode 100644
index 0000000..eea59a1
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiDirectServicesList.java
@@ -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";
+
+ }
+ }
+
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java
new file mode 100644
index 0000000..882f78e
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiP2pService.java
@@ -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;
+}
diff --git a/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java
new file mode 100644
index 0000000..20e85cb
--- /dev/null
+++ b/samples/WiFiDirectServiceDiscovery/src/com/example/android/wifidirect/discovery/WiFiServiceDiscoveryActivity.java
@@ -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);
+ }
+}