package com.example.socketdemo.activity; import android.content.DialogInterface; import android.content.IntentFilter; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Message; import android.support.v7.app.AlertDialog; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import com.example.socketdemo.R; import com.example.socketdemo.base.AppContext; import com.example.socketdemo.base.BaseActivity; import com.example.socketdemo.base.BaseTransfer; import com.example.socketdemo.bean.FileInfo; import com.example.socketdemo.common.Consts; import com.example.socketdemo.common.FileReceiver; import com.example.socketdemo.common.SpaceItemDecoration; import com.example.socketdemo.receiver.WifiBroadcaseReceiver; import com.example.socketdemo.utils.FileUtils; import com.example.socketdemo.utils.LogUtils; import com.example.socketdemo.utils.NetUtils; import com.example.socketdemo.wifitools.WifiMgr; import com.zhy.adapter.recyclerview.CommonAdapter; import com.zhy.adapter.recyclerview.MultiItemTypeAdapter; import com.zhy.adapter.recyclerview.base.ViewHolder; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import butterknife.BindView; import butterknife.OnClick; /** * Created by AA on 2017/3/28. */ public class ReceiveFilesActivity extends BaseActivity implements MultiItemTypeAdapter.OnItemClickListener { /** * 接收端初始化完毕 */ public static final int MSG_FILE_RECEIVER_INIT_SUCCESS = 0x661; /** * 更新适配器 */ public static final int MSG_UPDATE_ADAPTER = 0x662; /** * 发送选中要接收的文件列表 */ public static final int MSG_SEND_RECEIVE_FILE_LIST = 0x663; /** * 添加接收文件 */ public static final int MSG_ADD_FILEINFO = 0x664; /** * 更新进度条 */ public static final int MSG_UPDATE_PROGRESS = 0x665; /** * 设置当前状态 */ public static final int MSG_SET_STATUS = 0x666; @BindView(R.id.tv_receive_files_status) TextView tvStatus; @BindView(R.id.btn_receive_files) Button btnSendFileList; @BindView(R.id.rv_receive_files_choose_hotspot) RecyclerView mChooseHotspotRecyclerView; private CommonAdapter mChooseHotspotAdapter; @BindView(R.id.rv_receive_files) RecyclerView mReceiveFilesRecyclerView; private CommonAdapter> mReceiveFilesAdapter; /** * 选中待发送的文件列表 */ private List mSendFileInfos = new ArrayList<>(); /** * 接收文件线程列表数据 */ private List mFileReceiverList = new ArrayList<>(); /** * WiFi工具类 */ private WifiMgr mWifiMgr; /** * 扫描到的可用WiFi列表 */ private List mScanResults = new ArrayList<>(); /** * 用来接收文件的Socket */ private Socket mClientSocket; /** * UDP Socket */ private DatagramSocket mDatagramSocket; /** * 接收文件线程 */ private ReceiveServerRunnable mReceiveServerRunnable; /** * 是否已发送初始化指令 */ private boolean mIsSendInitOrder; /** * 获取权限是否成功 */ private boolean mIsPermissionGranted; /** * 当前所选WiFi的SSID */ private String mSelectedSSID; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == MSG_FILE_RECEIVER_INIT_SUCCESS) { //告知发送端,接收端初始化完毕 sendInitSuccessToFileSender(); } else if (msg.what == MSG_UPDATE_ADAPTER) { //更新适配器 setupReceiveFilesAdapter(); } else if (msg.what == MSG_SEND_RECEIVE_FILE_LIST) { //发送选中要接收的文件列表 sendFileListToFileSender(); } else if (msg.what == MSG_ADD_FILEINFO) { //添加接收文件 mReceiveFilesAdapter.notifyDataSetChanged(); } else if (msg.what == MSG_UPDATE_PROGRESS) { //更新进度条 int position = msg.arg1; int progress = msg.arg2; if (position >= 0 && position < mReceiveFilesAdapter.getItemCount()) { updateProgress(position, progress); } } else if (msg.what == MSG_SET_STATUS) { //设置当前状态 setStatus(msg.obj.toString()); } } }; @Override protected int getLayoutId() { return R.layout.activity_receive_files; } @Override protected String getTitleText() { return "接收文件"; } @Override protected void initData() { //请求权限 requestPermission(PERMISSION_CONNECT_WIFI, PERMISSION_REQ_CONNECT_WIFI); } @Override protected void onResume() { super.onResume(); if(mIsPermissionGranted && mWifiBroadcaseReceiver == null) { registerWifiReceiver(); } } @Override protected void onPause() { super.onPause(); if(mWifiBroadcaseReceiver != null) { unregisterWifiReceiver(); } } @Override public void onBackPressed() { if(hasFileReceiving()) { showTipsDialog("文件正在接收,是否退出?", "是", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finishActivity(); } }, "否", null); } else { finishActivity(); } } @Override protected void permissionSuccess(int requestCode) { super.permissionSuccess(requestCode); if(requestCode == PERMISSION_REQ_CONNECT_WIFI) { //权限请求成功 mIsPermissionGranted = true; //开启WiFi,监听WiFi广播 registerWifiReceiver(); mWifiMgr = new WifiMgr(getContext()); if(mWifiMgr.isWifiEnabled()) { setStatus("正在扫描可用WiFi..."); mWifiMgr.startScan(); } else { mWifiMgr.openWifi(); } } } @Override protected void permissionFail(int requestCode) { super.permissionFail(requestCode); if(requestCode == PERMISSION_REQ_CONNECT_WIFI) { //权限请求失败 mIsPermissionGranted = false; showTipsDialog("WiFi权限获取失败", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { onBackPressed(); } }); } } /** * 注册监听WiFi操作的系统广播 */ private void registerWifiReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); registerReceiver(mWifiBroadcaseReceiver, filter); } /** * 反注册WiFi相关的系统广播 */ private void unregisterWifiReceiver() { if (mWifiBroadcaseReceiver != null) { unregisterReceiver(mWifiBroadcaseReceiver); mWifiBroadcaseReceiver = null; } } /** * 开启文件接收服务 */ private void initReceiverServer() { mReceiveServerRunnable = new ReceiveServerRunnable(); new Thread(mReceiveServerRunnable).start(); } /** * 告知发送端初始化完毕 */ private void sendInitSuccessToFileSender() { new Thread() { @Override public void run() { try { //确保WiFi连接后获取正确IP地址 int tryCount = 0; String serverIp = mWifiMgr.getIpAddressFromHotspot(); while (serverIp.equals(Consts.DEFAULT_UNKNOW_IP) && tryCount < Consts.DEFAULT_TRY_COUNT) { Thread.sleep(1000); serverIp = mWifiMgr.getIpAddressFromHotspot(); tryCount ++; } //是否可以ping通指定IP地址 tryCount = 0; while (!NetUtils.pingIpAddress(serverIp) && tryCount < Consts.DEFAULT_TRY_COUNT) { Thread.sleep(500); LogUtils.i("Try to ping ------" + serverIp + " - " + tryCount); tryCount ++; } //创建UDP通信 if(mDatagramSocket == null) { //解决:java.net.BindException: bind failed: EADDRINUSE (Address already in use) mDatagramSocket = new DatagramSocket(null); mDatagramSocket.setReuseAddress(true); mDatagramSocket.bind(new InetSocketAddress(Consts.DEFAULT_SERVER_UDP_PORT)); } //发送初始化完毕指令 InetAddress ipAddress = InetAddress.getByName(serverIp); byte[] sendData = Consts.MSG_FILE_RECEIVER_INIT_SUCCESS.getBytes(BaseTransfer.UTF_8); DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT); mDatagramSocket.send(sendPacket); LogUtils.i("发送消息 ------->>>" + Consts.MSG_FILE_RECEIVER_INIT_SUCCESS); //接收文件列表 while (true) { byte[] receiveData = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); mDatagramSocket.receive(receivePacket); String response = new String(receivePacket.getData()).trim(); if(isNotEmptyString(response)) { //发送端发来的文件列表 LogUtils.e("接收到的消息 -------->>>" + response); parseFileInfoList(response); } } } catch (Exception e) { e.printStackTrace(); } } }.start(); } /** * 设置WiFi列表适配器 */ private void setupWifiAdapter() { if(mChooseHotspotAdapter == null) { mChooseHotspotAdapter = new CommonAdapter(getContext(), R.layout.item_choose_hotspot, mScanResults) { @Override protected void convert(ViewHolder holder, ScanResult scanResult, int position) { holder.setText(R.id.tv_item_choose_hotspot_ssid, scanResult.SSID); holder.setText(R.id.tv_item_choose_hotspot_level, String.format(getString(R.string.item_level), scanResult.level)); } }; //设置点击事件 mChooseHotspotAdapter.setOnItemClickListener(this); //设置适配器 mChooseHotspotRecyclerView.setAdapter(mChooseHotspotAdapter); //设置间隔 mChooseHotspotRecyclerView.addItemDecoration(new SpaceItemDecoration(10)); mChooseHotspotRecyclerView.setVisibility(View.VISIBLE); } else { mChooseHotspotAdapter.notifyDataSetChanged(); } } /** * 设置接收文件列表适配器 */ private void setupReceiveFilesAdapter() { List> fileInfos = AppContext.getAppContext().getReceiverFileInfoMap(); Collections.sort(fileInfos, Consts.DEFAULT_COMPARATOR); //设置适配器 mReceiveFilesAdapter = new CommonAdapter>(getContext(), R.layout.item_files_selector, fileInfos) { @Override protected void convert(ViewHolder holder, Map.Entry fileInfoMap, int position) { final FileInfo fileInfo = fileInfoMap.getValue(); //文件路径 holder.setText(R.id.tv_item_files_selector_file_path, fileInfo.getFilePath()); //文件大小 holder.setText(R.id.tv_item_files_selector_size, FileUtils.FormetFileSize(fileInfo.getSize())); //文件接收状态 if(fileInfo.getProgress() >= 100) { holder.setText(R.id.tv_item_files_selector_status, "接收完毕"); } else if(fileInfo.getProgress() == 0) { holder.setText(R.id.tv_item_files_selector_status, "准备接收"); } else if(fileInfo.getProgress() < 100) { holder.setText(R.id.tv_item_files_selector_status, "正在接收"); } else { holder.setText(R.id.tv_item_files_selector_status, "接收失败"); } //文件接收进度 ProgressBar progressBar = holder.getView(R.id.pb_item_files_selector); progressBar.setProgress(fileInfo.getProgress()); //选中文件 CheckBox checkBox = holder.getView(R.id.cb_item_files_selector); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) { mSendFileInfos.add(fileInfo); } else { mSendFileInfos.remove(fileInfo); } //选中的文件个数大于零才可点击底部按钮 btnSendFileList.setEnabled(mSendFileInfos.size() > 0); } }); } }; mReceiveFilesRecyclerView.setAdapter(mReceiveFilesAdapter); //设置ListView样式 mReceiveFilesRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); //分割线 mReceiveFilesRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); } /** * 更新文件接收进度 * @param position 文件索引 * @param progress 接收进度 */ private void updateProgress(int position, int progress) { FileInfo fileInfo = mReceiveFilesAdapter.getDatas().get(position).getValue(); fileInfo.setProgress(progress); mReceiveFilesAdapter.notifyItemChanged(position); if(position == AppContext.getAppContext().getReceiverFileInfoMap().size() - 1 && progress == 100) { toast("所有文件接收完毕"); LogUtils.e("所有文件接收完毕"); } } /** * 将字符串解析成FileInfo列表 * @param jsonStr */ private void parseFileInfoList(String jsonStr) { if(isNotEmptyString(jsonStr)) { List fileInfos = FileInfo.toObjectList(jsonStr); if(!isEmptyList(fileInfos)) { for(FileInfo fileInfo : fileInfos) { if(fileInfo != null && isNotEmptyString(fileInfo.getFilePath())) { AppContext.getAppContext().addReceiverFileInfo(fileInfo); } } //更新适配器 mHandler.sendEmptyMessage(MSG_UPDATE_ADAPTER); } } } /** * 发送选中的文件列表给发送端 */ private void sendFileListToFileSender() { new Thread() { @Override public void run() { try { //确保WiFi连接后获取正确IP地址 String serverIp = mWifiMgr.getIpAddressFromHotspot(); if(mDatagramSocket == null) { //解决:java.net.BindException: bind failed: EADDRINUSE (Address already in use) mDatagramSocket = new DatagramSocket(null); mDatagramSocket.setReuseAddress(true); mDatagramSocket.bind(new InetSocketAddress(Consts.DEFAULT_SERVER_UDP_PORT)); } //发送选中的文件列表 InetAddress ipAddress = InetAddress.getByName(serverIp); String jsonStr = FileInfo.toJsonStr(mSendFileInfos); DatagramPacket sendPacket = new DatagramPacket(jsonStr.getBytes(), jsonStr.getBytes().length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT); mDatagramSocket.send(sendPacket); LogUtils.i("Send Msg To FileSender ------->>>" + jsonStr); //发送开始发送文件指令 byte[] sendData = Consts.MSG_START_SEND.getBytes(BaseTransfer.UTF_8); DatagramPacket sendPacket2 = new DatagramPacket(sendData, sendData.length, ipAddress, Consts.DEFAULT_SERVER_UDP_PORT); mDatagramSocket.send(sendPacket2); LogUtils.i("Send Msg To FileSender ------->>>" + sendData); } catch (Exception e) { e.printStackTrace(); } } }.start(); } /** * 显示WiFi密码输入框 * @param title * @param listener */ protected void showDialogWithEditText(String title, final OnWifiPasswordConfirmListener listener) { View dialogView = LayoutInflater.from(this).inflate(R.layout.layout_dialog_with_edittext, null); final EditText etPassword = (EditText) dialogView.findViewById(R.id.et_dialog_with_edittext); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(title); builder.setView(dialogView); builder.setPositiveButton(getString(R.string.confirm), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (listener != null) { listener.onConfirm(etPassword.getText().toString().trim()); } } }); builder.setNegativeButton(getString(R.string.cancel), null); builder.create().show(); } /** * 设置状态 * @param status */ private void setStatus(String status) { tvStatus.setText(status); LogUtils.e(status); } /** * 是否还有文件在接收 * @return */ private boolean hasFileReceiving() { for(FileReceiver fileReceiver : mFileReceiverList) { if(fileReceiver != null && fileReceiver.isRunning()) { return true; } } return false; } /** * 停止所有文件发送任务 */ private void stopAllFileReceivingTask() { for(FileReceiver fileReceiver : mFileReceiverList) { if(fileReceiver != null) { fileReceiver.stop(); } } } /** * 关闭此Activity */ private void finishActivity() { //断开UDP Socket closeUdpSocket(); //停止所有文件接收任务 stopAllFileReceivingTask(); //断开接收文件的Socket closeClientSocket(); //清除WiFi网络 mWifiMgr.clearWifiConfig(); //清空接收文件列表 AppContext.getAppContext().clearReceiverFileInfoMap(); finish(); } /** * 断开接收文件的Socket */ private void closeClientSocket() { if(mClientSocket != null) { try { mClientSocket.close(); mClientSocket = null; } catch (IOException e) { e.printStackTrace(); } } } /** * 关闭UDP Socket */ private void closeUdpSocket() { if(mDatagramSocket != null) { mDatagramSocket.disconnect(); mDatagramSocket.close(); mDatagramSocket = null; } } @OnClick(R.id.btn_receive_files) public void sendReceiveFileListToFileSender() { //将选择的文件列表发给发送端,开始接收文件 AppContext.getAppContext().clearReceiverFileInfoMap(); for(FileInfo fileInfo : mSendFileInfos) { fileInfo.setPosition(mSendFileInfos.indexOf(fileInfo)); AppContext.getAppContext().addReceiverFileInfo(fileInfo); } setupReceiveFilesAdapter(); initReceiverServer(); } @Override public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) { if(position < mChooseHotspotAdapter.getItemCount() && position >= 0) { //获取当前点击WiFi的SSID ScanResult scanResult = mChooseHotspotAdapter.getDatas().get(position); mSelectedSSID = scanResult.SSID; if((scanResult.capabilities != null && !scanResult.capabilities.equals(WifiMgr.NO_PASSWORD)) || (scanResult.capabilities != null && !scanResult.capabilities.equals(WifiMgr.NO_PASSWORD_WPS))){ //弹出密码输入框 showDialogWithEditText(mSelectedSSID, new OnWifiPasswordConfirmListener() { @Override public void onConfirm(String password) { //使用密码连接WiFi if(isNotEmptyString(password)) { try { setStatus("正在连接Wifi..."); mWifiMgr.connectWifi(mSelectedSSID, password, mScanResults); } catch (InterruptedException e) { e.printStackTrace(); } } else { toast("密码不能为空"); } } }); } else { //连接免密码WiFi try { setStatus("正在连接Wifi..."); mWifiMgr.connectWifi(mSelectedSSID, "", mScanResults); } catch (InterruptedException e) { e.printStackTrace(); } } } } @Override public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) { return false; } /** * WiFi广播接收器 */ private WifiBroadcaseReceiver mWifiBroadcaseReceiver = new WifiBroadcaseReceiver() { @Override public void onWifiEnabled() { //WiFi已开启,开始扫描可用WiFi setStatus("正在扫描可用WiFi..."); mWifiMgr.startScan(); } @Override public void onWifiDisabled() { //WiFi已关闭,清除可用WiFi列表 mSelectedSSID = ""; mScanResults.clear(); setupWifiAdapter(); } @Override public void onScanResultsAvailable(List scanResults) { //扫描周围可用WiFi成功,设置可用WiFi列表 mScanResults.clear(); mScanResults.addAll(scanResults); setupWifiAdapter(); } @Override public void onWifiConnected(String connectedSSID) { //判断指定WiFi是否连接成功 if (connectedSSID.equals(mSelectedSSID) && !mIsSendInitOrder) { //连接成功 setStatus("Wifi连接成功..."); //显示发送列表,隐藏WiFi选择列表 mChooseHotspotRecyclerView.setVisibility(View.GONE); mReceiveFilesRecyclerView.setVisibility(View.VISIBLE); //告知发送端,接收端初始化完毕 mHandler.sendEmptyMessage(MSG_FILE_RECEIVER_INIT_SUCCESS); mIsSendInitOrder = true; } else { // //连接成功的不是设备WiFi,清除该WiFi,重新扫描周围WiFi // LogUtils.e("连接到错误WiFi,正在断开重连..."); // mWifiMgr.disconnectWifi(connectedSSID); // mWifiMgr.startScan(); } } @Override public void onWifiDisconnected() { } }; /** * ServerSocket启动线程 */ private class ReceiveServerRunnable implements Runnable { @Override public void run() { try { //发送选择接收的文件 mHandler.sendEmptyMessage(MSG_SEND_RECEIVE_FILE_LIST); Thread.sleep(3000); //开始接收文件 String serverIp = mWifiMgr.getIpAddressFromHotspot(); List> fileInfoList = AppContext.getAppContext().getReceiverFileInfoMap(); Collections.sort(fileInfoList, Consts.DEFAULT_COMPARATOR); for(final Map.Entry fileInfoMap : fileInfoList) { //连接发送端,逐个文件进行接收 final int position = fileInfoList.indexOf(fileInfoMap); mClientSocket = new Socket(serverIp, Consts.DEFAULT_FILE_RECEIVE_SERVER_PORT); FileReceiver fileReceiver = new FileReceiver(mClientSocket, fileInfoMap.getValue()); fileReceiver.setOnReceiveListener(new FileReceiver.OnReceiveListener() { @Override public void onStart() { mHandler.obtainMessage(MSG_SET_STATUS, "开始接收"+ FileUtils.getFileName(fileInfoMap.getValue().getFilePath())).sendToTarget(); } @Override public void onProgress(FileInfo fileInfo, long progress, long total) { //更新接收进度视图 int i_progress = (int) (progress * 100 / total); LogUtils.e("正在接收:" + fileInfo.getFilePath() + "\n当前进度:" + i_progress); Message msg = new Message(); msg.what = MSG_UPDATE_PROGRESS; msg.arg1 = position; msg.arg2 = i_progress; mHandler.sendMessage(msg); } @Override public void onSuccess(FileInfo fileInfo) { //接收成功 mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收成功").sendToTarget(); fileInfo.setResult(FileInfo.FLAG_SUCCESS); AppContext.getAppContext().updateReceiverFileInfo(fileInfo); Message msg = new Message(); msg.what = MSG_UPDATE_PROGRESS; msg.arg1 = position; msg.arg2 = 100; mHandler.sendMessage(msg); } @Override public void onFailure(Throwable throwable, FileInfo fileInfo) { if(fileInfo != null) { //接收失败 mHandler.obtainMessage(MSG_SET_STATUS, "文件:" + FileUtils.getFileName(fileInfo.getFilePath()) + "接收失败").sendToTarget(); fileInfo.setResult(FileInfo.FLAG_FAILURE); AppContext.getAppContext().updateReceiverFileInfo(fileInfo); Message msg = new Message(); msg.what = MSG_UPDATE_PROGRESS; msg.arg1 = position; msg.arg2 = -1; mHandler.sendMessage(msg); } } }); //加入线程池执行 mFileReceiverList.add(fileReceiver); AppContext.getAppContext().MAIN_EXECUTOR.execute(fileReceiver); } } catch (Exception e) { e.printStackTrace(); } } } private interface OnWifiPasswordConfirmListener { void onConfirm(String password); } }