mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-23 04:33:54 -04:00
Major Refactor!
Comprehensive refactoring of DeviceListFragment, includes: - Shifting all async-tasks to separate files - Exceptions in the async-tasks are now handled by showing a toast and closing the LocalFileTransferActivity - Added a new 'Error' status for file items - If a file has error status after transfer, then an error icon is shown for it TransferProgressFragment - Documentation and other additions/changes
This commit is contained in:
parent
147dbb033b
commit
91b26a9f08
@ -1,14 +1,11 @@
|
|||||||
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.net.wifi.p2p.WifiP2pDevice;
|
import android.net.wifi.p2p.WifiP2pDevice;
|
||||||
import android.net.wifi.p2p.WifiP2pDeviceList;
|
import android.net.wifi.p2p.WifiP2pDeviceList;
|
||||||
import android.net.wifi.p2p.WifiP2pInfo;
|
import android.net.wifi.p2p.WifiP2pInfo;
|
||||||
import android.net.wifi.p2p.WifiP2pManager;
|
import android.net.wifi.p2p.WifiP2pManager;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@ -23,7 +20,6 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import androidx.fragment.app.ListFragment;
|
import androidx.fragment.app.ListFragment;
|
||||||
@ -31,73 +27,115 @@ import androidx.fragment.app.ListFragment;
|
|||||||
import org.kiwix.kiwixmobile.R;
|
import org.kiwix.kiwixmobile.R;
|
||||||
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
|
import org.kiwix.kiwixmobile.utils.SharedPreferenceUtil;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.FileItem.TO_BE_SENT;
|
/**
|
||||||
|
* Part of the local file sharing module, this fragment is responsible for displaying details of
|
||||||
|
* the user device and the list of available peer devices, and once the user taps on a particular
|
||||||
|
* peer device item, connecting to that device and initiating file transfer.
|
||||||
|
*
|
||||||
|
* File transfer involves two phases:
|
||||||
|
* 1) Handshake with the selected peer device, using {@link PeerGroupHandshakeAsyncTask}
|
||||||
|
* 2) After handshake, starting the files transfer using {@link SenderDeviceAsyncTask} on the sender
|
||||||
|
* device and {@link ReceiverDeviceAsyncTask} files receiving device
|
||||||
|
*
|
||||||
|
* The starting point for the module is {@link LocalFileTransferActivity}
|
||||||
|
* */
|
||||||
public class DeviceListFragment extends ListFragment implements WifiP2pManager.PeerListListener, WifiP2pManager.ConnectionInfoListener {
|
public class DeviceListFragment extends ListFragment implements WifiP2pManager.PeerListListener, WifiP2pManager.ConnectionInfoListener {
|
||||||
|
|
||||||
public static final String TAG = "DeviceListFragment";
|
public static final String TAG = "DeviceListFragment";
|
||||||
|
|
||||||
private SharedPreferenceUtil sharedPreferenceUtil;
|
|
||||||
|
|
||||||
private View fragRootView = null;
|
|
||||||
private List<WifiP2pDevice> peerDevices = new ArrayList<WifiP2pDevice>();
|
|
||||||
private WifiP2pDevice userDevice;
|
|
||||||
private WifiP2pDevice selectedPeerDevice = null;
|
|
||||||
private InetAddress selectedPeerDeviceInetAddress;
|
|
||||||
private WifiP2pInfo groupInfo;
|
|
||||||
|
|
||||||
private InetAddress fileReceiverAddress;
|
|
||||||
public static int PEER_HANDSHAKE_PORT = 8009;
|
public static int PEER_HANDSHAKE_PORT = 8009;
|
||||||
public static int FILE_TRANSFER_PORT = 8008;
|
public static int FILE_TRANSFER_PORT = 8008;
|
||||||
|
|
||||||
private boolean fileSender = false;
|
private SharedPreferenceUtil sharedPreferenceUtil;
|
||||||
private ArrayList<Uri> fileUriList;
|
|
||||||
private int totalFiles = -1;
|
private LocalFileTransferActivity localFileTransferActivity; // Parent activity, starting point of the module
|
||||||
private int totalFilesSent = 0;
|
private TransferProgressFragment transferProgressFragment; // Sibling fragment, for displaying transfer progress
|
||||||
private ArrayList<FileItem> fileItems = new ArrayList<>();
|
|
||||||
private TransferProgressFragment transferProgressFragment;
|
private View deviceListFragmentRootView = null; // Root view corresponding to the DeviceListFragment
|
||||||
|
|
||||||
|
private boolean fileSender = false; // Whether file sending device or not
|
||||||
|
private ArrayList<Uri> fileUriList; // Uris of files to be shared, available only for the sender device
|
||||||
|
private int totalFilesForTransfer = -1;
|
||||||
|
private int filesSent = 0; // Count of number of files transferred until now
|
||||||
|
private ArrayList<FileItem> filesToSend = new ArrayList<>();
|
||||||
|
|
||||||
|
private WifiP2pDevice userDevice; // Represents the device on which the app is running
|
||||||
|
private WifiP2pInfo groupInfo; // Corresponds to the WiFi P2P group formed between the two devices
|
||||||
|
private List<WifiP2pDevice> peerDevices = new ArrayList<WifiP2pDevice>();
|
||||||
|
|
||||||
|
private WifiP2pDevice selectedPeerDevice = null;
|
||||||
|
private InetAddress selectedPeerDeviceInetAddress;
|
||||||
|
|
||||||
|
private InetAddress fileReceiverDeviceAddress; // IP address of the file receiving device
|
||||||
|
private boolean fileTransferStarted = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
this.setListAdapter(new WifiPeerListAdapter(getActivity(), R.layout.row_peer_device, peerDevices));
|
localFileTransferActivity = (LocalFileTransferActivity) getActivity();
|
||||||
|
|
||||||
if(((LocalFileTransferActivity) getActivity()).isFileSender()) {
|
// As DeviceListFragment extends ListFragment for the purpose of displaying list of peers
|
||||||
|
this.setListAdapter(new WifiPeerListAdapter(localFileTransferActivity, R.layout.row_peer_device, peerDevices));
|
||||||
|
|
||||||
|
if(localFileTransferActivity.isFileSender()) {
|
||||||
fileSender = true;
|
fileSender = true;
|
||||||
fileUriList = ((LocalFileTransferActivity) getActivity()).getFileURIArrayList();
|
fileUriList = localFileTransferActivity.getFileUriArrayList();
|
||||||
totalFiles = fileUriList.size();
|
totalFilesForTransfer = fileUriList.size();
|
||||||
|
|
||||||
for(int i = 0; i < fileUriList.size(); i++)
|
for(int i = 0; i < fileUriList.size(); i++) {
|
||||||
fileItems.add(new FileItem(getFileName(fileUriList.get(i)), TO_BE_SENT));
|
filesToSend.add(new FileItem(getFileName(fileUriList.get(i)), FileItem.TO_BE_SENT));
|
||||||
|
}
|
||||||
|
|
||||||
displayTransferProgressFragment();
|
displayTransferProgressFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
fragRootView = inflater.inflate(R.layout.fragment_device_list, null);
|
deviceListFragmentRootView = inflater.inflate(R.layout.fragment_device_list, null);
|
||||||
return fragRootView;
|
return deviceListFragmentRootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WifiP2pDevice getUserDevice() { return userDevice; }
|
@Override
|
||||||
|
public void onListItemClick(ListView l, View v, int position, long id) {
|
||||||
|
/* Connection can only be initiated by user of the sender device, & only when transfer has not been started */
|
||||||
|
if(!isFileSender() || fileTransferStarted)
|
||||||
|
return;
|
||||||
|
|
||||||
private static String getDeviceStatus(int status) {
|
selectedPeerDevice = (WifiP2pDevice) getListAdapter().getItem(position);
|
||||||
|
Toast.makeText(localFileTransferActivity, selectedPeerDevice.deviceName, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
((DeviceActionListener) localFileTransferActivity).connect(selectedPeerDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayTransferProgressFragment() {
|
||||||
|
transferProgressFragment = new TransferProgressFragment(filesToSend);
|
||||||
|
FragmentManager fragmentManager = localFileTransferActivity.getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.add(R.id.container_fragment_transfer_progress, transferProgressFragment)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUserDevice(WifiP2pDevice device) {
|
||||||
|
this.userDevice = device;
|
||||||
|
|
||||||
|
if(userDevice != null) {
|
||||||
|
TextView deviceName = deviceListFragmentRootView.findViewById(R.id.text_view_device_name);
|
||||||
|
TextView deviceStatus = deviceListFragmentRootView.findViewById(R.id.text_view_device_status);
|
||||||
|
|
||||||
|
if(deviceName != null) deviceName.setText(userDevice.deviceName);
|
||||||
|
if(deviceStatus != null) deviceStatus.setText(getDeviceStatus(userDevice.status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDeviceStatus(int status) {
|
||||||
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Peer Status: " + status);
|
Log.d(LocalFileTransferActivity.TAG, "Peer Status: " + status);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@ -111,36 +149,12 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onListItemClick(ListView l, View v, int position, long id) {
|
|
||||||
if(!isFileSender())
|
|
||||||
return;
|
|
||||||
|
|
||||||
selectedPeerDevice = (WifiP2pDevice) getListAdapter().getItem(position);
|
|
||||||
Toast.makeText(getActivity(), selectedPeerDevice.deviceName, Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
// TODO: Set sender depending upon receiving file URI in activity opening intent - DONE
|
|
||||||
((DeviceActionListener) getActivity()).connect(selectedPeerDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateUserDevice(WifiP2pDevice device) {
|
|
||||||
this.userDevice = device;
|
|
||||||
|
|
||||||
if(userDevice != null) {
|
|
||||||
TextView deviceName = fragRootView.findViewById(R.id.text_view_device_name);
|
|
||||||
TextView deviceStatus = fragRootView.findViewById(R.id.text_view_device_status);
|
|
||||||
|
|
||||||
if(deviceName != null) deviceName.setText(userDevice.deviceName);
|
|
||||||
if(deviceStatus != null) deviceStatus.setText(getDeviceStatus(userDevice.status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPeersAvailable(WifiP2pDeviceList peers) {
|
public void onPeersAvailable(WifiP2pDeviceList peers) {
|
||||||
|
|
||||||
ProgressBar searchingPeersProgressBar = fragRootView.findViewById(R.id.progress_bar_searching_peers);
|
ProgressBar searchingPeersProgressBar = deviceListFragmentRootView.findViewById(R.id.progress_bar_searching_peers);
|
||||||
searchingPeersProgressBar.setVisibility(View.GONE);
|
searchingPeersProgressBar.setVisibility(View.GONE);
|
||||||
FrameLayout frameLayoutPeerDevices = fragRootView.findViewById(R.id.frame_layout_peer_devices);
|
FrameLayout frameLayoutPeerDevices = deviceListFragmentRootView.findViewById(R.id.frame_layout_peer_devices);
|
||||||
frameLayoutPeerDevices.setVisibility(View.VISIBLE);
|
frameLayoutPeerDevices.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
peerDevices.clear();
|
peerDevices.clear();
|
||||||
@ -158,9 +172,9 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onInitiateDiscovery() {
|
public void onInitiateDiscovery() {
|
||||||
ProgressBar searchingPeersProgressBar = fragRootView.findViewById(R.id.progress_bar_searching_peers);
|
ProgressBar searchingPeersProgressBar = deviceListFragmentRootView.findViewById(R.id.progress_bar_searching_peers);
|
||||||
searchingPeersProgressBar.setVisibility(View.VISIBLE);
|
searchingPeersProgressBar.setVisibility(View.VISIBLE);
|
||||||
FrameLayout frameLayoutPeerDevices = fragRootView.findViewById(R.id.frame_layout_peer_devices);
|
FrameLayout frameLayoutPeerDevices = deviceListFragmentRootView.findViewById(R.id.frame_layout_peer_devices);
|
||||||
frameLayoutPeerDevices.setVisibility(View.INVISIBLE);
|
frameLayoutPeerDevices.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,54 +182,87 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
public void onConnectionInfoAvailable(WifiP2pInfo info) {
|
public void onConnectionInfoAvailable(WifiP2pInfo info) {
|
||||||
groupInfo = info;
|
groupInfo = info;
|
||||||
|
|
||||||
//TODO: Show progress of file transfer process - DONE
|
|
||||||
|
|
||||||
new PeerGroupHandshakeAsyncTask(this, groupInfo).execute();
|
new PeerGroupHandshakeAsyncTask(this, groupInfo).execute();
|
||||||
|
// TODO: Disable onclick listener (of list) for connecting to devices - DONE using boolean startedFileTransfer
|
||||||
// TODO: Disable onclick listener (of list) for connecting to devices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setClientAddress(InetAddress clientAddress) {
|
public void setClientAddress(InetAddress clientAddress) {
|
||||||
if(clientAddress != null) selectedPeerDeviceInetAddress = clientAddress;
|
if(clientAddress == null) {
|
||||||
|
// null is returned only in case of a failed handshake
|
||||||
|
Toast.makeText(localFileTransferActivity, "Peer device not cooperating for transfer", Toast.LENGTH_LONG).show();
|
||||||
|
localFileTransferActivity.closeLocalFileTransferActivity();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If control reaches here, means handshake was successful
|
||||||
|
selectedPeerDeviceInetAddress = clientAddress;
|
||||||
startFileTransfer();
|
startFileTransfer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startFileTransfer() {
|
||||||
|
fileTransferStarted = true;
|
||||||
|
|
||||||
|
if(groupInfo.groupFormed && !fileSender) {
|
||||||
|
displayTransferProgressFragment();
|
||||||
|
|
||||||
|
new ReceiverDeviceAsyncTask(this, transferProgressFragment).execute();
|
||||||
|
Toast.makeText(localFileTransferActivity, "Preparing to receive files...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
} else if(groupInfo.groupFormed) {
|
||||||
|
{
|
||||||
|
Log.d(LocalFileTransferActivity.TAG, "Starting file transfer");
|
||||||
|
|
||||||
|
Toast.makeText(localFileTransferActivity, "Starting file transfer...", Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
if(groupInfo.isGroupOwner) fileReceiverDeviceAddress = selectedPeerDeviceInetAddress;
|
||||||
|
else fileReceiverDeviceAddress = groupInfo.groupOwnerAddress;
|
||||||
|
|
||||||
|
//TODO: Fix this
|
||||||
|
for(int i = 0; i < 20000000; i++);
|
||||||
|
|
||||||
|
for(int i = 0; i < totalFilesForTransfer; i++) {
|
||||||
|
new SenderDeviceAsyncTask(this, transferProgressFragment, i).execute(fileUriList.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper methods */
|
||||||
|
|
||||||
|
public WifiP2pDevice getUserDevice() {
|
||||||
|
return userDevice;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isFileSender() {
|
public boolean isFileSender() {
|
||||||
return fileSender;
|
return fileSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTotalFiles() {
|
public int getTotalFilesForTransfer() {
|
||||||
return totalFiles;
|
return totalFilesForTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTotalFiles(int totalFiles) {
|
public void setTotalFilesForTransfer(int totalFilesForTransfer) {
|
||||||
this.totalFiles = totalFiles;
|
this.totalFilesForTransfer = totalFilesForTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<FileItem> getFileItems() {
|
public ArrayList<FileItem> getFileItems() {
|
||||||
return fileItems;
|
return filesToSend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFileItems(ArrayList<FileItem> fileItems) {
|
public void setFileItems(ArrayList<FileItem> fileItems) {
|
||||||
this.fileItems = fileItems;
|
this.filesToSend = fileItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<Uri> getFileUriList() {
|
public ArrayList<Uri> getFileUriList() {
|
||||||
return fileUriList;
|
return fileUriList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void incrementTotalFilesSent() {
|
public void incrementTotalFilesSent() {
|
||||||
this.totalFilesSent++;
|
this.filesSent++;
|
||||||
}
|
|
||||||
|
|
||||||
public int getTotalFilesSent() {
|
|
||||||
return totalFilesSent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allFilesSent() {
|
public boolean allFilesSent() {
|
||||||
return (totalFilesSent == totalFiles);
|
return (filesSent == totalFilesForTransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSharedPreferenceUtil(SharedPreferenceUtil sharedPreferenceUtil) {
|
public void setSharedPreferenceUtil(SharedPreferenceUtil sharedPreferenceUtil) {
|
||||||
@ -226,42 +273,32 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
return (sharedPreferenceUtil.getPrefStorage() + "/Kiwix/");
|
return (sharedPreferenceUtil.getPrefStorage() + "/Kiwix/");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayTransferProgressFragment() {
|
public InetAddress getFileReceiverDeviceAddress() {
|
||||||
transferProgressFragment = new TransferProgressFragment(fileItems);
|
return fileReceiverDeviceAddress;
|
||||||
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
|
|
||||||
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
|
||||||
fragmentTransaction.add(R.id.container_fragment_transfer_progress, transferProgressFragment)
|
|
||||||
.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startFileTransfer() {
|
|
||||||
|
|
||||||
if(groupInfo.groupFormed && !fileSender) {
|
public static String getFileName(Uri fileUri) {
|
||||||
displayTransferProgressFragment();
|
String fileUriString = fileUri.toString();
|
||||||
|
// Returns text after location of last slash in the file path
|
||||||
|
return fileUriString.substring(fileUriString.lastIndexOf('/')+1);
|
||||||
|
}
|
||||||
|
|
||||||
new FileServerAsyncTask(getActivity(), this, transferProgressFragment).execute();
|
public static void copyToOutputStream(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||||
Toast.makeText(getActivity(), "File receiving device", Toast.LENGTH_SHORT).show();
|
byte[] bufferForBytes = new byte[1024];
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
} else if(groupInfo.groupFormed) {
|
Log.d(TAG, "Copying to OutputStream...");
|
||||||
{
|
while ((bytesRead = inputStream.read(bufferForBytes)) != -1) {
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Starting file transfer");
|
outputStream.write(bufferForBytes, 0, bytesRead);
|
||||||
|
|
||||||
Toast.makeText(getActivity(), "Starting file transfer", Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
if(groupInfo.isGroupOwner) fileReceiverAddress = selectedPeerDeviceInetAddress;
|
|
||||||
else fileReceiverAddress = groupInfo.groupOwnerAddress;
|
|
||||||
|
|
||||||
//TODO: Fix this
|
|
||||||
for(int i = 0; i < 10000000; i++);
|
|
||||||
|
|
||||||
for(int i = 0; i < totalFiles; i++) {
|
|
||||||
new FileSenderAsyncTask(getContext(), this, groupInfo, transferProgressFragment, i).execute(fileUriList.get(i));
|
|
||||||
transferProgressFragment.changeStatus(i, FileItem.SENDING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
inputStream.close();
|
||||||
|
Log.d(LocalFileTransferActivity.TAG, "Both streams closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class WifiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {
|
private class WifiPeerListAdapter extends ArrayAdapter<WifiP2pDevice> {
|
||||||
|
|
||||||
private List<WifiP2pDevice> listItems;
|
private List<WifiP2pDevice> listItems;
|
||||||
@ -277,7 +314,7 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
|
|
||||||
View rowView = convertView;
|
View rowView = convertView;
|
||||||
if(rowView == null) {
|
if(rowView == null) {
|
||||||
LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
LayoutInflater layoutInflater = getActivity().getLayoutInflater();
|
||||||
rowView = layoutInflater.inflate(R.layout.row_peer_device, parent, false);
|
rowView = layoutInflater.inflate(R.layout.row_peer_device, parent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +324,7 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
TextView deviceName = rowView.findViewById(R.id.row_device_name);
|
TextView deviceName = rowView.findViewById(R.id.row_device_name);
|
||||||
TextView deviceStatus = rowView.findViewById(R.id.row_device_status);
|
TextView deviceStatus = rowView.findViewById(R.id.row_device_status);
|
||||||
|
|
||||||
if(deviceName != null) deviceName.setText(device.deviceName);
|
if(deviceName != null) deviceName.setText(device.deviceName);
|
||||||
if(deviceStatus != null) deviceStatus.setText(getDeviceStatus(device.status));
|
if(deviceStatus != null) deviceStatus.setText(getDeviceStatus(device.status));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,334 +335,8 @@ public class DeviceListFragment extends ListFragment implements WifiP2pManager.P
|
|||||||
public interface DeviceActionListener {
|
public interface DeviceActionListener {
|
||||||
void cancelSearch();
|
void cancelSearch();
|
||||||
|
|
||||||
void connect(WifiP2pDevice peerDevice);
|
void connect(@NonNull WifiP2pDevice peerDevice);
|
||||||
|
|
||||||
void closeLocalFileTransferActivity();
|
void closeLocalFileTransferActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PeerGroupHandshakeAsyncTask extends AsyncTask<Void, Void, InetAddress> {
|
|
||||||
|
|
||||||
private DeviceListFragment deviceListFragment;
|
|
||||||
private WifiP2pInfo groupInfo;
|
|
||||||
|
|
||||||
public PeerGroupHandshakeAsyncTask(DeviceListFragment deviceListFragment, WifiP2pInfo groupInfo) {
|
|
||||||
this.deviceListFragment = deviceListFragment;
|
|
||||||
this.groupInfo = groupInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected InetAddress doInBackground(Void... voids) {
|
|
||||||
if(groupInfo.groupFormed && groupInfo.isGroupOwner) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
ServerSocket serverSocket = new ServerSocket(PEER_HANDSHAKE_PORT);
|
|
||||||
serverSocket.setReuseAddress(true);
|
|
||||||
Socket client = serverSocket.accept();
|
|
||||||
ObjectInputStream objectInputStream = new ObjectInputStream(client.getInputStream());
|
|
||||||
Object object = objectInputStream.readObject();
|
|
||||||
if (object.getClass().equals(String.class) && ((String) object).equals("Request Kiwix File Sharing")) {
|
|
||||||
Log.d(TAG, "Client IP address: "+ client.getInetAddress());
|
|
||||||
|
|
||||||
if(deviceListFragment.isFileSender()) {
|
|
||||||
ObjectOutputStream objectOutputStream = new ObjectOutputStream(client.getOutputStream());
|
|
||||||
objectOutputStream.writeObject(""+deviceListFragment.getTotalFiles());
|
|
||||||
Log.d(TAG, "### SENDER DEVICE");
|
|
||||||
ArrayList<Uri> fileUriList = deviceListFragment.getFileUriList();
|
|
||||||
for (int i = 0; i < fileUriList.size(); i++) {
|
|
||||||
objectOutputStream.writeObject(getFileName(fileUriList.get(i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Object totalFilesObject = objectInputStream.readObject();
|
|
||||||
if(totalFilesObject.getClass().equals(String.class)) {
|
|
||||||
int total = Integer.parseInt((String) totalFilesObject);
|
|
||||||
deviceListFragment.setTotalFiles(total);
|
|
||||||
Log.d(TAG, "### Transfer of "+ total + " files");
|
|
||||||
ArrayList<FileItem> fileItems = new ArrayList<>();
|
|
||||||
for (int i = 0; i < total; i++) {
|
|
||||||
Object fileNameObject = objectInputStream.readObject();
|
|
||||||
if(fileNameObject.getClass().equals(String.class)){
|
|
||||||
Log.d(TAG, "File - "+ (String) fileNameObject);
|
|
||||||
fileItems.add(new FileItem((String) fileNameObject, TO_BE_SENT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deviceListFragment.setFileItems(fileItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return client.getInetAddress();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(groupInfo.groupFormed && !groupInfo.isGroupOwner) {
|
|
||||||
|
|
||||||
String hostAddress = groupInfo.groupOwnerAddress.getHostAddress();
|
|
||||||
try {
|
|
||||||
Socket socket = new Socket();
|
|
||||||
socket.setReuseAddress(true);
|
|
||||||
socket.connect((new InetSocketAddress(hostAddress, PEER_HANDSHAKE_PORT)), 15000);
|
|
||||||
OutputStream os = socket.getOutputStream();
|
|
||||||
ObjectOutputStream oos = new ObjectOutputStream(os);
|
|
||||||
oos.writeObject(new String("Request Kiwix File Sharing"));
|
|
||||||
if(deviceListFragment.isFileSender()) {
|
|
||||||
oos.writeObject(""+deviceListFragment.getTotalFiles());
|
|
||||||
Log.d(TAG, "### SENDER DEVICE");
|
|
||||||
ArrayList<Uri> fileUriList = deviceListFragment.getFileUriList();
|
|
||||||
for (int i = 0; i < fileUriList.size(); i++) {
|
|
||||||
oos.writeObject(getFileName(fileUriList.get(i)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
|
|
||||||
Object totalFilesObject = objectInputStream.readObject();
|
|
||||||
if(totalFilesObject.getClass().equals(String.class)) {
|
|
||||||
int total = Integer.parseInt((String) totalFilesObject);
|
|
||||||
deviceListFragment.setTotalFiles(total);
|
|
||||||
Log.d(TAG, "### Transfer of "+ total + " files");
|
|
||||||
ArrayList<FileItem> fileItems = new ArrayList<>();
|
|
||||||
for (int i = 0; i < total; i++) {
|
|
||||||
Object fileNameObject = objectInputStream.readObject();
|
|
||||||
if(fileNameObject.getClass().equals(String.class)) {
|
|
||||||
Log.d(TAG, "File - "+ (String) fileNameObject);
|
|
||||||
fileItems.add(new FileItem((String) fileNameObject, TO_BE_SENT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deviceListFragment.setFileItems(fileItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oos.close();
|
|
||||||
os.close();
|
|
||||||
socket.close();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
//Log.d(TAG, e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(InetAddress inetAddress) {
|
|
||||||
(deviceListFragment).setClientAddress(inetAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getFileName(Uri fileUri) {
|
|
||||||
String fileName = "";
|
|
||||||
String fileUriString = fileUri.toString();
|
|
||||||
|
|
||||||
// Searches for location of last slash in the file path
|
|
||||||
for(int loc = fileUriString.length()-1; loc >= 0; loc--) {
|
|
||||||
if(fileUriString.charAt(loc) == '/') {
|
|
||||||
return fileUriString.substring(loc+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetAddress getFileReceiverAddress() {
|
|
||||||
return fileReceiverAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FileSenderAsyncTask extends AsyncTask<Uri, Void, String> {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private DeviceListFragment deviceListFragment;
|
|
||||||
private WifiP2pInfo groupInfo;
|
|
||||||
private TransferProgressFragment transferProgressFragment;
|
|
||||||
private ArrayList<FileItem> fileItems;
|
|
||||||
private int fileItemIndex;
|
|
||||||
|
|
||||||
public FileSenderAsyncTask(Context context, DeviceListFragment deviceListFragment, WifiP2pInfo groupInfo, TransferProgressFragment transferProgressFragment, int fileItemIndex) {
|
|
||||||
this.context = context;
|
|
||||||
this.deviceListFragment = deviceListFragment;
|
|
||||||
this.groupInfo = groupInfo;
|
|
||||||
this.transferProgressFragment = transferProgressFragment;
|
|
||||||
this.fileItems = deviceListFragment.getFileItems();
|
|
||||||
this.fileItemIndex = fileItemIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
//fileItemIndex = deviceListFragment.getTotalFilesSent();
|
|
||||||
|
|
||||||
//TODO: Remove runnable for onPreExecute, onPostExecute, onProgressUpdate
|
|
||||||
deviceListFragment.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENDING);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String doInBackground(Uri... fileUris) {
|
|
||||||
|
|
||||||
Uri fileUri = fileUris[0];
|
|
||||||
|
|
||||||
Socket socket = new Socket();
|
|
||||||
try {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Opening client socket - ");
|
|
||||||
socket.bind(null);
|
|
||||||
for(int i = 0; i < 10000000; i++);
|
|
||||||
String hostAddress = deviceListFragment.getFileReceiverAddress().getHostAddress();
|
|
||||||
socket.connect((new InetSocketAddress(hostAddress, FILE_TRANSFER_PORT)), 15000);
|
|
||||||
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Client socket - " + socket.isConnected());
|
|
||||||
OutputStream stream = socket.getOutputStream();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "OutputStream found");
|
|
||||||
ContentResolver cr = context.getContentResolver();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "ContentResolver obtained");
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
is = cr.openInputStream(fileUri);
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Opened InputStream");
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, e.toString());
|
|
||||||
}
|
|
||||||
DeviceListFragment.copyFile(is, stream);
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Client: Data written");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LocalFileTransferActivity.TAG, e.getMessage());
|
|
||||||
} finally {
|
|
||||||
if (socket != null) {
|
|
||||||
if (socket.isConnected()) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Give up
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(String s) {
|
|
||||||
deviceListFragment.incrementTotalFilesSent();
|
|
||||||
|
|
||||||
deviceListFragment.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(deviceListFragment.allFilesSent()) {
|
|
||||||
Toast.makeText(context, "All files transferred", Toast.LENGTH_SHORT).show();
|
|
||||||
deviceListFragment.getActivity().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FileServerAsyncTask extends AsyncTask<Void, Void, String> {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private DeviceListFragment deviceListFragment;
|
|
||||||
private TransferProgressFragment transferProgressFragment;
|
|
||||||
private int fileItemIndex;
|
|
||||||
|
|
||||||
public FileServerAsyncTask(Context context, DeviceListFragment deviceListFragment, TransferProgressFragment transferProgressFragment) {
|
|
||||||
this.context = context;
|
|
||||||
this.deviceListFragment = deviceListFragment;
|
|
||||||
this.transferProgressFragment = transferProgressFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String doInBackground(Void... voids) {
|
|
||||||
try {
|
|
||||||
ServerSocket serverSocket = new ServerSocket(FILE_TRANSFER_PORT);
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Server: Socket opened at " + FILE_TRANSFER_PORT);
|
|
||||||
|
|
||||||
final String KIWIX_ROOT = deviceListFragment.getZimStorageRootPath();
|
|
||||||
|
|
||||||
int totalFileCount = deviceListFragment.getTotalFiles();
|
|
||||||
for(int currentFile = 1; currentFile <= totalFileCount; currentFile++) {
|
|
||||||
Socket client = serverSocket.accept();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Server: Client connected");
|
|
||||||
fileItemIndex = currentFile-1;
|
|
||||||
deviceListFragment.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENDING);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ArrayList<FileItem> fileItems = deviceListFragment.getFileItems();
|
|
||||||
String incomingFileName = fileItems.get(currentFile-1).getFileName();
|
|
||||||
|
|
||||||
// TODO:? File selector, file not exists,
|
|
||||||
final File clientNoteFileLocation = new File(KIWIX_ROOT + incomingFileName);
|
|
||||||
File dirs = new File(clientNoteFileLocation.getParent());
|
|
||||||
if(!dirs.exists()) {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Parent creation result: "+dirs.mkdirs());
|
|
||||||
} else {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Parent directories exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "File creation: "+clientNoteFileLocation.createNewFile());
|
|
||||||
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Copying files");
|
|
||||||
InputStream inputStream = client.getInputStream();
|
|
||||||
copyFile(inputStream, new FileOutputStream(clientNoteFileLocation));
|
|
||||||
|
|
||||||
deviceListFragment.getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
deviceListFragment.incrementTotalFilesSent();
|
|
||||||
}
|
|
||||||
serverSocket.close();
|
|
||||||
|
|
||||||
return "";
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(LocalFileTransferActivity.TAG, e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(String s) {
|
|
||||||
super.onPostExecute(s);
|
|
||||||
Toast.makeText(context, "File transfer complete", Toast.LENGTH_LONG).show();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "File transfer complete");
|
|
||||||
((LocalFileTransferActivity) deviceListFragment.getActivity()).closeLocalFileTransferActivity();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean copyFile(InputStream inputStream, OutputStream out) {
|
|
||||||
byte buf[] = new byte[1024];
|
|
||||||
int len;
|
|
||||||
try {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Copying to OutputStream...");
|
|
||||||
while ((len = inputStream.read(buf)) != -1) {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
|
|
||||||
}
|
|
||||||
out.close();
|
|
||||||
inputStream.close();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Both streams closed");
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, e.toString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
||||||
|
|
||||||
public class FileItem {
|
public class FileItem {
|
||||||
public static short TO_BE_SENT = -1;
|
public static final short TO_BE_SENT = -1;
|
||||||
public static short SENDING = 0;
|
public static final short SENDING = 0;
|
||||||
public static short SENT = +1;
|
public static final short SENT = +1;
|
||||||
|
public static final short ERROR = +2;
|
||||||
|
|
||||||
private String fileName = "";
|
private String fileName = "";
|
||||||
private short fileStatus = TO_BE_SENT;
|
private short fileStatus = TO_BE_SENT;
|
||||||
|
@ -42,10 +42,16 @@ public class FileListAdapter extends RecyclerView.Adapter<FileListAdapter.FileVi
|
|||||||
holder.statusImage.setVisibility(View.GONE);
|
holder.statusImage.setVisibility(View.GONE);
|
||||||
holder.progressBar.setVisibility(View.VISIBLE);
|
holder.progressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
} else if(fileItem.getFileStatus() == FileItem.SENT) {
|
} else if(fileItem.getFileStatus() != FileItem.TO_BE_SENT){ // Icon for TO_BE_SENT is assigned by default in the item layout
|
||||||
holder.progressBar.setVisibility(View.GONE);
|
holder.progressBar.setVisibility(View.GONE);
|
||||||
|
|
||||||
holder.statusImage.setImageResource(R.drawable.ic_baseline_check_24px);
|
switch (fileItem.getFileStatus()) {
|
||||||
|
case FileItem.SENT : holder.statusImage.setImageResource(R.drawable.ic_baseline_check_24px);
|
||||||
|
break;
|
||||||
|
case FileItem.ERROR: holder.statusImage.setImageResource(R.drawable.ic_baseline_error_24px);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
holder.statusImage.setVisibility(View.VISIBLE);
|
holder.statusImage.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ public class FileTransferService extends IntentService {
|
|||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
Log.d(LocalFileTransferActivity.TAG, e.toString());
|
Log.d(LocalFileTransferActivity.TAG, e.toString());
|
||||||
}
|
}
|
||||||
DeviceListFragment.copyFile(is, stream);
|
DeviceListFragment.copyToOutputStream(is, stream);
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Client: Data written");
|
Log.d(LocalFileTransferActivity.TAG, "Client: Data written");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(LocalFileTransferActivity.TAG, e.getMessage());
|
Log.e(LocalFileTransferActivity.TAG, e.getMessage());
|
||||||
|
@ -63,7 +63,7 @@ public class LocalFileTransferActivity extends AppCompatActivity implements Wifi
|
|||||||
@Inject
|
@Inject
|
||||||
SharedPreferenceUtil sharedPreferenceUtil;
|
SharedPreferenceUtil sharedPreferenceUtil;
|
||||||
|
|
||||||
private ArrayList<Uri> fileURIArrayList; // For sender device, stores URIs of files to be transferred
|
private ArrayList<Uri> fileUriArrayList; // For sender device, stores Uris of files to be transferred
|
||||||
private Boolean fileSendingDevice = false;// Whether the device is the file sender or not
|
private Boolean fileSendingDevice = false;// Whether the device is the file sender or not
|
||||||
|
|
||||||
|
|
||||||
@ -86,15 +86,15 @@ public class LocalFileTransferActivity extends AppCompatActivity implements Wifi
|
|||||||
KiwixApplication.getApplicationComponent().inject(this);
|
KiwixApplication.getApplicationComponent().inject(this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Presence of file URIs decides whether the device with the activity open is a sender or receiver:
|
* Presence of file Uris decides whether the device with the activity open is a sender or receiver:
|
||||||
* - On the sender device, this activity is started from the app chooser post selection
|
* - On the sender device, this activity is started from the app chooser post selection
|
||||||
* of files to share in the Library
|
* of files to share in the Library
|
||||||
* - On the receiver device, the activity is started directly from within the 'Get Content'
|
* - On the receiver device, the activity is started directly from within the 'Get Content'
|
||||||
* activity, without any file URIs
|
* activity, without any file Uris
|
||||||
* */
|
* */
|
||||||
Intent filesIntent = getIntent();
|
Intent filesIntent = getIntent();
|
||||||
fileURIArrayList = filesIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
fileUriArrayList = filesIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||||
if(fileURIArrayList != null && fileURIArrayList.size() > 0) {
|
if(fileUriArrayList != null && fileUriArrayList.size() > 0) {
|
||||||
setDeviceAsFileSender();
|
setDeviceAsFileSender();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,8 +186,8 @@ public class LocalFileTransferActivity extends AppCompatActivity implements Wifi
|
|||||||
return fileSendingDevice;
|
return fileSendingDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull ArrayList<Uri> getFileURIArrayList() {
|
public @NonNull ArrayList<Uri> getFileUriArrayList() {
|
||||||
return fileURIArrayList;
|
return fileUriArrayList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWifiP2pEnabled(boolean wifiP2pEnabled) {
|
public void setWifiP2pEnabled(boolean wifiP2pEnabled) {
|
||||||
@ -276,7 +276,7 @@ public class LocalFileTransferActivity extends AppCompatActivity implements Wifi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(final WifiP2pDevice peerDevice) {
|
public void connect(@NonNull final WifiP2pDevice peerDevice) {
|
||||||
WifiP2pConfig config = new WifiP2pConfig();
|
WifiP2pConfig config = new WifiP2pConfig();
|
||||||
config.deviceAddress = peerDevice.deviceAddress;
|
config.deviceAddress = peerDevice.deviceAddress;
|
||||||
config.wps.setup = WpsInfo.PBC;
|
config.wps.setup = WpsInfo.PBC;
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.net.wifi.p2p.WifiP2pInfo;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.kiwix.kiwixmobile.BuildConfig;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.PEER_HANDSHAKE_PORT;
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.getFileName;
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.FileItem.TO_BE_SENT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for the local file sharing module, used in {@link DeviceListFragment}.
|
||||||
|
*
|
||||||
|
* Once two peer devices are connected through wifi direct, this task is executed to perform a
|
||||||
|
* handshake between the two devices. The purpose of the handshake is to allow the file sending
|
||||||
|
* device to obtain the IP address of the file receiving device (When the file sending device
|
||||||
|
* is the wifi direct group owner, it doesn't have the IP address of the peer device by default).
|
||||||
|
*
|
||||||
|
* After obtaining the IP address, the sender also shares metadata regarding the file transfer
|
||||||
|
* (no. of files & their names) with the receiver. Finally, the onPostExecute() of the sender
|
||||||
|
* initiates the file transfer through {@link SenderDeviceAsyncTask} on the sender and using
|
||||||
|
* {@link ReceiverDeviceAsyncTask} on the receiver.
|
||||||
|
* */
|
||||||
|
class PeerGroupHandshakeAsyncTask extends AsyncTask<Void, Void, InetAddress> {
|
||||||
|
|
||||||
|
private static final String TAG = "PeerGrpHndshakeAsyncTsk";
|
||||||
|
private final String HANDSHAKE_MESSAGE = "Request Kiwix File Sharing";
|
||||||
|
|
||||||
|
private DeviceListFragment deviceListFragment;
|
||||||
|
private WifiP2pInfo groupInfo;
|
||||||
|
|
||||||
|
public PeerGroupHandshakeAsyncTask(DeviceListFragment deviceListFragment, WifiP2pInfo groupInfo) {
|
||||||
|
this.deviceListFragment = deviceListFragment;
|
||||||
|
this.groupInfo = groupInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InetAddress doInBackground(Void... voids) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(groupInfo.groupFormed && groupInfo.isGroupOwner) {
|
||||||
|
ServerSocket serverSocket = new ServerSocket(PEER_HANDSHAKE_PORT);
|
||||||
|
serverSocket.setReuseAddress(true);
|
||||||
|
Socket server = serverSocket.accept();
|
||||||
|
|
||||||
|
ObjectInputStream objectInputStream = new ObjectInputStream(server.getInputStream());
|
||||||
|
Object object = objectInputStream.readObject();
|
||||||
|
|
||||||
|
// Verify that the peer trying to communicate is a kiwix app intending to transfer files
|
||||||
|
if (object.getClass().equals(String.class) && object.equals(HANDSHAKE_MESSAGE)) {
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "Client IP address: "+ server.getInetAddress());
|
||||||
|
|
||||||
|
exchangeFileTransferMetadata(server.getOutputStream(), server.getInputStream());
|
||||||
|
|
||||||
|
server.close();
|
||||||
|
|
||||||
|
return server.getInetAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(groupInfo.groupFormed) { //&& !groupInfo.isGroupOwner
|
||||||
|
Socket client = new Socket();
|
||||||
|
client.setReuseAddress(true);
|
||||||
|
client.connect((new InetSocketAddress(groupInfo.groupOwnerAddress.getHostAddress(), PEER_HANDSHAKE_PORT)), 15000);
|
||||||
|
|
||||||
|
ObjectOutputStream objectOutputStream = new ObjectOutputStream(client.getOutputStream());
|
||||||
|
objectOutputStream.writeObject(HANDSHAKE_MESSAGE); // Send message for the peer device to verify
|
||||||
|
|
||||||
|
exchangeFileTransferMetadata(client.getOutputStream(), client.getInputStream());
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
|
||||||
|
return groupInfo.groupOwnerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // So a null is only returned in case of an exception
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exchangeFileTransferMetadata(OutputStream outputStream, InputStream inputStream) {
|
||||||
|
try {
|
||||||
|
if(deviceListFragment.isFileSender()) {
|
||||||
|
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
|
||||||
|
// Send total number of files which will be transferred
|
||||||
|
objectOutputStream.writeObject(""+deviceListFragment.getTotalFilesForTransfer());
|
||||||
|
|
||||||
|
ArrayList<Uri> fileUriList = deviceListFragment.getFileUriList();
|
||||||
|
for (Uri fileUri : fileUriList) { // Send the names of each of those files, in order
|
||||||
|
objectOutputStream.writeObject(getFileName(fileUri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // Device is not the file sender
|
||||||
|
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
|
||||||
|
// Read the number of files
|
||||||
|
Object totalFilesObject = objectInputStream.readObject();
|
||||||
|
|
||||||
|
if(totalFilesObject.getClass().equals(String.class)) {
|
||||||
|
int total = Integer.parseInt((String) totalFilesObject);
|
||||||
|
deviceListFragment.setTotalFilesForTransfer(total);
|
||||||
|
|
||||||
|
ArrayList<FileItem> fileItems = new ArrayList<>();
|
||||||
|
for (int i = 0; i < total; i++) { // Read names of each of those files, in order
|
||||||
|
Object fileNameObject = objectInputStream.readObject();
|
||||||
|
|
||||||
|
if(fileNameObject.getClass().equals(String.class)) {
|
||||||
|
fileItems.add(new FileItem((String) fileNameObject, TO_BE_SENT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceListFragment.setFileItems(fileItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(InetAddress inetAddress) {
|
||||||
|
deviceListFragment.setClientAddress(inetAddress);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.kiwix.kiwixmobile.BuildConfig;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.FILE_TRANSFER_PORT;
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.copyToOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for the local file sharing module, used in {@link DeviceListFragment}.
|
||||||
|
*
|
||||||
|
* Once the handshake has successfully taken place, this async-task is used to receive files from
|
||||||
|
* the sender device on the FILE_TRANSFER_PORT port. No. of files to be received (and their names)
|
||||||
|
* are learnt beforehand during the handshake.
|
||||||
|
*
|
||||||
|
* A single Task is used for the entire file transfer (the server socket accepts connections as
|
||||||
|
* many times as the no. of files).
|
||||||
|
* */
|
||||||
|
class ReceiverDeviceAsyncTask extends AsyncTask<Void, Short, Boolean> {
|
||||||
|
|
||||||
|
private static final String TAG = "ReceiverDeviceAsyncTask";
|
||||||
|
|
||||||
|
private DeviceListFragment deviceListFragment;
|
||||||
|
private TransferProgressFragment transferProgressFragment;
|
||||||
|
private int fileItemIndex;
|
||||||
|
|
||||||
|
public ReceiverDeviceAsyncTask(DeviceListFragment deviceListFragment, TransferProgressFragment transferProgressFragment) {
|
||||||
|
this.deviceListFragment = deviceListFragment;
|
||||||
|
this.transferProgressFragment = transferProgressFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... voids) {
|
||||||
|
try {
|
||||||
|
ServerSocket serverSocket = new ServerSocket(FILE_TRANSFER_PORT);
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "Server: Socket opened at " + FILE_TRANSFER_PORT);
|
||||||
|
|
||||||
|
final String KIWIX_ROOT = deviceListFragment.getZimStorageRootPath();
|
||||||
|
|
||||||
|
int totalFileCount = deviceListFragment.getTotalFilesForTransfer();
|
||||||
|
for(int currentFile = 1; currentFile <= totalFileCount; currentFile++) {
|
||||||
|
|
||||||
|
Socket client = serverSocket.accept();
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "Server: Client connected for file " + currentFile);
|
||||||
|
|
||||||
|
fileItemIndex = currentFile-1;
|
||||||
|
publishProgress(FileItem.SENDING);
|
||||||
|
|
||||||
|
ArrayList<FileItem> fileItems = deviceListFragment.getFileItems();
|
||||||
|
String incomingFileName = fileItems.get(fileItemIndex).getFileName();
|
||||||
|
|
||||||
|
final File clientNoteFileLocation = new File(KIWIX_ROOT + incomingFileName);
|
||||||
|
File dirs = new File(clientNoteFileLocation.getParent());
|
||||||
|
if(!dirs.exists() && !dirs.mkdirs()) {
|
||||||
|
Log.d(TAG, "ERROR: Required parent directories couldn't be created");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean fileCreated = clientNoteFileLocation.createNewFile();
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "File creation: "+ fileCreated);
|
||||||
|
|
||||||
|
copyToOutputStream(client.getInputStream(), new FileOutputStream(clientNoteFileLocation));
|
||||||
|
|
||||||
|
publishProgress(FileItem.SENT);
|
||||||
|
deviceListFragment.incrementTotalFilesSent();
|
||||||
|
}
|
||||||
|
serverSocket.close();
|
||||||
|
|
||||||
|
return true; // Returned in case of a succesful file transfer
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, e.getMessage());
|
||||||
|
return false; // Returned when an error was encountered during transfer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Short... values) {
|
||||||
|
short fileStatus = values[0];
|
||||||
|
transferProgressFragment.changeStatus(fileItemIndex, fileStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean allFilesReceived) {
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "File transfer complete");
|
||||||
|
|
||||||
|
if(allFilesReceived) {
|
||||||
|
Toast.makeText(deviceListFragment.getActivity(), "File transfer complete", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(deviceListFragment.getActivity(), "An error was encountered during transfer", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
((LocalFileTransferActivity) deviceListFragment.getActivity()).closeLocalFileTransferActivity();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package org.kiwix.kiwixmobile.zim_manager.local_file_transfer;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.kiwix.kiwixmobile.BuildConfig;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.FILE_TRANSFER_PORT;
|
||||||
|
import static org.kiwix.kiwixmobile.zim_manager.local_file_transfer.DeviceListFragment.getFileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for the local file sharing module, used in {@link DeviceListFragment}.
|
||||||
|
*
|
||||||
|
* Once the handshake between the two connected devices has taked place, this async-task is used
|
||||||
|
* on the sender device to transfer the file to the receiver device at the FILE_TRANSFER_PORT port.
|
||||||
|
*
|
||||||
|
* It takes in the uri of a single file, and copies all the bytes from input stream of the file to
|
||||||
|
* the output stream of the receiver device. Also changes the status of the corresponding FileItem
|
||||||
|
* on the list of files for transfer in {@link TransferProgressFragment}.
|
||||||
|
*
|
||||||
|
* A new task is created by the sender for every file to be transferred
|
||||||
|
* */
|
||||||
|
class SenderDeviceAsyncTask extends AsyncTask<Uri, Void, Boolean> {
|
||||||
|
|
||||||
|
private static final String TAG = "SenderDeviceAsyncTask";
|
||||||
|
|
||||||
|
private DeviceListFragment deviceListFragment;
|
||||||
|
private TransferProgressFragment transferProgressFragment;
|
||||||
|
private int fileItemIndex;
|
||||||
|
|
||||||
|
public SenderDeviceAsyncTask(DeviceListFragment deviceListFragment, TransferProgressFragment transferProgressFragment, int fileItemIndex) {
|
||||||
|
this.deviceListFragment = deviceListFragment;
|
||||||
|
this.transferProgressFragment = transferProgressFragment;
|
||||||
|
this.fileItemIndex = fileItemIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Uri... fileUris) {
|
||||||
|
Uri fileUri = fileUris[0]; // Uri of file to be transferred
|
||||||
|
Socket socket = new Socket(); // Represents the sender device
|
||||||
|
boolean fileSendSuccessful; // Whether this task was successful in sending the file
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.bind(null);
|
||||||
|
|
||||||
|
String hostAddress = deviceListFragment.getFileReceiverDeviceAddress().getHostAddress();
|
||||||
|
socket.connect((new InetSocketAddress(hostAddress, FILE_TRANSFER_PORT)), 15000);
|
||||||
|
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "Sender socket - " + socket.isConnected());
|
||||||
|
|
||||||
|
OutputStream socketOutputStream = socket.getOutputStream();
|
||||||
|
|
||||||
|
ContentResolver contentResolver = deviceListFragment.getActivity().getContentResolver();
|
||||||
|
InputStream fileInputStream = contentResolver.openInputStream(fileUri);
|
||||||
|
|
||||||
|
DeviceListFragment.copyToOutputStream(fileInputStream, socketOutputStream);
|
||||||
|
if(BuildConfig.DEBUG) Log.d(TAG, "Sender: Data written");
|
||||||
|
|
||||||
|
fileSendSuccessful = true;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, e.getMessage());
|
||||||
|
fileSendSuccessful = false;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (socket.isConnected()) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileSendSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean fileSendSuccessful) {
|
||||||
|
deviceListFragment.incrementTotalFilesSent();
|
||||||
|
|
||||||
|
if(fileSendSuccessful) {
|
||||||
|
transferProgressFragment.changeStatus(fileItemIndex, FileItem.SENT);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(deviceListFragment.getActivity(), "Error sending file "+ getFileName(deviceListFragment.getFileUriList().get(fileItemIndex)), Toast.LENGTH_SHORT).show();
|
||||||
|
transferProgressFragment.changeStatus(fileItemIndex, FileItem.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(deviceListFragment.allFilesSent()) {
|
||||||
|
Toast.makeText(deviceListFragment.getActivity(), "All files transferred", Toast.LENGTH_SHORT).show();
|
||||||
|
deviceListFragment.getActivity().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
app/src/main/res/drawable/ic_baseline_error_24px.xml
Normal file
9
app/src/main/res/drawable/ic_baseline_error_24px.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||||
|
</vector>
|
Loading…
x
Reference in New Issue
Block a user