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.app.Fragment; 20 import android.app.ProgressDialog; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.net.Uri; 25 import android.net.wifi.WpsInfo; 26 import android.net.wifi.p2p.WifiP2pConfig; 27 import android.net.wifi.p2p.WifiP2pDevice; 28 import android.net.wifi.p2p.WifiP2pInfo; 29 import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener; 30 import android.os.AsyncTask; 31 import android.os.Bundle; 32 import android.os.Environment; 33 import android.util.Log; 34 import android.view.LayoutInflater; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.widget.TextView; 38 39 import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener; 40 41 import java.io.File; 42 import java.io.FileOutputStream; 43 import java.io.IOException; 44 import java.io.InputStream; 45 import java.io.OutputStream; 46 import java.net.ServerSocket; 47 import java.net.Socket; 48 49 /** 50 * A fragment that manages a particular peer and allows interaction with device 51 * i.e. setting up network connection and transferring data. 52 */ 53 public class DeviceDetailFragment extends Fragment implements ConnectionInfoListener { 54 55 protected static final int CHOOSE_FILE_RESULT_CODE = 20; 56 private View mContentView = null; 57 private WifiP2pDevice device; 58 private WifiP2pInfo info; 59 ProgressDialog progressDialog = null; 60 61 @Override 62 public void onActivityCreated(Bundle savedInstanceState) { 63 super.onActivityCreated(savedInstanceState); 64 } 65 66 @Override 67 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 68 69 mContentView = inflater.inflate(R.layout.device_detail, null); 70 mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() { 71 72 @Override 73 public void onClick(View v) { 74 WifiP2pConfig config = new WifiP2pConfig(); 75 config.deviceAddress = device.deviceAddress; 76 config.wps.setup = WpsInfo.PBC; 77 if (progressDialog != null && progressDialog.isShowing()) { 78 progressDialog.dismiss(); 79 } 80 progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel", 81 "Connecting to :" + device.deviceAddress, true, true 82 // new DialogInterface.OnCancelListener() { 83 // 84 // @Override 85 // public void onCancel(DialogInterface dialog) { 86 // ((DeviceActionListener) getActivity()).cancelDisconnect(); 87 // } 88 // } 89 ); 90 ((DeviceActionListener) getActivity()).connect(config); 91 92 } 93 }); 94 95 mContentView.findViewById(R.id.btn_disconnect).setOnClickListener( 96 new View.OnClickListener() { 97 98 @Override 99 public void onClick(View v) { 100 ((DeviceActionListener) getActivity()).disconnect(); 101 } 102 }); 103 104 mContentView.findViewById(R.id.btn_start_client).setOnClickListener( 105 new View.OnClickListener() { 106 107 @Override 108 public void onClick(View v) { 109 // Allow user to pick an image from Gallery or other 110 // registered apps 111 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 112 intent.setType("image/*"); 113 startActivityForResult(intent, CHOOSE_FILE_RESULT_CODE); 114 } 115 }); 116 117 return mContentView; 118 } 119 120 @Override 121 public void onActivityResult(int requestCode, int resultCode, Intent data) { 122 123 // User has picked an image. Transfer it to group owner i.e peer using 124 // FileTransferService. 125 Uri uri = data.getData(); 126 TextView statusText = (TextView) mContentView.findViewById(R.id.status_text); 127 statusText.setText("Sending: " + uri); 128 Log.d(WiFiDirectActivity.TAG, "Intent----------- " + uri); 129 Intent serviceIntent = new Intent(getActivity(), FileTransferService.class); 130 serviceIntent.setAction(FileTransferService.ACTION_SEND_FILE); 131 serviceIntent.putExtra(FileTransferService.EXTRAS_FILE_PATH, uri.toString()); 132 serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_ADDRESS, 133 info.groupOwnerAddress.getHostAddress()); 134 serviceIntent.putExtra(FileTransferService.EXTRAS_GROUP_OWNER_PORT, 8988); 135 getActivity().startService(serviceIntent); 136 } 137 138 @Override 139 public void onConnectionInfoAvailable(final WifiP2pInfo info) { 140 if (progressDialog != null && progressDialog.isShowing()) { 141 progressDialog.dismiss(); 142 } 143 this.info = info; 144 this.getView().setVisibility(View.VISIBLE); 145 146 // The owner IP is now known. 147 TextView view = (TextView) mContentView.findViewById(R.id.group_owner); 148 view.setText(getResources().getString(R.string.group_owner_text) 149 + ((info.isGroupOwner == true) ? getResources().getString(R.string.yes) 150 : getResources().getString(R.string.no))); 151 152 // InetAddress from WifiP2pInfo struct. 153 view = (TextView) mContentView.findViewById(R.id.device_info); 154 view.setText("Group Owner IP - " + info.groupOwnerAddress.getHostAddress()); 155 156 // After the group negotiation, we assign the group owner as the file 157 // server. The file server is single threaded, single connection server 158 // socket. 159 if (info.groupFormed && info.isGroupOwner) { 160 new FileServerAsyncTask(getActivity(), mContentView.findViewById(R.id.status_text)) 161 .execute(); 162 } else if (info.groupFormed) { 163 // The other device acts as the client. In this case, we enable the 164 // get file button. 165 mContentView.findViewById(R.id.btn_start_client).setVisibility(View.VISIBLE); 166 ((TextView) mContentView.findViewById(R.id.status_text)).setText(getResources() 167 .getString(R.string.client_text)); 168 } 169 170 // hide the connect button 171 mContentView.findViewById(R.id.btn_connect).setVisibility(View.GONE); 172 } 173 174 /** 175 * Updates the UI with device data 176 * 177 * @param device the device to be displayed 178 */ 179 public void showDetails(WifiP2pDevice device) { 180 this.device = device; 181 this.getView().setVisibility(View.VISIBLE); 182 TextView view = (TextView) mContentView.findViewById(R.id.device_address); 183 view.setText(device.deviceAddress); 184 view = (TextView) mContentView.findViewById(R.id.device_info); 185 view.setText(device.toString()); 186 187 } 188 189 /** 190 * Clears the UI fields after a disconnect or direct mode disable operation. 191 */ 192 public void resetViews() { 193 mContentView.findViewById(R.id.btn_connect).setVisibility(View.VISIBLE); 194 TextView view = (TextView) mContentView.findViewById(R.id.device_address); 195 view.setText(R.string.empty); 196 view = (TextView) mContentView.findViewById(R.id.device_info); 197 view.setText(R.string.empty); 198 view = (TextView) mContentView.findViewById(R.id.group_owner); 199 view.setText(R.string.empty); 200 view = (TextView) mContentView.findViewById(R.id.status_text); 201 view.setText(R.string.empty); 202 mContentView.findViewById(R.id.btn_start_client).setVisibility(View.GONE); 203 this.getView().setVisibility(View.GONE); 204 } 205 206 /** 207 * A simple server socket that accepts connection and writes some data on 208 * the stream. 209 */ 210 public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> { 211 212 private Context context; 213 private TextView statusText; 214 215 /** 216 * @param context 217 * @param statusText 218 */ 219 public FileServerAsyncTask(Context context, View statusText) { 220 this.context = context; 221 this.statusText = (TextView) statusText; 222 } 223 224 @Override 225 protected String doInBackground(Void... params) { 226 try { 227 ServerSocket serverSocket = new ServerSocket(8988); 228 Log.d(WiFiDirectActivity.TAG, "Server: Socket opened"); 229 Socket client = serverSocket.accept(); 230 Log.d(WiFiDirectActivity.TAG, "Server: connection done"); 231 final File f = new File(Environment.getExternalStorageDirectory() + "/" 232 + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() 233 + ".jpg"); 234 235 File dirs = new File(f.getParent()); 236 if (!dirs.exists()) 237 dirs.mkdirs(); 238 f.createNewFile(); 239 240 Log.d(WiFiDirectActivity.TAG, "server: copying files " + f.toString()); 241 InputStream inputstream = client.getInputStream(); 242 copyFile(inputstream, new FileOutputStream(f)); 243 serverSocket.close(); 244 return f.getAbsolutePath(); 245 } catch (IOException e) { 246 Log.e(WiFiDirectActivity.TAG, e.getMessage()); 247 return null; 248 } 249 } 250 251 /* 252 * (non-Javadoc) 253 * @see android.os.AsyncTask#onPostExecute(java.lang.Object) 254 */ 255 @Override 256 protected void onPostExecute(String result) { 257 if (result != null) { 258 statusText.setText("File copied - " + result); 259 Intent intent = new Intent(); 260 intent.setAction(android.content.Intent.ACTION_VIEW); 261 intent.setDataAndType(Uri.parse("file://" + result), "image/*"); 262 context.startActivity(intent); 263 } 264 265 } 266 267 /* 268 * (non-Javadoc) 269 * @see android.os.AsyncTask#onPreExecute() 270 */ 271 @Override 272 protected void onPreExecute() { 273 statusText.setText("Opening a server socket"); 274 } 275 276 } 277 278 public static boolean copyFile(InputStream inputStream, OutputStream out) { 279 byte buf[] = new byte[1024]; 280 int len; 281 try { 282 while ((len = inputStream.read(buf)) != -1) { 283 out.write(buf, 0, len); 284 285 } 286 out.close(); 287 inputStream.close(); 288 } catch (IOException e) { 289 Log.d(WiFiDirectActivity.TAG, e.toString()); 290 return false; 291 } 292 return true; 293 } 294 295 } 296