2014-11-16 23:14:06 -08:00
|
|
|
/*
|
|
|
|
* Copyright 2014 Albert Vaca Cintora <albertvaka@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
|
2018-09-29 20:28:47 +02:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2014-11-16 23:14:06 -08:00
|
|
|
|
2014-06-27 14:44:40 +02:00
|
|
|
package org.kde.kdeconnect.Plugins.SharePlugin;
|
|
|
|
|
2017-05-31 15:51:07 +02:00
|
|
|
import android.Manifest;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.app.Activity;
|
2016-12-11 21:03:39 +01:00
|
|
|
import android.app.DownloadManager;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.ClipboardManager;
|
2015-09-07 00:09:28 -07:00
|
|
|
import android.content.ContentResolver;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.res.Resources;
|
2015-09-07 00:09:28 -07:00
|
|
|
import android.database.Cursor;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.net.Uri;
|
2018-11-20 21:49:33 +01:00
|
|
|
import android.os.Build;
|
2017-07-29 11:30:26 +02:00
|
|
|
import android.os.Bundle;
|
2018-11-25 17:33:12 +01:00
|
|
|
import android.os.Handler;
|
|
|
|
import android.os.Looper;
|
2015-09-07 00:09:28 -07:00
|
|
|
import android.provider.MediaStore;
|
2018-11-25 17:33:12 +01:00
|
|
|
import android.support.annotation.WorkerThread;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.support.v4.app.NotificationCompat;
|
2015-08-10 00:26:58 -07:00
|
|
|
import android.support.v4.content.ContextCompat;
|
2018-11-20 21:49:33 +01:00
|
|
|
import android.support.v4.content.FileProvider;
|
2016-09-27 20:26:57 +02:00
|
|
|
import android.support.v4.provider.DocumentFile;
|
2014-06-27 14:44:40 +02:00
|
|
|
import android.util.Log;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
import org.kde.kdeconnect.Helpers.FilesHelper;
|
2016-12-11 13:54:21 +01:00
|
|
|
import org.kde.kdeconnect.Helpers.MediaStoreHelper;
|
2016-12-05 23:42:58 +01:00
|
|
|
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
2018-03-04 11:31:37 +01:00
|
|
|
import org.kde.kdeconnect.NetworkPacket;
|
2014-06-27 14:44:40 +02:00
|
|
|
import org.kde.kdeconnect.Plugins.Plugin;
|
2018-10-16 14:43:00 +02:00
|
|
|
import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
|
2014-06-27 14:44:40 +02:00
|
|
|
import org.kde.kdeconnect_tp.R;
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
import java.io.BufferedOutputStream;
|
2014-06-27 14:44:40 +02:00
|
|
|
import java.io.File;
|
2017-07-11 10:35:53 +02:00
|
|
|
import java.io.FileNotFoundException;
|
2014-06-27 14:44:40 +02:00
|
|
|
import java.io.InputStream;
|
2017-07-29 11:30:26 +02:00
|
|
|
import java.net.URL;
|
2015-09-07 00:09:28 -07:00
|
|
|
import java.util.ArrayList;
|
2018-11-25 17:33:12 +01:00
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
public class SharePlugin extends Plugin implements ReceiveFileRunnable.CallBack {
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-10-26 23:53:58 +02:00
|
|
|
private final static String PACKET_TYPE_SHARE_REQUEST = "kdeconnect.share.request";
|
2016-05-31 17:19:39 +02:00
|
|
|
|
2018-10-26 23:53:58 +02:00
|
|
|
private final static boolean openUrlsDirectly = true;
|
2018-11-25 17:33:12 +01:00
|
|
|
private ShareNotification shareNotification;
|
|
|
|
private FinishReceivingRunnable finishReceivingRunnable;
|
|
|
|
private ExecutorService executorService;
|
|
|
|
private ShareInfo currentShareInfo;
|
|
|
|
private Handler handler;
|
|
|
|
|
|
|
|
public SharePlugin() {
|
|
|
|
executorService = Executors.newSingleThreadExecutor();
|
|
|
|
handler = new Handler(Looper.getMainLooper());
|
|
|
|
}
|
2015-09-11 03:45:31 -07:00
|
|
|
|
2017-07-11 13:50:40 +02:00
|
|
|
@Override
|
|
|
|
public boolean onCreate() {
|
2018-03-03 16:26:33 +01:00
|
|
|
optionalPermissionExplanation = R.string.share_optional_permission_explanation;
|
2017-07-11 13:50:40 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-06-27 14:44:40 +02:00
|
|
|
@Override
|
|
|
|
public String getDisplayName() {
|
|
|
|
return context.getResources().getString(R.string.pref_plugin_sharereceiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Drawable getIcon() {
|
2015-08-10 00:26:58 -07:00
|
|
|
return ContextCompat.getDrawable(context, R.drawable.share_plugin_action);
|
2014-06-27 14:44:40 +02:00
|
|
|
}
|
|
|
|
|
2014-09-16 15:45:31 +02:00
|
|
|
@Override
|
2015-04-12 00:11:30 -07:00
|
|
|
public String getDescription() {
|
|
|
|
return context.getResources().getString(R.string.pref_plugin_sharereceiver_desc);
|
2014-06-27 14:44:40 +02:00
|
|
|
}
|
|
|
|
|
2015-08-20 00:59:21 -07:00
|
|
|
@Override
|
|
|
|
public boolean hasMainActivity() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getActionName() {
|
|
|
|
return context.getString(R.string.send_files);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void startMainActivity(Activity parentActivity) {
|
2015-09-07 00:09:28 -07:00
|
|
|
Intent intent = new Intent(parentActivity, SendFileActivity.class);
|
|
|
|
intent.putExtra("deviceId", device.getDeviceId());
|
|
|
|
parentActivity.startActivity(intent);
|
2015-08-20 00:59:21 -07:00
|
|
|
}
|
|
|
|
|
2014-06-27 14:44:40 +02:00
|
|
|
@Override
|
2015-04-12 00:11:30 -07:00
|
|
|
public boolean hasSettings() {
|
2014-06-27 14:44:40 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-11-25 17:33:12 +01:00
|
|
|
@WorkerThread
|
2018-03-04 11:31:37 +01:00
|
|
|
public boolean onPacketReceived(NetworkPacket np) {
|
2014-06-27 14:44:40 +02:00
|
|
|
try {
|
2018-12-02 19:33:27 +01:00
|
|
|
if (np.has("filename")) {
|
2017-07-11 10:35:53 +02:00
|
|
|
if (isPermissionGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
|
|
|
receiveFile(np);
|
|
|
|
} else {
|
2017-05-31 15:51:07 +02:00
|
|
|
Log.i("SharePlugin", "no Permission for Storage");
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
} else if (np.has("text")) {
|
|
|
|
Log.i("SharePlugin", "hasText");
|
|
|
|
receiveText(np);
|
|
|
|
} else if (np.has("url")) {
|
|
|
|
receiveUrl(np);
|
|
|
|
} else {
|
|
|
|
Log.e("SharePlugin", "Error: Nothing attached!");
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e("SharePlugin", "Exception");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
2016-12-11 21:03:39 +01:00
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
return true;
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-03-04 11:31:37 +01:00
|
|
|
private void receiveUrl(NetworkPacket np) {
|
2017-07-11 10:35:53 +02:00
|
|
|
String url = np.getString("url");
|
|
|
|
|
|
|
|
Log.i("SharePlugin", "hasUrl: " + url);
|
|
|
|
|
|
|
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
|
|
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
|
|
|
|
if (openUrlsDirectly) {
|
|
|
|
context.startActivity(browserIntent);
|
|
|
|
} else {
|
|
|
|
Resources res = context.getResources();
|
2018-10-28 20:27:52 +01:00
|
|
|
|
|
|
|
PendingIntent resultPendingIntent = PendingIntent.getActivity(
|
|
|
|
context,
|
2017-07-11 10:35:53 +02:00
|
|
|
0,
|
2018-10-28 20:27:52 +01:00
|
|
|
browserIntent,
|
2017-07-11 10:35:53 +02:00
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT
|
|
|
|
);
|
|
|
|
|
2018-03-24 01:02:25 +01:00
|
|
|
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
|
|
|
2018-05-15 00:32:51 +02:00
|
|
|
Notification noti = new NotificationCompat.Builder(context, NotificationHelper.Channels.DEFAULT)
|
2017-07-11 10:35:53 +02:00
|
|
|
.setContentTitle(res.getString(R.string.received_url_title, device.getName()))
|
|
|
|
.setContentText(res.getString(R.string.received_url_text, url))
|
|
|
|
.setContentIntent(resultPendingIntent)
|
|
|
|
.setTicker(res.getString(R.string.received_url_title, device.getName()))
|
|
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
|
|
.setAutoCancel(true)
|
|
|
|
.setDefaults(Notification.DEFAULT_ALL)
|
|
|
|
.build();
|
|
|
|
|
|
|
|
NotificationHelper.notifyCompat(notificationManager, (int) System.currentTimeMillis(), noti);
|
|
|
|
}
|
|
|
|
}
|
2017-02-20 20:20:06 +01:00
|
|
|
|
2018-03-04 11:31:37 +01:00
|
|
|
private void receiveText(NetworkPacket np) {
|
2017-07-11 10:35:53 +02:00
|
|
|
String text = np.getString("text");
|
2018-10-26 22:51:13 +02:00
|
|
|
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
|
|
cm.setText(text);
|
2018-12-07 11:54:00 +01:00
|
|
|
handler.post(() -> Toast.makeText(context, R.string.shareplugin_text_saved, Toast.LENGTH_LONG).show());
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
@WorkerThread
|
2018-03-04 11:31:37 +01:00
|
|
|
private void receiveFile(NetworkPacket np) {
|
2018-11-25 17:33:12 +01:00
|
|
|
if (finishReceivingRunnable != null) {
|
|
|
|
Log.i("SharePlugin", "receiveFile: canceling finishReceivingRunnable");
|
|
|
|
handler.removeCallbacks(finishReceivingRunnable);
|
|
|
|
finishReceivingRunnable = null;
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
ShareInfo info = new ShareInfo();
|
|
|
|
info.currentFileNumber = currentShareInfo == null ? 1 : currentShareInfo.currentFileNumber + 1;
|
2018-12-03 20:21:06 +01:00
|
|
|
info.payload = np.getPayload();
|
2018-11-25 17:33:12 +01:00
|
|
|
info.fileSize = np.getPayloadSize();
|
|
|
|
info.fileName = np.getString("filename", Long.toString(System.currentTimeMillis()));
|
|
|
|
info.shouldOpen = np.getBoolean("open");
|
|
|
|
info.setNumberOfFiles(np.getInt("numberOfFiles", 1));
|
|
|
|
info.setTotalTransferSize(np.getLong("totalPayloadSize", 1));
|
|
|
|
|
|
|
|
if (currentShareInfo == null) {
|
|
|
|
currentShareInfo = info;
|
|
|
|
} else {
|
|
|
|
synchronized (currentShareInfo) {
|
|
|
|
currentShareInfo.setNumberOfFiles(info.numberOfFiles());
|
|
|
|
currentShareInfo.setTotalTransferSize(info.totalTransferSize());
|
|
|
|
}
|
|
|
|
}
|
2015-04-04 13:53:46 -07:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
String filename = info.fileName;
|
2018-11-20 21:49:33 +01:00
|
|
|
final DocumentFile destinationFolderDocument;
|
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
//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.
|
2018-11-20 21:49:33 +01:00
|
|
|
//If the file should be opened immediately store it in the standard location to avoid the FileProvider trouble (See ShareNotification::setURI)
|
|
|
|
if (np.getBoolean("open") || !ShareSettingsActivity.isCustomDestinationEnabled(context)) {
|
|
|
|
final String defaultPath = ShareSettingsActivity.getDefaultDestinationDirectory().getAbsolutePath();
|
2018-11-25 17:33:12 +01:00
|
|
|
filename = FilesHelper.findNonExistingNameForNewFile(defaultPath, filename);
|
2018-11-20 21:49:33 +01:00
|
|
|
destinationFolderDocument = DocumentFile.fromFile(new File(defaultPath));
|
|
|
|
} else {
|
|
|
|
destinationFolderDocument = ShareSettingsActivity.getDestinationDirectory(context);
|
|
|
|
}
|
2017-07-11 10:35:53 +02:00
|
|
|
String displayName = FilesHelper.getFileNameWithoutExt(filename);
|
2018-11-25 17:33:12 +01:00
|
|
|
String mimeType = FilesHelper.getMimeTypeFromFile(filename);
|
2017-05-31 15:51:07 +02:00
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
if ("*/*".equals(mimeType)) {
|
|
|
|
displayName = filename;
|
|
|
|
}
|
2016-12-11 19:19:51 +01:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
info.fileDocument = destinationFolderDocument.createFile(mimeType, displayName);
|
|
|
|
assert info.fileDocument != null;
|
2018-11-20 21:49:33 +01:00
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
if (shareNotification == null) {
|
|
|
|
shareNotification = new ShareNotification(device);
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
|
2018-12-02 19:33:27 +01:00
|
|
|
if (info.fileDocument == null) {
|
|
|
|
onError(info, new RuntimeException(context.getString(R.string.cannot_create_file, filename)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
shareNotification.setTitle(context.getResources().getQuantityString(R.plurals.incoming_file_title, info.numberOfFiles(), info.numberOfFiles(), device.getName()));
|
|
|
|
shareNotification.show();
|
2018-11-20 21:49:33 +01:00
|
|
|
|
2018-12-02 19:33:27 +01:00
|
|
|
if (np.hasPayload()) {
|
|
|
|
try {
|
|
|
|
info.outputStream = new BufferedOutputStream(context.getContentResolver().openOutputStream(info.fileDocument.getUri()));
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReceiveFileRunnable runnable = new ReceiveFileRunnable(info, this);
|
|
|
|
executorService.execute(runnable);
|
|
|
|
} else {
|
|
|
|
onProgress(info, 100);
|
|
|
|
onSuccess(info);
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
}
|
|
|
|
|
2016-09-27 20:26:57 +02:00
|
|
|
@Override
|
2018-10-16 14:43:00 +02:00
|
|
|
public void startPreferencesActivity(DeviceSettingsActivity parentActivity) {
|
2016-09-27 20:26:57 +02:00
|
|
|
Intent intent = new Intent(parentActivity, ShareSettingsActivity.class);
|
|
|
|
intent.putExtra("plugin_display_name", getDisplayName());
|
|
|
|
intent.putExtra("plugin_key", getPluginKey());
|
|
|
|
parentActivity.startActivity(intent);
|
|
|
|
}
|
|
|
|
|
2018-10-29 15:08:28 +01:00
|
|
|
void queuedSendUriList(final ArrayList<Uri> uriList) {
|
2016-11-30 16:47:51 +01:00
|
|
|
|
2016-12-08 20:50:37 +01:00
|
|
|
//Read all the data early, as we only have permissions to do it while the activity is alive
|
2018-03-04 11:31:37 +01:00
|
|
|
final ArrayList<NetworkPacket> toSend = new ArrayList<>();
|
2016-12-08 20:50:37 +01:00
|
|
|
for (Uri uri : uriList) {
|
2018-12-06 16:55:58 +01:00
|
|
|
NetworkPacket np = uriToNetworkPacket(context, uri);
|
|
|
|
|
|
|
|
if (np != null) {
|
|
|
|
toSend.add(np);
|
|
|
|
}
|
2016-12-08 20:50:37 +01:00
|
|
|
}
|
2016-11-30 16:47:51 +01:00
|
|
|
|
2016-12-08 20:50:37 +01:00
|
|
|
//Callback that shows a progress notification
|
|
|
|
final NotificationUpdateCallback notificationUpdateCallback = new NotificationUpdateCallback(context, device, toSend);
|
|
|
|
|
|
|
|
//Do the sending in background
|
2018-05-09 14:02:56 +02:00
|
|
|
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;
|
2016-12-08 20:50:37 +01:00
|
|
|
}
|
|
|
|
}
|
2018-05-09 14:02:56 +02:00
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
2016-12-08 20:50:37 +01:00
|
|
|
}
|
|
|
|
}).start();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//Create the network package from the URI
|
2018-03-04 11:31:37 +01:00
|
|
|
private static NetworkPacket uriToNetworkPacket(final Context context, final Uri uri) {
|
2016-12-08 20:50:37 +01:00
|
|
|
|
|
|
|
try {
|
2016-11-30 16:47:51 +01:00
|
|
|
|
2015-09-07 00:09:28 -07:00
|
|
|
ContentResolver cr = context.getContentResolver();
|
2016-07-13 18:20:15 +02:00
|
|
|
InputStream inputStream = cr.openInputStream(uri);
|
2015-09-07 00:09:28 -07:00
|
|
|
|
2018-03-04 11:31:37 +01:00
|
|
|
NetworkPacket np = new NetworkPacket(PACKET_TYPE_SHARE_REQUEST);
|
2015-09-07 00:09:28 -07:00
|
|
|
long size = -1;
|
|
|
|
|
|
|
|
if (uri.getScheme().equals("file")) {
|
|
|
|
// file:// is a non media uri, so we cannot query the ContentProvider
|
|
|
|
|
|
|
|
np.set("filename", uri.getLastPathSegment());
|
|
|
|
|
|
|
|
try {
|
|
|
|
size = new File(uri.getPath()).length();
|
2017-07-11 10:35:53 +02:00
|
|
|
} catch (Exception e) {
|
2015-09-07 00:09:28 -07:00
|
|
|
Log.e("SendFileActivity", "Could not obtain file size");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
} else {
|
2015-09-07 00:09:28 -07:00
|
|
|
// Probably a content:// uri, so we query the Media content provider
|
|
|
|
|
|
|
|
Cursor cursor = null;
|
|
|
|
try {
|
2017-07-11 10:35:53 +02:00
|
|
|
String[] proj = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME};
|
2015-09-07 00:09:28 -07:00
|
|
|
cursor = cr.query(uri, proj, null, null, null);
|
|
|
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
|
|
|
cursor.moveToFirst();
|
|
|
|
String path = cursor.getString(column_index);
|
|
|
|
np.set("filename", Uri.parse(path).getLastPathSegment());
|
|
|
|
size = new File(path).length();
|
2017-07-11 10:35:53 +02:00
|
|
|
} catch (Exception unused) {
|
2015-09-07 00:09:28 -07:00
|
|
|
|
2016-11-30 16:52:51 +01:00
|
|
|
Log.w("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
|
2015-09-07 00:09:28 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
|
|
|
cursor.moveToFirst();
|
|
|
|
String name = cursor.getString(column_index);
|
|
|
|
np.set("filename", name);
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Log.e("SendFileActivity", "Could not obtain file name");
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
|
|
|
cursor.moveToFirst();
|
|
|
|
//For some reason this size can differ from the actual file size!
|
|
|
|
size = cursor.getInt(column_index);
|
2017-07-11 10:35:53 +02:00
|
|
|
} catch (Exception e) {
|
2015-09-07 00:09:28 -07:00
|
|
|
Log.e("SendFileActivity", "Could not obtain file size");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
} finally {
|
2017-07-11 10:35:53 +02:00
|
|
|
try {
|
|
|
|
cursor.close();
|
|
|
|
} catch (Exception e) {
|
|
|
|
}
|
2015-09-07 00:09:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-12-03 20:21:06 +01:00
|
|
|
np.setPayload(new NetworkPacket.Payload(inputStream, size));
|
2016-07-13 18:20:15 +02:00
|
|
|
|
2016-12-08 20:50:37 +01:00
|
|
|
return np;
|
2015-09-07 00:09:28 -07:00
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e("SendFileActivity", "Exception sending files");
|
|
|
|
e.printStackTrace();
|
2016-12-08 20:50:37 +01:00
|
|
|
return null;
|
2015-09-07 00:09:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-29 15:08:28 +01:00
|
|
|
public void share(Intent intent) {
|
2017-07-29 11:30:26 +02:00
|
|
|
Bundle extras = intent.getExtras();
|
|
|
|
if (extras != null) {
|
|
|
|
if (extras.containsKey(Intent.EXTRA_STREAM)) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
ArrayList<Uri> uriList;
|
|
|
|
if (!Intent.ACTION_SEND.equals(intent.getAction())) {
|
|
|
|
uriList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
|
|
|
} else {
|
|
|
|
Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
|
|
|
|
uriList = new ArrayList<>();
|
|
|
|
uriList.add(uri);
|
|
|
|
}
|
|
|
|
|
2018-10-29 15:08:28 +01:00
|
|
|
queuedSendUriList(uriList);
|
2017-07-29 11:30:26 +02:00
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.e("ShareActivity", "Exception");
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
|
|
|
|
String text = extras.getString(Intent.EXTRA_TEXT);
|
|
|
|
String subject = extras.getString(Intent.EXTRA_SUBJECT);
|
|
|
|
|
|
|
|
//Hack: Detect shared youtube videos, so we can open them in the browser instead of as text
|
|
|
|
if (subject != null && subject.endsWith("YouTube")) {
|
|
|
|
int index = text.indexOf(": http://youtu.be/");
|
|
|
|
if (index > 0) {
|
|
|
|
text = text.substring(index + 2); //Skip ": "
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean isUrl;
|
|
|
|
try {
|
|
|
|
new URL(text);
|
|
|
|
isUrl = true;
|
|
|
|
} catch (Exception e) {
|
|
|
|
isUrl = false;
|
|
|
|
}
|
2018-03-04 11:31:37 +01:00
|
|
|
NetworkPacket np = new NetworkPacket(SharePlugin.PACKET_TYPE_SHARE_REQUEST);
|
2017-07-29 11:30:26 +02:00
|
|
|
if (isUrl) {
|
|
|
|
np.set("url", text);
|
|
|
|
} else {
|
|
|
|
np.set("text", text);
|
|
|
|
}
|
2018-03-04 11:31:37 +01:00
|
|
|
device.sendPacket(np);
|
2017-07-29 11:30:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2016-12-08 20:50:37 +01:00
|
|
|
|
2015-09-08 14:54:04 -07:00
|
|
|
@Override
|
2018-03-04 11:31:37 +01:00
|
|
|
public String[] getSupportedPacketTypes() {
|
|
|
|
return new String[]{PACKET_TYPE_SHARE_REQUEST};
|
2015-09-08 14:54:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-03-04 11:31:37 +01:00
|
|
|
public String[] getOutgoingPacketTypes() {
|
|
|
|
return new String[]{PACKET_TYPE_SHARE_REQUEST};
|
2015-09-08 14:54:04 -07:00
|
|
|
}
|
|
|
|
|
2017-05-31 15:51:07 +02:00
|
|
|
@Override
|
2017-07-11 13:50:40 +02:00
|
|
|
public String[] getOptionalPermissions() {
|
|
|
|
return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
2017-05-31 15:51:07 +02:00
|
|
|
}
|
2018-11-25 17:33:12 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onProgress(ShareInfo info, int progress) {
|
|
|
|
if (progress == 0 && currentShareInfo != info) {
|
|
|
|
currentShareInfo = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
shareNotification.setProgress(progress, context.getResources().getQuantityString(R.plurals.incoming_files_text, info.numberOfFiles(), info.fileName, info.currentFileNumber, info.numberOfFiles()));
|
|
|
|
shareNotification.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSuccess(ShareInfo info) {
|
|
|
|
Log.i("SharePlugin", "onSuccess() - Transfer finished for file: " + info.fileDocument.getUri().getPath());
|
|
|
|
|
|
|
|
if (info.shouldOpen) {
|
|
|
|
shareNotification.cancel();
|
|
|
|
|
|
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
|
|
if (Build.VERSION.SDK_INT >= 24) {
|
|
|
|
//Nougat and later require "content://" uris instead of "file://" uris
|
|
|
|
File file = new File(info.fileDocument.getUri().getPath());
|
|
|
|
Uri contentUri = FileProvider.getUriForFile(device.getContext(), "org.kde.kdeconnect_tp.fileprovider", file);
|
|
|
|
intent.setDataAndType(contentUri, info.fileDocument.getType());
|
|
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
|
} else {
|
|
|
|
intent.setDataAndType(info.fileDocument.getUri(), info.fileDocument.getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
context.startActivity(intent);
|
|
|
|
} else {
|
|
|
|
if (!ShareSettingsActivity.isCustomDestinationEnabled(context)) {
|
|
|
|
Log.i("SharePlugin", "Adding to downloads");
|
|
|
|
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
|
|
manager.addCompletedDownload(info.fileDocument.getUri().getLastPathSegment(), device.getName(), true, info.fileDocument.getType(), info.fileDocument.getUri().getPath(), info.fileSize, false);
|
|
|
|
} else {
|
|
|
|
//Make sure it is added to the Android Gallery anyway
|
|
|
|
MediaStoreHelper.indexFile(context, info.fileDocument.getUri());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info.numberOfFiles() == 1 || info.currentFileNumber == info.numberOfFiles()) {
|
|
|
|
finishReceivingRunnable = new FinishReceivingRunnable(info);
|
|
|
|
Log.i("SharePlugin", "onSuccess() - scheduling finishReceivingRunnable");
|
|
|
|
handler.postDelayed(finishReceivingRunnable, 1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onError(ShareInfo info, Throwable error) {
|
|
|
|
Log.e("SharePlugin", "onError: " + error.getMessage());
|
|
|
|
|
|
|
|
info.fileDocument.delete();
|
|
|
|
|
2018-12-02 19:33:27 +01:00
|
|
|
//TODO: Show error in notification
|
2018-11-25 17:33:12 +01:00
|
|
|
int failedFiles = info.numberOfFiles() - (info.currentFileNumber - 1);
|
2018-12-19 22:57:28 +08:00
|
|
|
shareNotification.setFinished(context.getResources().getQuantityString(R.plurals.received_files_fail_title, failedFiles, device.getName(), failedFiles, info.numberOfFiles()));
|
2018-11-25 17:33:12 +01:00
|
|
|
shareNotification.show();
|
|
|
|
shareNotification = null;
|
2018-11-30 16:29:44 +01:00
|
|
|
currentShareInfo = null;
|
2018-11-25 17:33:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private class FinishReceivingRunnable implements Runnable {
|
|
|
|
private final ShareInfo info;
|
|
|
|
|
|
|
|
private FinishReceivingRunnable(ShareInfo info) {
|
|
|
|
this.info = info;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2018-11-30 16:29:44 +01:00
|
|
|
Log.i("SharePlugin", "FinishReceivingRunnable: Finishing up");
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
if (shareNotification != null) {
|
|
|
|
//Update the notification and allow to open the file from it
|
2018-12-19 22:57:28 +08:00
|
|
|
shareNotification.setFinished(context.getResources().getQuantityString(R.plurals.received_files_title, info.numberOfFiles(), device.getName(), info.numberOfFiles()));
|
2018-11-25 17:33:12 +01:00
|
|
|
|
|
|
|
if (info.numberOfFiles() == 1) {
|
|
|
|
shareNotification.setURI(info.fileDocument.getUri(), info.fileDocument.getType(), info.fileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
shareNotification.show();
|
|
|
|
shareNotification = null;
|
|
|
|
}
|
2018-11-30 16:29:44 +01:00
|
|
|
|
|
|
|
finishReceivingRunnable = null;
|
|
|
|
currentShareInfo = null;
|
2018-11-25 17:33:12 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-27 14:44:40 +02:00
|
|
|
}
|