diff --git a/res/values/strings.xml b/res/values/strings.xml
index f746029a..1f3bd0cb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -135,11 +135,13 @@
- File: %1s
- (File %2$d of %3$d) : %1$s
- Sending file to %1s
- Sending files to %1s
+
+ - Sending %1$d file to %2$s
+ - Sending %1$d files to %2$s
+
- - Sent %1$d file
- - Sent %1$d out of %2$d files
+ - File: %1$s
+ - (File %2$d of %3$d) : %1$s
- Received file from %1$s
@@ -149,12 +151,16 @@
- Failed receiving file from %1$s
- Failed receiving %2$d of %3$d files from %1$s
+
+ - Sent file to %1$s
+ - Sent %2$d files to %1$s"
+
+
+ - Failed sending file to %1$s
+ - Failed sending %2$d of %3$d files to %1$s
+
Tap to open \'%1s\'
Cannot create file %s
- Sent file to %1s
- %1s
- Failed to send file to %1s
- %1s
Tap to answer
Reconnect
Send Right Click
diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java
index b82434c0..0a5485ec 100644
--- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java
+++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java
@@ -201,7 +201,7 @@ public class LanLink extends BaseLink {
long size = np.getPayloadSize();
long progress = 0;
long timeSinceLastUpdate = -1;
- while ((bytesRead = inputStream.read(buffer)) != -1) {
+ while (!np.isCanceled() && (bytesRead = inputStream.read(buffer)) != -1) {
//Log.e("ok",""+bytesRead);
progress += bytesRead;
outputStream.write(buffer, 0, bytesRead);
@@ -223,7 +223,9 @@ public class LanLink extends BaseLink {
}
}
- callback.onSuccess();
+ if (!np.isCanceled()) {
+ callback.onSuccess();
+ }
return true;
} catch (Exception e) {
if (callback != null) {
diff --git a/src/org/kde/kdeconnect/NetworkPacket.java b/src/org/kde/kdeconnect/NetworkPacket.java
index fdb385f3..87ccdc07 100644
--- a/src/org/kde/kdeconnect/NetworkPacket.java
+++ b/src/org/kde/kdeconnect/NetworkPacket.java
@@ -57,6 +57,7 @@ public class NetworkPacket {
private JSONObject mBody;
private Payload mPayload;
private JSONObject mPayloadTransferInfo;
+ private volatile boolean canceled;
private NetworkPacket() {
@@ -70,6 +71,9 @@ public class NetworkPacket {
mPayloadTransferInfo = new JSONObject();
}
+ public boolean isCanceled() { return canceled; }
+ public void cancel() { canceled = true; }
+
public String getType() {
return mType;
}
@@ -317,7 +321,7 @@ public class NetworkPacket {
private Socket inputSocket;
private long payloadSize;
- Payload(long payloadSize) {
+ public Payload(long payloadSize) {
this((InputStream)null, payloadSize);
}
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeReceiveFileJob.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeReceiveFileJob.java
index 6347f8ba..d275f9ee 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeReceiveFileJob.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeReceiveFileJob.java
@@ -47,7 +47,7 @@ import androidx.core.content.FileProvider;
import androidx.documentfile.provider.DocumentFile;
public class CompositeReceiveFileJob extends BackgroundJob {
- private final ShareNotification shareNotification;
+ private final ReceiveNotification receiveNotification;
private NetworkPacket currentNetworkPacket;
private String currentFileName;
private int currentFileNum;
@@ -66,8 +66,8 @@ public class CompositeReceiveFileJob extends BackgroundJob {
lock = new Object();
networkPacketList = new ArrayList<>();
- shareNotification = new ShareNotification(device);
- shareNotification.addCancelAction(getId());
+ receiveNotification = new ReceiveNotification(device);
+ receiveNotification.addCancelAction(getId());
currentFileNum = 0;
totalNumFiles = 0;
totalPayloadSize = 0;
@@ -87,7 +87,7 @@ public class CompositeReceiveFileJob extends BackgroundJob {
this.totalNumFiles = numberOfFiles;
this.totalPayloadSize = totalPayloadSize;
- shareNotification.setTitle(getDevice().getContext().getResources()
+ receiveNotification.setTitle(getDevice().getContext().getResources()
.getQuantityString(R.plurals.incoming_file_title, totalNumFiles, totalNumFiles, getDevice().getName()));
}
}
@@ -100,7 +100,7 @@ public class CompositeReceiveFileJob extends BackgroundJob {
totalNumFiles = networkPacket.getInt(SharePlugin.KEY_NUMBER_OF_FILES, 1);
totalPayloadSize = networkPacket.getLong(SharePlugin.KEY_TOTAL_PAYLOAD_SIZE);
- shareNotification.setTitle(getDevice().getContext().getResources()
+ receiveNotification.setTitle(getDevice().getContext().getResources()
.getQuantityString(R.plurals.incoming_file_title, totalNumFiles, totalNumFiles, getDevice().getName()));
}
}
@@ -149,6 +149,7 @@ public class CompositeReceiveFileJob extends BackgroundJob {
publishFile(fileDocument, received);
}
} else {
+ //TODO: Only set progress to 100 if this is the only file/packet to send
setProgress(100);
publishFile(fileDocument, 0);
}
@@ -180,7 +181,7 @@ public class CompositeReceiveFileJob extends BackgroundJob {
isRunning = false;
if (canceled) {
- shareNotification.cancel();
+ receiveNotification.cancel();
return;
}
@@ -190,23 +191,23 @@ public class CompositeReceiveFileJob extends BackgroundJob {
}
if (numFiles == 1 && currentNetworkPacket.has("open")) {
- shareNotification.cancel();
+ receiveNotification.cancel();
openFile(fileDocument);
} else {
//Update the notification and allow to open the file from it
- shareNotification.setFinished(getDevice().getContext().getResources().getQuantityString(R.plurals.received_files_title, numFiles, getDevice().getName(), numFiles));
+ receiveNotification.setFinished(getDevice().getContext().getResources().getQuantityString(R.plurals.received_files_title, numFiles, getDevice().getName(), numFiles));
if (totalNumFiles == 1 && fileDocument != null) {
- shareNotification.setURI(fileDocument.getUri(), fileDocument.getType(), fileDocument.getName());
+ receiveNotification.setURI(fileDocument.getUri(), fileDocument.getType(), fileDocument.getName());
}
- shareNotification.show();
+ receiveNotification.show();
}
reportResult(null);
} catch (ActivityNotFoundException e) {
- shareNotification.setFinished(getDevice().getContext().getString(R.string.no_app_for_opening));
- shareNotification.show();
+ receiveNotification.setFinished(getDevice().getContext().getString(R.string.no_app_for_opening));
+ receiveNotification.show();
} catch (Exception e) {
isRunning = false;
@@ -217,8 +218,8 @@ public class CompositeReceiveFileJob extends BackgroundJob {
failedFiles = (totalNumFiles - currentFileNum + 1);
}
- shareNotification.setFinished(getDevice().getContext().getResources().getQuantityString(R.plurals.received_files_fail_title, failedFiles, getDevice().getName(), failedFiles, totalNumFiles));
- shareNotification.show();
+ receiveNotification.setFinished(getDevice().getContext().getResources().getQuantityString(R.plurals.received_files_fail_title, failedFiles, getDevice().getName(), failedFiles, totalNumFiles));
+ receiveNotification.show();
reportError(e);
} finally {
closeAllInputStreams();
@@ -238,7 +239,7 @@ public class CompositeReceiveFileJob extends BackgroundJob {
//We need to check for already existing files only when storing in the default path.
//User-defined paths use the new Storage Access Framework that already handles this.
- //If the file should be opened immediately store it in the standard location to avoid the FileProvider trouble (See ShareNotification::setURI)
+ //If the file should be opened immediately store it in the standard location to avoid the FileProvider trouble (See ReceiveNotification::setURI)
if (open || !ShareSettingsFragment.isCustomDestinationEnabled(getDevice().getContext())) {
final String defaultPath = ShareSettingsFragment.getDefaultDestinationDirectory().getAbsolutePath();
filenameToUse = FilesHelper.findNonExistingNameForNewFile(defaultPath, filenameToUse);
@@ -300,10 +301,10 @@ public class CompositeReceiveFileJob extends BackgroundJob {
private void setProgress(int progress) {
synchronized (lock) {
- shareNotification.setProgress(progress, getDevice().getContext().getResources()
+ receiveNotification.setProgress(progress, getDevice().getContext().getResources()
.getQuantityString(R.plurals.incoming_files_text, totalNumFiles, currentFileName, currentFileNum, totalNumFiles));
}
- shareNotification.show();
+ receiveNotification.show();
}
private void publishFile(DocumentFile fileDocument, long size) {
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java
new file mode 100644
index 00000000..a3d1b1e1
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2019 Erik Duisters
+ *
+ * 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 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+package org.kde.kdeconnect.Plugins.SharePlugin;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.NetworkPacket;
+import org.kde.kdeconnect.async.BackgroundJob;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+
+public class CompositeUploadFileJob extends BackgroundJob {
+ private boolean isRunning;
+ private Handler handler;
+ private String currentFileName;
+ private int currentFileNum;
+ private boolean updatePacketPending;
+ private long totalSend;
+ private int prevProgressPercentage;
+ private UploadNotification uploadNotification;
+
+ private final Object lock; //Use to protect concurrent access to the variables below
+ private final List networkPacketList;
+ private NetworkPacket currentNetworkPacket;
+ private final Device.SendPacketStatusCallback sendPacketStatusCallback;
+ private int totalNumFiles;
+ private long totalPayloadSize;
+
+ CompositeUploadFileJob(@NonNull Device device, @NonNull Callback callback) {
+ super(device, callback);
+
+ isRunning = false;
+ handler = new Handler(Looper.getMainLooper());
+ currentFileNum = 0;
+ currentFileName = "";
+ updatePacketPending = false;
+
+ lock = new Object();
+ networkPacketList = new ArrayList<>();
+ totalNumFiles = 0;
+ totalPayloadSize = 0;
+ totalSend = 0;
+ prevProgressPercentage = 0;
+ uploadNotification = new UploadNotification(getDevice());
+ uploadNotification.addCancelAction(getId());
+
+ sendPacketStatusCallback = new SendPacketStatusCallback();
+ }
+
+ private Device getDevice() { return requestInfo; }
+
+ @Override
+ public void run() {
+ boolean done;
+
+ isRunning = true;
+
+ synchronized (lock) {
+ done = networkPacketList.isEmpty();
+ }
+
+ try {
+ while (!done && !canceled) {
+ synchronized (lock) {
+ currentNetworkPacket = networkPacketList.remove(0);
+ }
+
+ currentFileName = currentNetworkPacket.getString("filename");
+ currentFileNum++;
+
+ setProgress(prevProgressPercentage);
+
+ addTotalsToNetworkPacket(currentNetworkPacket);
+
+ if (!getDevice().sendPacketBlocking(currentNetworkPacket, sendPacketStatusCallback)) {
+ throw new RuntimeException("Sending packet failed");
+ }
+
+ synchronized (lock) {
+ done = networkPacketList.isEmpty();
+ }
+ }
+
+ if (canceled) {
+ uploadNotification.cancel();
+ } else {
+ uploadNotification.setFinished(getDevice().getContext().getResources().getQuantityString(R.plurals.sent_files_title, currentFileNum, getDevice().getName(), currentFileNum));
+ uploadNotification.show();
+
+ reportResult(null);
+ }
+ } catch (RuntimeException e) {
+ int failedFiles;
+ synchronized (lock) {
+ failedFiles = (totalNumFiles - currentFileNum + 1);
+ uploadNotification.setFinished(getDevice().getContext().getResources()
+ .getQuantityString(R.plurals.send_files_fail_title, failedFiles, getDevice().getName(),
+ failedFiles, totalNumFiles));
+ }
+
+ uploadNotification.show();
+ reportError(e);
+ } finally {
+ isRunning = false;
+
+ for (NetworkPacket networkPacket : networkPacketList) {
+ networkPacket.getPayload().close();
+ }
+ networkPacketList.clear();
+ }
+ }
+
+ private void addTotalsToNetworkPacket(NetworkPacket networkPacket) {
+ synchronized (lock) {
+ networkPacket.set(SharePlugin.KEY_NUMBER_OF_FILES, totalNumFiles);
+ networkPacket.set(SharePlugin.KEY_TOTAL_PAYLOAD_SIZE, totalPayloadSize);
+ }
+ }
+
+ private void setProgress(int progress) {
+ synchronized (lock) {
+ uploadNotification.setProgress(progress, getDevice().getContext().getResources()
+ .getQuantityString(R.plurals.outgoing_files_text, totalNumFiles, currentFileName, currentFileNum, totalNumFiles));
+ }
+ uploadNotification.show();
+ }
+
+ void addNetworkPacket(@NonNull NetworkPacket networkPacket) {
+ synchronized (lock) {
+ networkPacketList.add(networkPacket);
+
+ totalNumFiles++;
+
+ if (networkPacket.getPayloadSize() >= 0) {
+ totalPayloadSize += networkPacket.getPayloadSize();
+ }
+
+ uploadNotification.setTitle(getDevice().getContext().getResources()
+ .getQuantityString(R.plurals.outgoing_file_title, totalNumFiles, totalNumFiles, getDevice().getName()));
+
+ //Give SharePlugin some time to add more NetworkPackets
+ if (isRunning && !updatePacketPending) {
+ updatePacketPending = true;
+ handler.post(this::sendUpdatePacket);
+ }
+ }
+ }
+
+ private void sendUpdatePacket() {
+ NetworkPacket np = new NetworkPacket(SharePlugin.PACKET_TYPE_SHARE_REQUEST_UPDATE);
+
+ synchronized (lock) {
+ np.set("numberOfFiles", totalNumFiles);
+ np.set("totalPayloadSize", totalPayloadSize);
+ updatePacketPending = false;
+ }
+
+ getDevice().sendPacket(np);
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+
+ currentNetworkPacket.cancel();
+ }
+
+ private class SendPacketStatusCallback extends Device.SendPacketStatusCallback {
+ @Override
+ public void onProgressChanged(int percent) {
+ float send = totalSend + (currentNetworkPacket.getPayloadSize() * ((float)percent / 100));
+ int progress = (int)((send * 100) / totalPayloadSize);
+
+ if (progress != prevProgressPercentage) {
+ setProgress(progress);
+ prevProgressPercentage = progress;
+ }
+ }
+
+ @Override
+ public void onSuccess() {
+ if (currentNetworkPacket.getPayloadSize() == 0) {
+ synchronized (lock) {
+ if (networkPacketList.isEmpty()) {
+ setProgress(100);
+ }
+ }
+ }
+
+ totalSend += currentNetworkPacket.getPayloadSize();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ //Ignored
+ }
+ }
+}
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/NotificationUpdateCallback.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/NotificationUpdateCallback.java
deleted file mode 100644
index e742b0dd..00000000
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/NotificationUpdateCallback.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.kde.kdeconnect.Plugins.SharePlugin;
-
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.Log;
-
-import org.kde.kdeconnect.Device;
-import org.kde.kdeconnect.Helpers.NotificationHelper;
-import org.kde.kdeconnect.NetworkPacket;
-import org.kde.kdeconnect_tp.R;
-
-import java.util.ArrayList;
-
-import androidx.core.app.NotificationCompat;
-
-class NotificationUpdateCallback extends Device.SendPacketStatusCallback {
-
- private final Resources res;
- private final Device device;
- private final NotificationManager notificationManager;
- private final NotificationCompat.Builder builder;
-
- private final ArrayList toSend;
-
- private final int notificationId;
-
- private int sentFiles = 0;
- private final int numFiles;
-
- NotificationUpdateCallback(Context context, Device device, ArrayList toSend) {
- this.toSend = toSend;
- this.device = device;
- this.res = context.getResources();
-
- String title;
- if (toSend.size() > 1) {
- title = res.getString(R.string.outgoing_files_title, device.getName());
- } else {
- title = res.getString(R.string.outgoing_file_title, device.getName());
- }
-
- notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
- builder = new NotificationCompat.Builder(context, NotificationHelper.Channels.FILETRANSFER)
- .setSmallIcon(android.R.drawable.stat_sys_upload)
- .setAutoCancel(true)
- .setOngoing(true)
- .setProgress(100, 0, false)
- .setContentTitle(title)
- .setTicker(title);
-
- notificationId = (int) System.currentTimeMillis();
-
- numFiles = toSend.size();
-
- }
-
- @Override
- public void onProgressChanged(int progress) {
- builder.setProgress(100 * numFiles, (100 * sentFiles) + progress, false);
- NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
- }
-
- @Override
- public void onSuccess() {
- sentFiles++;
- if (sentFiles == numFiles) {
- updateDone(true);
- } else {
- updateText();
- }
- NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
- }
-
- @Override
- public void onFailure(Throwable e) {
- updateDone(false);
- NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
- Log.e("KDEConnect", "Exception", e);
- }
-
- private void updateText() {
- String text;
- text = res.getQuantityString(R.plurals.outgoing_files_text, numFiles, sentFiles, numFiles);
- builder.setContentText(text);
- }
-
- private void updateDone(boolean successful) {
- int icon;
- String title;
- String text;
-
- if (successful) {
- if (numFiles > 1) {
- text = res.getQuantityString(R.plurals.outgoing_files_text, numFiles, sentFiles, numFiles);
- } else {
- final String filename = toSend.get(0).getString("filename");
- text = res.getString(R.string.sent_file_text, filename);
- }
- title = res.getString(R.string.sent_file_title, device.getName());
- icon = android.R.drawable.stat_sys_upload_done;
- } else {
- final String filename = toSend.get(sentFiles).getString("filename");
- title = res.getString(R.string.sent_file_failed_title, device.getName());
- text = res.getString(R.string.sent_file_failed_text, filename);
- icon = android.R.drawable.stat_notify_error;
- }
-
- builder.setOngoing(false)
- .setTicker(title)
- .setContentTitle(title)
- .setContentText(text)
- .setSmallIcon(icon)
- .setProgress(0, 0, false); //setting progress to 0 out of 0 remove the progress bar
- }
-
-}
-
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareNotification.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/ReceiveNotification.java
similarity index 99%
rename from src/org/kde/kdeconnect/Plugins/SharePlugin/ShareNotification.java
rename to src/org/kde/kdeconnect/Plugins/SharePlugin/ReceiveNotification.java
index 5870ccd4..848ec315 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/ShareNotification.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/ReceiveNotification.java
@@ -43,7 +43,7 @@ import java.io.InputStream;
import androidx.core.app.NotificationCompat;
import androidx.core.content.FileProvider;
-class ShareNotification {
+class ReceiveNotification {
private final NotificationManager notificationManager;
private final int notificationId;
private NotificationCompat.Builder builder;
@@ -54,7 +54,7 @@ class ShareNotification {
private static final int bigImageWidth = 1440;
private static final int bigImageHeight = 720;
- public ShareNotification(Device device) {
+ public ReceiveNotification(Device device) {
this.device = device;
notificationId = (int) System.currentTimeMillis();
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/SendFileActivity.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/SendFileActivity.java
index 9dfbe308..dac88f1b 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/SendFileActivity.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/SendFileActivity.java
@@ -89,7 +89,7 @@ public class SendFileActivity extends AppCompatActivity {
if (uris.isEmpty()) {
Log.w("SendFileActivity", "No files to send?");
} else {
- BackgroundService.RunWithPlugin(this, mDeviceId, SharePlugin.class, plugin -> plugin.queuedSendUriList(uris));
+ BackgroundService.RunWithPlugin(this, mDeviceId, SharePlugin.class, plugin -> plugin.sendUriList(uris));
}
}
finish();
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
index b99908e1..dc18dac8 100644
--- a/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/SharePlugin.java
@@ -56,7 +56,7 @@ public class SharePlugin extends Plugin {
final static String CANCEL_SHARE_BACKGROUND_JOB_ID_EXTRA = "backgroundJobId";
private final static String PACKET_TYPE_SHARE_REQUEST = "kdeconnect.share.request";
- private final static String PACKET_TYPE_SHARE_REQUEST_UPDATE = "kdeconnect.share.request.update";
+ final static String PACKET_TYPE_SHARE_REQUEST_UPDATE = "kdeconnect.share.request.update";
final static String KEY_NUMBER_OF_FILES = "numberOfFiles";
final static String KEY_TOTAL_PAYLOAD_SIZE = "totalPayloadSize";
@@ -65,6 +65,7 @@ public class SharePlugin extends Plugin {
private final Handler handler;
private CompositeReceiveFileJob receiveFileJob;
+ private CompositeUploadFileJob uploadFileJob;
private final Callback receiveFileJobCallback;
public SharePlugin() {
@@ -147,7 +148,8 @@ public class SharePlugin extends Plugin {
}
} catch (Exception e) {
- Log.e("SharePlugin", "Exception", e);
+ Log.e("SharePlugin", "Exception");
+ e.printStackTrace();
}
return true;
@@ -204,36 +206,28 @@ public class SharePlugin extends Plugin {
return ShareSettingsFragment.newInstance(getPluginKey());
}
- void queuedSendUriList(final ArrayList uriList) {
+ void sendUriList(final ArrayList uriList) {
+ CompositeUploadFileJob job = null;
+
+ if (uploadFileJob == null) {
+ job = new CompositeUploadFileJob(device, this.receiveFileJobCallback);
+ } else {
+ job = uploadFileJob;
+ }
+
//Read all the data early, as we only have permissions to do it while the activity is alive
- final ArrayList toSend = new ArrayList<>();
for (Uri uri : uriList) {
NetworkPacket np = FilesHelper.uriToNetworkPacket(context, uri, PACKET_TYPE_SHARE_REQUEST);
if (np != null) {
- toSend.add(np);
+ job.addNetworkPacket(np);
}
}
- //Callback that shows a progress notification
- final NotificationUpdateCallback notificationUpdateCallback = new NotificationUpdateCallback(context, device, toSend);
-
- //Do the sending in background
- new Thread(() -> {
- //Actually send the files
- try {
- for (NetworkPacket np : toSend) {
- boolean success = device.sendPacketBlocking(np, notificationUpdateCallback);
- if (!success) {
- Log.e("SharePlugin", "Error sending files");
- return;
- }
- }
- } catch (Exception e) {
- Log.e("SharePlugin", "Error sending files", e);
- }
- }).start();
-
+ if (job != uploadFileJob) {
+ uploadFileJob = job;
+ backgroundJobHandler.runJob(uploadFileJob);
+ }
}
public void share(Intent intent) {
@@ -252,9 +246,10 @@ public class SharePlugin extends Plugin {
uriList.add(uri);
}
- queuedSendUriList(uriList);
+ sendUriList(uriList);
} catch (Exception e) {
- Log.e("ShareActivity", "Exception", e);
+ Log.e("ShareActivity", "Exception");
+ e.printStackTrace();
}
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
@@ -302,11 +297,13 @@ public class SharePlugin extends Plugin {
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
}
- private class Callback implements CompositeReceiveFileJob.Callback {
+ private class Callback implements BackgroundJob.Callback {
@Override
public void onResult(@NonNull BackgroundJob job, Void result) {
if (job == receiveFileJob) {
receiveFileJob = null;
+ } else if (job == uploadFileJob) {
+ uploadFileJob = null;
}
}
@@ -314,6 +311,8 @@ public class SharePlugin extends Plugin {
public void onError(@NonNull BackgroundJob job, @NonNull Throwable error) {
if (job == receiveFileJob) {
receiveFileJob = null;
+ } else if (job == uploadFileJob) {
+ uploadFileJob = null;
}
}
}
@@ -327,6 +326,8 @@ public class SharePlugin extends Plugin {
if (job == receiveFileJob) {
receiveFileJob = null;
+ } else if (job == uploadFileJob) {
+ uploadFileJob = null;
}
}
}
diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java
new file mode 100644
index 00000000..dbc47592
--- /dev/null
+++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019 Erik Duisters
+ *
+ * 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 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * 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 .
+ */
+
+package org.kde.kdeconnect.Plugins.SharePlugin;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.Helpers.NotificationHelper;
+import org.kde.kdeconnect_tp.R;
+
+import androidx.core.app.NotificationCompat;
+import androidx.preference.PreferenceManager;
+
+class UploadNotification {
+ private final NotificationManager notificationManager;
+ private NotificationCompat.Builder builder;
+ private final int notificationId;
+ private final Device device;
+ private long currentJobId;
+
+ UploadNotification(Device device) {
+ this.device = device;
+
+ notificationId = (int) System.currentTimeMillis();
+ notificationManager = (NotificationManager) device.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
+ builder = new NotificationCompat.Builder(device.getContext(), NotificationHelper.Channels.FILETRANSFER)
+ .setSmallIcon(android.R.drawable.stat_sys_upload)
+ .setAutoCancel(true)
+ .setOngoing(true)
+ .setProgress(100, 0, true);
+ }
+
+ void addCancelAction(long jobId) {
+ builder.mActions.clear();
+
+ currentJobId = jobId;
+ Intent cancelIntent = new Intent(device.getContext(), ShareBroadcastReceiver.class);
+ cancelIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ cancelIntent.setAction(SharePlugin.ACTION_CANCEL_SHARE);
+ cancelIntent.putExtra(SharePlugin.CANCEL_SHARE_BACKGROUND_JOB_ID_EXTRA, jobId);
+ cancelIntent.putExtra(SharePlugin.CANCEL_SHARE_DEVICE_ID_EXTRA, device.getDeviceId());
+ PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(device.getContext(), 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ builder.addAction(R.drawable.ic_reject_pairing, device.getContext().getString(R.string.cancel), cancelPendingIntent);
+ }
+
+ public void setTitle(String title) {
+ builder.setContentTitle(title);
+ builder.setTicker(title);
+ }
+
+ public void setProgress(int progress, String progressMessage) {
+ builder.setProgress( 100, progress, false);
+ builder.setContentText(progressMessage);
+ builder.setStyle(new NotificationCompat.BigTextStyle().bigText(progressMessage));
+ }
+
+ public void setFinished(String message) {
+ builder = new NotificationCompat.Builder(device.getContext(), NotificationHelper.Channels.DEFAULT);
+ builder.setContentTitle(message)
+ .setTicker(message)
+ .setSmallIcon(android.R.drawable.stat_sys_upload_done)
+ .setAutoCancel(true)
+ .setOngoing(false);
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(device.getContext());
+ if (prefs.getBoolean("share_notification_preference", true)) {
+ builder.setDefaults(Notification.DEFAULT_ALL);
+ }
+ }
+
+ public void setFailed(String message) {
+ setFinished(message);
+ builder.setSmallIcon(android.R.drawable.stat_notify_error);
+ }
+
+ public void cancel() {
+ notificationManager.cancel(notificationId);
+ }
+
+ void show() {
+ NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
+ }
+}
+