diff options
author | Anirudh Dewani <anirudhd@google.com> | 2012-06-25 17:51:02 -0700 |
---|---|---|
committer | Anirudh Dewani <anirudhd@google.com> | 2012-06-26 14:06:59 -0700 |
commit | 672fa25cea55f0de063fe45e29425e6da7bbcbe6 (patch) | |
tree | cfe85a8e2305c4e6eb97fe5903f2156a998b6b7a /samples/WiFiDirectServiceDiscovery | |
parent | 7fa6b8dd1bcc351bd29e437eb3cb9f2acc7d7eb9 (diff) | |
download | android_development-672fa25cea55f0de063fe45e29425e6da7bbcbe6.tar.gz android_development-672fa25cea55f0de063fe45e29425e6da7bbcbe6.tar.bz2 android_development-672fa25cea55f0de063fe45e29425e6da7bbcbe6.zip |
WiFi Direct Service Discovery sample
Change-Id: Id5ac219cbf03a8a96203ed682f729df9bbb59fcd
Diffstat (limited to 'samples/WiFiDirectServiceDiscovery')
20 files changed, 1086 insertions, 0 deletions
diff --git a/samples/WiFiDirectServiceDiscovery/Android.mk b/samples/WiFiDirectServiceDiscovery/Android.mk new file mode 100644 index 000000000..1682aa283 --- /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 000000000..17e4fd555 --- /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 000000000..58f5962b9 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/WiFiDirectServiceDiscovery/res/drawable-hdpi/ic_launcher.png diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..99238729d --- /dev/null +++ b/samples/WiFiDirectServiceDiscovery/res/drawable-ldpi/ic_launcher.png diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/WiFiDirectServiceDiscovery/res/drawable-mdpi/ic_launcher.png diff --git a/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/WiFiDirectServiceDiscovery/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml b/samples/WiFiDirectServiceDiscovery/res/layout/devices_list.xml new file mode 100644 index 000000000..d7f0af9f6 --- /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 000000000..7a6fba4e0 --- /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 000000000..c1c1bf8f8 --- /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 000000000..fa6090f55 --- /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 000000000..3742271d9 --- /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 000000000..d537f1067 --- /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 000000000..a182fd825 --- /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 000000000..483d817e8 --- /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 000000000..e4a1858b0 --- /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 000000000..d606dbaa0 --- /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 000000000..eea59a1fc --- /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 000000000..882f78eef --- /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 000000000..20e85cb6e --- /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); + } +} |