mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-08 06:42:21 -04:00
Merge pull request #2011 from yash1ts/#1835-wifi-direct-manager-to-kotlin
WifiDirectManager to Kotlin
This commit is contained in:
commit
20b5d187c8
@ -34,6 +34,7 @@
|
|||||||
<ID>PackageNaming:ShareFiles.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
|
<ID>PackageNaming:ShareFiles.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
|
||||||
<ID>PackageNaming:SimplePageChangeListener.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
|
<ID>PackageNaming:SimplePageChangeListener.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
|
||||||
<ID>PackageNaming:StartMultiSelection.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
|
<ID>PackageNaming:StartMultiSelection.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
|
||||||
|
<ID>PackageNaming:WifiDirectManager.kt$package org.kiwix.kiwixmobile.local_file_transfer</ID>
|
||||||
<ID>PackageNaming:WifiP2pDelegate.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
<ID>PackageNaming:WifiP2pDelegate.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
||||||
<ID>PackageNaming:WifiP2pViewHolder.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
<ID>PackageNaming:WifiP2pViewHolder.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
||||||
<ID>PackageNaming:WifiPeerListAdapter.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
<ID>PackageNaming:WifiPeerListAdapter.kt$package org.kiwix.kiwixmobile.local_file_transfer.adapter</ID>
|
||||||
@ -50,6 +51,7 @@
|
|||||||
<ID>TooManyFunctions:KiwixActivityComponent.kt$KiwixActivityComponent$KiwixActivityComponent</ID>
|
<ID>TooManyFunctions:KiwixActivityComponent.kt$KiwixActivityComponent$KiwixActivityComponent</ID>
|
||||||
<ID>TooManyFunctions:KiwixMainActivity.kt$KiwixMainActivity$KiwixMainActivity</ID>
|
<ID>TooManyFunctions:KiwixMainActivity.kt$KiwixMainActivity$KiwixMainActivity</ID>
|
||||||
<ID>TooManyFunctions:LibraryFragment.kt$LibraryFragment$LibraryFragment</ID>
|
<ID>TooManyFunctions:LibraryFragment.kt$LibraryFragment$LibraryFragment</ID>
|
||||||
|
<ID>TooManyFunctions:WifiDirectManager.kt$WifiDirectManager$WifiDirectManager</ID>
|
||||||
<ID>TooManyFunctions:ZimFileSelectFragment.kt$ZimFileSelectFragment$ZimFileSelectFragment</ID>
|
<ID>TooManyFunctions:ZimFileSelectFragment.kt$ZimFileSelectFragment$ZimFileSelectFragment</ID>
|
||||||
<ID>TooManyFunctions:ZimHostActivity.kt$ZimHostActivity$ZimHostActivity</ID>
|
<ID>TooManyFunctions:ZimHostActivity.kt$ZimHostActivity$ZimHostActivity</ID>
|
||||||
<ID>TooManyFunctions:ZimManageViewModel.kt$ZimManageViewModel$ZimManageViewModel</ID>
|
<ID>TooManyFunctions:ZimManageViewModel.kt$ZimManageViewModel$ZimManageViewModel</ID>
|
||||||
|
@ -1,482 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kiwix Android
|
|
||||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.local_file_transfer;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.net.wifi.WpsInfo;
|
|
||||||
import android.net.wifi.p2p.WifiP2pConfig;
|
|
||||||
import android.net.wifi.p2p.WifiP2pDevice;
|
|
||||||
import android.net.wifi.p2p.WifiP2pDeviceList;
|
|
||||||
import android.net.wifi.p2p.WifiP2pInfo;
|
|
||||||
import android.net.wifi.p2p.WifiP2pManager;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import kotlin.jvm.functions.Function0;
|
|
||||||
import org.kiwix.kiwixmobile.core.BuildConfig;
|
|
||||||
import org.kiwix.kiwixmobile.core.R;
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.AlertDialogShower;
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.KiwixDialog;
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil;
|
|
||||||
|
|
||||||
import static android.os.Looper.getMainLooper;
|
|
||||||
import static org.kiwix.kiwixmobile.local_file_transfer.FileItem.FileStatus.ERROR;
|
|
||||||
import static org.kiwix.kiwixmobile.local_file_transfer.LocalFileTransferActivity.showToast;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manager for the Wifi-P2p API, used in the local file transfer module
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("MissingPermission")
|
|
||||||
public class WifiDirectManager
|
|
||||||
implements WifiP2pManager.ChannelListener, WifiP2pManager.PeerListListener,
|
|
||||||
WifiP2pManager.ConnectionInfoListener,
|
|
||||||
KiwixWifiP2pBroadcastReceiver.P2pEventListener {
|
|
||||||
|
|
||||||
private static final String TAG = "WifiDirectManager";
|
|
||||||
public static int FILE_TRANSFER_PORT = 8008;
|
|
||||||
|
|
||||||
private @NonNull Activity activity;
|
|
||||||
private @NonNull Callbacks callbacks;
|
|
||||||
|
|
||||||
private SharedPreferenceUtil sharedPreferenceUtil;
|
|
||||||
private AlertDialogShower alertDialogShower;
|
|
||||||
|
|
||||||
/* Variables related to the WiFi P2P API */
|
|
||||||
private boolean isWifiP2pEnabled = false; // Whether WiFi has been enabled or not
|
|
||||||
private boolean shouldRetry = true; // Whether channel has retried connecting previously
|
|
||||||
|
|
||||||
private WifiP2pManager manager; // Overall manager of Wifi p2p connections for the module
|
|
||||||
private WifiP2pManager.Channel channel; // Interface to the device's underlying wifi-p2p framework
|
|
||||||
|
|
||||||
private BroadcastReceiver receiver = null; // For receiving the broadcasts given by above filter
|
|
||||||
|
|
||||||
private WifiP2pInfo groupInfo; // Corresponds to P2P group formed between the two devices
|
|
||||||
|
|
||||||
private WifiP2pDevice senderSelectedPeerDevice = null;
|
|
||||||
|
|
||||||
private PeerGroupHandshakeAsyncTask peerGroupHandshakeAsyncTask;
|
|
||||||
private SenderDeviceAsyncTask senderDeviceAsyncTask;
|
|
||||||
private ReceiverDeviceAsyncTask receiverDeviceAsyncTask;
|
|
||||||
|
|
||||||
private InetAddress selectedPeerDeviceInetAddress;
|
|
||||||
private InetAddress fileReceiverDeviceAddress; // IP address of the file receiving device
|
|
||||||
|
|
||||||
private ArrayList<FileItem> filesForTransfer;
|
|
||||||
|
|
||||||
private boolean isFileSender = false; // Whether the device is the file sender or not
|
|
||||||
private boolean hasSenderStartedConnection = false;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public WifiDirectManager(@NonNull Activity activity,
|
|
||||||
@NonNull SharedPreferenceUtil sharedPreferenceUtil,
|
|
||||||
@NonNull AlertDialogShower alertDialogShower) {
|
|
||||||
this.activity = activity;
|
|
||||||
this.callbacks = (Callbacks) activity;
|
|
||||||
this.sharedPreferenceUtil = sharedPreferenceUtil;
|
|
||||||
this.alertDialogShower = alertDialogShower;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialisations for using the WiFi P2P API */
|
|
||||||
public void startWifiDirectManager(@Nullable ArrayList<FileItem> filesForTransfer) {
|
|
||||||
this.filesForTransfer = filesForTransfer;
|
|
||||||
this.isFileSender = (filesForTransfer != null && filesForTransfer.size() > 0);
|
|
||||||
|
|
||||||
manager = (WifiP2pManager) activity.getSystemService(Context.WIFI_P2P_SERVICE);
|
|
||||||
channel = manager.initialize(activity, getMainLooper(), null);
|
|
||||||
registerWifiDirectBroadcastReceiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerWifiDirectBroadcastReceiver() {
|
|
||||||
receiver = new KiwixWifiP2pBroadcastReceiver(this);
|
|
||||||
|
|
||||||
// For specifying broadcasts (of the P2P API) that the module needs to respond to
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
|
|
||||||
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
|
|
||||||
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
|
|
||||||
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
|
|
||||||
|
|
||||||
activity.registerReceiver(receiver, intentFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterWifiDirectBroadcastReceiver() {
|
|
||||||
activity.unregisterReceiver(receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void discoverPeerDevices() {
|
|
||||||
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
displayToast(R.string.discovery_initiated, Toast.LENGTH_SHORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(int reason) {
|
|
||||||
String errorMessage = getErrorMessage(reason);
|
|
||||||
Log.d(TAG, activity.getString(R.string.discovery_failed) + ": " + errorMessage);
|
|
||||||
displayToast(R.string.discovery_failed, Toast.LENGTH_SHORT);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* From KiwixWifiP2pBroadcastReceiver.P2pEventListener callback-interface*/
|
|
||||||
@Override
|
|
||||||
public void onWifiP2pStateChanged(boolean isEnabled) {
|
|
||||||
this.isWifiP2pEnabled = isEnabled;
|
|
||||||
|
|
||||||
if (!isWifiP2pEnabled) {
|
|
||||||
displayToast(R.string.discovery_needs_wifi, Toast.LENGTH_SHORT);
|
|
||||||
callbacks.onConnectionToPeersLost();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "WiFi P2P state changed - " + isWifiP2pEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeersChanged() {
|
|
||||||
/* List of available peers has changed, so request & use the new list through
|
|
||||||
* PeerListListener.requestPeers() callback */
|
|
||||||
manager.requestPeers(channel, this);
|
|
||||||
Log.d(TAG, "P2P peers changed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectionChanged(boolean isConnected) {
|
|
||||||
if (isConnected) {
|
|
||||||
// Request connection info about the wifi p2p group formed upon connection
|
|
||||||
manager.requestConnectionInfo(channel, this);
|
|
||||||
} else {
|
|
||||||
// Not connected after connection change -> Disconnected
|
|
||||||
callbacks.onConnectionToPeersLost();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDeviceChanged(@Nullable WifiP2pDevice userDevice) {
|
|
||||||
// Update UI with wifi-direct details about the user device
|
|
||||||
callbacks.onUserDeviceDetailsAvailable(userDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* From WifiP2pManager.ChannelListener interface */
|
|
||||||
@Override
|
|
||||||
public void onChannelDisconnected() {
|
|
||||||
// Upon disconnection, retry one more time
|
|
||||||
if (shouldRetry) {
|
|
||||||
Log.d(TAG, "Channel lost, trying again");
|
|
||||||
callbacks.onConnectionToPeersLost();
|
|
||||||
shouldRetry = false;
|
|
||||||
manager.initialize(activity, getMainLooper(), this);
|
|
||||||
} else {
|
|
||||||
displayToast(R.string.severe_loss_error, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* From WifiP2pManager.PeerListListener callback-interface */
|
|
||||||
@Override
|
|
||||||
public void onPeersAvailable(@NonNull WifiP2pDeviceList peers) {
|
|
||||||
callbacks.updateListOfAvailablePeers(peers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* From WifiP2pManager.ConnectionInfoListener callback-interface */
|
|
||||||
@Override
|
|
||||||
public void onConnectionInfoAvailable(@NonNull WifiP2pInfo groupInfo) {
|
|
||||||
/* Devices have successfully connected, and 'info' holds information about the wifi p2p group formed */
|
|
||||||
this.groupInfo = groupInfo;
|
|
||||||
performHandshakeWithSelectedPeerDevice();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper methods */
|
|
||||||
public boolean isWifiP2pEnabled() {
|
|
||||||
return isWifiP2pEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupFormed() {
|
|
||||||
return groupInfo.groupFormed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupOwner() {
|
|
||||||
return groupInfo.isGroupOwner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull InetAddress getGroupOwnerAddress() {
|
|
||||||
return groupInfo.groupOwnerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendToDevice(@NonNull WifiP2pDevice senderSelectedPeerDevice) {
|
|
||||||
/* Connection can only be initiated by user of the sender device, & only when transfer has not been started */
|
|
||||||
if (!isFileSender || hasSenderStartedConnection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.senderSelectedPeerDevice = senderSelectedPeerDevice;
|
|
||||||
|
|
||||||
alertDialogShower.show(
|
|
||||||
new KiwixDialog.FileTransferConfirmation(senderSelectedPeerDevice.deviceName),
|
|
||||||
new Function0<Unit>() {
|
|
||||||
@Override public Unit invoke() {
|
|
||||||
hasSenderStartedConnection = true;
|
|
||||||
connect();
|
|
||||||
displayToast(R.string.performing_handshake, Toast.LENGTH_LONG);
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() {
|
|
||||||
if (senderSelectedPeerDevice == null) {
|
|
||||||
Log.d(TAG, "No device set as selected");
|
|
||||||
}
|
|
||||||
|
|
||||||
WifiP2pConfig config = new WifiP2pConfig();
|
|
||||||
config.deviceAddress = senderSelectedPeerDevice.deviceAddress;
|
|
||||||
config.wps.setup = WpsInfo.PBC;
|
|
||||||
|
|
||||||
manager.connect(channel, config, new WifiP2pManager.ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
// UI updated from broadcast receiver
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(int reason) {
|
|
||||||
String errorMessage = getErrorMessage(reason);
|
|
||||||
Log.d(TAG, activity.getString(R.string.connection_failed) + ": " + errorMessage);
|
|
||||||
displayToast(R.string.connection_failed, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void performHandshakeWithSelectedPeerDevice() {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(TAG, "Starting handshake");
|
|
||||||
}
|
|
||||||
peerGroupHandshakeAsyncTask = new PeerGroupHandshakeAsyncTask(this);
|
|
||||||
peerGroupHandshakeAsyncTask.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFileSender() {
|
|
||||||
return isFileSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTotalFilesForTransfer() {
|
|
||||||
return filesForTransfer.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull ArrayList<FileItem> getFilesForTransfer() {
|
|
||||||
return filesForTransfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilesForTransfer(@NonNull ArrayList<FileItem> fileItems) {
|
|
||||||
this.filesForTransfer = fileItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull String getZimStorageRootPath() {
|
|
||||||
return (sharedPreferenceUtil.getPrefStorage() + "/Kiwix/");
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull InetAddress getFileReceiverDeviceAddress() {
|
|
||||||
return fileReceiverDeviceAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void copyToOutputStream(@NonNull InputStream inputStream,
|
|
||||||
@NonNull OutputStream outputStream) throws IOException {
|
|
||||||
byte[] bufferForBytes = new byte[1024];
|
|
||||||
int bytesRead;
|
|
||||||
|
|
||||||
Log.d(TAG, "Copying to OutputStream...");
|
|
||||||
while ((bytesRead = inputStream.read(bufferForBytes)) != -1) {
|
|
||||||
outputStream.write(bufferForBytes, 0, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
outputStream.close();
|
|
||||||
inputStream.close();
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Both streams closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClientAddress(@Nullable InetAddress clientAddress) {
|
|
||||||
if (clientAddress == null) {
|
|
||||||
// null is returned only in case of a failed handshake
|
|
||||||
displayToast(R.string.device_not_cooperating, Toast.LENGTH_LONG);
|
|
||||||
callbacks.onFileTransferComplete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If control reaches here, means handshake was successful
|
|
||||||
selectedPeerDeviceInetAddress = clientAddress;
|
|
||||||
startFileTransfer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startFileTransfer() {
|
|
||||||
if (isGroupFormed()) {
|
|
||||||
if (isFileSender) {
|
|
||||||
Log.d(LocalFileTransferActivity.TAG, "Starting file transfer");
|
|
||||||
|
|
||||||
fileReceiverDeviceAddress =
|
|
||||||
(isGroupOwner()) ? selectedPeerDeviceInetAddress : getGroupOwnerAddress();
|
|
||||||
|
|
||||||
displayToast(R.string.preparing_files, Toast.LENGTH_LONG);
|
|
||||||
senderDeviceAsyncTask = new SenderDeviceAsyncTask(this, activity);
|
|
||||||
senderDeviceAsyncTask.execute(filesForTransfer.toArray(new FileItem[0]));
|
|
||||||
} else {
|
|
||||||
callbacks.onFilesForTransferAvailable(filesForTransfer);
|
|
||||||
|
|
||||||
receiverDeviceAsyncTask = new ReceiverDeviceAsyncTask(this);
|
|
||||||
receiverDeviceAsyncTask.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void changeStatus(int itemIndex, @FileItem.FileStatus int status) {
|
|
||||||
filesForTransfer.get(itemIndex).setFileStatus(status);
|
|
||||||
callbacks.onFileStatusChanged(itemIndex);
|
|
||||||
|
|
||||||
if (status == ERROR) {
|
|
||||||
displayToast(R.string.error_transferring, filesForTransfer.get(itemIndex).getFileName(),
|
|
||||||
Toast.LENGTH_SHORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelAsyncTasks(AsyncTask... tasks) {
|
|
||||||
for (AsyncTask asyncTask : tasks) {
|
|
||||||
if (asyncTask != null) {
|
|
||||||
asyncTask.cancel(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopWifiDirectManager() {
|
|
||||||
cancelAsyncTasks(peerGroupHandshakeAsyncTask, senderDeviceAsyncTask, receiverDeviceAsyncTask);
|
|
||||||
|
|
||||||
if (isFileSender) {
|
|
||||||
closeChannel();
|
|
||||||
} else {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterWifiDirectBroadcastReceiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
manager.removeGroup(channel, new WifiP2pManager.ActionListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(int reasonCode) {
|
|
||||||
Log.d(TAG, "Disconnect failed. Reason: " + reasonCode);
|
|
||||||
closeChannel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
Log.d(TAG, "Disconnect successful");
|
|
||||||
closeChannel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void closeChannel() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
|
||||||
channel.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NonNull String getErrorMessage(int reason) {
|
|
||||||
switch (reason) {
|
|
||||||
case WifiP2pManager.ERROR:
|
|
||||||
return "Internal error";
|
|
||||||
case WifiP2pManager.BUSY:
|
|
||||||
return "Framework busy, unable to service request";
|
|
||||||
case WifiP2pManager.P2P_UNSUPPORTED:
|
|
||||||
return "P2P unsupported on this device";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ("Unknown error code - " + reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull String getDeviceStatus(int status) {
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, "Peer Status: " + status);
|
|
||||||
switch (status) {
|
|
||||||
case WifiP2pDevice.AVAILABLE:
|
|
||||||
return "Available";
|
|
||||||
case WifiP2pDevice.INVITED:
|
|
||||||
return "Invited";
|
|
||||||
case WifiP2pDevice.CONNECTED:
|
|
||||||
return "Connected";
|
|
||||||
case WifiP2pDevice.FAILED:
|
|
||||||
return "Failed";
|
|
||||||
case WifiP2pDevice.UNAVAILABLE:
|
|
||||||
return "Unavailable";
|
|
||||||
|
|
||||||
default:
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NonNull String getFileName(@NonNull Uri fileUri) {
|
|
||||||
String fileUriString = fileUri.toString();
|
|
||||||
// Returns text after location of last slash in the file path
|
|
||||||
return fileUriString.substring(fileUriString.lastIndexOf('/') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void displayToast(int stringResourceId, @NonNull String templateValue, int duration) {
|
|
||||||
showToast(activity, activity.getString(stringResourceId, templateValue), duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void displayToast(int stringResourceId, int duration) {
|
|
||||||
showToast(activity, stringResourceId, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onFileTransferAsyncTaskComplete(boolean wereAllFilesTransferred) {
|
|
||||||
if (wereAllFilesTransferred) {
|
|
||||||
displayToast(R.string.file_transfer_complete, Toast.LENGTH_LONG);
|
|
||||||
} else {
|
|
||||||
displayToast(R.string.error_during_transfer, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
callbacks.onFileTransferComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Callbacks {
|
|
||||||
void onUserDeviceDetailsAvailable(@Nullable WifiP2pDevice userDevice);
|
|
||||||
|
|
||||||
void onConnectionToPeersLost();
|
|
||||||
|
|
||||||
void updateListOfAvailablePeers(@NonNull WifiP2pDeviceList peers);
|
|
||||||
|
|
||||||
void onFilesForTransferAvailable(@NonNull ArrayList<FileItem> filesForTransfer);
|
|
||||||
|
|
||||||
void onFileStatusChanged(int itemIndex);
|
|
||||||
|
|
||||||
void onFileTransferComplete();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,389 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.kiwix.kiwixmobile.local_file_transfer
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.net.Uri
|
||||||
|
import android.net.wifi.WpsInfo
|
||||||
|
import android.net.wifi.p2p.WifiP2pConfig
|
||||||
|
import android.net.wifi.p2p.WifiP2pDevice
|
||||||
|
import android.net.wifi.p2p.WifiP2pDeviceList
|
||||||
|
import android.net.wifi.p2p.WifiP2pInfo
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager.ActionListener
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager.Channel
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager.ChannelListener
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener
|
||||||
|
import android.net.wifi.p2p.WifiP2pManager.PeerListListener
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import org.kiwix.kiwixmobile.R
|
||||||
|
import org.kiwix.kiwixmobile.core.BuildConfig
|
||||||
|
import org.kiwix.kiwixmobile.core.extensions.toast
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.AlertDialogShower
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.KiwixDialog.FileTransferConfirmation
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
|
import org.kiwix.kiwixmobile.local_file_transfer.FileItem.FileStatus
|
||||||
|
import org.kiwix.kiwixmobile.local_file_transfer.KiwixWifiP2pBroadcastReceiver.P2pEventListener
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.util.ArrayList
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manager for the Wifi-P2p API, used in the local file transfer module
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("MissingPermission")
|
||||||
|
class WifiDirectManager @Inject constructor(
|
||||||
|
private val activity: Activity,
|
||||||
|
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||||
|
private val alertDialogShower: AlertDialogShower
|
||||||
|
) : ChannelListener, PeerListListener, ConnectionInfoListener, P2pEventListener {
|
||||||
|
private val callbacks = activity as Callbacks
|
||||||
|
|
||||||
|
/* Helper methods */
|
||||||
|
/* Variables related to the WiFi P2P API */
|
||||||
|
// Whether WiFi has been enabled or not
|
||||||
|
var isWifiP2pEnabled = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
// Whether channel has retried connecting previously
|
||||||
|
private var shouldRetry = true
|
||||||
|
|
||||||
|
// Overall manager of Wifi p2p connections for the module
|
||||||
|
private lateinit var manager: WifiP2pManager
|
||||||
|
|
||||||
|
// Interface to the device's underlying wifi-p2p framework
|
||||||
|
private lateinit var channel: Channel
|
||||||
|
|
||||||
|
// For receiving the broadcasts given by above filter
|
||||||
|
private lateinit var receiver: BroadcastReceiver
|
||||||
|
|
||||||
|
// Corresponds to P2P group formed between the two devices
|
||||||
|
private lateinit var groupInfo: WifiP2pInfo
|
||||||
|
private lateinit var senderSelectedPeerDevice: WifiP2pDevice
|
||||||
|
private var peerGroupHandshakeAsyncTask: PeerGroupHandshakeAsyncTask? = null
|
||||||
|
private var senderDeviceAsyncTask: SenderDeviceAsyncTask? = null
|
||||||
|
private var receiverDeviceAsyncTask: ReceiverDeviceAsyncTask? = null
|
||||||
|
private lateinit var selectedPeerDeviceInetAddress: InetAddress
|
||||||
|
|
||||||
|
// IP address of the file receiving device
|
||||||
|
private lateinit var fileReceiverDeviceAddress: InetAddress
|
||||||
|
private lateinit var filesForTransfer: ArrayList<FileItem>
|
||||||
|
|
||||||
|
// Whether the device is the file sender or not
|
||||||
|
var isFileSender = false
|
||||||
|
private set
|
||||||
|
private var hasSenderStartedConnection = false
|
||||||
|
|
||||||
|
/* Initialisations for using the WiFi P2P API */
|
||||||
|
fun startWifiDirectManager(filesForTransfer: ArrayList<FileItem>) {
|
||||||
|
this.filesForTransfer = filesForTransfer
|
||||||
|
isFileSender = filesForTransfer.isNotEmpty()
|
||||||
|
manager = activity.getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager
|
||||||
|
channel = manager.initialize(activity, Looper.getMainLooper(), null)
|
||||||
|
registerWifiDirectBroadcastReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerWifiDirectBroadcastReceiver() {
|
||||||
|
receiver = KiwixWifiP2pBroadcastReceiver(this)
|
||||||
|
|
||||||
|
// For specifying broadcasts (of the P2P API) that the module needs to respond to
|
||||||
|
val intentFilter = IntentFilter()
|
||||||
|
intentFilter.apply {
|
||||||
|
addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)
|
||||||
|
addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION)
|
||||||
|
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
|
||||||
|
addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
|
||||||
|
}
|
||||||
|
activity.registerReceiver(receiver, intentFilter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterWifiDirectBroadcastReceiver() = activity.unregisterReceiver(receiver)
|
||||||
|
|
||||||
|
fun discoverPeerDevices() {
|
||||||
|
manager.discoverPeers(channel, object : ActionListener {
|
||||||
|
override fun onSuccess() {
|
||||||
|
activity.toast(R.string.discovery_initiated, Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(reason: Int) {
|
||||||
|
Log.d(
|
||||||
|
TAG, "${activity.getString(R.string.discovery_failed)}: " +
|
||||||
|
getErrorMessage(reason)
|
||||||
|
)
|
||||||
|
activity.toast(R.string.discovery_failed, Toast.LENGTH_SHORT)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From KiwixWifiP2pBroadcastReceiver.P2pEventListener callback-interface*/
|
||||||
|
override fun onWifiP2pStateChanged(isEnabled: Boolean) {
|
||||||
|
isWifiP2pEnabled = isEnabled
|
||||||
|
if (!isWifiP2pEnabled) {
|
||||||
|
activity.toast(R.string.discovery_needs_wifi, Toast.LENGTH_SHORT)
|
||||||
|
callbacks.onConnectionToPeersLost()
|
||||||
|
}
|
||||||
|
Log.d(TAG, "WiFi P2P state changed - $isWifiP2pEnabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPeersChanged() {
|
||||||
|
/* List of available peers has changed, so request & use the new list through
|
||||||
|
* PeerListListener.requestPeers() callback */
|
||||||
|
manager.requestPeers(channel, this)
|
||||||
|
Log.d(TAG, "P2P peers changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onConnectionChanged(isConnected: Boolean) {
|
||||||
|
if (isConnected) {
|
||||||
|
// Request connection info about the wifi p2p group formed upon connection
|
||||||
|
manager.requestConnectionInfo(channel, this)
|
||||||
|
} else {
|
||||||
|
// Not connected after connection change -> Disconnected
|
||||||
|
callbacks.onConnectionToPeersLost()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update UI with wifi-direct details about the user device
|
||||||
|
override fun onDeviceChanged(userDevice: WifiP2pDevice?) =
|
||||||
|
callbacks.onUserDeviceDetailsAvailable(userDevice)
|
||||||
|
|
||||||
|
/* From WifiP2pManager.ChannelListener interface */
|
||||||
|
override fun onChannelDisconnected() {
|
||||||
|
// Upon disconnection, retry one more time
|
||||||
|
if (shouldRetry) {
|
||||||
|
Log.d(TAG, "Channel lost, trying again")
|
||||||
|
callbacks.onConnectionToPeersLost()
|
||||||
|
shouldRetry = false
|
||||||
|
manager.initialize(activity, Looper.getMainLooper(), this)
|
||||||
|
} else {
|
||||||
|
activity.toast(R.string.severe_loss_error, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* From WifiP2pManager.PeerListListener callback-interface */
|
||||||
|
override fun onPeersAvailable(peers: WifiP2pDeviceList) =
|
||||||
|
callbacks.updateListOfAvailablePeers(peers)
|
||||||
|
|
||||||
|
/* From WifiP2pManager.ConnectionInfoListener callback-interface */
|
||||||
|
override fun onConnectionInfoAvailable(groupInfo: WifiP2pInfo) {
|
||||||
|
/* Devices have successfully connected, and 'info' holds information about the wifi p2p group formed */
|
||||||
|
this.groupInfo = groupInfo
|
||||||
|
performHandshakeWithSelectedPeerDevice()
|
||||||
|
}
|
||||||
|
|
||||||
|
val isGroupFormed: Boolean
|
||||||
|
get() = groupInfo.groupFormed
|
||||||
|
|
||||||
|
val isGroupOwner: Boolean
|
||||||
|
get() = groupInfo.isGroupOwner
|
||||||
|
|
||||||
|
val groupOwnerAddress: InetAddress
|
||||||
|
get() = groupInfo.groupOwnerAddress
|
||||||
|
|
||||||
|
fun sendToDevice(senderSelectedPeerDevice: WifiP2pDevice) {
|
||||||
|
/* Connection can only be initiated by user of the sender device, & only when transfer has not been started */
|
||||||
|
if (isFileSender && !hasSenderStartedConnection) {
|
||||||
|
this.senderSelectedPeerDevice = senderSelectedPeerDevice
|
||||||
|
alertDialogShower.show(
|
||||||
|
FileTransferConfirmation(senderSelectedPeerDevice.deviceName), {
|
||||||
|
hasSenderStartedConnection = true
|
||||||
|
connect()
|
||||||
|
activity.toast(R.string.performing_handshake, Toast.LENGTH_LONG)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun connect() {
|
||||||
|
val config = WifiP2pConfig().apply {
|
||||||
|
deviceAddress = senderSelectedPeerDevice.deviceAddress
|
||||||
|
wps.setup = WpsInfo.PBC
|
||||||
|
}
|
||||||
|
manager.connect(channel, config, object : ActionListener {
|
||||||
|
override fun onSuccess() {
|
||||||
|
// UI updated from broadcast receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(reason: Int) {
|
||||||
|
val errorMessage = getErrorMessage(reason)
|
||||||
|
Log.d(TAG, activity.getString(R.string.connection_failed) + ": " + errorMessage)
|
||||||
|
activity.toast(R.string.connection_failed, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun performHandshakeWithSelectedPeerDevice() {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(TAG, "Starting handshake")
|
||||||
|
}
|
||||||
|
peerGroupHandshakeAsyncTask = PeerGroupHandshakeAsyncTask(this)
|
||||||
|
.also { it.execute() }
|
||||||
|
}
|
||||||
|
|
||||||
|
val totalFilesForTransfer: Int
|
||||||
|
get() = filesForTransfer.size
|
||||||
|
|
||||||
|
fun getFilesForTransfer() = filesForTransfer
|
||||||
|
|
||||||
|
fun setFilesForTransfer(fileItems: ArrayList<FileItem>) {
|
||||||
|
filesForTransfer = fileItems
|
||||||
|
}
|
||||||
|
|
||||||
|
val zimStorageRootPath
|
||||||
|
get() = sharedPreferenceUtil.prefStorage + "/Kiwix/"
|
||||||
|
|
||||||
|
fun getFileReceiverDeviceAddress() = fileReceiverDeviceAddress
|
||||||
|
|
||||||
|
fun setClientAddress(clientAddress: InetAddress) {
|
||||||
|
|
||||||
|
// If control reaches here, means handshake was successful
|
||||||
|
selectedPeerDeviceInetAddress = clientAddress
|
||||||
|
startFileTransfer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startFileTransfer() {
|
||||||
|
if (isGroupFormed) {
|
||||||
|
if (isFileSender) {
|
||||||
|
Log.d(LocalFileTransferActivity.TAG, "Starting file transfer")
|
||||||
|
fileReceiverDeviceAddress =
|
||||||
|
if (isGroupOwner) selectedPeerDeviceInetAddress else groupOwnerAddress
|
||||||
|
activity.toast(R.string.preparing_files, Toast.LENGTH_LONG)
|
||||||
|
senderDeviceAsyncTask = SenderDeviceAsyncTask(this, activity).also {
|
||||||
|
it.execute()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callbacks.onFilesForTransferAvailable(filesForTransfer)
|
||||||
|
receiverDeviceAsyncTask = ReceiverDeviceAsyncTask(this).also {
|
||||||
|
it.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun changeStatus(itemIndex: Int, @FileStatus status: Int) {
|
||||||
|
filesForTransfer[itemIndex].fileStatus = status
|
||||||
|
callbacks.onFileStatusChanged(itemIndex)
|
||||||
|
if (status == FileStatus.ERROR) {
|
||||||
|
displayToast(
|
||||||
|
R.string.error_transferring, filesForTransfer[itemIndex].fileName,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelAsyncTasks(vararg tasks: AsyncTask<*, *, *>?) =
|
||||||
|
tasks.forEach {
|
||||||
|
it?.cancel(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stopWifiDirectManager() {
|
||||||
|
cancelAsyncTasks(peerGroupHandshakeAsyncTask, senderDeviceAsyncTask, receiverDeviceAsyncTask)
|
||||||
|
if (isFileSender) {
|
||||||
|
closeChannel()
|
||||||
|
} else {
|
||||||
|
disconnect()
|
||||||
|
}
|
||||||
|
unregisterWifiDirectBroadcastReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun disconnect() {
|
||||||
|
manager.removeGroup(channel, object : ActionListener {
|
||||||
|
override fun onFailure(reasonCode: Int) {
|
||||||
|
Log.d(TAG, "Disconnect failed. Reason: $reasonCode")
|
||||||
|
closeChannel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess() {
|
||||||
|
Log.d(TAG, "Disconnect successful")
|
||||||
|
closeChannel()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun closeChannel() {
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
|
||||||
|
channel.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getErrorMessage(reason: Int): String {
|
||||||
|
return when (reason) {
|
||||||
|
WifiP2pManager.ERROR -> "Internal error"
|
||||||
|
WifiP2pManager.BUSY -> "Framework busy, unable to service request"
|
||||||
|
WifiP2pManager.P2P_UNSUPPORTED -> "P2P unsupported on this device"
|
||||||
|
else -> "Unknown error code - $reason"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun displayToast(stringResourceId: Int, templateValue: String, duration: Int) =
|
||||||
|
activity.toast(activity.getString(stringResourceId, templateValue), duration)
|
||||||
|
|
||||||
|
fun onFileTransferAsyncTaskComplete(wereAllFilesTransferred: Boolean) {
|
||||||
|
if (wereAllFilesTransferred) {
|
||||||
|
activity.toast(R.string.file_transfer_complete, Toast.LENGTH_LONG)
|
||||||
|
} else {
|
||||||
|
activity.toast(R.string.error_during_transfer, Toast.LENGTH_LONG)
|
||||||
|
}
|
||||||
|
callbacks.onFileTransferComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callbacks {
|
||||||
|
fun onUserDeviceDetailsAvailable(userDevice: WifiP2pDevice?)
|
||||||
|
fun onConnectionToPeersLost()
|
||||||
|
fun updateListOfAvailablePeers(peers: WifiP2pDeviceList)
|
||||||
|
fun onFilesForTransferAvailable(filesForTransfer: ArrayList<FileItem>)
|
||||||
|
fun onFileStatusChanged(itemIndex: Int)
|
||||||
|
fun onFileTransferComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "WifiDirectManager"
|
||||||
|
@JvmField var FILE_TRANSFER_PORT = 8008
|
||||||
|
@JvmStatic @Throws(IOException::class) fun copyToOutputStream(
|
||||||
|
inputStream: InputStream,
|
||||||
|
outputStream: OutputStream
|
||||||
|
) {
|
||||||
|
inputStream.use { input -> outputStream.use { output -> input.copyTo(output) } }
|
||||||
|
Log.d(LocalFileTransferActivity.TAG, "Both streams closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic fun getDeviceStatus(status: Int): String {
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, "Peer Status: $status")
|
||||||
|
return when (status) {
|
||||||
|
WifiP2pDevice.AVAILABLE -> "Available"
|
||||||
|
WifiP2pDevice.INVITED -> "Invited"
|
||||||
|
WifiP2pDevice.CONNECTED -> "Connected"
|
||||||
|
WifiP2pDevice.FAILED -> "Failed"
|
||||||
|
WifiP2pDevice.UNAVAILABLE -> "Unavailable"
|
||||||
|
else -> "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns text after location of last slash in the file path
|
||||||
|
@JvmStatic fun getFileName(fileUri: Uri) = "$fileUri".substringAfterLast('/')
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user