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
17package com.example.android.wifidirect;
18
19import android.app.Fragment;
20import android.app.ProgressDialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.net.Uri;
25import android.net.wifi.WpsInfo;
26import android.net.wifi.p2p.WifiP2pConfig;
27import android.net.wifi.p2p.WifiP2pDevice;
28import android.net.wifi.p2p.WifiP2pInfo;
29import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener;
30import android.os.AsyncTask;
31import android.os.Bundle;
32import android.os.Environment;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.TextView;
38
39import com.example.android.wifidirect.DeviceListFragment.DeviceActionListener;
40
41import java.io.File;
42import java.io.FileOutputStream;
43import java.io.IOException;
44import java.io.InputStream;
45import java.io.OutputStream;
46import java.net.ServerSocket;
47import 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 */
53public 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