diff --git a/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java index a3d1b1e1..a9e22827 100644 --- a/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java +++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/CompositeUploadFileJob.java @@ -1,222 +1,221 @@ /* * 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()); + uploadNotification = new UploadNotification(getDevice(), 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/UploadNotification.java b/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java index 14854318..37fc8f79 100644 --- a/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java +++ b/src/org/kde/kdeconnect/Plugins/SharePlugin/UploadNotification.java @@ -1,106 +1,107 @@ /* * 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 jobId; - UploadNotification(Device device) { + UploadNotification(Device device, long jobId) { this.device = device; + this.jobId = jobId; 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); + addCancelAction(); } - void addCancelAction(long jobId) { - builder.mActions.clear(); - + void addCancelAction() { 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()); } }