1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.wifidirect;
18 
19 import android.Manifest;
20 import android.app.Activity;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.pm.PackageManager;
26 import android.net.wifi.WifiManager;
27 import android.net.wifi.p2p.WifiP2pConfig;
28 import android.net.wifi.p2p.WifiP2pDevice;
29 import android.net.wifi.p2p.WifiP2pManager;
30 import android.net.wifi.p2p.WifiP2pManager.ActionListener;
31 import android.net.wifi.p2p.WifiP2pManager.Channel;
32 import android.net.wifi.p2p.WifiP2pManager.ChannelListener;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.provider.Settings;
36 import android.util.Log;
37 import android.view.Menu;
38 import android.view.MenuInflater;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.widget.Toast;
42 
43 import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;
44 
45 /**
46  * An activity that uses WiFi Direct APIs to discover and connect with available
47  * devices. WiFi Direct APIs are asynchronous and rely on callback mechanism
48  * using interfaces to notify the application of operation success or failure.
49  * The application should also register a BroadcastReceiver for notification of
50  * WiFi state related events.
51  */
52 public class WiFiDirectActivity extends Activity implements ChannelListener, DeviceActionListener {
53 
54     public static final String TAG = "wifidirectdemo";
55 
56     private static final int PERMISSIONS_REQUEST_CODE_ACCESS_FINE_LOCATION = 1001;
57 
58     private WifiP2pManager manager;
59     private boolean isWifiP2pEnabled = false;
60     private boolean retryChannel = false;
61 
62     private final IntentFilter intentFilter = new IntentFilter();
63     private Channel channel;
64     private BroadcastReceiver receiver = null;
65 
66     /**
67      * @param isWifiP2pEnabled the isWifiP2pEnabled to set
68      */
setIsWifiP2pEnabled(boolean isWifiP2pEnabled)69     public void setIsWifiP2pEnabled(boolean isWifiP2pEnabled) {
70         this.isWifiP2pEnabled = isWifiP2pEnabled;
71     }
72 
73     @Override
onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)74     public void onRequestPermissionsResult(int requestCode, String[] permissions,
75             int[] grantResults) {
76         switch (requestCode) {
77         case PERMISSIONS_REQUEST_CODE_ACCESS_FINE_LOCATION:
78             if  (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
79                 Log.e(TAG, "Fine location permission is not granted!");
80                 finish();
81             }
82             break;
83         }
84     }
85 
initP2p()86     private boolean initP2p() {
87         // Device capability definition check
88         if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) {
89             Log.e(TAG, "Wi-Fi Direct is not supported by this device.");
90             return false;
91         }
92 
93         // Hardware capability check
94         WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
95         if (wifiManager == null) {
96             Log.e(TAG, "Cannot get Wi-Fi system service.");
97             return false;
98         }
99 
100         if (!wifiManager.isP2pSupported()) {
101             Log.e(TAG, "Wi-Fi Direct is not supported by the hardware or Wi-Fi is off.");
102             return false;
103         }
104 
105         manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
106         if (manager == null) {
107             Log.e(TAG, "Cannot get Wi-Fi Direct system service.");
108             return false;
109         }
110 
111         channel = manager.initialize(this, getMainLooper(), null);
112         if (channel == null) {
113             Log.e(TAG, "Cannot initialize Wi-Fi Direct.");
114             return false;
115         }
116 
117         return true;
118     }
119 
120     @Override
onCreate(Bundle savedInstanceState)121     public void onCreate(Bundle savedInstanceState) {
122         super.onCreate(savedInstanceState);
123         setContentView(R.layout.main);
124 
125         // add necessary intent values to be matched.
126 
127         intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
128         intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
129         intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
130         intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
131 
132         if (!initP2p()) {
133             finish();
134         }
135 
136         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
137                     && checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)
138                     != PackageManager.PERMISSION_GRANTED) {
139             requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
140                     WiFiDirectActivity.PERMISSIONS_REQUEST_CODE_ACCESS_FINE_LOCATION);
141             // After this point you wait for callback in
142             // onRequestPermissionsResult(int, String[], int[]) overridden method
143         }
144     }
145 
146     /** register the BroadcastReceiver with the intent values to be matched */
147     @Override
onResume()148     public void onResume() {
149         super.onResume();
150         receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);
151         registerReceiver(receiver, intentFilter);
152     }
153 
154     @Override
onPause()155     public void onPause() {
156         super.onPause();
157         unregisterReceiver(receiver);
158     }
159 
160     /**
161      * Remove all peers and clear all fields. This is called on
162      * BroadcastReceiver receiving a state change event.
163      */
resetData()164     public void resetData() {
165         DeviceListFragment fragmentList = (DeviceListFragment) getFragmentManager()
166                 .findFragmentById(R.id.frag_list);
167         DeviceDetailFragment fragmentDetails = (DeviceDetailFragment) getFragmentManager()
168                 .findFragmentById(R.id.frag_detail);
169         if (fragmentList != null) {
170             fragmentList.clearPeers();
171         }
172         if (fragmentDetails != null) {
173             fragmentDetails.resetViews();
174         }
175     }
176 
177     @Override
onCreateOptionsMenu(Menu menu)178     public boolean onCreateOptionsMenu(Menu menu) {
179         MenuInflater inflater = getMenuInflater();
180         inflater.inflate(R.menu.action_items, menu);
181         return true;
182     }
183 
184     /*
185      * (non-Javadoc)
186      * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
187      */
188     @Override
onOptionsItemSelected(MenuItem item)189     public boolean onOptionsItemSelected(MenuItem item) {
190         switch (item.getItemId()) {
191             case R.id.atn_direct_enable:
192                 if (manager != null && channel != null) {
193 
194                     // Since this is the system wireless settings activity, it's
195                     // not going to send us a result. We will be notified by
196                     // WiFiDeviceBroadcastReceiver instead.
197 
198                     startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
199                 } else {
200                     Log.e(TAG, "channel or manager is null");
201                 }
202                 return true;
203 
204             case R.id.atn_direct_discover:
205                 if (!isWifiP2pEnabled) {
206                     Toast.makeText(WiFiDirectActivity.this, R.string.p2p_off_warning,
207                             Toast.LENGTH_SHORT).show();
208                     return true;
209                 }
210                 final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
211                         .findFragmentById(R.id.frag_list);
212                 fragment.onInitiateDiscovery();
213                 manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
214 
215                     @Override
216                     public void onSuccess() {
217                         Toast.makeText(WiFiDirectActivity.this, "Discovery Initiated",
218                                 Toast.LENGTH_SHORT).show();
219                     }
220 
221                     @Override
222                     public void onFailure(int reasonCode) {
223                         Toast.makeText(WiFiDirectActivity.this, "Discovery Failed : " + reasonCode,
224                                 Toast.LENGTH_SHORT).show();
225                     }
226                 });
227                 return true;
228             default:
229                 return super.onOptionsItemSelected(item);
230         }
231     }
232 
233     @Override
showDetails(WifiP2pDevice device)234     public void showDetails(WifiP2pDevice device) {
235         DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
236                 .findFragmentById(R.id.frag_detail);
237         fragment.showDetails(device);
238 
239     }
240 
241     @Override
connect(WifiP2pConfig config)242     public void connect(WifiP2pConfig config) {
243         manager.connect(channel, config, new ActionListener() {
244 
245             @Override
246             public void onSuccess() {
247                 // WiFiDirectBroadcastReceiver will notify us. Ignore for now.
248             }
249 
250             @Override
251             public void onFailure(int reason) {
252                 Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.",
253                         Toast.LENGTH_SHORT).show();
254             }
255         });
256     }
257 
258     @Override
disconnect()259     public void disconnect() {
260         final DeviceDetailFragment fragment = (DeviceDetailFragment) getFragmentManager()
261                 .findFragmentById(R.id.frag_detail);
262         fragment.resetViews();
263         manager.removeGroup(channel, new ActionListener() {
264 
265             @Override
266             public void onFailure(int reasonCode) {
267                 Log.d(TAG, "Disconnect failed. Reason :" + reasonCode);
268 
269             }
270 
271             @Override
272             public void onSuccess() {
273                 fragment.getView().setVisibility(View.GONE);
274             }
275 
276         });
277     }
278 
279     @Override
onChannelDisconnected()280     public void onChannelDisconnected() {
281         // we will try once more
282         if (manager != null && !retryChannel) {
283             Toast.makeText(this, "Channel lost. Trying again", Toast.LENGTH_LONG).show();
284             resetData();
285             retryChannel = true;
286             manager.initialize(this, getMainLooper(), this);
287         } else {
288             Toast.makeText(this,
289                     "Severe! Channel is probably lost premanently. Try Disable/Re-Enable P2P.",
290                     Toast.LENGTH_LONG).show();
291         }
292     }
293 
294     @Override
cancelDisconnect()295     public void cancelDisconnect() {
296 
297         /*
298          * A cancel abort request by user. Disconnect i.e. removeGroup if
299          * already connected. Else, request WifiP2pManager to abort the ongoing
300          * request
301          */
302         if (manager != null) {
303             final DeviceListFragment fragment = (DeviceListFragment) getFragmentManager()
304                     .findFragmentById(R.id.frag_list);
305             if (fragment.getDevice() == null
306                     || fragment.getDevice().status == WifiP2pDevice.CONNECTED) {
307                 disconnect();
308             } else if (fragment.getDevice().status == WifiP2pDevice.AVAILABLE
309                     || fragment.getDevice().status == WifiP2pDevice.INVITED) {
310 
311                 manager.cancelConnect(channel, new ActionListener() {
312 
313                     @Override
314                     public void onSuccess() {
315                         Toast.makeText(WiFiDirectActivity.this, "Aborting connection",
316                                 Toast.LENGTH_SHORT).show();
317                     }
318 
319                     @Override
320                     public void onFailure(int reasonCode) {
321                         Toast.makeText(WiFiDirectActivity.this,
322                                 "Connect abort request failed. Reason Code: " + reasonCode,
323                                 Toast.LENGTH_SHORT).show();
324                     }
325                 });
326             }
327         }
328 
329     }
330 }
331