mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-31 06:05:12 +00:00
Upload files using a CompositeUploadFileJob making the upload cancelable
This commit is contained in:
@@ -135,11 +135,13 @@
|
||||
<item quantity="one">File: %1s</item>
|
||||
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
|
||||
</plurals>
|
||||
<string name="outgoing_file_title">Sending file to %1s</string>
|
||||
<string name="outgoing_files_title">Sending files to %1s</string>
|
||||
<plurals name="outgoing_file_title">
|
||||
<item quantity="one">Sending %1$d file to %2$s</item>
|
||||
<item quantity="other">Sending %1$d files to %2$s</item>
|
||||
</plurals>
|
||||
<plurals name="outgoing_files_text">
|
||||
<item quantity="one">Sent %1$d file</item>
|
||||
<item quantity="other">Sent %1$d out of %2$d files</item>
|
||||
<item quantity="one">File: %1$s</item>
|
||||
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
|
||||
</plurals>
|
||||
<plurals name="received_files_title">
|
||||
<item quantity="one">Received file from %1$s</item>
|
||||
@@ -149,12 +151,16 @@
|
||||
<item quantity="one">Failed receiving file from %1$s</item>
|
||||
<item quantity="other">Failed receiving %2$d of %3$d files from %1$s</item>
|
||||
</plurals>
|
||||
<plurals name="sent_files_title">
|
||||
<item quantity="one">Sent file to %1$s</item>
|
||||
<item quantity="other">Sent %2$d files to %1$s"</item>
|
||||
</plurals>
|
||||
<plurals name="send_files_fail_title">
|
||||
<item quantity="one">Failed sending file to %1$s</item>
|
||||
<item quantity="other">Failed sending %2$d of %3$d files to %1$s</item>
|
||||
</plurals>
|
||||
<string name="received_file_text">Tap to open \'%1s\'</string>
|
||||
<string name="cannot_create_file">Cannot create file %s</string>
|
||||
<string name="sent_file_title">Sent file to %1s</string>
|
||||
<string name="sent_file_text">%1s</string>
|
||||
<string name="sent_file_failed_title">Failed to send file to %1s</string>
|
||||
<string name="sent_file_failed_text">%1s</string>
|
||||
<string name="tap_to_answer">Tap to answer</string>
|
||||
<string name="reconnect">Reconnect</string>
|
||||
<string name="right_click">Send Right Click</string>
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ import androidx.core.content.FileProvider;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
public class CompositeReceiveFileJob extends BackgroundJob<Device, Void> {
|
||||
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<Device, Void> {
|
||||
|
||||
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<Device, Void> {
|
||||
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<Device, Void> {
|
||||
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<Device, Void> {
|
||||
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<Device, Void> {
|
||||
isRunning = false;
|
||||
|
||||
if (canceled) {
|
||||
shareNotification.cancel();
|
||||
receiveNotification.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,23 +191,23 @@ public class CompositeReceiveFileJob extends BackgroundJob<Device, Void> {
|
||||
}
|
||||
|
||||
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<Device, Void> {
|
||||
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<Device, Void> {
|
||||
|
||||
//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<Device, Void> {
|
||||
|
||||
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) {
|
||||
|
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2019 Erik Duisters <e.duisters1@gmail.com>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Device, Void> {
|
||||
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<NetworkPacket> networkPacketList;
|
||||
private NetworkPacket currentNetworkPacket;
|
||||
private final Device.SendPacketStatusCallback sendPacketStatusCallback;
|
||||
private int totalNumFiles;
|
||||
private long totalPayloadSize;
|
||||
|
||||
CompositeUploadFileJob(@NonNull Device device, @NonNull Callback<Void> 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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<NetworkPacket> toSend;
|
||||
|
||||
private final int notificationId;
|
||||
|
||||
private int sentFiles = 0;
|
||||
private final int numFiles;
|
||||
|
||||
NotificationUpdateCallback(Context context, Device device, ArrayList<NetworkPacket> 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
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
@@ -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();
|
||||
|
@@ -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<Uri> uriList) {
|
||||
void sendUriList(final ArrayList<Uri> 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<NetworkPacket> 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<Void> {
|
||||
private class Callback implements BackgroundJob.Callback<Void> {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2019 Erik Duisters <e.duisters1@gmail.com>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user