2017-07-11 10:35:53 +02:00
|
|
|
package org.kde.kdeconnect.Plugins.SharePlugin;
|
|
|
|
|
|
|
|
/*
|
2020-08-17 16:17:20 +02:00
|
|
|
* SPDX-FileCopyrightText: 2017 Nicolas Fella <nicolas.fella@gmx.de>
|
2017-07-11 10:35:53 +02:00
|
|
|
*
|
2020-08-17 16:17:20 +02:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
2018-09-29 20:28:47 +02:00
|
|
|
*/
|
2017-07-11 10:35:53 +02:00
|
|
|
|
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.SharedPreferences;
|
2018-03-25 14:31:16 +02:00
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.graphics.BitmapFactory;
|
2017-07-11 10:35:53 +02:00
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.Build;
|
|
|
|
import android.preference.PreferenceManager;
|
|
|
|
|
2020-07-07 16:45:02 +05:30
|
|
|
import androidx.core.app.NotificationCompat;
|
|
|
|
import androidx.core.content.ContextCompat;
|
|
|
|
import androidx.core.content.FileProvider;
|
|
|
|
|
2017-07-11 10:35:53 +02:00
|
|
|
import org.kde.kdeconnect.Device;
|
|
|
|
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
2024-12-29 02:34:10 +01:00
|
|
|
import org.kde.kdeconnect_tp.BuildConfig;
|
2017-07-11 10:35:53 +02:00
|
|
|
import org.kde.kdeconnect_tp.R;
|
|
|
|
|
2017-12-20 17:49:36 +01:00
|
|
|
import java.io.File;
|
2018-10-30 12:30:21 +01:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2017-12-20 17:49:36 +01:00
|
|
|
|
2019-04-07 17:54:12 +00:00
|
|
|
class ReceiveNotification {
|
2018-10-27 00:01:30 +02:00
|
|
|
private final NotificationManager notificationManager;
|
|
|
|
private final int notificationId;
|
2017-07-11 10:35:53 +02:00
|
|
|
private NotificationCompat.Builder builder;
|
2018-10-27 00:01:30 +02:00
|
|
|
private final Device device;
|
2023-03-05 23:47:42 +01:00
|
|
|
private final long jobId;
|
2017-07-11 10:35:53 +02:00
|
|
|
|
2018-11-07 17:40:50 +01:00
|
|
|
//https://documentation.onesignal.com/docs/android-customizations#section-big-picture
|
|
|
|
private static final int bigImageWidth = 1440;
|
|
|
|
private static final int bigImageHeight = 720;
|
|
|
|
|
2019-06-04 12:51:24 +00:00
|
|
|
public ReceiveNotification(Device device, long jobId) {
|
2017-07-11 10:35:53 +02:00
|
|
|
this.device = device;
|
2018-11-25 17:33:12 +01:00
|
|
|
|
2019-06-04 12:51:24 +00:00
|
|
|
this.jobId = jobId;
|
2017-07-11 10:35:53 +02:00
|
|
|
notificationId = (int) System.currentTimeMillis();
|
2020-07-07 16:45:02 +05:30
|
|
|
notificationManager = ContextCompat.getSystemService(device.getContext(), NotificationManager.class);
|
2023-09-12 05:14:37 +02:00
|
|
|
builder = new NotificationCompat.Builder(device.getContext(), NotificationHelper.Channels.FILETRANSFER_DOWNLOAD)
|
2017-07-11 10:35:53 +02:00
|
|
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
|
|
.setAutoCancel(true)
|
|
|
|
.setOngoing(true)
|
|
|
|
.setProgress(100, 0, true);
|
2019-06-04 12:51:24 +00:00
|
|
|
addCancelAction();
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void show() {
|
|
|
|
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
|
|
|
|
}
|
|
|
|
|
2018-11-20 21:49:33 +01:00
|
|
|
public void cancel() {
|
|
|
|
notificationManager.cancel(notificationId);
|
|
|
|
}
|
|
|
|
|
2019-06-04 12:51:24 +00:00
|
|
|
public void addCancelAction() {
|
2019-03-08 19:07:01 +01:00
|
|
|
|
|
|
|
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());
|
2024-10-08 22:12:47 +02:00
|
|
|
PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(device.getContext(), 0, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
2019-03-08 19:07:01 +01:00
|
|
|
|
2020-07-08 15:43:33 +05:30
|
|
|
builder.addAction(R.drawable.ic_reject_pairing_24dp, device.getContext().getString(R.string.cancel), cancelPendingIntent);
|
2019-03-08 19:07:01 +01:00
|
|
|
}
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
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);
|
2019-01-26 17:01:50 +01:00
|
|
|
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(progressMessage));
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
public void setFinished(String message) {
|
2023-09-12 05:14:37 +02:00
|
|
|
builder = new NotificationCompat.Builder(device.getContext(), NotificationHelper.Channels.FILETRANSFER_DOWNLOAD);
|
2017-07-11 10:35:53 +02:00
|
|
|
builder.setContentTitle(message)
|
|
|
|
.setTicker(message)
|
|
|
|
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
|
|
.setAutoCancel(true)
|
|
|
|
.setOngoing(false);
|
|
|
|
|
|
|
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(device.getContext());
|
|
|
|
if (prefs.getBoolean("share_notification_preference", true)) {
|
|
|
|
builder.setDefaults(Notification.DEFAULT_ALL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 05:06:30 +02:00
|
|
|
public void setFailed(String message) {
|
|
|
|
setFinished(message);
|
2023-09-12 05:43:33 +02:00
|
|
|
builder.setSmallIcon(android.R.drawable.stat_notify_error)
|
|
|
|
.setChannelId(NotificationHelper.Channels.FILETRANSFER_ERROR);
|
|
|
|
|
2023-09-12 05:06:30 +02:00
|
|
|
}
|
|
|
|
|
2018-11-25 17:33:12 +01:00
|
|
|
public void setURI(Uri destinationUri, String mimeType, String filename) {
|
2017-12-20 17:49:36 +01:00
|
|
|
/*
|
|
|
|
* We only support file URIs (because sending a content uri to another app does not work for security reasons).
|
|
|
|
* In effect, that means only the default download folder currently works.
|
|
|
|
*
|
|
|
|
* TODO: implement our own content provider (instead of support-v4's FileProvider). It should:
|
|
|
|
* - Proxy to real files (in case of the default download folder)
|
|
|
|
* - Proxy to the underlying content uri (in case of a custom download folder)
|
|
|
|
*/
|
2018-03-25 14:31:16 +02:00
|
|
|
|
|
|
|
//If it's an image, try to show it in the notification
|
|
|
|
if (mimeType.startsWith("image/")) {
|
2018-11-07 17:40:50 +01:00
|
|
|
//https://developer.android.com/topic/performance/graphics/load-bitmap
|
|
|
|
final BitmapFactory.Options options = new BitmapFactory.Options();
|
|
|
|
options.inJustDecodeBounds = true;
|
|
|
|
|
|
|
|
try (InputStream decodeBoundsInputStream = device.getContext().getContentResolver().openInputStream(destinationUri);
|
|
|
|
InputStream decodeInputStream = device.getContext().getContentResolver().openInputStream(destinationUri)) {
|
|
|
|
BitmapFactory.decodeStream(decodeBoundsInputStream, null, options);
|
|
|
|
|
|
|
|
options.inJustDecodeBounds = false;
|
|
|
|
options.inSampleSize = calculateInSampleSize(options, bigImageWidth, bigImageHeight);
|
|
|
|
|
|
|
|
Bitmap image = BitmapFactory.decodeStream(decodeInputStream, null, options);
|
2018-03-25 14:31:16 +02:00
|
|
|
if (image != null) {
|
|
|
|
builder.setLargeIcon(image);
|
|
|
|
builder.setStyle(new NotificationCompat.BigPictureStyle()
|
2018-09-29 20:28:47 +02:00
|
|
|
.bigPicture(image));
|
2018-03-25 14:31:16 +02:00
|
|
|
}
|
2018-10-30 12:30:21 +01:00
|
|
|
} catch (IOException ignored) {
|
2018-09-29 20:28:47 +02:00
|
|
|
}
|
2018-03-25 14:31:16 +02:00
|
|
|
}
|
2018-11-07 17:40:50 +01:00
|
|
|
|
2017-12-20 17:49:36 +01:00
|
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
2018-03-25 14:20:12 +02:00
|
|
|
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
|
|
|
shareIntent.setType(mimeType);
|
2023-03-15 22:01:39 +00:00
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && "file".equals(destinationUri.getScheme())) {
|
2017-12-20 17:49:36 +01:00
|
|
|
//Nougat and later require "content://" uris instead of "file://" uris
|
|
|
|
File file = new File(destinationUri.getPath());
|
2024-12-29 02:34:10 +01:00
|
|
|
Uri contentUri = FileProvider.getUriForFile(device.getContext(), BuildConfig.APPLICATION_ID+".fileprovider", file);
|
2017-12-20 17:49:36 +01:00
|
|
|
intent.setDataAndType(contentUri, mimeType);
|
|
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
2018-03-25 14:20:12 +02:00
|
|
|
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
2017-12-20 17:49:36 +01:00
|
|
|
} else {
|
2017-07-11 10:35:53 +02:00
|
|
|
intent.setDataAndType(destinationUri, mimeType);
|
2018-03-25 14:20:12 +02:00
|
|
|
shareIntent.putExtra(Intent.EXTRA_STREAM, destinationUri);
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|
2017-12-20 17:49:36 +01:00
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
2018-03-25 14:20:12 +02:00
|
|
|
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
2018-10-28 20:27:52 +01:00
|
|
|
|
|
|
|
PendingIntent resultPendingIntent = PendingIntent.getActivity(
|
|
|
|
device.getContext(),
|
|
|
|
0,
|
|
|
|
intent,
|
2024-10-08 15:48:23 +00:00
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
2018-10-28 20:27:52 +01:00
|
|
|
);
|
|
|
|
|
2017-12-20 17:49:36 +01:00
|
|
|
builder.setContentText(device.getContext().getResources().getString(R.string.received_file_text, filename))
|
|
|
|
.setContentIntent(resultPendingIntent);
|
2018-03-25 14:20:12 +02:00
|
|
|
|
|
|
|
shareIntent = Intent.createChooser(shareIntent,
|
|
|
|
device.getContext().getString(R.string.share_received_file, destinationUri.getLastPathSegment()));
|
2018-11-06 21:49:37 +01:00
|
|
|
PendingIntent sharePendingIntent = PendingIntent.getActivity(device.getContext(), (int) System.currentTimeMillis(),
|
2024-10-08 15:48:23 +00:00
|
|
|
shareIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
2018-03-25 14:20:12 +02:00
|
|
|
NotificationCompat.Action.Builder shareAction = new NotificationCompat.Action.Builder(
|
|
|
|
R.drawable.ic_share_white, device.getContext().getString(R.string.share), sharePendingIntent);
|
|
|
|
builder.addAction(shareAction.build());
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|
2018-11-07 17:40:50 +01:00
|
|
|
|
|
|
|
private int calculateInSampleSize(BitmapFactory.Options options, int targetWidth, int targetHeight) {
|
|
|
|
int inSampleSize = 1;
|
|
|
|
|
|
|
|
if (options.outHeight > targetHeight || options.outWidth > targetWidth) {
|
|
|
|
final int halfHeight = options.outHeight / 2;
|
|
|
|
final int halfWidth = options.outWidth / 2;
|
|
|
|
|
|
|
|
while ((halfHeight / inSampleSize) >= targetHeight
|
|
|
|
&& (halfWidth / inSampleSize) >= targetWidth) {
|
|
|
|
inSampleSize *= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return inSampleSize;
|
|
|
|
}
|
2017-07-11 10:35:53 +02:00
|
|
|
}
|