mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-28 11:35:48 +01:00
Add support for picture attachments in notifications
Also adds a local cache for pictures which is handled by the NotificationListener itself. If the picture is hosted in a third party content provider it gets queried. If the Bitmap is embedded in the notification, the local cache is used. In any case the NotificationSpec just holds a String to the absolute file path
This commit is contained in:
parent
7aa7de6463
commit
4aa145560a
@ -25,9 +25,11 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@ -38,6 +40,7 @@ import android.os.Handler;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.service.notification.NotificationListenerService;
|
import android.service.notification.NotificationListenerService;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
|
|
||||||
@ -51,6 +54,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -123,6 +130,17 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
public static final ArrayList<String> notificationStack = new ArrayList<>();
|
public static final ArrayList<String> notificationStack = new ArrayList<>();
|
||||||
private static final ArrayList<Integer> notificationsActive = new ArrayList<>();
|
private static final ArrayList<Integer> notificationsActive = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final Set<String> supportedPictureMimeTypes = new HashSet<String>() {{
|
||||||
|
add("image/"); //for im.vector.app
|
||||||
|
add("image/jpeg");
|
||||||
|
add("image/png");
|
||||||
|
add("image/gif");
|
||||||
|
add("image/bmp");
|
||||||
|
add("image/webp");
|
||||||
|
}};
|
||||||
|
|
||||||
|
private File notificationPictureCacheDirectory;
|
||||||
|
|
||||||
private long activeCallPostTime;
|
private long activeCallPostTime;
|
||||||
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
|
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
|
||||||
|
|
||||||
@ -244,6 +262,8 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
filterLocal.addAction(ACTION_MUTE);
|
filterLocal.addAction(ACTION_MUTE);
|
||||||
filterLocal.addAction(ACTION_REPLY);
|
filterLocal.addAction(ACTION_REPLY);
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
|
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
|
||||||
|
createNotificationPictureCacheDirectory();
|
||||||
|
cleanUpNotificationPictureProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -251,6 +271,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
||||||
notificationStack.clear();
|
notificationStack.clear();
|
||||||
notificationsActive.clear();
|
notificationsActive.clear();
|
||||||
|
cleanUpNotificationPictureProvider();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,6 +695,41 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
notificationSpec.body = sanitizeUnicode(contentCS.toString());
|
notificationSpec.body = sanitizeUnicode(contentCS.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotificationCompat.MessagingStyle messagingStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification);
|
||||||
|
if (messagingStyle != null) {
|
||||||
|
List<NotificationCompat.MessagingStyle.Message> messages = messagingStyle.getMessages();
|
||||||
|
if (!messages.isEmpty()) {
|
||||||
|
// Get the last message (assumed to be the most recent)
|
||||||
|
NotificationCompat.MessagingStyle.Message lastMessage = messages.get(messages.size() - 1);
|
||||||
|
|
||||||
|
if (supportedPictureMimeTypes.contains(lastMessage.getDataMimeType())) {
|
||||||
|
ContentResolver contentResolver = getContentResolver();
|
||||||
|
try (Cursor cursor = contentResolver.query(lastMessage.getDataUri(), null, null, null, null)) {
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
int dataIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
|
||||||
|
notificationSpec.picturePath = cursor.getString(dataIndex);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extras.containsKey(NotificationCompat.EXTRA_PICTURE)) {
|
||||||
|
final Bitmap bmp = (Bitmap) extras.get(NotificationCompat.EXTRA_PICTURE);
|
||||||
|
File pictureFile = new File(this.notificationPictureCacheDirectory, String.valueOf(notificationSpec.getId()));
|
||||||
|
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(pictureFile)) {
|
||||||
|
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
notificationSpec.picturePath = pictureFile.getAbsolutePath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed to save picture to notification cache: {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
bmp.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (notificationSpec.type == NotificationType.COL_REMINDER
|
if (notificationSpec.type == NotificationType.COL_REMINDER
|
||||||
&& notificationSpec.body == null
|
&& notificationSpec.body == null
|
||||||
&& notificationSpec.title != null) {
|
&& notificationSpec.title != null) {
|
||||||
@ -792,6 +848,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
for (int notificationId : notificationsActive) {
|
for (int notificationId : notificationsActive) {
|
||||||
if (!activeNotificationsIds.contains(notificationId)) {
|
if (!activeNotificationsIds.contains(notificationId)) {
|
||||||
notificationsToRemove.add(notificationId);
|
notificationsToRemove.add(notificationId);
|
||||||
|
deleteNotificationPicture(notificationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -815,6 +872,27 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void deleteNotificationPicture(int notificationId) {
|
||||||
|
File pictureFile = new File(this.notificationPictureCacheDirectory, String.valueOf(notificationId));
|
||||||
|
if (pictureFile.exists())
|
||||||
|
pictureFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanUpNotificationPictureProvider() {
|
||||||
|
File[] pictureFiles = this.notificationPictureCacheDirectory.listFiles();
|
||||||
|
if (pictureFiles == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (File pictureFile : pictureFiles) {
|
||||||
|
pictureFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNotificationPictureCacheDirectory() {
|
||||||
|
final File cacheDir = getApplicationContext().getExternalCacheDir();
|
||||||
|
this.notificationPictureCacheDirectory = new File(cacheDir, "notification-pictures");
|
||||||
|
this.notificationPictureCacheDirectory.mkdir();
|
||||||
|
}
|
||||||
private void logNotification(StatusBarNotification sbn, boolean posted) {
|
private void logNotification(StatusBarNotification sbn, boolean posted) {
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Notification {} {}: packageName={}, when={}, priority={}, category={}",
|
"Notification {} {}: packageName={}, when={}, priority={}, category={}",
|
||||||
|
@ -179,6 +179,7 @@ public class GBDeviceService implements DeviceService {
|
|||||||
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor)
|
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor)
|
||||||
.putExtra(EXTRA_NOTIFICATION_SOURCEAPPID, notificationSpec.sourceAppId)
|
.putExtra(EXTRA_NOTIFICATION_SOURCEAPPID, notificationSpec.sourceAppId)
|
||||||
.putExtra(EXTRA_NOTIFICATION_ICONID, notificationSpec.iconId)
|
.putExtra(EXTRA_NOTIFICATION_ICONID, notificationSpec.iconId)
|
||||||
|
.putExtra(NOTIFICATION_PICTURE_PATH, notificationSpec.picturePath)
|
||||||
.putExtra(EXTRA_NOTIFICATION_DNDSUPPRESSED, notificationSpec.dndSuppressed);
|
.putExtra(EXTRA_NOTIFICATION_DNDSUPPRESSED, notificationSpec.dndSuppressed);
|
||||||
invokeService(intent);
|
invokeService(intent);
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,7 @@ public interface DeviceService extends EventHandler {
|
|||||||
String EXTRA_NOTIFICATION_ACTIONS = "notification_actions";
|
String EXTRA_NOTIFICATION_ACTIONS = "notification_actions";
|
||||||
String EXTRA_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
|
String EXTRA_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
|
||||||
String EXTRA_NOTIFICATION_ICONID = "notification_iconid";
|
String EXTRA_NOTIFICATION_ICONID = "notification_iconid";
|
||||||
|
String NOTIFICATION_PICTURE_PATH = "notification_picture_path";
|
||||||
String EXTRA_NOTIFICATION_DNDSUPPRESSED = "notification_dndsuppressed";
|
String EXTRA_NOTIFICATION_DNDSUPPRESSED = "notification_dndsuppressed";
|
||||||
String EXTRA_FIND_START = "find_start";
|
String EXTRA_FIND_START = "find_start";
|
||||||
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
||||||
|
@ -48,6 +48,7 @@ public class NotificationSpec {
|
|||||||
*/
|
*/
|
||||||
public int iconId;
|
public int iconId;
|
||||||
|
|
||||||
|
public String picturePath;
|
||||||
/**
|
/**
|
||||||
* The color that should be assigned to this notification when displayed on a Pebble
|
* The color that should be assigned to this notification when displayed on a Pebble
|
||||||
*/
|
*/
|
||||||
|
@ -862,6 +862,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
|||||||
notificationSpec.flags = intentCopy.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
notificationSpec.flags = intentCopy.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
||||||
notificationSpec.sourceAppId = intentCopy.getStringExtra(EXTRA_NOTIFICATION_SOURCEAPPID);
|
notificationSpec.sourceAppId = intentCopy.getStringExtra(EXTRA_NOTIFICATION_SOURCEAPPID);
|
||||||
notificationSpec.iconId = intentCopy.getIntExtra(EXTRA_NOTIFICATION_ICONID, 0);
|
notificationSpec.iconId = intentCopy.getIntExtra(EXTRA_NOTIFICATION_ICONID, 0);
|
||||||
|
notificationSpec.picturePath = intent.getStringExtra(NOTIFICATION_PICTURE_PATH);
|
||||||
notificationSpec.dndSuppressed = intentCopy.getIntExtra(EXTRA_NOTIFICATION_DNDSUPPRESSED, 0);
|
notificationSpec.dndSuppressed = intentCopy.getIntExtra(EXTRA_NOTIFICATION_DNDSUPPRESSED, 0);
|
||||||
|
|
||||||
if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) {
|
if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user