Add support for dynamic Pebble background colors (#819)
Pebble: Add support for dynamic Pebble background colors - Add a couple additional icon types - Add Lighthouse (currently unused) - Add Transit (public transportation app) - Tweak the colors on existing icon types - Implement logic to grab primary (vibrant) color from app logo - The color will be used when displaying a notification for an app that does not have any configs bound to it. - Alter NotificationType to support a color (named pebbleColor) - Alter the Pebble notification poster to listen to the color from the notification - Alter the DeviceCommunicationService to allow for color passthrough. - Add logic to convert HEX or Integer representations of RGB888 colors to Pebble RGB222 format. - make the package name retrieved lowercase. Fixes: #815master
parent
67584be314
commit
6ec1555178
|
@ -70,6 +70,7 @@ dependencies {
|
||||||
compile 'com.android.support:support-v4:25.3.1'
|
compile 'com.android.support:support-v4:25.3.1'
|
||||||
compile 'com.android.support:gridlayout-v7:25.3.1'
|
compile 'com.android.support:gridlayout-v7:25.3.1'
|
||||||
compile 'com.android.support:design:25.3.1'
|
compile 'com.android.support:design:25.3.1'
|
||||||
|
compile 'com.android.support:palette-v7:25.3.1'
|
||||||
compile 'com.github.tony19:logback-android-classic:1.1.1-6'
|
compile 'com.github.tony19:logback-android-classic:1.1.1-6'
|
||||||
compile 'org.slf4j:slf4j-api:1.7.7'
|
compile 'org.slf4j:slf4j-api:1.7.7'
|
||||||
compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
|
compile 'com.github.PhilJay:MPAndroidChart:v3.0.2'
|
||||||
|
|
|
@ -126,6 +126,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||||
notificationSpec.sender = testString;
|
notificationSpec.sender = testString;
|
||||||
notificationSpec.subject = testString;
|
notificationSpec.subject = testString;
|
||||||
notificationSpec.type = NotificationType.values()[sendTypeSpinner.getSelectedItemPosition()];
|
notificationSpec.type = NotificationType.values()[sendTypeSpinner.getSelectedItemPosition()];
|
||||||
|
notificationSpec.pebbleColor = notificationSpec.type.color;
|
||||||
notificationSpec.id = -1;
|
notificationSpec.id = -1;
|
||||||
GBApplication.deviceService().onNotification(notificationSpec);
|
GBApplication.deviceService().onNotification(notificationSpec);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.media.MediaMetadata;
|
import android.media.MediaMetadata;
|
||||||
import android.media.session.PlaybackState;
|
import android.media.session.PlaybackState;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -42,21 +45,26 @@ import android.support.v4.media.session.MediaControllerCompat;
|
||||||
import android.support.v4.media.session.MediaSessionCompat;
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import android.support.v7.app.NotificationCompat;
|
import android.support.v7.app.NotificationCompat;
|
||||||
|
import android.support.v7.graphics.Palette;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType;
|
import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||||
|
|
||||||
public class NotificationListener extends NotificationListenerService {
|
public class NotificationListener extends NotificationListenerService {
|
||||||
|
@ -192,7 +200,7 @@ public class NotificationListener extends NotificationListenerService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String source = sbn.getPackageName();
|
String source = sbn.getPackageName().toLowerCase();
|
||||||
Notification notification = sbn.getNotification();
|
Notification notification = sbn.getNotification();
|
||||||
NotificationSpec notificationSpec = new NotificationSpec();
|
NotificationSpec notificationSpec = new NotificationSpec();
|
||||||
notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better
|
notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better
|
||||||
|
@ -211,6 +219,9 @@ public class NotificationListener extends NotificationListenerService {
|
||||||
|
|
||||||
boolean preferBigText = false;
|
boolean preferBigText = false;
|
||||||
|
|
||||||
|
// Get the app ID that generated this notification. For now only used by pebble color, but may be more useful later.
|
||||||
|
notificationSpec.sourceAppId = source;
|
||||||
|
|
||||||
notificationSpec.type = AppNotificationType.getInstance().get(source);
|
notificationSpec.type = AppNotificationType.getInstance().get(source);
|
||||||
|
|
||||||
if (source.startsWith("com.fsck.k9")) {
|
if (source.startsWith("com.fsck.k9")) {
|
||||||
|
@ -221,6 +232,9 @@ public class NotificationListener extends NotificationListenerService {
|
||||||
notificationSpec.type = NotificationType.UNKNOWN;
|
notificationSpec.type = NotificationType.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get color
|
||||||
|
notificationSpec.pebbleColor = getPebbleColorForNotification(notificationSpec);
|
||||||
|
|
||||||
LOG.info("Processing notification " + notificationSpec.id + " from source " + source + " with flags: " + notification.flags);
|
LOG.info("Processing notification " + notificationSpec.id + " from source " + source + " with flags: " + notification.flags);
|
||||||
|
|
||||||
dissectNotificationTo(notification, notificationSpec, preferBigText);
|
dissectNotificationTo(notification, notificationSpec, preferBigText);
|
||||||
|
@ -379,13 +393,9 @@ public class NotificationListener extends NotificationListenerService {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldIgnoreSource(sbn.getPackageName()))
|
return shouldIgnoreSource(sbn.getPackageName()) || shouldIgnoreNotification(
|
||||||
return true;
|
sbn.getNotification());
|
||||||
|
|
||||||
if (shouldIgnoreNotification(sbn.getNotification()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldIgnoreSource(String source) {
|
private boolean shouldIgnoreSource(String source) {
|
||||||
|
@ -442,12 +452,46 @@ public class NotificationListener extends NotificationListenerService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) {
|
return (notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT;
|
||||||
// LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification color that should be used for this Pebble notification.
|
||||||
|
*
|
||||||
|
* Note that this method will *not* edit the NotificationSpec passed in. It will only evaluate the PebbleColor.
|
||||||
|
*
|
||||||
|
* See Issue #815 on GitHub to see how notification colors are set.
|
||||||
|
*
|
||||||
|
* @param notificationSpec The NotificationSpec to read from.
|
||||||
|
* @return Returns a PebbleColor that best represents this notification.
|
||||||
|
*/
|
||||||
|
private byte getPebbleColorForNotification(NotificationSpec notificationSpec) {
|
||||||
|
String appId = notificationSpec.sourceAppId;
|
||||||
|
NotificationType existingType = notificationSpec.type;
|
||||||
|
|
||||||
|
// If the notification type is known, return the associated color.
|
||||||
|
if (existingType != NotificationType.UNKNOWN) {
|
||||||
|
return existingType.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we go and attempt to find the color from the app icon.
|
||||||
|
Drawable icon;
|
||||||
|
try {
|
||||||
|
icon = getApplicationContext().getPackageManager().getApplicationIcon(appId);
|
||||||
|
Objects.requireNonNull(icon);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// If we can't get the icon, we go with the default defined above.
|
||||||
|
LOG.warn("Could not get icon for AppID " + appId, ex);
|
||||||
|
return PebbleColor.IslamicGreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmapIcon = BitmapUtil.convertDrawableToBitmap(icon);
|
||||||
|
int iconPrimaryColor = new Palette.Builder(bitmapIcon)
|
||||||
|
.generate()
|
||||||
|
.getVibrantColor(Color.parseColor("#aa0000"));
|
||||||
|
|
||||||
|
return PebbleUtils.getPebbleColor(iconPrimaryColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -141,7 +141,8 @@ public class GBDeviceService implements DeviceService {
|
||||||
.putExtra(EXTRA_NOTIFICATION_BODY, notificationSpec.body)
|
.putExtra(EXTRA_NOTIFICATION_BODY, notificationSpec.body)
|
||||||
.putExtra(EXTRA_NOTIFICATION_ID, notificationSpec.id)
|
.putExtra(EXTRA_NOTIFICATION_ID, notificationSpec.id)
|
||||||
.putExtra(EXTRA_NOTIFICATION_TYPE, notificationSpec.type)
|
.putExtra(EXTRA_NOTIFICATION_TYPE, notificationSpec.type)
|
||||||
.putExtra(EXTRA_NOTIFICATION_SOURCENAME, notificationSpec.sourceName);
|
.putExtra(EXTRA_NOTIFICATION_SOURCENAME, notificationSpec.sourceName)
|
||||||
|
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor);
|
||||||
invokeService(intent);
|
invokeService(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,9 @@ public class AppNotificationType extends HashMap<String, NotificationType> {
|
||||||
|
|
||||||
// Slack
|
// Slack
|
||||||
put("com.slack", NotificationType.SLACK);
|
put("com.slack", NotificationType.SLACK);
|
||||||
|
|
||||||
|
// Transit
|
||||||
|
put("com.thetransitapp.droid", NotificationType.TRANSIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ public interface DeviceService extends EventHandler {
|
||||||
String EXTRA_NOTIFICATION_SUBJECT = "notification_subject";
|
String EXTRA_NOTIFICATION_SUBJECT = "notification_subject";
|
||||||
String EXTRA_NOTIFICATION_TITLE = "notification_title";
|
String EXTRA_NOTIFICATION_TITLE = "notification_title";
|
||||||
String EXTRA_NOTIFICATION_TYPE = "notification_type";
|
String EXTRA_NOTIFICATION_TYPE = "notification_type";
|
||||||
|
String EXTRA_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
|
||||||
String EXTRA_FIND_START = "find_start";
|
String EXTRA_FIND_START = "find_start";
|
||||||
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
||||||
String EXTRA_CALL_COMMAND = "call_command";
|
String EXTRA_CALL_COMMAND = "call_command";
|
||||||
|
|
|
@ -29,4 +29,14 @@ public class NotificationSpec {
|
||||||
public NotificationType type;
|
public NotificationType type;
|
||||||
public String sourceName;
|
public String sourceName;
|
||||||
public String[] cannedReplies;
|
public String[] cannedReplies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The application that generated the notification.
|
||||||
|
*/
|
||||||
|
public String sourceAppId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color that should be assigned to this notification when displayed on a Pebble
|
||||||
|
*/
|
||||||
|
public byte pebbleColor;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID;
|
||||||
public enum NotificationType {
|
public enum NotificationType {
|
||||||
|
|
||||||
// TODO: this this pebbleism needs to be moved somewhere else
|
// TODO: this this pebbleism needs to be moved somewhere else
|
||||||
UNKNOWN(PebbleIconID.NOTIFICATION_GENERIC, PebbleColor.Red),
|
UNKNOWN(PebbleIconID.NOTIFICATION_GENERIC, PebbleColor.DarkCandyAppleRed),
|
||||||
|
|
||||||
AMAZON(PebbleIconID.NOTIFICATION_AMAZON, PebbleColor.ChromeYellow),
|
AMAZON(PebbleIconID.NOTIFICATION_AMAZON, PebbleColor.ChromeYellow),
|
||||||
BBM(PebbleIconID.NOTIFICATION_BLACKBERRY_MESSENGER, PebbleColor.DarkGray),
|
BBM(PebbleIconID.NOTIFICATION_BLACKBERRY_MESSENGER, PebbleColor.DarkGray),
|
||||||
|
@ -46,6 +46,7 @@ public enum NotificationType {
|
||||||
INSTAGRAM(PebbleIconID.NOTIFICATION_INSTAGRAM, PebbleColor.CobaltBlue),
|
INSTAGRAM(PebbleIconID.NOTIFICATION_INSTAGRAM, PebbleColor.CobaltBlue),
|
||||||
KAKAO_TALK(PebbleIconID.NOTIFICATION_KAKAOTALK, PebbleColor.Yellow),
|
KAKAO_TALK(PebbleIconID.NOTIFICATION_KAKAOTALK, PebbleColor.Yellow),
|
||||||
KIK(PebbleIconID.NOTIFICATION_KIK, PebbleColor.IslamicGreen),
|
KIK(PebbleIconID.NOTIFICATION_KIK, PebbleColor.IslamicGreen),
|
||||||
|
LIGHTHOUSE(PebbleIconID.NOTIFICATION_LIGHTHOUSE, PebbleColor.PictonBlue), // ??? - No idea what this is, but it works.
|
||||||
LINE(PebbleIconID.NOTIFICATION_LINE, PebbleColor.IslamicGreen),
|
LINE(PebbleIconID.NOTIFICATION_LINE, PebbleColor.IslamicGreen),
|
||||||
LINKEDIN(PebbleIconID.NOTIFICATION_LINKEDIN, PebbleColor.CobaltBlue),
|
LINKEDIN(PebbleIconID.NOTIFICATION_LINKEDIN, PebbleColor.CobaltBlue),
|
||||||
MAILBOX(PebbleIconID.NOTIFICATION_MAILBOX, PebbleColor.VividCerulean),
|
MAILBOX(PebbleIconID.NOTIFICATION_MAILBOX, PebbleColor.VividCerulean),
|
||||||
|
@ -56,6 +57,7 @@ public enum NotificationType {
|
||||||
SLACK(PebbleIconID.NOTIFICATION_SLACK, PebbleColor.Folly),
|
SLACK(PebbleIconID.NOTIFICATION_SLACK, PebbleColor.Folly),
|
||||||
SNAPCHAT(PebbleIconID.NOTIFICATION_SNAPCHAT, PebbleColor.Icterine),
|
SNAPCHAT(PebbleIconID.NOTIFICATION_SNAPCHAT, PebbleColor.Icterine),
|
||||||
TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.VividCerulean),
|
TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.VividCerulean),
|
||||||
|
TRANSIT(PebbleIconID.LOCATION, PebbleColor.JaegerGreen),
|
||||||
TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon),
|
TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon),
|
||||||
VIBER(PebbleIconID.NOTIFICATION_VIBER, PebbleColor.VividViolet),
|
VIBER(PebbleIconID.NOTIFICATION_VIBER, PebbleColor.VividViolet),
|
||||||
WECHAT(PebbleIconID.NOTIFICATION_WECHAT, PebbleColor.KellyGreen),
|
WECHAT(PebbleIconID.NOTIFICATION_WECHAT, PebbleColor.KellyGreen),
|
||||||
|
|
|
@ -137,6 +137,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUS
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_FLAGS;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_FLAGS;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_ID;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_ID;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PEBBLE_COLOR;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PHONENUMBER;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PHONENUMBER;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SENDER;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SENDER;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SOURCENAME;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SOURCENAME;
|
||||||
|
@ -342,6 +343,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||||
notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
||||||
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
|
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
|
||||||
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
|
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
|
||||||
|
notificationSpec.pebbleColor = (byte) intent.getSerializableExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR);
|
||||||
notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||||
notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
||||||
|
|
||||||
|
|
|
@ -496,10 +496,13 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||||
|
|
||||||
if (mFwMajor >= 3) {
|
if (mFwMajor >= 3) {
|
||||||
// 3.x notification
|
// 3.x notification
|
||||||
return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.type, notificationSpec.cannedReplies);
|
return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body,
|
||||||
|
notificationSpec.sourceName, hasHandle, notificationSpec.type, notificationSpec.pebbleColor,
|
||||||
|
notificationSpec.cannedReplies);
|
||||||
} else if (mForceProtocol || notificationSpec.type != NotificationType.GENERIC_EMAIL) {
|
} else if (mForceProtocol || notificationSpec.type != NotificationType.GENERIC_EMAIL) {
|
||||||
// 2.x notification
|
// 2.x notification
|
||||||
return encodeExtensibleNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies);
|
return encodeExtensibleNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body,
|
||||||
|
notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies);
|
||||||
} else {
|
} else {
|
||||||
// 1.x notification on FW 2.X
|
// 1.x notification on FW 2.X
|
||||||
String[] parts = {title, notificationSpec.body, ts.toString(), subtitle};
|
String[] parts = {title, notificationSpec.body, ts.toString(), subtitle};
|
||||||
|
@ -920,7 +923,9 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||||
return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array());
|
return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, NotificationType notificationType, String[] cannedReplies) {
|
private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName,
|
||||||
|
boolean hasHandle, NotificationType notificationType, byte backgroundColor,
|
||||||
|
String[] cannedReplies) {
|
||||||
final short NOTIFICATION_PIN_LENGTH = 46;
|
final short NOTIFICATION_PIN_LENGTH = 46;
|
||||||
final short ACTION_LENGTH_MIN = 10;
|
final short ACTION_LENGTH_MIN = 10;
|
||||||
|
|
||||||
|
@ -931,7 +936,6 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
int icon_id = notificationType.icon;
|
int icon_id = notificationType.icon;
|
||||||
byte color_id = notificationType.color;
|
|
||||||
|
|
||||||
// Calculate length first
|
// Calculate length first
|
||||||
byte actions_count;
|
byte actions_count;
|
||||||
|
@ -1021,7 +1025,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||||
|
|
||||||
buf.put((byte) 28); // background_color
|
buf.put((byte) 28); // background_color
|
||||||
buf.putShort((short) 1); // length of int
|
buf.putShort((short) 1); // length of int
|
||||||
buf.put(color_id);
|
buf.put(backgroundColor);
|
||||||
|
|
||||||
// dismiss action
|
// dismiss action
|
||||||
buf.put(dismiss_action_id);
|
buf.put(dismiss_action_id);
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
public class BitmapUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Bitmap from any given Drawable.
|
||||||
|
*
|
||||||
|
* Note that this code will fail if the drawable is 0x0.
|
||||||
|
*
|
||||||
|
* @param drawable A Drawable to convert.
|
||||||
|
* @return A Bitmap representing the drawable.
|
||||||
|
*/
|
||||||
|
public static Bitmap convertDrawableToBitmap(Drawable drawable) {
|
||||||
|
// If whoever made this drawable decided to be nice to us...
|
||||||
|
if (drawable instanceof BitmapDrawable) {
|
||||||
|
return ((BitmapDrawable) drawable).getBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
|
||||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
drawable.draw(canvas);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.util;
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
public class PebbleUtils {
|
public class PebbleUtils {
|
||||||
public static String getPlatformName(String hwRev) {
|
public static String getPlatformName(String hwRev) {
|
||||||
String platformName;
|
String platformName;
|
||||||
|
@ -63,4 +65,29 @@ public class PebbleUtils {
|
||||||
String platformName = getPlatformName(hwRev);
|
String platformName = getPlatformName(hwRev);
|
||||||
return !"aplite".equals(platformName);
|
return !"aplite".equals(platformName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the closest Pebble-compatible color from the associated Android Color Integer.
|
||||||
|
* @param color An Android Color Integer to convert
|
||||||
|
* @return A byte representing the closest Pebble color.
|
||||||
|
*/
|
||||||
|
public static byte getPebbleColor(int color) {
|
||||||
|
// 85 here is determined by dividing 255 by 3, or reducing an 8-bit color to a 2-bit color. (2^3 = 8)
|
||||||
|
|
||||||
|
int colorRed = ((color >> 16) & 0xFF) / 85;
|
||||||
|
int colorGreen = ((color >> 8) & 0xFF) / 85;
|
||||||
|
int colorBlue = (color & 0xFF) / 85;
|
||||||
|
|
||||||
|
// Bit shifting, woo!
|
||||||
|
return (byte) ((0b11 << 6) | ((colorRed & 0b11) << 4) | ((colorGreen & 0b11) << 2) | (colorBlue & 0b11));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the closest Pebble-compatible color from the associated Hex string.
|
||||||
|
* @param colorHex A Hex-formatted string (#FFDD00) to convert.
|
||||||
|
* @return A byte representing the closest Pebble color.
|
||||||
|
*/
|
||||||
|
public static byte getPebbleColor(String colorHex) {
|
||||||
|
return getPebbleColor(Color.parseColor(colorHex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.test;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class PebbleUtilsTest extends TestBase {
|
||||||
|
@Test
|
||||||
|
public void testHexToPebbleColorConversion() {
|
||||||
|
Map<String, Byte> testCases = new HashMap<>();
|
||||||
|
|
||||||
|
testCases.put("#000000", PebbleColor.Black);
|
||||||
|
testCases.put("#ffffff", PebbleColor.White);
|
||||||
|
testCases.put("#00ff00", PebbleColor.Green);
|
||||||
|
|
||||||
|
testCases.put("#452435", PebbleColor.Black);
|
||||||
|
testCases.put("#334afd", PebbleColor.DukeBlue);
|
||||||
|
testCases.put("#ccb75c", PebbleColor.Brass);
|
||||||
|
testCases.put("#1b1c94", PebbleColor.OxfordBlue);
|
||||||
|
testCases.put("#90f892", PebbleColor.MayGreen);
|
||||||
|
testCases.put("#ff7301", PebbleColor.Orange);
|
||||||
|
|
||||||
|
testCases.put("#00aa00", PebbleColor.IslamicGreen);
|
||||||
|
|
||||||
|
for (String colorKey : testCases.keySet()) {
|
||||||
|
byte evaluatedColor = PebbleUtils.getPebbleColor(colorKey);
|
||||||
|
assertEquals("Color " + colorKey + " failed to translate properly!",
|
||||||
|
testCases.get(colorKey).byteValue(), evaluatedColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntToPebbleColorConversion() {
|
||||||
|
Map<Integer, Byte> testCases = new HashMap<>();
|
||||||
|
|
||||||
|
testCases.put(0x000000, PebbleColor.Black);
|
||||||
|
testCases.put(0xffffff, PebbleColor.White);
|
||||||
|
testCases.put(0x00ff00, PebbleColor.Green);
|
||||||
|
|
||||||
|
testCases.put(0x00aa00, PebbleColor.IslamicGreen);
|
||||||
|
|
||||||
|
for (int colorKey : testCases.keySet()) {
|
||||||
|
byte evaluatedColor = PebbleUtils.getPebbleColor(colorKey);
|
||||||
|
assertEquals("Color " + Integer.toHexString(colorKey) + " failed to translate properly!",
|
||||||
|
testCases.get(colorKey).byteValue(), evaluatedColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue