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()); + } +} +