2015-08-03 23:51:53 +02:00
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble ;
2015-01-07 14:00:18 +01:00
2015-09-13 18:37:59 +02:00
import android.util.Base64 ;
2015-05-11 23:30:38 +02:00
import android.util.Pair ;
2015-03-07 17:44:39 +01:00
2015-09-13 18:20:15 +02:00
import org.json.JSONArray ;
import org.json.JSONException ;
import org.json.JSONObject ;
2015-05-12 11:06:22 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2015-01-07 14:00:18 +01:00
import java.nio.ByteBuffer ;
import java.nio.ByteOrder ;
2015-05-11 23:30:38 +02:00
import java.util.ArrayList ;
2015-05-21 18:17:39 +02:00
import java.util.Arrays ;
2015-10-04 15:53:11 +02:00
import java.util.HashMap ;
import java.util.Map ;
2015-05-15 21:34:38 +02:00
import java.util.Random ;
2015-01-07 14:00:18 +01:00
import java.util.SimpleTimeZone ;
2015-05-12 11:06:22 +02:00
import java.util.UUID ;
2015-01-07 14:00:18 +01:00
2015-06-23 11:54:33 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent ;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo ;
2015-08-16 00:32:36 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement ;
2015-09-17 19:21:22 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppMessage ;
2015-06-23 11:54:33 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl ;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl ;
2015-08-31 22:27:25 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl ;
2015-06-24 23:55:51 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot ;
2015-06-23 11:54:33 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes ;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo ;
2015-09-13 18:20:15 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor ;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID ;
2015-08-09 21:42:27 +02:00
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp ;
2015-09-24 14:45:21 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec ;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType ;
2015-09-13 18:20:15 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand ;
2015-08-18 01:26:15 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol ;
2015-03-26 12:06:26 +01:00
2015-04-01 18:34:52 +02:00
public class PebbleProtocol extends GBDeviceProtocol {
2015-03-07 17:44:39 +01:00
2015-05-12 06:28:11 +02:00
private static final Logger LOG = LoggerFactory . getLogger ( PebbleProtocol . class ) ;
2015-03-07 17:44:39 +01:00
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_TIME = 11 ;
static final short ENDPOINT_FIRMWAREVERSION = 16 ;
2015-03-26 12:06:26 +01:00
public static final short ENDPOINT_PHONEVERSION = 17 ;
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_SYSTEMMESSAGE = 18 ;
static final short ENDPOINT_MUSICCONTROL = 32 ;
static final short ENDPOINT_PHONECONTROL = 33 ;
static final short ENDPOINT_APPLICATIONMESSAGE = 48 ;
static final short ENDPOINT_LAUNCHER = 49 ;
2015-08-21 16:06:23 +02:00
static final short ENDPOINT_APPRUNSTATE = 52 ; // 3.x only
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_LOGS = 2000 ;
static final short ENDPOINT_PING = 2001 ;
static final short ENDPOINT_LOGDUMP = 2002 ;
static final short ENDPOINT_RESET = 2003 ;
static final short ENDPOINT_APP = 2004 ;
static final short ENDPOINT_APPLOGS = 2006 ;
static final short ENDPOINT_NOTIFICATION = 3000 ;
2015-05-15 21:34:38 +02:00
static final short ENDPOINT_EXTENSIBLENOTIFS = 3010 ;
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_RESOURCE = 4000 ;
static final short ENDPOINT_SYSREG = 5000 ;
static final short ENDPOINT_FCTREG = 5001 ;
static final short ENDPOINT_APPMANAGER = 6000 ;
2015-08-11 13:21:29 +02:00
static final short ENDPOINT_APPFETCH = 6001 ; // 3.x only
2015-03-26 12:06:26 +01:00
public static final short ENDPOINT_DATALOG = 6778 ;
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_RUNKEEPER = 7000 ;
static final short ENDPOINT_SCREENSHOT = 8000 ;
2015-07-22 20:53:18 +02:00
static final short ENDPOINT_NOTIFICATIONACTION = 11440 ; // 3.x only, TODO: find a better name
2015-06-16 23:14:51 +02:00
static final short ENDPOINT_BLOBDB = ( short ) 45531 ; // 3.x only
2015-01-07 14:00:18 +01:00
static final short ENDPOINT_PUTBYTES = ( short ) 48879 ;
2015-08-11 13:55:35 +02:00
static final byte APPRUNSTATE_START = 1 ;
2015-09-13 21:44:26 +02:00
static final byte APPRUNSTATE_STOP = 2 ;
2015-08-11 13:55:35 +02:00
2015-08-14 12:50:44 +02:00
static final byte BLOBDB_INSERT = 1 ;
static final byte BLOBDB_DELETE = 4 ;
static final byte BLOBDB_CLEAR = 5 ;
static final byte BLOBDB_PIN = 1 ;
static final byte BLOBDB_APP = 2 ;
static final byte BLOBDB_REMINDER = 3 ;
static final byte BLOBDB_NOTIFICATION = 4 ;
2015-09-12 16:55:47 +02:00
// This is not in the Pebble protocol
static final byte NOTIFICATION_UNDEFINED = - 1 ;
2015-01-07 14:00:18 +01:00
static final byte NOTIFICATION_EMAIL = 0 ;
static final byte NOTIFICATION_SMS = 1 ;
static final byte NOTIFICATION_TWITTER = 2 ;
static final byte NOTIFICATION_FACEBOOK = 3 ;
static final byte PHONECONTROL_ANSWER = 1 ;
static final byte PHONECONTROL_HANGUP = 2 ;
static final byte PHONECONTROL_GETSTATE = 3 ;
static final byte PHONECONTROL_INCOMINGCALL = 4 ;
static final byte PHONECONTROL_OUTGOINGCALL = 5 ;
static final byte PHONECONTROL_MISSEDCALL = 6 ;
static final byte PHONECONTROL_RING = 7 ;
static final byte PHONECONTROL_START = 8 ;
static final byte PHONECONTROL_END = 9 ;
2015-02-08 23:53:40 +01:00
static final byte MUSICCONTROL_SETMUSICINFO = 16 ;
2015-02-12 16:00:45 +01:00
static final byte MUSICCONTROL_PLAYPAUSE = 1 ;
static final byte MUSICCONTROL_PAUSE = 2 ;
static final byte MUSICCONTROL_PLAY = 3 ;
static final byte MUSICCONTROL_NEXT = 4 ;
static final byte MUSICCONTROL_PREVIOUS = 5 ;
static final byte MUSICCONTROL_VOLUMEUP = 6 ;
static final byte MUSICCONTROL_VOLUMEDOWN = 7 ;
static final byte MUSICCONTROL_GETNOWPLAYING = 7 ;
2015-02-08 23:53:40 +01:00
2015-08-27 18:01:19 +02:00
static final byte NOTIFICATIONACTION_ACK = 0 ;
static final byte NOTIFICATIONACTION_NACK = 1 ;
static final byte NOTIFICATIONACTION_INVOKE = 0x02 ;
static final byte NOTIFICATIONACTION_RESPONSE = 0x11 ;
2015-03-25 22:23:45 +01:00
static final byte TIME_GETTIME = 0 ;
static final byte TIME_SETTIME = 2 ;
2015-08-18 00:12:40 +02:00
static final byte TIME_SETTIME_UTC = 3 ;
2015-03-25 22:23:45 +01:00
static final byte FIRMWAREVERSION_GETVERSION = 0 ;
static final byte APPMANAGER_GETAPPBANKSTATUS = 1 ;
2015-03-26 18:11:47 +01:00
static final byte APPMANAGER_REMOVEAPP = 2 ;
2015-04-07 19:33:23 +02:00
static final byte APPMANAGER_REFRESHAPP = 3 ;
2015-05-18 20:56:19 +02:00
static final byte APPMANAGER_GETUUIDS = 5 ;
2015-03-26 18:11:47 +01:00
static final int APPMANAGER_RES_SUCCESS = 1 ;
2015-03-25 22:23:45 +01:00
2015-05-05 14:41:10 +02:00
static final byte APPLICATIONMESSAGE_PUSH = 1 ;
static final byte APPLICATIONMESSAGE_REQUEST = 2 ;
static final byte APPLICATIONMESSAGE_ACK = ( byte ) 0xff ;
static final byte APPLICATIONMESSAGE_NACK = ( byte ) 0x7f ;
2015-08-26 23:17:32 +02:00
static final byte DATALOG_OPENSESSION = 0x01 ;
static final byte DATALOG_SENDDATA = 0x02 ;
static final byte DATALOG_CLOSE = 0x03 ;
2015-08-09 21:42:27 +02:00
static final byte DATALOG_TIMEOUT = 0x07 ;
2015-08-26 23:17:32 +02:00
static final byte DATALOG_REPORTSESSIONS = ( byte ) 0x84 ;
2015-08-09 21:42:27 +02:00
static final byte DATALOG_ACK = ( byte ) 0x85 ;
static final byte DATALOG_NACK = ( byte ) 0x86 ;
static final byte PING_PING = 0 ;
static final byte PING_PONG = 1 ;
2015-05-06 11:41:38 +02:00
2015-04-06 20:58:35 +02:00
static final byte PUTBYTES_INIT = 1 ;
static final byte PUTBYTES_SEND = 2 ;
static final byte PUTBYTES_COMMIT = 3 ;
static final byte PUTBYTES_ABORT = 4 ;
static final byte PUTBYTES_COMPLETE = 5 ;
2015-08-03 23:09:49 +02:00
public static final byte PUTBYTES_TYPE_FIRMWARE = 1 ;
public static final byte PUTBYTES_TYPE_RECOVERY = 2 ;
public static final byte PUTBYTES_TYPE_SYSRESOURCES = 3 ;
2015-04-07 19:33:23 +02:00
public static final byte PUTBYTES_TYPE_RESOURCES = 4 ;
2015-04-06 20:58:35 +02:00
public static final byte PUTBYTES_TYPE_BINARY = 5 ;
2015-10-06 16:56:01 +02:00
public static final byte PUTBYTES_TYPE_FILE = 6 ;
2015-04-07 19:33:23 +02:00
public static final byte PUTBYTES_TYPE_WORKER = 7 ;
2015-01-07 14:00:18 +01:00
2015-06-24 00:23:38 +02:00
static final byte RESET_REBOOT = 0 ;
2015-05-18 22:40:39 +02:00
2015-06-24 00:23:38 +02:00
static final byte SCREENSHOT_TAKE = 0 ;
static final byte SYSTEMMESSAGE_FIRMWARESTART = 1 ;
static final byte SYSTEMMESSAGE_FIRMWARECOMPLETE = 2 ;
static final byte SYSTEMMESSAGE_FIRMWAREFAIL = 3 ;
2015-04-17 12:23:19 +02:00
2015-05-06 11:41:38 +02:00
static final byte PHONEVERSION_REQUEST = 0 ;
2015-02-08 23:53:40 +01:00
static final byte PHONEVERSION_APPVERSION_MAGIC = 2 ; // increase this if pebble complains
static final byte PHONEVERSION_APPVERSION_MAJOR = 2 ;
2015-03-04 23:47:47 +01:00
static final byte PHONEVERSION_APPVERSION_MINOR = 3 ;
static final byte PHONEVERSION_APPVERSION_PATCH = 0 ;
2015-02-08 23:53:40 +01:00
2015-01-20 23:51:55 +01:00
2015-05-12 06:28:11 +02:00
static final int PHONEVERSION_SESSION_CAPS_GAMMARAY = 0x80000000 ;
2015-01-20 23:51:55 +01:00
static final int PHONEVERSION_REMOTE_CAPS_TELEPHONY = 0x00000010 ;
static final int PHONEVERSION_REMOTE_CAPS_SMS = 0x00000020 ;
static final int PHONEVERSION_REMOTE_CAPS_GPS = 0x00000040 ;
static final int PHONEVERSION_REMOTE_CAPS_BTLE = 0x00000080 ;
static final int PHONEVERSION_REMOTE_CAPS_REARCAMERA = 0x00000100 ;
static final int PHONEVERSION_REMOTE_CAPS_ACCEL = 0x00000200 ;
static final int PHONEVERSION_REMOTE_CAPS_GYRO = 0x00000400 ;
static final int PHONEVERSION_REMOTE_CAPS_COMPASS = 0x00000800 ;
static final byte PHONEVERSION_REMOTE_OS_UNKNOWN = 0 ;
static final byte PHONEVERSION_REMOTE_OS_IOS = 1 ;
2015-05-18 20:56:19 +02:00
static final byte PHONEVERSION_REMOTE_OS_ANDROID = 2 ;
2015-01-20 23:51:55 +01:00
static final byte PHONEVERSION_REMOTE_OS_OSX = 3 ;
static final byte PHONEVERSION_REMOTE_OS_LINUX = 4 ;
static final byte PHONEVERSION_REMOTE_OS_WINDOWS = 5 ;
2015-01-07 14:00:18 +01:00
2015-05-21 18:17:39 +02:00
static final byte TYPE_BYTEARRAY = 0 ;
static final byte TYPE_CSTRING = 1 ;
2015-09-17 19:21:22 +02:00
static final byte TYPE_UINT = 2 ;
static final byte TYPE_INT = 3 ;
2015-04-06 20:58:35 +02:00
static final short LENGTH_PREFIX = 4 ;
2015-06-19 23:54:31 +02:00
static final short LENGTH_SIMPLEMESSAGE = 1 ;
2015-08-11 13:55:35 +02:00
2015-08-16 11:33:32 +02:00
static final short LENGTH_APPFETCH = 2 ;
2015-08-11 13:55:35 +02:00
static final short LENGTH_APPRUNSTATE = 17 ;
2015-08-21 14:29:12 +02:00
static final short LENGTH_BLOBDB = 21 ;
2015-08-11 13:55:35 +02:00
static final short LENGTH_PING = 5 ;
static final short LENGTH_PHONEVERSION = 17 ;
2015-08-17 13:07:34 +02:00
static final short LENGTH_REMOVEAPP_2X = 17 ;
2015-04-07 19:33:23 +02:00
static final short LENGTH_REFRESHAPP = 5 ;
2015-08-11 13:55:35 +02:00
static final short LENGTH_SETTIME = 5 ;
static final short LENGTH_SYSTEMMESSAGE = 2 ;
2015-08-16 00:32:36 +02:00
static final short LENGTH_UPLOADSTART_2X = 7 ;
static final short LENGTH_UPLOADSTART_3X = 10 ;
2015-04-06 20:58:35 +02:00
static final short LENGTH_UPLOADCHUNK = 9 ;
2015-04-06 23:37:17 +02:00
static final short LENGTH_UPLOADCOMMIT = 9 ;
static final short LENGTH_UPLOADCOMPLETE = 5 ;
2015-04-09 18:48:52 +02:00
static final short LENGTH_UPLOADCANCEL = 5 ;
2015-04-06 20:58:35 +02:00
2015-08-14 12:50:44 +02:00
static final byte LENGTH_UUID = 16 ;
2015-09-23 23:19:38 +02:00
// base is -5
private static final String [ ] hwRevisions = {
// Emulator
"spalding_bb2" , "snowy_bb2" , "snowy_bb" , "bb2" , "bb" ,
"unknown" ,
// Pebble
"ev1" , "ev2" , "ev2_3" , "ev2_4" , "v1_5" , "v2_0" ,
// Pebble Time
2015-09-23 23:53:16 +02:00
"snowy_evt2" , "snowy_dvt" , "spalding_dvt" , "snowy_s3" , "spalding"
2015-09-23 23:19:38 +02:00
} ;
2015-11-23 23:04:46 +01:00
private static final Random mRandom = new Random ( ) ;
2015-05-18 20:56:19 +02:00
2015-06-13 00:26:55 +02:00
boolean isFw3x = false ;
2015-06-19 12:34:33 +02:00
boolean mForceProtocol = false ;
2015-06-24 23:55:51 +02:00
GBDeviceEventScreenshot mDevEventScreenshot = null ;
int mScreenshotRemaining = - 1 ;
2015-06-19 12:34:33 +02:00
2015-08-22 00:14:14 +02:00
//monochrome black + white
static final byte [ ] clut_pebble = {
0x00 , 0x00 , 0x00 , 0x00 ,
( byte ) 0xff , ( byte ) 0xff , ( byte ) 0xff , 0x00
} ;
// linear BGR222 (6 bit, 64 entries)
static final byte [ ] clut_pebbletime = new byte [ ] {
0x00 , 0x00 , 0x00 , 0x00 ,
0x55 , 0x00 , 0x00 , 0x00 ,
( byte ) 0xaa , 0x00 , 0x00 , 0x00 ,
( byte ) 0xff , 0x00 , 0x00 , 0x00 ,
0x00 , 0x55 , 0x00 , 0x00 ,
0x55 , 0x55 , 0x00 , 0x00 ,
( byte ) 0xaa , 0x55 , 0x00 , 0x00 ,
( byte ) 0xff , 0x55 , 0x00 , 0x00 ,
0x00 , ( byte ) 0xaa , 0x00 , 0x00 ,
0x55 , ( byte ) 0xaa , 0x00 , 0x00 ,
( byte ) 0xaa , ( byte ) 0xaa , 0x00 , 0x00 ,
( byte ) 0xff , ( byte ) 0xaa , 0x00 , 0x00 ,
0x00 , ( byte ) 0xff , 0x00 , 0x00 ,
0x55 , ( byte ) 0xff , 0x00 , 0x00 ,
( byte ) 0xaa , ( byte ) 0xff , 0x00 , 0x00 ,
( byte ) 0xff , ( byte ) 0xff , 0x00 , 0x00 ,
0x00 , 0x00 , 0x55 , 0x00 ,
0x55 , 0x00 , 0x55 , 0x00 ,
( byte ) 0xaa , 0x00 , 0x55 , 0x00 ,
( byte ) 0xff , 0x00 , 0x55 , 0x00 ,
0x00 , 0x55 , 0x55 , 0x00 ,
0x55 , 0x55 , 0x55 , 0x00 ,
( byte ) 0xaa , 0x55 , 0x55 , 0x00 ,
( byte ) 0xff , 0x55 , 0x55 , 0x00 ,
0x00 , ( byte ) 0xaa , 0x55 , 0x00 ,
0x55 , ( byte ) 0xaa , 0x55 , 0x00 ,
( byte ) 0xaa , ( byte ) 0xaa , 0x55 , 0x00 ,
( byte ) 0xff , ( byte ) 0xaa , 0x55 , 0x00 ,
0x00 , ( byte ) 0xff , 0x55 , 0x00 ,
0x55 , ( byte ) 0xff , 0x55 , 0x00 ,
( byte ) 0xaa , ( byte ) 0xff , 0x55 , 0x00 ,
( byte ) 0xff , ( byte ) 0xff , 0x55 , 0x00 ,
0x00 , 0x00 , ( byte ) 0xaa , 0x00 ,
0x55 , 0x00 , ( byte ) 0xaa , 0x00 ,
( byte ) 0xaa , 0x00 , ( byte ) 0xaa , 0x00 ,
( byte ) 0xff , 0x00 , ( byte ) 0xaa , 0x00 ,
0x00 , 0x55 , ( byte ) 0xaa , 0x00 ,
0x55 , 0x55 , ( byte ) 0xaa , 0x00 ,
( byte ) 0xaa , 0x55 , ( byte ) 0xaa , 0x00 ,
( byte ) 0xff , 0x55 , ( byte ) 0xaa , 0x00 ,
0x00 , ( byte ) 0xaa , ( byte ) 0xaa , 0x00 ,
0x55 , ( byte ) 0xaa , ( byte ) 0xaa , 0x00 ,
( byte ) 0xaa , ( byte ) 0xaa , ( byte ) 0xaa , 0x00 ,
( byte ) 0xff , ( byte ) 0xaa , ( byte ) 0xaa , 0x00 ,
0x00 , ( byte ) 0xff , ( byte ) 0xaa , 0x00 ,
0x55 , ( byte ) 0xff , ( byte ) 0xaa , 0x00 ,
( byte ) 0xaa , ( byte ) 0xff , ( byte ) 0xaa , 0x00 ,
( byte ) 0xff , ( byte ) 0xff , ( byte ) 0xaa , 0x00 ,
0x00 , 0x00 , ( byte ) 0xff , 0x00 ,
0x55 , 0x00 , ( byte ) 0xff , 0x00 ,
( byte ) 0xaa , 0x00 , ( byte ) 0xff , 0x00 ,
( byte ) 0xff , 0x00 , ( byte ) 0xff , 0x00 ,
0x00 , 0x55 , ( byte ) 0xff , 0x00 ,
0x55 , 0x55 , ( byte ) 0xff , 0x00 ,
( byte ) 0xaa , 0x55 , ( byte ) 0xff , 0x00 ,
( byte ) 0xff , 0x55 , ( byte ) 0xff , 0x00 ,
0x00 , ( byte ) 0xaa , ( byte ) 0xff , 0x00 ,
0x55 , ( byte ) 0xaa , ( byte ) 0xff , 0x00 ,
( byte ) 0xaa , ( byte ) 0xaa , ( byte ) 0xff , 0x00 ,
( byte ) 0xff , ( byte ) 0xaa , ( byte ) 0xff , 0x00 ,
0x00 , ( byte ) 0xff , ( byte ) 0xff , 0x00 ,
0x55 , ( byte ) 0xff , ( byte ) 0xff , 0x00 ,
( byte ) 0xaa , ( byte ) 0xff , ( byte ) 0xff , 0x00 ,
( byte ) 0xff , ( byte ) 0xff , ( byte ) 0xff , 0x00 ,
} ;
2015-05-21 18:17:39 +02:00
byte last_id = - 1 ;
2015-11-23 23:04:46 +01:00
private final ArrayList < UUID > tmpUUIDS = new ArrayList < > ( ) ;
2015-05-18 20:56:19 +02:00
2015-10-04 15:53:11 +02:00
private static final UUID UUID_GBPEBBLE = UUID . fromString ( "61476764-7465-7262-6469-656775527a6c" ) ;
private static final UUID UUID_MORPHEUZ = UUID . fromString ( "5be44f1d-d262-4ea6-aa30-ddbec1e3cab2" ) ;
private static final UUID UUID_WHETHERNEAT = UUID . fromString ( "3684003b-a685-45f9-a713-abc6364ba051" ) ;
private static final UUID UUID_MISFIT = UUID . fromString ( "0b73b76a-cd65-4dc2-9585-aaa213320858" ) ;
2015-11-23 23:04:46 +01:00
private static final Map < UUID , AppMessageHandler > mAppMessageHandlers = new HashMap < > ( ) ;
2015-10-04 15:53:11 +02:00
{
mAppMessageHandlers . put ( UUID_GBPEBBLE , new AppMessageHandlerGBPebble ( UUID_GBPEBBLE , PebbleProtocol . this ) ) ;
mAppMessageHandlers . put ( UUID_MORPHEUZ , new AppMessageHandlerMorpheuz ( UUID_MORPHEUZ , PebbleProtocol . this ) ) ;
mAppMessageHandlers . put ( UUID_WHETHERNEAT , new AppMessageHandlerWeatherNeat ( UUID_WHETHERNEAT , PebbleProtocol . this ) ) ;
2015-10-21 16:11:16 +02:00
mAppMessageHandlers . put ( UUID_MISFIT , new AppMessageHandlerMisfit ( UUID_MISFIT , PebbleProtocol . this ) ) ;
2015-10-04 15:53:11 +02:00
}
2015-05-18 20:56:19 +02:00
2015-06-21 23:53:23 +02:00
private static byte [ ] encodeSimpleMessage ( short endpoint , byte command ) {
2015-06-19 23:54:31 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_SIMPLEMESSAGE ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_SIMPLEMESSAGE ) ;
buf . putShort ( endpoint ) ;
buf . put ( command ) ;
return buf . array ( ) ;
}
2015-03-25 22:23:45 +01:00
private static byte [ ] encodeMessage ( short endpoint , byte type , int cookie , String [ ] parts ) {
2015-01-07 14:00:18 +01:00
// Calculate length first
int length = LENGTH_PREFIX + 1 ;
2015-03-22 00:34:54 +01:00
if ( parts ! = null ) {
for ( String s : parts ) {
if ( s = = null | | s . equals ( "" ) ) {
length + + ; // encode null or empty strings as 0x00 later
continue ;
}
length + = ( 1 + s . getBytes ( ) . length ) ;
2015-02-08 23:53:40 +01:00
}
2015-01-07 14:00:18 +01:00
}
2015-02-08 23:53:40 +01:00
if ( endpoint = = ENDPOINT_PHONECONTROL ) {
length + = 4 ; //for cookie;
}
2015-01-07 14:00:18 +01:00
// Encode Prefix
ByteBuffer buf = ByteBuffer . allocate ( length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) ( length - LENGTH_PREFIX ) ) ;
buf . putShort ( endpoint ) ;
buf . put ( type ) ;
2015-02-08 23:53:40 +01:00
if ( endpoint = = ENDPOINT_PHONECONTROL ) {
buf . putInt ( cookie ) ;
}
2015-01-07 14:00:18 +01:00
// Encode Pascal-Style Strings
2015-03-22 00:34:54 +01:00
if ( parts ! = null ) {
for ( String s : parts ) {
if ( s = = null | | s . equals ( "" ) ) {
//buf.put((byte)0x01);
buf . put ( ( byte ) 0x00 ) ;
continue ;
}
2015-01-07 14:00:18 +01:00
2015-03-22 00:34:54 +01:00
int partlength = s . getBytes ( ) . length ;
if ( partlength > 255 ) partlength = 255 ;
buf . put ( ( byte ) partlength ) ;
buf . put ( s . getBytes ( ) , 0 , partlength ) ;
}
2015-01-07 14:00:18 +01:00
}
return buf . array ( ) ;
}
2015-09-24 14:45:21 +02:00
@Override
public byte [ ] encodeNotification ( NotificationSpec notificationSpec ) {
boolean hasHandle = notificationSpec . id ! = - 1 ;
int id = notificationSpec . id ! = - 1 ? notificationSpec . id : mRandom . nextInt ( ) ;
String title ;
String subtitle = null ;
// for SMS and EMAIL that came in though SMS or K9 receiver
if ( notificationSpec . sender ! = null ) {
title = notificationSpec . sender ;
subtitle = notificationSpec . subject ;
} else {
title = notificationSpec . title ;
}
2015-05-21 18:57:34 +02:00
Long ts = System . currentTimeMillis ( ) ;
2015-08-18 00:12:40 +02:00
if ( ! isFw3x ) {
ts + = ( SimpleTimeZone . getDefault ( ) . getOffset ( ts ) ) ;
}
2015-05-21 18:57:34 +02:00
ts / = 1000 ;
2015-01-07 14:00:18 +01:00
2015-07-19 00:26:43 +02:00
if ( isFw3x ) {
// 3.x notification
2015-09-17 23:08:05 +02:00
//return encodeTimelinePin(id, (int) ((ts + 600) & 0xffffffffL), (short) 90, PebbleIconID.TIMELINE_CALENDAR, title); // really, this is just for testing
2015-09-25 00:53:40 +02:00
return encodeBlobdbNotification ( id , ( int ) ( ts & 0xffffffffL ) , title , subtitle , notificationSpec . body , notificationSpec . sourceName , hasHandle , notificationSpec . type ) ;
2015-09-24 14:45:21 +02:00
} else if ( mForceProtocol | | notificationSpec . type ! = NotificationType . EMAIL ) {
2015-07-19 00:26:43 +02:00
// 2.x notification
2015-09-25 00:53:40 +02:00
return encodeExtensibleNotification ( id , ( int ) ( ts & 0xffffffffL ) , title , subtitle , notificationSpec . body , notificationSpec . sourceName , hasHandle ) ;
2015-07-19 00:26:43 +02:00
} else {
// 1.x notification on FW 2.X
2015-09-24 14:45:21 +02:00
String [ ] parts = { title , notificationSpec . body , ts . toString ( ) , subtitle } ;
2015-09-12 16:55:47 +02:00
// be aware that type is at this point always NOTIFICATION_EMAIL
2015-09-24 14:45:21 +02:00
return encodeMessage ( ENDPOINT_NOTIFICATION , NOTIFICATION_EMAIL , 0 , parts ) ;
2015-05-15 21:34:38 +02:00
}
2015-07-19 00:26:43 +02:00
}
2015-06-19 12:34:33 +02:00
2015-05-12 06:28:11 +02:00
@Override
2015-08-21 00:58:18 +02:00
public byte [ ] encodeSetTime ( ) {
long ts = System . currentTimeMillis ( ) ;
2015-08-18 00:12:40 +02:00
long ts_offset = ( SimpleTimeZone . getDefault ( ) . getOffset ( ts ) ) ;
ByteBuffer buf ;
if ( isFw3x ) {
String timezone = SimpleTimeZone . getDefault ( ) . getDisplayName ( false , SimpleTimeZone . SHORT ) ;
short length = ( short ) ( LENGTH_SETTIME + timezone . length ( ) + 3 ) ;
buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( length ) ;
buf . putShort ( ENDPOINT_TIME ) ;
buf . put ( TIME_SETTIME_UTC ) ;
buf . putInt ( ( int ) ( ts / 1000 ) ) ;
buf . putShort ( ( short ) ( ts_offset / 60000 ) ) ;
buf . put ( ( byte ) timezone . length ( ) ) ;
buf . put ( timezone . getBytes ( ) ) ;
LOG . info ( timezone ) ;
} else {
buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_SETTIME ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_SETTIME ) ;
buf . putShort ( ENDPOINT_TIME ) ;
buf . put ( TIME_SETTIME ) ;
buf . putInt ( ( int ) ( ( ts + ts_offset ) / 1000 ) ) ;
}
2015-01-07 14:00:18 +01:00
return buf . array ( ) ;
}
2015-06-21 23:53:23 +02:00
@Override
public byte [ ] encodeFindDevice ( boolean start ) {
2015-08-03 23:09:49 +02:00
return encodeSetCallState ( "Where are you?" , "Gadgetbridge" , start ? ServiceCommand . CALL_INCOMING : ServiceCommand . CALL_END ) ;
2015-06-21 23:53:23 +02:00
}
2015-09-25 00:53:40 +02:00
private static byte [ ] encodeExtensibleNotification ( int id , int timestamp , String title , String subtitle , String body , String sourceName , boolean hasHandle ) {
2015-09-02 22:43:22 +02:00
final short ACTION_LENGTH_MIN = 10 ;
2015-07-21 01:33:13 +02:00
String [ ] parts = { title , subtitle , body } ;
2015-05-15 21:34:38 +02:00
// Calculate length first
2015-09-02 22:43:22 +02:00
byte actions_count ;
short actions_length ;
String dismiss_string ;
String open_string = "Open on phone" ;
2015-09-25 00:53:40 +02:00
String mute_string = "Mute" ;
if ( sourceName ! = null ) {
mute_string + = " " + sourceName ;
}
2015-09-02 22:43:22 +02:00
byte dismiss_action_id ;
2015-09-25 00:53:40 +02:00
2015-09-01 21:58:36 +02:00
if ( hasHandle ) {
2015-09-25 00:53:40 +02:00
actions_count = 3 ;
2015-09-02 22:43:22 +02:00
dismiss_string = "Dismiss" ;
dismiss_action_id = 0x02 ;
2015-09-25 00:53:40 +02:00
actions_length = ( short ) ( ACTION_LENGTH_MIN * actions_count + dismiss_string . getBytes ( ) . length + open_string . getBytes ( ) . length + mute_string . getBytes ( ) . length ) ;
2015-09-01 21:58:36 +02:00
} else {
2015-09-02 22:43:22 +02:00
actions_count = 1 ;
dismiss_string = "Dismiss all" ;
dismiss_action_id = 0x03 ;
2015-09-19 15:32:09 +02:00
actions_length = ( short ) ( ACTION_LENGTH_MIN * actions_count + dismiss_string . getBytes ( ) . length ) ;
2015-09-01 21:58:36 +02:00
}
2015-05-15 21:34:38 +02:00
byte attributes_count = 0 ;
2015-09-02 22:43:22 +02:00
int length = 21 + 10 + actions_length ;
2015-05-15 21:34:38 +02:00
if ( parts ! = null ) {
for ( String s : parts ) {
if ( s = = null | | s . equals ( "" ) ) {
continue ;
}
attributes_count + + ;
length + = ( 3 + s . getBytes ( ) . length ) ;
}
}
// Encode Prefix
ByteBuffer buf = ByteBuffer . allocate ( length + LENGTH_PREFIX ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) ( length ) ) ;
buf . putShort ( ENDPOINT_EXTENSIBLENOTIFS ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ; // !
buf . put ( ( byte ) 0x00 ) ; // ?
buf . put ( ( byte ) 0x01 ) ; // add notifications
2015-09-05 20:40:12 +02:00
buf . putInt ( 0x00000000 ) ; // flags - ?
2015-05-15 21:34:38 +02:00
buf . putInt ( id ) ;
buf . putInt ( 0x00000000 ) ; // ANCS id
buf . putInt ( timestamp ) ;
buf . put ( ( byte ) 0x01 ) ; // layout - ?
2015-09-02 22:43:22 +02:00
buf . put ( attributes_count ) ;
buf . put ( actions_count ) ;
2015-05-15 21:34:38 +02:00
byte attribute_id = 0 ;
// Encode Pascal-Style Strings
if ( parts ! = null ) {
for ( String s : parts ) {
attribute_id + + ;
if ( s = = null | | s . equals ( "" ) ) {
continue ;
}
int partlength = s . getBytes ( ) . length ;
if ( partlength > 255 ) partlength = 255 ;
buf . put ( attribute_id ) ;
buf . putShort ( ( short ) partlength ) ;
buf . put ( s . getBytes ( ) , 0 , partlength ) ;
}
}
2015-08-31 22:27:25 +02:00
2015-09-02 22:43:22 +02:00
// dismiss action
buf . put ( dismiss_action_id ) ;
buf . put ( ( byte ) 0x04 ) ; // dismiss
2015-08-31 22:27:25 +02:00
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) dismiss_string . getBytes ( ) . length ) ;
2015-09-02 22:43:22 +02:00
buf . put ( dismiss_string . getBytes ( ) ) ;
// open action
if ( hasHandle ) {
buf . put ( ( byte ) 0x01 ) ;
2015-09-25 00:53:40 +02:00
buf . put ( ( byte ) 0x02 ) ; // generic
2015-09-02 22:43:22 +02:00
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) open_string . getBytes ( ) . length ) ;
2015-09-02 22:43:22 +02:00
buf . put ( open_string . getBytes ( ) ) ;
2015-09-25 00:53:40 +02:00
buf . put ( ( byte ) 0x04 ) ;
buf . put ( ( byte ) 0x02 ) ; // generic
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
buf . putShort ( ( short ) mute_string . getBytes ( ) . length ) ;
buf . put ( mute_string . getBytes ( ) ) ;
2015-09-02 22:43:22 +02:00
}
2015-08-31 22:27:25 +02:00
2015-05-15 21:34:38 +02:00
return buf . array ( ) ;
}
2015-08-21 14:29:12 +02:00
private byte [ ] encodeBlobdb ( UUID uuid , byte command , byte db , byte [ ] blob ) {
int length = LENGTH_BLOBDB ;
if ( blob ! = null ) {
length + = blob . length + 2 ;
}
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) length ) ;
buf . putShort ( ENDPOINT_BLOBDB ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
buf . put ( command ) ;
buf . putShort ( ( short ) mRandom . nextInt ( ) ) ; // token
buf . put ( db ) ;
buf . put ( LENGTH_UUID ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
if ( blob ! = null ) {
buf . putShort ( ( short ) blob . length ) ;
buf . put ( blob ) ;
}
return buf . array ( ) ;
}
2015-09-08 14:15:46 +02:00
private byte [ ] encodeTimelinePin ( int id , int timestamp , short duration , int icon_id , String title ) {
final short TIMELINE_PIN_LENGTH = 46 ;
icon_id | = 0x80000000 ;
UUID uuid = new UUID ( mRandom . nextLong ( ) , ( ( long ) mRandom . nextInt ( ) < < 32 ) | id ) ;
byte attributes_count = 2 ;
byte actions_count = 0 ;
2015-09-19 15:32:09 +02:00
int attributes_length = 10 + title . getBytes ( ) . length ;
2015-09-08 14:15:46 +02:00
int pin_length = TIMELINE_PIN_LENGTH + attributes_length ;
ByteBuffer buf = ByteBuffer . allocate ( pin_length ) ;
// pin - 46 bytes
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
buf . putLong ( 0 ) ; // parent
buf . putLong ( 0 ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
buf . putInt ( timestamp ) ; // 32-bit timestamp
buf . putShort ( duration ) ;
buf . put ( ( byte ) 0x02 ) ; // type (0x02 = pin)
buf . putShort ( ( short ) 0x0001 ) ; // flags 0x0001 = ?
buf . put ( ( byte ) 0x02 ) ; // layout (0x02 = pin?)
buf . putShort ( ( short ) attributes_length ) ; // total length of all attributes and actions in bytes
buf . put ( attributes_count ) ;
buf . put ( actions_count ) ;
buf . put ( ( byte ) 4 ) ; // icon
buf . putShort ( ( short ) 4 ) ; // length of int
buf . putInt ( icon_id ) ;
buf . put ( ( byte ) 1 ) ; // title
buf . putShort ( ( short ) title . getBytes ( ) . length ) ;
buf . put ( title . getBytes ( ) ) ;
return encodeBlobdb ( uuid , BLOBDB_INSERT , BLOBDB_PIN , buf . array ( ) ) ;
}
2015-09-25 00:53:40 +02:00
private byte [ ] encodeBlobdbNotification ( int id , int timestamp , String title , String subtitle , String body , String sourceName , boolean hasHandle , NotificationType notificationType ) {
2015-09-02 22:43:22 +02:00
final short NOTIFICATION_PIN_LENGTH = 46 ;
final short ACTION_LENGTH_MIN = 10 ;
2015-08-17 13:57:01 +02:00
String [ ] parts = { title , subtitle , body } ;
2015-09-12 16:55:47 +02:00
int icon_id ;
2015-09-13 13:41:56 +02:00
byte color_id ;
2015-09-24 14:45:21 +02:00
switch ( notificationType ) {
case EMAIL :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . GENERIC_EMAIL ;
color_id = PebbleColor . JaegerGreen ;
2015-08-17 13:57:01 +02:00
break ;
2015-09-24 14:45:21 +02:00
case SMS :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . GENERIC_SMS ;
color_id = PebbleColor . VividViolet ;
2015-09-12 16:55:47 +02:00
break ;
default :
2015-09-24 14:45:21 +02:00
switch ( notificationType ) {
2015-09-13 00:39:53 +02:00
case TWITTER :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . NOTIFICATION_TWITTER ;
color_id = PebbleColor . BlueMoon ;
2015-09-13 00:39:53 +02:00
break ;
case EMAIL :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . GENERIC_EMAIL ;
color_id = PebbleColor . JaegerGreen ;
2015-09-13 00:39:53 +02:00
break ;
2015-09-13 22:47:56 +02:00
case SMS :
icon_id = PebbleIconID . GENERIC_SMS ;
color_id = PebbleColor . VividViolet ;
break ;
2015-09-13 00:39:53 +02:00
case FACEBOOK :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . NOTIFICATION_FACEBOOK ;
color_id = PebbleColor . VeryLightBlue ;
2015-09-13 00:39:53 +02:00
break ;
2015-09-13 13:32:18 +02:00
case CHAT :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . NOTIFICATION_HIPCHAT ;
color_id = PebbleColor . Inchworm ;
2015-09-13 13:32:18 +02:00
break ;
2015-09-13 00:39:53 +02:00
default :
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . NOTIFICATION_GENERIC ;
color_id = PebbleColor . Red ;
2015-09-13 00:39:53 +02:00
break ;
}
2015-09-12 16:55:47 +02:00
break ;
2015-08-17 13:57:01 +02:00
}
2015-06-16 23:14:51 +02:00
// Calculate length first
2015-09-02 22:43:22 +02:00
byte actions_count ;
short actions_length ;
String dismiss_string ;
String open_string = "Open on phone" ;
2015-09-25 00:53:40 +02:00
String mute_string = "Mute" ;
if ( sourceName ! = null ) {
mute_string + = " " + sourceName ;
}
2015-09-02 22:43:22 +02:00
byte dismiss_action_id ;
2015-09-01 21:58:36 +02:00
if ( hasHandle ) {
2015-09-25 00:53:40 +02:00
actions_count = 3 ;
2015-09-02 22:43:22 +02:00
dismiss_string = "Dismiss" ;
dismiss_action_id = 0x02 ;
2015-09-25 00:53:40 +02:00
actions_length = ( short ) ( ACTION_LENGTH_MIN * actions_count + dismiss_string . getBytes ( ) . length + open_string . getBytes ( ) . length + mute_string . getBytes ( ) . length ) ;
2015-09-01 21:58:36 +02:00
} else {
2015-09-02 22:43:22 +02:00
actions_count = 1 ;
dismiss_string = "Dismiss all" ;
dismiss_action_id = 0x03 ;
2015-09-19 15:32:09 +02:00
actions_length = ( short ) ( ACTION_LENGTH_MIN * actions_count + dismiss_string . getBytes ( ) . length ) ;
2015-09-01 21:58:36 +02:00
}
2015-09-13 13:41:56 +02:00
byte attributes_count = 2 ; // icon
short attributes_length = ( short ) ( 11 + actions_length ) ;
2015-06-16 23:14:51 +02:00
if ( parts ! = null ) {
for ( String s : parts ) {
if ( s = = null | | s . equals ( "" ) ) {
continue ;
}
attributes_count + + ;
attributes_length + = ( 3 + s . getBytes ( ) . length ) ;
}
}
2015-08-21 14:29:12 +02:00
UUID uuid = UUID . randomUUID ( ) ;
2015-07-21 22:05:25 +02:00
short pin_length = ( short ) ( NOTIFICATION_PIN_LENGTH + attributes_length ) ;
2015-08-21 14:29:12 +02:00
ByteBuffer buf = ByteBuffer . allocate ( pin_length ) ;
2015-06-16 23:14:51 +02:00
2015-08-21 14:29:12 +02:00
// pin - 46 bytes
2015-06-16 23:14:51 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
2015-08-21 14:29:12 +02:00
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
2015-09-01 21:58:36 +02:00
buf . putInt ( ( int ) ( uuid . getLeastSignificantBits ( ) > > > 32 ) ) ;
buf . putInt ( id ) ;
2015-08-21 14:29:12 +02:00
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
2015-09-01 21:58:36 +02:00
buf . putInt ( ( int ) ( uuid . getLeastSignificantBits ( ) > > > 32 ) ) ;
buf . putInt ( id ) ;
2015-06-16 23:14:51 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
buf . putInt ( timestamp ) ; // 32-bit timestamp
buf . putShort ( ( short ) 0 ) ; // duration
buf . put ( ( byte ) 0x01 ) ; // type (0x01 = notification)
2015-08-22 00:29:52 +02:00
buf . putShort ( ( short ) 0x0001 ) ; // flags 0x0001 = ?
2015-08-18 00:12:40 +02:00
buf . put ( ( byte ) 0x04 ) ; // layout (0x04 = notification?)
2015-07-21 22:05:25 +02:00
buf . putShort ( attributes_length ) ; // total length of all attributes and actions in bytes
2015-07-21 21:29:08 +02:00
buf . put ( attributes_count ) ;
buf . put ( actions_count ) ;
2015-06-16 23:14:51 +02:00
byte attribute_id = 0 ;
// Encode Pascal-Style Strings
if ( parts ! = null ) {
for ( String s : parts ) {
attribute_id + + ;
if ( s = = null | | s . equals ( "" ) ) {
continue ;
}
int partlength = s . getBytes ( ) . length ;
if ( partlength > 255 ) partlength = 255 ;
buf . put ( attribute_id ) ;
buf . putShort ( ( short ) partlength ) ;
buf . put ( s . getBytes ( ) , 0 , partlength ) ;
}
}
2015-08-17 13:57:01 +02:00
buf . put ( ( byte ) 4 ) ; // icon
buf . putShort ( ( short ) 4 ) ; // length of int
2015-09-12 16:55:47 +02:00
buf . putInt ( 0x80000000 | icon_id ) ;
2015-08-17 13:57:01 +02:00
2015-09-13 13:41:56 +02:00
buf . put ( ( byte ) 28 ) ; // background_color
buf . putShort ( ( short ) 1 ) ; // length of int
buf . put ( color_id ) ;
2015-09-02 22:43:22 +02:00
// dismiss action
buf . put ( dismiss_action_id ) ;
2015-08-27 18:01:19 +02:00
buf . put ( ( byte ) 0x02 ) ; // generic action, dismiss did not do anything
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) dismiss_string . getBytes ( ) . length ) ;
2015-09-02 22:43:22 +02:00
buf . put ( dismiss_string . getBytes ( ) ) ;
2015-09-01 21:58:36 +02:00
2015-09-25 00:53:40 +02:00
// open and mute actions
2015-09-02 22:43:22 +02:00
if ( hasHandle ) {
buf . put ( ( byte ) 0x01 ) ;
buf . put ( ( byte ) 0x02 ) ; // generic action
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) open_string . getBytes ( ) . length ) ;
2015-09-02 22:43:22 +02:00
buf . put ( open_string . getBytes ( ) ) ;
2015-09-25 00:53:40 +02:00
buf . put ( ( byte ) 0x04 ) ;
buf . put ( ( byte ) 0x02 ) ; // generic action
buf . put ( ( byte ) 0x01 ) ; // number attributes
buf . put ( ( byte ) 0x01 ) ; // attribute id (title)
buf . putShort ( ( short ) mute_string . getBytes ( ) . length ) ;
buf . put ( mute_string . getBytes ( ) ) ;
2015-09-02 22:43:22 +02:00
}
2015-08-21 14:29:12 +02:00
return encodeBlobdb ( UUID . randomUUID ( ) , BLOBDB_INSERT , BLOBDB_NOTIFICATION , buf . array ( ) ) ;
2015-06-16 23:14:51 +02:00
}
2015-09-25 00:53:40 +02:00
public byte [ ] encodeActionResponse2x ( int id , byte actionId , int iconId , String caption ) {
2015-09-19 15:32:09 +02:00
short length = ( short ) ( 18 + caption . getBytes ( ) . length ) ;
2015-09-05 20:40:12 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( length ) ;
buf . putShort ( ENDPOINT_EXTENSIBLENOTIFS ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
buf . put ( NOTIFICATIONACTION_RESPONSE ) ;
buf . putInt ( id ) ;
2015-09-25 00:53:40 +02:00
buf . put ( actionId ) ;
2015-09-05 20:40:12 +02:00
buf . put ( NOTIFICATIONACTION_ACK ) ;
buf . put ( ( byte ) 2 ) ; //nr of attributes
buf . put ( ( byte ) 6 ) ; // icon
buf . putShort ( ( short ) 4 ) ; // length
buf . putInt ( iconId ) ;
buf . put ( ( byte ) 2 ) ; // title
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) caption . getBytes ( ) . length ) ;
2015-09-05 20:40:12 +02:00
buf . put ( caption . getBytes ( ) ) ;
return buf . array ( ) ;
}
2015-09-01 21:58:36 +02:00
public byte [ ] encodeActionResponse ( UUID uuid , int iconId , String caption ) {
2015-09-19 15:32:09 +02:00
short length = ( short ) ( 29 + caption . getBytes ( ) . length ) ;
2015-08-27 18:01:19 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( length ) ;
buf . putShort ( ENDPOINT_NOTIFICATIONACTION ) ;
buf . put ( NOTIFICATIONACTION_RESPONSE ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
buf . put ( NOTIFICATIONACTION_ACK ) ;
buf . put ( ( byte ) 2 ) ; //nr of attributes
buf . put ( ( byte ) 6 ) ; // icon
buf . putShort ( ( short ) 4 ) ; // length
2015-09-01 21:58:36 +02:00
buf . putInt ( 0x80000000 | iconId ) ;
2015-08-27 18:01:19 +02:00
buf . put ( ( byte ) 2 ) ; // title
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) caption . getBytes ( ) . length ) ;
2015-09-01 21:58:36 +02:00
buf . put ( caption . getBytes ( ) ) ;
2015-08-27 18:01:19 +02:00
return buf . array ( ) ;
}
2015-08-19 01:40:39 +02:00
public byte [ ] encodeInstallMetadata ( UUID uuid , String appName , short appVersion , short sdkVersion , int flags , int iconId ) {
2015-08-14 12:50:44 +02:00
final short METADATA_LENGTH = 126 ;
byte [ ] name_buf = new byte [ 96 ] ;
2015-09-19 15:32:09 +02:00
System . arraycopy ( appName . getBytes ( ) , 0 , name_buf , 0 , appName . getBytes ( ) . length ) ;
2015-08-21 14:29:12 +02:00
ByteBuffer buf = ByteBuffer . allocate ( METADATA_LENGTH ) ;
2015-08-14 12:50:44 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ; // watchapp uuid
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
2015-08-19 01:40:39 +02:00
buf . putInt ( flags ) ;
2015-08-16 11:33:32 +02:00
buf . putInt ( iconId ) ;
2015-08-14 12:50:44 +02:00
buf . putShort ( appVersion ) ;
buf . putShort ( sdkVersion ) ;
buf . put ( ( byte ) 0 ) ; // app_face_bgcolor
buf . put ( ( byte ) 0 ) ; // app_face_template_id
buf . put ( name_buf ) ; // 96 bytes
2015-08-21 14:29:12 +02:00
return encodeBlobdb ( uuid , BLOBDB_INSERT , BLOBDB_APP , buf . array ( ) ) ;
2015-08-14 12:50:44 +02:00
}
2015-08-16 11:33:32 +02:00
public byte [ ] encodeAppFetchAck ( ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_APPFETCH ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_APPFETCH ) ;
buf . putShort ( ENDPOINT_APPFETCH ) ;
buf . put ( ( byte ) 0x01 ) ;
buf . put ( ( byte ) 0x01 ) ;
return buf . array ( ) ;
}
2015-04-07 23:57:12 +02:00
public byte [ ] encodeGetTime ( ) {
2015-06-19 23:54:31 +02:00
return encodeSimpleMessage ( ENDPOINT_TIME , TIME_GETTIME ) ;
2015-04-07 23:57:12 +02:00
}
2015-05-12 06:28:11 +02:00
@Override
2015-08-03 23:09:49 +02:00
public byte [ ] encodeSetCallState ( String number , String name , ServiceCommand command ) {
2015-02-08 23:53:40 +01:00
String [ ] parts = { number , name } ;
2015-02-12 16:00:45 +01:00
byte pebbleCmd ;
switch ( command ) {
case CALL_START :
pebbleCmd = PHONECONTROL_START ;
break ;
case CALL_END :
pebbleCmd = PHONECONTROL_END ;
break ;
case CALL_INCOMING :
pebbleCmd = PHONECONTROL_INCOMINGCALL ;
break ;
case CALL_OUTGOING :
2015-03-07 14:40:59 +01:00
// pebbleCmd = PHONECONTROL_OUTGOINGCALL;
/ *
* HACK / WORKAROUND for non - working outgoing call display .
* Just send a incoming call command immediately followed by a start call command
* This prevents vibration of the Pebble .
* /
byte [ ] callmsg = encodeMessage ( ENDPOINT_PHONECONTROL , PHONECONTROL_INCOMINGCALL , 0 , parts ) ;
byte [ ] startmsg = encodeMessage ( ENDPOINT_PHONECONTROL , PHONECONTROL_START , 0 , parts ) ;
byte [ ] msg = new byte [ callmsg . length + startmsg . length ] ;
System . arraycopy ( callmsg , 0 , msg , 0 , callmsg . length ) ;
System . arraycopy ( startmsg , 0 , msg , startmsg . length , startmsg . length ) ;
return msg ;
// END HACK
2015-02-12 16:00:45 +01:00
default :
return null ;
}
return encodeMessage ( ENDPOINT_PHONECONTROL , pebbleCmd , 0 , parts ) ;
2015-02-08 23:53:40 +01:00
}
2015-05-12 06:28:11 +02:00
@Override
2015-04-01 18:34:52 +02:00
public byte [ ] encodeSetMusicInfo ( String artist , String album , String track ) {
2015-02-08 23:53:40 +01:00
String [ ] parts = { artist , album , track } ;
return encodeMessage ( ENDPOINT_MUSICCONTROL , MUSICCONTROL_SETMUSICINFO , 0 , parts ) ;
2015-01-07 14:00:18 +01:00
}
2015-05-12 06:28:11 +02:00
@Override
2015-04-01 18:34:52 +02:00
public byte [ ] encodeFirmwareVersionReq ( ) {
2015-06-19 23:54:31 +02:00
return encodeSimpleMessage ( ENDPOINT_FIRMWAREVERSION , FIRMWAREVERSION_GETVERSION ) ;
2015-03-25 22:23:45 +01:00
}
2015-05-12 06:28:11 +02:00
@Override
2015-04-01 18:34:52 +02:00
public byte [ ] encodeAppInfoReq ( ) {
2015-06-19 23:54:31 +02:00
return encodeSimpleMessage ( ENDPOINT_APPMANAGER , APPMANAGER_GETUUIDS ) ;
2015-03-22 00:34:54 +01:00
}
2015-05-18 22:40:39 +02:00
@Override
2015-09-13 21:44:26 +02:00
public byte [ ] encodeAppStart ( UUID uuid , boolean start ) {
2015-08-11 13:55:35 +02:00
if ( isFw3x ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_APPRUNSTATE ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_APPRUNSTATE ) ;
buf . putShort ( ENDPOINT_APPRUNSTATE ) ;
2015-09-13 21:44:26 +02:00
buf . put ( start ? APPRUNSTATE_START : APPRUNSTATE_STOP ) ;
2015-08-11 13:55:35 +02:00
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
return buf . array ( ) ;
} else {
ArrayList < Pair < Integer , Object > > pairs = new ArrayList < > ( ) ;
2015-09-13 21:44:26 +02:00
int param = start ? 1 : 0 ;
pairs . add ( new Pair < > ( 1 , ( Object ) param ) ) ;
2015-08-11 13:55:35 +02:00
return encodeApplicationMessagePush ( ENDPOINT_LAUNCHER , uuid , pairs ) ;
}
2015-05-18 22:40:39 +02:00
}
2015-05-12 06:28:11 +02:00
@Override
2015-05-18 20:56:19 +02:00
public byte [ ] encodeAppDelete ( UUID uuid ) {
2015-08-17 13:07:34 +02:00
if ( isFw3x ) {
2015-08-21 14:29:12 +02:00
return encodeBlobdb ( uuid , BLOBDB_DELETE , BLOBDB_APP , null ) ;
2015-08-17 13:07:34 +02:00
} else {
2015-08-21 14:29:12 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_REMOVEAPP_2X ) ;
2015-08-17 13:07:34 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_REMOVEAPP_2X ) ;
buf . putShort ( ENDPOINT_APPMANAGER ) ;
buf . put ( APPMANAGER_REMOVEAPP ) ;
2015-08-21 14:29:12 +02:00
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
return buf . array ( ) ;
2015-08-17 13:07:34 +02:00
}
2015-03-26 18:11:47 +01:00
}
2015-07-24 01:34:50 +02:00
private byte [ ] encodePhoneVersion2x ( byte os ) {
2015-03-26 18:11:47 +01:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_PHONEVERSION ) ;
2015-01-20 23:51:55 +01:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
2015-03-26 18:11:47 +01:00
buf . putShort ( LENGTH_PHONEVERSION ) ;
2015-01-20 23:51:55 +01:00
buf . putShort ( ENDPOINT_PHONEVERSION ) ;
2015-02-06 23:28:24 +01:00
buf . put ( ( byte ) 0x01 ) ;
buf . putInt ( - 1 ) ; //0xffffffff
2015-01-20 23:51:55 +01:00
if ( os = = PHONEVERSION_REMOTE_OS_ANDROID ) {
buf . putInt ( PHONEVERSION_SESSION_CAPS_GAMMARAY ) ;
} else {
buf . putInt ( 0 ) ;
}
buf . putInt ( PHONEVERSION_REMOTE_CAPS_SMS | PHONEVERSION_REMOTE_CAPS_TELEPHONY | os ) ;
2015-02-08 23:53:40 +01:00
buf . put ( PHONEVERSION_APPVERSION_MAGIC ) ;
buf . put ( PHONEVERSION_APPVERSION_MAJOR ) ;
buf . put ( PHONEVERSION_APPVERSION_MINOR ) ;
buf . put ( PHONEVERSION_APPVERSION_PATCH ) ;
2015-01-20 23:51:55 +01:00
return buf . array ( ) ;
}
2015-07-24 01:34:50 +02:00
private byte [ ] encodePhoneVersion3x ( byte os ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + 25 ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) 25 ) ;
buf . putShort ( ENDPOINT_PHONEVERSION ) ;
buf . put ( ( byte ) 0x01 ) ;
buf . putInt ( - 1 ) ; //0xffffffff
buf . putInt ( 0 ) ;
buf . putInt ( os ) ;
buf . put ( PHONEVERSION_APPVERSION_MAGIC ) ;
buf . put ( ( byte ) 3 ) ; // major?
buf . put ( ( byte ) 0 ) ; // minor?
buf . put ( ( byte ) 1 ) ; // patch?
buf . put ( ( byte ) 3 ) ; // ???
buf . put ( ( byte ) 0 ) ; // ???
buf . put ( ( byte ) 0 ) ; // ???
buf . put ( ( byte ) 0 ) ; // ???
buf . putInt ( 0 ) ; // ???
return buf . array ( ) ;
}
public byte [ ] encodePhoneVersion ( byte os ) {
return encodePhoneVersion3x ( os ) ;
}
2015-05-18 22:40:39 +02:00
@Override
public byte [ ] encodeReboot ( ) {
2015-06-19 23:54:31 +02:00
return encodeSimpleMessage ( ENDPOINT_RESET , RESET_REBOOT ) ;
2015-05-18 22:40:39 +02:00
}
2015-06-24 00:23:38 +02:00
@Override
public byte [ ] encodeScreenshotReq ( ) {
2015-06-24 23:55:51 +02:00
return encodeSimpleMessage ( ENDPOINT_SCREENSHOT , SCREENSHOT_TAKE ) ;
2015-06-24 00:23:38 +02:00
}
2015-04-06 20:58:35 +02:00
/* pebble specific install methods */
2015-10-06 16:56:01 +02:00
public byte [ ] encodeUploadStart ( byte type , int app_id , int size , String filename ) {
2015-08-16 00:32:36 +02:00
short length ;
if ( isFw3x ) {
length = LENGTH_UPLOADSTART_3X ;
type | = 0b10000000 ;
} else {
length = LENGTH_UPLOADSTART_2X ;
}
2015-10-06 16:56:01 +02:00
if ( type = = PUTBYTES_TYPE_FILE & & filename ! = null ) {
length + = filename . getBytes ( ) . length + 1 ;
}
2015-08-16 00:32:36 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
2015-04-06 20:58:35 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
2015-08-16 00:32:36 +02:00
buf . putShort ( length ) ;
2015-04-06 20:58:35 +02:00
buf . putShort ( ENDPOINT_PUTBYTES ) ;
buf . put ( PUTBYTES_INIT ) ;
buf . putInt ( size ) ;
buf . put ( type ) ;
2015-10-06 16:56:01 +02:00
2015-08-16 00:32:36 +02:00
if ( isFw3x ) {
buf . putInt ( app_id ) ;
} else {
// slot
buf . put ( ( byte ) app_id ) ;
}
2015-10-06 16:56:01 +02:00
if ( type = = PUTBYTES_TYPE_FILE & & filename ! = null ) {
buf . put ( filename . getBytes ( ) ) ;
buf . put ( ( byte ) 0 ) ;
}
2015-04-06 20:58:35 +02:00
return buf . array ( ) ;
}
public byte [ ] encodeUploadChunk ( int token , byte [ ] buffer , int size ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_UPLOADCHUNK + size ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) ( LENGTH_UPLOADCHUNK + size ) ) ;
buf . putShort ( ENDPOINT_PUTBYTES ) ;
buf . put ( PUTBYTES_SEND ) ;
buf . putInt ( token ) ;
buf . putInt ( size ) ;
buf . put ( buffer , 0 , size ) ;
return buf . array ( ) ;
}
2015-04-06 23:37:17 +02:00
public byte [ ] encodeUploadCommit ( int token , int crc ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_UPLOADCOMMIT ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_UPLOADCOMMIT ) ;
buf . putShort ( ENDPOINT_PUTBYTES ) ;
buf . put ( PUTBYTES_COMMIT ) ;
buf . putInt ( token ) ;
buf . putInt ( crc ) ;
return buf . array ( ) ;
}
public byte [ ] encodeUploadComplete ( int token ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_UPLOADCOMPLETE ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_UPLOADCOMPLETE ) ;
buf . putShort ( ENDPOINT_PUTBYTES ) ;
buf . put ( PUTBYTES_COMPLETE ) ;
buf . putInt ( token ) ;
return buf . array ( ) ;
}
2015-04-09 18:48:52 +02:00
public byte [ ] encodeUploadCancel ( int token ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_UPLOADCANCEL ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_UPLOADCANCEL ) ;
buf . putShort ( ENDPOINT_PUTBYTES ) ;
buf . put ( PUTBYTES_ABORT ) ;
buf . putInt ( token ) ;
return buf . array ( ) ;
}
2015-04-17 12:23:19 +02:00
private byte [ ] encodeSystemMessage ( byte systemMessage ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_SYSTEMMESSAGE ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_SYSTEMMESSAGE ) ;
buf . putShort ( ENDPOINT_SYSTEMMESSAGE ) ;
buf . put ( ( byte ) 0 ) ;
buf . put ( systemMessage ) ;
return buf . array ( ) ;
}
public byte [ ] encodeInstallFirmwareStart ( ) {
return encodeSystemMessage ( SYSTEMMESSAGE_FIRMWARESTART ) ;
}
public byte [ ] encodeInstallFirmwareComplete ( ) {
return encodeSystemMessage ( SYSTEMMESSAGE_FIRMWARECOMPLETE ) ;
}
public byte [ ] encodeInstallFirmwareError ( ) {
return encodeSystemMessage ( SYSTEMMESSAGE_FIRMWAREFAIL ) ;
}
2015-04-07 19:33:23 +02:00
public byte [ ] encodeAppRefresh ( int index ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_REFRESHAPP ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_REFRESHAPP ) ;
buf . putShort ( ENDPOINT_APPMANAGER ) ;
buf . put ( APPMANAGER_REFRESHAPP ) ;
buf . putInt ( index ) ;
return buf . array ( ) ;
}
2015-04-06 23:37:17 +02:00
2015-04-26 01:43:24 +02:00
public byte [ ] encodeDatalog ( byte handle , byte reply ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + 2 ) ;
2015-04-26 00:53:48 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
2015-04-26 01:43:24 +02:00
buf . putShort ( ( short ) 2 ) ;
2015-04-26 00:53:48 +02:00
buf . putShort ( ENDPOINT_DATALOG ) ;
2015-04-26 01:43:24 +02:00
buf . put ( reply ) ;
buf . put ( handle ) ;
2015-04-26 00:53:48 +02:00
return buf . array ( ) ;
}
2015-05-21 18:17:39 +02:00
byte [ ] encodeApplicationMessageAck ( UUID uuid , byte id ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + 18 ) ; // +ACK
2015-05-05 14:41:10 +02:00
buf . order ( ByteOrder . BIG_ENDIAN ) ;
2015-05-06 23:45:25 +02:00
buf . putShort ( ( short ) 18 ) ;
2015-05-05 14:41:10 +02:00
buf . putShort ( ENDPOINT_APPLICATIONMESSAGE ) ;
2015-05-06 23:45:25 +02:00
buf . put ( APPLICATIONMESSAGE_ACK ) ;
2015-05-21 18:17:39 +02:00
buf . put ( id ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
2015-05-11 23:30:38 +02:00
return buf . array ( ) ;
}
2015-08-09 21:42:27 +02:00
private static byte [ ] encodePing ( byte command , int cookie ) {
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + LENGTH_PING ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( LENGTH_PING ) ;
buf . putShort ( ENDPOINT_PING ) ;
buf . put ( command ) ;
buf . putInt ( cookie ) ;
return buf . array ( ) ;
}
2015-05-11 23:30:38 +02:00
2015-05-21 18:17:39 +02:00
private ArrayList < Pair < Integer , Object > > decodeDict ( ByteBuffer buf ) {
2015-11-23 22:46:12 +01:00
ArrayList < Pair < Integer , Object > > dict = new ArrayList < > ( ) ;
2015-05-21 18:17:39 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
byte dictSize = buf . get ( ) ;
while ( dictSize - - > 0 ) {
Integer key = buf . getInt ( ) ;
byte type = buf . get ( ) ;
2015-09-17 19:21:22 +02:00
short length = buf . getShort ( ) ;
2015-05-21 18:17:39 +02:00
switch ( type ) {
2015-09-17 19:21:22 +02:00
case TYPE_INT :
case TYPE_UINT :
2015-05-21 18:17:39 +02:00
dict . add ( new Pair < Integer , Object > ( key , buf . getInt ( ) ) ) ;
break ;
case TYPE_CSTRING :
case TYPE_BYTEARRAY :
byte [ ] bytes = new byte [ length ] ;
buf . get ( bytes ) ;
if ( type = = TYPE_BYTEARRAY ) {
dict . add ( new Pair < Integer , Object > ( key , bytes ) ) ;
} else {
dict . add ( new Pair < Integer , Object > ( key , Arrays . toString ( bytes ) ) ) ;
}
break ;
default :
}
}
return dict ;
}
2015-09-17 19:21:22 +02:00
private GBDeviceEvent [ ] decodeDictToJSONAppMessage ( UUID uuid , ByteBuffer buf ) throws JSONException {
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
byte dictSize = buf . get ( ) ;
if ( dictSize = = 0 ) {
LOG . info ( "dict size is 0, ignoring" ) ;
return null ;
}
JSONArray jsonArray = new JSONArray ( ) ;
while ( dictSize - - > 0 ) {
JSONObject jsonObject = new JSONObject ( ) ;
Integer key = buf . getInt ( ) ;
byte type = buf . get ( ) ;
short length = buf . getShort ( ) ;
jsonObject . put ( "key" , key ) ;
jsonObject . put ( "length" , length ) ;
switch ( type ) {
case TYPE_UINT :
jsonObject . put ( "type" , "uint" ) ;
if ( length = = 1 ) {
jsonObject . put ( "value" , buf . get ( ) & 0xff ) ;
} else if ( length = = 2 ) {
jsonObject . put ( "value" , buf . getShort ( ) & 0xffff ) ;
} else {
jsonObject . put ( "value" , buf . getInt ( ) & 0xffffffffL ) ;
}
break ;
case TYPE_INT :
jsonObject . put ( "type" , "int" ) ;
if ( length = = 1 ) {
jsonObject . put ( "value" , buf . get ( ) ) ;
} else if ( length = = 2 ) {
jsonObject . put ( "value" , buf . getShort ( ) ) ;
} else {
jsonObject . put ( "value" , buf . getInt ( ) ) ;
}
break ;
case TYPE_BYTEARRAY :
case TYPE_CSTRING :
byte [ ] bytes = new byte [ length ] ;
buf . get ( bytes ) ;
if ( type = = TYPE_BYTEARRAY ) {
jsonObject . put ( "type" , "bytes" ) ;
jsonObject . put ( "value" , Base64 . encode ( bytes , Base64 . NO_WRAP ) ) ;
} else {
jsonObject . put ( "type" , "string" ) ;
jsonObject . put ( "value" , Arrays . toString ( bytes ) ) ;
}
break ;
default :
LOG . info ( "unknown type in appmessage, ignoring" ) ;
return null ;
}
jsonArray . put ( jsonObject ) ;
}
// this is a hack we send an ack to the Pebble immediately because we cannot map the transaction_id from the intent back to a uuid yet
GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes ( ) ;
sendBytesAck . encodedBytes = encodeApplicationMessageAck ( uuid , last_id ) ;
GBDeviceEventAppMessage appMessage = new GBDeviceEventAppMessage ( ) ;
appMessage . appUUID = uuid ;
appMessage . id = last_id & 0xff ;
appMessage . message = jsonArray . toString ( ) ;
return new GBDeviceEvent [ ] { appMessage , sendBytesAck } ;
}
2015-05-21 18:17:39 +02:00
byte [ ] encodeApplicationMessagePush ( short endpoint , UUID uuid , ArrayList < Pair < Integer , Object > > pairs ) {
2015-08-14 12:50:44 +02:00
int length = LENGTH_UUID + 3 ; // UUID + (PUSH + id + length of dict)
2015-05-11 23:30:38 +02:00
for ( Pair < Integer , Object > pair : pairs ) {
length + = 7 ; // key + type + length
if ( pair . second instanceof Integer ) {
length + = 4 ;
2015-09-19 15:32:09 +02:00
} else if ( pair . second instanceof Short ) {
2015-09-18 00:03:34 +02:00
length + = 2 ;
2015-09-19 15:32:09 +02:00
} else if ( pair . second instanceof Byte ) {
2015-09-18 00:03:34 +02:00
length + = 1 ;
2015-05-11 23:30:38 +02:00
} else if ( pair . second instanceof String ) {
2015-09-19 15:32:09 +02:00
length + = ( ( String ) pair . second ) . getBytes ( ) . length + 1 ;
2015-09-13 18:37:59 +02:00
} else if ( pair . second instanceof byte [ ] ) {
length + = ( ( byte [ ] ) pair . second ) . length ;
2015-05-11 23:30:38 +02:00
}
}
2015-09-13 18:37:59 +02:00
2015-05-11 23:30:38 +02:00
ByteBuffer buf = ByteBuffer . allocate ( LENGTH_PREFIX + length ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
buf . putShort ( ( short ) length ) ;
buf . putShort ( endpoint ) ; // 48 or 49
2015-05-05 14:41:10 +02:00
buf . put ( APPLICATIONMESSAGE_PUSH ) ;
buf . put ( + + last_id ) ;
2015-05-12 11:06:22 +02:00
buf . putLong ( uuid . getMostSignificantBits ( ) ) ;
buf . putLong ( uuid . getLeastSignificantBits ( ) ) ;
2015-05-11 23:30:38 +02:00
buf . put ( ( byte ) pairs . size ( ) ) ;
2015-09-18 00:03:34 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
2015-05-11 23:30:38 +02:00
for ( Pair < Integer , Object > pair : pairs ) {
buf . putInt ( pair . first ) ;
if ( pair . second instanceof Integer ) {
2015-09-17 19:21:22 +02:00
buf . put ( TYPE_INT ) ;
2015-09-18 00:03:34 +02:00
buf . putShort ( ( short ) 4 ) ; // length
2015-05-11 23:30:38 +02:00
buf . putInt ( ( int ) pair . second ) ;
2015-09-18 00:03:34 +02:00
} else if ( pair . second instanceof Short ) {
buf . put ( TYPE_INT ) ;
buf . putShort ( ( short ) 2 ) ; // length
buf . putShort ( ( short ) pair . second ) ;
} else if ( pair . second instanceof Byte ) {
buf . put ( TYPE_INT ) ;
buf . putShort ( ( short ) 1 ) ; // length
buf . put ( ( byte ) pair . second ) ;
2015-05-11 23:30:38 +02:00
} else if ( pair . second instanceof String ) {
2015-09-13 18:37:59 +02:00
String str = ( String ) pair . second ;
2015-05-21 18:17:39 +02:00
buf . put ( TYPE_CSTRING ) ;
2015-09-19 15:32:09 +02:00
buf . putShort ( ( short ) ( str . getBytes ( ) . length + 1 ) ) ;
2015-09-13 18:37:59 +02:00
buf . put ( str . getBytes ( ) ) ;
2015-05-11 23:30:38 +02:00
buf . put ( ( byte ) 0 ) ;
2015-09-13 18:37:59 +02:00
} else if ( pair . second instanceof byte [ ] ) {
byte [ ] bytes = ( byte [ ] ) pair . second ;
buf . put ( TYPE_BYTEARRAY ) ;
buf . putShort ( ( short ) bytes . length ) ;
buf . put ( bytes ) ;
2015-05-11 23:30:38 +02:00
}
}
2015-05-05 14:41:10 +02:00
return buf . array ( ) ;
}
2015-09-13 18:20:15 +02:00
public byte [ ] encodeApplicationMessageFromJSON ( UUID uuid , JSONArray jsonArray ) {
ArrayList < Pair < Integer , Object > > pairs = new ArrayList < > ( ) ;
for ( int i = 0 ; i < jsonArray . length ( ) ; i + + ) {
try {
JSONObject jsonObject = ( JSONObject ) jsonArray . get ( i ) ;
String type = ( String ) jsonObject . get ( "type" ) ;
int key = ( int ) jsonObject . get ( "key" ) ;
2015-09-18 00:03:34 +02:00
int length = ( int ) jsonObject . get ( "length" ) ;
switch ( type ) {
case "uint" :
case "int" :
if ( length = = 1 ) {
pairs . add ( new Pair < > ( key , ( Object ) ( byte ) jsonObject . getInt ( "value" ) ) ) ;
} else if ( length = = 2 ) {
pairs . add ( new Pair < > ( key , ( Object ) ( short ) jsonObject . getInt ( "value" ) ) ) ;
} else {
if ( type . equals ( "uint" ) ) {
pairs . add ( new Pair < > ( key , ( Object ) ( int ) ( jsonObject . getInt ( "value" ) & 0xffffffffL ) ) ) ;
} else {
pairs . add ( new Pair < > ( key , ( Object ) jsonObject . getInt ( "value" ) ) ) ;
}
}
break ;
case "string" :
pairs . add ( new Pair < > ( key , ( Object ) jsonObject . getString ( "value" ) ) ) ;
break ;
case "bytes" :
byte [ ] bytes = Base64 . decode ( jsonObject . getString ( "value" ) , Base64 . NO_WRAP ) ;
pairs . add ( new Pair < > ( key , ( Object ) bytes ) ) ;
break ;
2015-09-13 18:20:15 +02:00
}
} catch ( JSONException e ) {
return null ;
}
}
return encodeApplicationMessagePush ( ENDPOINT_APPLICATIONMESSAGE , uuid , pairs ) ;
}
2015-06-25 23:34:50 +02:00
private static byte reverseBits ( byte in ) {
byte out = 0 ;
for ( int i = 0 ; i < 8 ; i + + ) {
byte bit = ( byte ) ( in & 1 ) ;
out = ( byte ) ( ( out < < 1 ) | bit ) ;
in = ( byte ) ( in > > 1 ) ;
}
return out ;
}
2015-05-12 11:06:22 +02:00
2015-08-26 23:17:32 +02:00
private GBDeviceEventScreenshot decodeScreenshot ( ByteBuffer buf , int length ) {
2015-06-24 23:55:51 +02:00
if ( mDevEventScreenshot = = null ) {
byte result = buf . get ( ) ;
mDevEventScreenshot = new GBDeviceEventScreenshot ( ) ;
int version = buf . getInt ( ) ;
2015-08-22 00:14:14 +02:00
if ( result ! = 0 ) {
2015-06-24 23:55:51 +02:00
return null ;
}
mDevEventScreenshot . width = buf . getInt ( ) ;
mDevEventScreenshot . height = buf . getInt ( ) ;
2015-08-22 00:14:14 +02:00
if ( version = = 1 ) {
mDevEventScreenshot . bpp = 1 ;
mDevEventScreenshot . clut = clut_pebble ;
} else {
mDevEventScreenshot . bpp = 8 ;
mDevEventScreenshot . clut = clut_pebbletime ;
2015-06-24 23:55:51 +02:00
}
2015-08-22 00:14:14 +02:00
mScreenshotRemaining = ( mDevEventScreenshot . width * mDevEventScreenshot . height * mDevEventScreenshot . bpp ) / 8 ;
2015-06-24 23:55:51 +02:00
mDevEventScreenshot . data = new byte [ mScreenshotRemaining ] ;
length - = 13 ;
}
if ( mScreenshotRemaining = = - 1 ) {
return null ;
}
2015-06-25 23:34:50 +02:00
for ( int i = 0 ; i < length ; i + + ) {
2015-08-22 00:14:14 +02:00
byte corrected = buf . get ( ) ;
if ( mDevEventScreenshot . bpp = = 1 ) {
corrected = reverseBits ( corrected ) ;
} else {
corrected = ( byte ) ( corrected & 0b00111111 ) ;
}
2015-06-25 23:34:50 +02:00
mDevEventScreenshot . data [ mDevEventScreenshot . data . length - mScreenshotRemaining + i ] = corrected ;
}
2015-06-24 23:55:51 +02:00
mScreenshotRemaining - = length ;
LOG . info ( "Screenshot remaining bytes " + mScreenshotRemaining ) ;
if ( mScreenshotRemaining = = 0 ) {
mScreenshotRemaining = - 1 ;
LOG . info ( "Got screenshot : " + mDevEventScreenshot . width + "x" + mDevEventScreenshot . height + " " + "pixels" ) ;
GBDeviceEventScreenshot devEventScreenshot = mDevEventScreenshot ;
mDevEventScreenshot = null ;
return devEventScreenshot ;
}
return null ;
}
2015-09-05 20:40:12 +02:00
private GBDeviceEvent [ ] decodeNotificationAction2x ( ByteBuffer buf ) {
2015-07-21 01:33:13 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
byte command = buf . get ( ) ;
2015-08-31 22:27:25 +02:00
if ( command = = 0x02 ) {
2015-08-27 15:02:29 +02:00
int id = buf . getInt ( ) ;
2015-08-31 22:27:25 +02:00
byte action = buf . get ( ) ;
2015-09-25 00:53:40 +02:00
if ( action > = 0x01 & & action < = 0x04 ) {
2015-08-31 22:27:25 +02:00
GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl ( ) ;
devEvtNotificationControl . handle = id ;
2015-09-05 20:40:12 +02:00
GBDeviceEventSendBytes sendBytesAck = null ;
2015-08-31 22:27:25 +02:00
switch ( action ) {
case 0x01 :
devEvtNotificationControl . event = GBDeviceEventNotificationControl . Event . OPEN ;
2015-09-05 20:40:12 +02:00
sendBytesAck = new GBDeviceEventSendBytes ( ) ;
2015-09-25 00:53:40 +02:00
sendBytesAck . encodedBytes = encodeActionResponse2x ( id , action , 6 , "Opened" ) ;
2015-08-31 22:27:25 +02:00
break ;
case 0x02 :
devEvtNotificationControl . event = GBDeviceEventNotificationControl . Event . DISMISS ;
break ;
2015-09-01 21:58:36 +02:00
case 0x03 :
devEvtNotificationControl . event = GBDeviceEventNotificationControl . Event . DISMISS_ALL ;
break ;
2015-09-25 00:53:40 +02:00
case 0x04 :
devEvtNotificationControl . event = GBDeviceEventNotificationControl . Event . MUTE ;
sendBytesAck = new GBDeviceEventSendBytes ( ) ;
sendBytesAck . encodedBytes = encodeActionResponse2x ( id , action , 6 , "Muted" ) ;
break ;
2015-08-31 22:27:25 +02:00
default :
return null ;
}
2015-09-05 20:40:12 +02:00
return new GBDeviceEvent [ ] { sendBytesAck , devEvtNotificationControl } ;
2015-07-22 20:53:18 +02:00
}
2015-08-27 15:02:29 +02:00
LOG . info ( "unexpected paramerter in dismiss action: " + action ) ;
}
return null ;
}
2015-08-27 18:01:19 +02:00
private GBDeviceEvent [ ] decodeNotificationAction3x ( ByteBuffer buf ) {
2015-08-27 15:02:29 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
byte command = buf . get ( ) ;
2015-08-27 18:01:19 +02:00
if ( command = = NOTIFICATIONACTION_INVOKE ) {
buf . order ( ByteOrder . BIG_ENDIAN ) ;
long uuid_high = buf . getLong ( ) ;
long uuid_low = buf . getLong ( ) ;
2015-10-04 15:53:11 +02:00
int id = ( int ) ( uuid_low & 0xffffffffL ) ;
2015-08-27 18:01:19 +02:00
byte action = buf . get ( ) ;
2015-09-25 00:53:40 +02:00
if ( action > = 0x01 & & action < = 0x04 ) {
2015-08-31 22:27:25 +02:00
GBDeviceEventNotificationControl dismissNotification = new GBDeviceEventNotificationControl ( ) ;
dismissNotification . handle = id ;
2015-09-01 21:58:36 +02:00
String caption = "undefined" ;
int icon_id = 1 ;
switch ( action ) {
case 0x01 :
dismissNotification . event = GBDeviceEventNotificationControl . Event . OPEN ;
caption = "Opened" ;
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . DURING_PHONE_CALL ;
2015-09-01 21:58:36 +02:00
break ;
case 0x02 :
dismissNotification . event = GBDeviceEventNotificationControl . Event . DISMISS ;
caption = "Dismissed" ;
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . RESULT_DISMISSED ;
2015-09-01 21:58:36 +02:00
break ;
case 0x03 :
dismissNotification . event = GBDeviceEventNotificationControl . Event . DISMISS_ALL ;
caption = "All dismissed" ;
2015-09-13 15:21:07 +02:00
icon_id = PebbleIconID . RESULT_DISMISSED ;
2015-09-01 21:58:36 +02:00
break ;
2015-09-25 00:53:40 +02:00
case 0x04 :
dismissNotification . event = GBDeviceEventNotificationControl . Event . MUTE ;
caption = "Muted" ;
icon_id = PebbleIconID . RESULT_MUTE ;
break ;
2015-09-01 21:58:36 +02:00
}
2015-08-27 18:01:19 +02:00
GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes ( ) ;
2015-09-01 21:58:36 +02:00
sendBytesAck . encodedBytes = encodeActionResponse ( new UUID ( uuid_high , uuid_low ) , icon_id , caption ) ;
2015-08-27 18:01:19 +02:00
return new GBDeviceEvent [ ] { sendBytesAck , dismissNotification } ;
2015-07-21 01:33:13 +02:00
}
2015-08-27 18:01:19 +02:00
LOG . info ( "unexpected action: " + action ) ;
2015-07-21 01:33:13 +02:00
}
return null ;
}
2015-08-26 23:17:32 +02:00
private GBDeviceEventSendBytes decodePing ( ByteBuffer buf ) {
2015-08-09 21:42:27 +02:00
byte command = buf . get ( ) ;
if ( command = = PING_PING ) {
int cookie = buf . getInt ( ) ;
LOG . info ( "Received PING - will reply" ) ;
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes ( ) ;
sendBytes . encodedBytes = encodePing ( PING_PONG , cookie ) ;
return sendBytes ;
}
return null ;
}
2015-08-16 00:32:36 +02:00
private GBDeviceEventAppManagement decodeAppFetch ( ByteBuffer buf ) {
2015-08-11 13:21:29 +02:00
byte command = buf . get ( ) ;
if ( command = = 0x01 ) {
long uuid_high = buf . getLong ( ) ;
long uuid_low = buf . getLong ( ) ;
UUID uuid = new UUID ( uuid_high , uuid_low ) ;
2015-08-17 12:55:17 +02:00
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
2015-08-11 13:21:29 +02:00
int app_id = buf . getInt ( ) ;
2015-08-16 00:32:36 +02:00
GBDeviceEventAppManagement fetchRequest = new GBDeviceEventAppManagement ( ) ;
fetchRequest . type = GBDeviceEventAppManagement . EventType . INSTALL ;
fetchRequest . event = GBDeviceEventAppManagement . Event . REQUEST ;
fetchRequest . token = app_id ;
fetchRequest . uuid = uuid ;
return fetchRequest ;
2015-08-11 13:21:29 +02:00
}
return null ;
}
2015-08-26 23:17:32 +02:00
private GBDeviceEventSendBytes decodeDatalog ( ByteBuffer buf , short length ) {
byte command = buf . get ( ) ;
byte id = buf . get ( ) ;
2015-08-27 15:02:29 +02:00
switch ( command ) {
case DATALOG_TIMEOUT :
LOG . info ( "DATALOG TIMEOUT. id=" + ( id & 0xff ) + " - ignoring" ) ;
return null ;
case DATALOG_SENDDATA :
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
int items_left = buf . getInt ( ) ;
int crc = buf . getInt ( ) ;
LOG . info ( "DATALOG SENDDATA. id=" + ( id & 0xff ) + ", items_left=" + items_left + ", total length=" + ( length - 9 ) ) ;
break ;
case DATALOG_OPENSESSION :
buf . order ( ByteOrder . BIG_ENDIAN ) ;
long uuid_high = buf . getLong ( ) ;
long uuid_low = buf . getLong ( ) ;
UUID uuid = new UUID ( uuid_high , uuid_low ) ;
buf . order ( ByteOrder . LITTLE_ENDIAN ) ;
int timestamp = buf . getInt ( ) ;
int log_tag = buf . getInt ( ) ;
byte item_type = buf . get ( ) ;
short item_size = buf . get ( ) ;
LOG . info ( "DATALOG OPENSESSION. id=" + ( id & 0xff ) + ", App UUID=" + uuid . toString ( ) + ", item_type=" + item_type + ", item_size=" + item_size ) ;
break ;
default :
LOG . info ( "unknown DATALOG command: " + ( command & 0xff ) ) ;
break ;
2015-08-26 23:17:32 +02:00
}
LOG . info ( "sending ACK (0x85)" ) ;
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes ( ) ;
sendBytes . encodedBytes = encodeDatalog ( id , DATALOG_ACK ) ;
return sendBytes ;
}
2015-05-12 06:28:11 +02:00
@Override
2015-08-27 15:02:29 +02:00
public GBDeviceEvent [ ] decodeResponse ( byte [ ] responseData ) {
2015-01-07 14:00:18 +01:00
ByteBuffer buf = ByteBuffer . wrap ( responseData ) ;
buf . order ( ByteOrder . BIG_ENDIAN ) ;
short length = buf . getShort ( ) ;
short endpoint = buf . getShort ( ) ;
2015-08-27 15:02:29 +02:00
GBDeviceEvent devEvts [ ] = null ;
2015-06-24 23:55:51 +02:00
byte pebbleCmd = - 1 ;
2015-01-07 14:00:18 +01:00
switch ( endpoint ) {
2015-02-12 16:00:45 +01:00
case ENDPOINT_MUSICCONTROL :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-06-23 11:54:33 +02:00
GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl ( ) ;
2015-02-12 16:00:45 +01:00
switch ( pebbleCmd ) {
case MUSICCONTROL_NEXT :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . NEXT ;
2015-02-12 16:00:45 +01:00
break ;
case MUSICCONTROL_PREVIOUS :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . PREVIOUS ;
2015-02-12 16:00:45 +01:00
break ;
case MUSICCONTROL_PLAY :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . PLAY ;
2015-02-12 16:00:45 +01:00
break ;
case MUSICCONTROL_PAUSE :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . PAUSE ;
2015-02-12 16:00:45 +01:00
break ;
case MUSICCONTROL_PLAYPAUSE :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . PLAYPAUSE ;
2015-02-12 16:00:45 +01:00
break ;
2015-04-13 22:25:23 +02:00
case MUSICCONTROL_VOLUMEUP :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . VOLUMEUP ;
2015-04-13 22:25:23 +02:00
break ;
case MUSICCONTROL_VOLUMEDOWN :
2015-06-23 11:54:33 +02:00
musicCmd . event = GBDeviceEventMusicControl . Event . VOLUMEDOWN ;
2015-04-13 22:25:23 +02:00
break ;
2015-02-12 16:00:45 +01:00
default :
break ;
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { musicCmd } ;
2015-01-07 14:00:18 +01:00
break ;
2015-03-07 17:44:39 +01:00
case ENDPOINT_PHONECONTROL :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-06-23 11:54:33 +02:00
GBDeviceEventCallControl callCmd = new GBDeviceEventCallControl ( ) ;
2015-03-07 17:44:39 +01:00
switch ( pebbleCmd ) {
case PHONECONTROL_HANGUP :
2015-06-23 11:54:33 +02:00
callCmd . event = GBDeviceEventCallControl . Event . END ;
2015-03-07 17:44:39 +01:00
break ;
default :
2015-06-23 11:54:33 +02:00
LOG . info ( "Unknown PHONECONTROL event" + pebbleCmd ) ;
2015-03-07 17:44:39 +01:00
break ;
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { callCmd } ;
2015-03-07 17:44:39 +01:00
break ;
2015-03-22 00:34:54 +01:00
case ENDPOINT_FIRMWAREVERSION :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-06-23 11:54:33 +02:00
GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo ( ) ;
2015-03-26 12:06:26 +01:00
2015-04-20 12:48:32 +02:00
buf . getInt ( ) ; // skip
byte [ ] tmp = new byte [ 32 ] ;
buf . get ( tmp , 0 , 32 ) ;
2015-03-22 00:34:54 +01:00
2015-04-20 12:48:32 +02:00
versionCmd . fwVersion = new String ( tmp ) . trim ( ) ;
2015-06-18 18:39:32 +02:00
if ( versionCmd . fwVersion . startsWith ( "v3" ) ) {
2015-06-11 20:40:31 +02:00
isFw3x = true ;
}
2015-04-20 12:48:32 +02:00
buf . get ( tmp , 0 , 9 ) ;
2015-09-23 23:19:38 +02:00
int hwRev = buf . get ( ) + 5 ;
if ( hwRev > = 0 & & hwRev < hwRevisions . length ) {
versionCmd . hwVersion = hwRevisions [ hwRev ] ;
2015-04-20 12:48:32 +02:00
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { versionCmd } ;
2015-03-22 00:34:54 +01:00
break ;
2015-03-25 22:23:45 +01:00
case ENDPOINT_APPMANAGER :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-03-25 22:23:45 +01:00
switch ( pebbleCmd ) {
case APPMANAGER_GETAPPBANKSTATUS :
2015-06-23 11:54:33 +02:00
GBDeviceEventAppInfo appInfoCmd = new GBDeviceEventAppInfo ( ) ;
2015-04-06 20:58:35 +02:00
int slotCount = buf . getInt ( ) ;
int slotsUsed = buf . getInt ( ) ;
2015-03-25 22:23:45 +01:00
byte [ ] appName = new byte [ 32 ] ;
2015-03-31 23:34:19 +02:00
byte [ ] appCreator = new byte [ 32 ] ;
2015-04-06 20:58:35 +02:00
appInfoCmd . apps = new GBDeviceApp [ slotsUsed ] ;
boolean [ ] slotInUse = new boolean [ slotCount ] ;
2015-03-25 22:23:45 +01:00
2015-04-06 20:58:35 +02:00
for ( int i = 0 ; i < slotsUsed ; i + + ) {
2015-03-26 18:11:47 +01:00
int id = buf . getInt ( ) ;
int index = buf . getInt ( ) ;
2015-04-06 20:58:35 +02:00
slotInUse [ index ] = true ;
2015-03-25 22:23:45 +01:00
buf . get ( appName , 0 , 32 ) ;
2015-03-31 23:34:19 +02:00
buf . get ( appCreator , 0 , 32 ) ;
2015-03-25 22:23:45 +01:00
int flags = buf . getInt ( ) ;
2015-03-31 23:34:19 +02:00
GBDeviceApp . Type appType ;
2015-04-10 22:26:52 +02:00
if ( ( flags & 16 ) = = 16 ) { // FIXME: verify this assumption
appType = GBDeviceApp . Type . APP_ACTIVITYTRACKER ;
} else if ( ( flags & 1 ) = = 1 ) { // FIXME: verify this assumption
appType = GBDeviceApp . Type . WATCHFACE ;
} else {
appType = GBDeviceApp . Type . APP_GENERIC ;
2015-03-31 23:34:19 +02:00
}
2015-03-26 12:06:26 +01:00
Short appVersion = buf . getShort ( ) ;
2015-05-18 20:56:19 +02:00
appInfoCmd . apps [ i ] = new GBDeviceApp ( tmpUUIDS . get ( i ) , new String ( appName ) . trim ( ) , new String ( appCreator ) . trim ( ) , appVersion . toString ( ) , appType ) ;
2015-03-25 22:23:45 +01:00
}
2015-04-06 20:58:35 +02:00
for ( int i = 0 ; i < slotCount ; i + + ) {
if ( ! slotInUse [ i ] ) {
appInfoCmd . freeSlot = ( byte ) i ;
2015-05-12 06:28:11 +02:00
LOG . info ( "found free slot " + i ) ;
2015-04-06 20:58:35 +02:00
break ;
}
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { appInfoCmd } ;
2015-03-25 22:23:45 +01:00
break ;
2015-05-18 20:56:19 +02:00
case APPMANAGER_GETUUIDS :
2015-06-23 11:54:33 +02:00
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes ( ) ;
2015-06-19 23:54:31 +02:00
sendBytes . encodedBytes = encodeSimpleMessage ( ENDPOINT_APPMANAGER , APPMANAGER_GETAPPBANKSTATUS ) ;
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { sendBytes } ;
2015-05-18 20:56:19 +02:00
tmpUUIDS . clear ( ) ;
slotsUsed = buf . getInt ( ) ;
for ( int i = 0 ; i < slotsUsed ; i + + ) {
long uuid_high = buf . getLong ( ) ;
long uuid_low = buf . getLong ( ) ;
UUID uuid = new UUID ( uuid_high , uuid_low ) ;
LOG . info ( "found uuid: " + uuid ) ;
tmpUUIDS . add ( uuid ) ;
}
break ;
2015-03-26 18:11:47 +01:00
case APPMANAGER_REMOVEAPP :
2015-08-16 00:32:36 +02:00
GBDeviceEventAppManagement deleteRes = new GBDeviceEventAppManagement ( ) ;
deleteRes . type = GBDeviceEventAppManagement . EventType . DELETE ;
2015-03-26 18:11:47 +01:00
int result = buf . getInt ( ) ;
switch ( result ) {
case APPMANAGER_RES_SUCCESS :
2015-08-16 00:32:36 +02:00
deleteRes . event = GBDeviceEventAppManagement . Event . SUCCESS ;
2015-03-26 18:11:47 +01:00
break ;
default :
2015-08-16 00:32:36 +02:00
deleteRes . event = GBDeviceEventAppManagement . Event . FAILURE ;
2015-03-26 18:11:47 +01:00
break ;
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { deleteRes } ;
2015-03-26 18:11:47 +01:00
break ;
2015-03-25 22:23:45 +01:00
default :
2015-06-23 11:54:33 +02:00
LOG . info ( "Unknown APPMANAGER event" + pebbleCmd ) ;
2015-03-25 22:23:45 +01:00
break ;
}
break ;
2015-04-06 20:58:35 +02:00
case ENDPOINT_PUTBYTES :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-08-16 00:32:36 +02:00
GBDeviceEventAppManagement installRes = new GBDeviceEventAppManagement ( ) ;
installRes . type = GBDeviceEventAppManagement . EventType . INSTALL ;
2015-04-06 20:58:35 +02:00
switch ( pebbleCmd ) {
case PUTBYTES_INIT :
installRes . token = buf . getInt ( ) ;
2015-08-16 00:32:36 +02:00
installRes . event = GBDeviceEventAppManagement . Event . SUCCESS ;
2015-04-06 20:58:35 +02:00
break ;
default :
installRes . token = buf . getInt ( ) ;
2015-08-16 00:32:36 +02:00
installRes . event = GBDeviceEventAppManagement . Event . FAILURE ;
2015-04-06 20:58:35 +02:00
break ;
}
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { installRes } ;
2015-04-06 20:58:35 +02:00
break ;
2015-05-05 14:41:10 +02:00
case ENDPOINT_APPLICATIONMESSAGE :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-05-05 14:41:10 +02:00
last_id = buf . get ( ) ;
2015-05-12 11:06:22 +02:00
long uuid_high = buf . getLong ( ) ;
long uuid_low = buf . getLong ( ) ;
2015-05-21 18:17:39 +02:00
2015-05-05 14:41:10 +02:00
switch ( pebbleCmd ) {
case APPLICATIONMESSAGE_PUSH :
2015-05-12 11:06:22 +02:00
UUID uuid = new UUID ( uuid_high , uuid_low ) ;
2015-05-21 18:17:39 +02:00
LOG . info ( "got APPLICATIONMESSAGE PUSH from UUID " + uuid ) ;
2015-10-04 15:53:11 +02:00
AppMessageHandler handler = mAppMessageHandlers . get ( uuid ) ;
if ( handler ! = null ) {
2015-07-15 00:32:13 +02:00
ArrayList < Pair < Integer , Object > > dict = decodeDict ( buf ) ;
2015-10-04 15:53:11 +02:00
devEvts = handler . handleMessage ( dict ) ;
2015-09-17 19:21:22 +02:00
} else {
try {
devEvts = decodeDictToJSONAppMessage ( uuid , buf ) ;
} catch ( JSONException e ) {
e . printStackTrace ( ) ;
return null ;
}
2015-05-06 11:41:38 +02:00
}
2015-05-05 14:41:10 +02:00
break ;
case APPLICATIONMESSAGE_ACK :
2015-05-12 06:28:11 +02:00
LOG . info ( "got APPLICATIONMESSAGE ACK" ) ;
2015-05-05 14:41:10 +02:00
break ;
case APPLICATIONMESSAGE_NACK :
2015-05-12 06:28:11 +02:00
LOG . info ( "got APPLICATIONMESSAGE NACK" ) ;
2015-05-05 14:41:10 +02:00
break ;
case APPLICATIONMESSAGE_REQUEST :
2015-05-12 06:28:11 +02:00
LOG . info ( "got APPLICATIONMESSAGE REQUEST" ) ;
2015-05-05 14:41:10 +02:00
break ;
default :
break ;
}
break ;
2015-05-06 11:41:38 +02:00
case ENDPOINT_PHONEVERSION :
2015-06-24 23:55:51 +02:00
pebbleCmd = buf . get ( ) ;
2015-05-06 11:41:38 +02:00
switch ( pebbleCmd ) {
case PHONEVERSION_REQUEST :
2015-05-12 06:28:11 +02:00
LOG . info ( "Pebble asked for Phone/App Version - repLYING!" ) ;
2015-06-23 11:54:33 +02:00
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes ( ) ;
2015-05-06 11:41:38 +02:00
sendBytes . encodedBytes = encodePhoneVersion ( PHONEVERSION_REMOTE_OS_ANDROID ) ;
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { sendBytes } ;
2015-05-06 11:41:38 +02:00
break ;
default :
break ;
}
break ;
2015-08-26 23:17:32 +02:00
case ENDPOINT_DATALOG :
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { decodeDatalog ( buf , length ) } ;
2015-08-26 23:17:32 +02:00
break ;
2015-06-24 23:55:51 +02:00
case ENDPOINT_SCREENSHOT :
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { decodeScreenshot ( buf , length ) } ;
2015-06-24 23:55:51 +02:00
break ;
2015-07-21 01:33:13 +02:00
case ENDPOINT_EXTENSIBLENOTIFS :
2015-09-05 20:40:12 +02:00
devEvts = decodeNotificationAction2x ( buf ) ;
2015-08-27 15:02:29 +02:00
break ;
2015-07-22 20:53:18 +02:00
case ENDPOINT_NOTIFICATIONACTION :
2015-08-27 18:01:19 +02:00
devEvts = decodeNotificationAction3x ( buf ) ;
2015-07-21 01:33:13 +02:00
break ;
2015-08-09 21:42:27 +02:00
case ENDPOINT_PING :
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { decodePing ( buf ) } ;
2015-08-11 13:21:29 +02:00
break ;
case ENDPOINT_APPFETCH :
2015-08-27 15:02:29 +02:00
devEvts = new GBDeviceEvent [ ] { decodeAppFetch ( buf ) } ;
2015-08-09 21:42:27 +02:00
break ;
2015-01-07 14:00:18 +01:00
default :
2015-03-26 18:11:47 +01:00
break ;
2015-01-07 14:00:18 +01:00
}
2015-08-27 15:02:29 +02:00
return devEvts ;
2015-01-07 14:00:18 +01:00
}
2015-06-19 12:34:33 +02:00
public void setForceProtocol ( boolean force ) {
LOG . info ( "setting force protocol to " + force ) ;
mForceProtocol = force ;
}
2015-01-07 14:00:18 +01:00
}