2015-08-03 23:51:53 +02:00
package nodomain.freeyourgadget.gadgetbridge.service.devices.miband ;
2015-04-14 01:24:03 +02:00
2015-05-01 01:49:43 +02:00
import android.bluetooth.BluetoothGatt ;
import android.bluetooth.BluetoothGattCharacteristic ;
2015-05-17 22:57:37 +02:00
import android.content.SharedPreferences ;
2015-06-06 23:24:00 +02:00
import android.net.Uri ;
2015-05-17 22:57:37 +02:00
import android.preference.PreferenceManager ;
2015-06-25 14:48:46 +02:00
import android.widget.Toast ;
2015-05-01 01:49:43 +02:00
2015-05-18 20:56:19 +02:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2015-04-19 02:37:29 +02:00
import java.io.IOException ;
2015-06-25 14:34:21 +02:00
import java.util.ArrayList ;
2015-05-12 20:32:34 +02:00
import java.util.Arrays ;
2015-04-23 14:11:57 +02:00
import java.util.Calendar ;
2015-05-24 00:11:14 +02:00
import java.util.GregorianCalendar ;
2015-04-26 00:53:48 +02:00
import java.util.UUID ;
2015-04-19 02:37:29 +02:00
2015-06-06 00:40:16 +02:00
import nodomain.freeyourgadget.gadgetbridge.R ;
2015-08-18 17:37:51 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo ;
2015-08-14 23:50:21 +02:00
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo ;
2015-08-03 23:09:49 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst ;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator ;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService ;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile ;
2015-08-14 23:50:21 +02:00
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State ;
2015-08-03 23:09:49 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.Alarm ;
2015-08-14 23:50:21 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand ;
2015-08-03 23:09:49 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport ;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction ;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder ;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction ;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction ;
2015-08-18 00:08:22 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation ;
2015-08-18 00:52:02 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.UpdateFirmwareOperation ;
2015-08-14 23:50:21 +02:00
import nodomain.freeyourgadget.gadgetbridge.util.GB ;
2015-08-03 23:09:49 +02:00
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PAUSE ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_PROFILE ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.FLASH_ORIGINAL_COLOUR ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_GENERIC ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_K9MAIL ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_SMS ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_DURATION ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_PAUSE ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_PROFILE ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefIntValue ;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefStringValue ;
2015-05-17 22:57:37 +02:00
2015-04-14 01:24:03 +02:00
public class MiBandSupport extends AbstractBTLEDeviceSupport {
2015-07-21 22:25:26 +02:00
private static final Logger LOG = LoggerFactory . getLogger ( MiBandSupport . class ) ;
2015-06-20 23:22:22 +02:00
private volatile boolean telephoneRinging ;
2015-06-21 00:34:05 +02:00
private volatile boolean isLocatingDevice ;
2015-07-21 22:25:26 +02:00
2015-07-23 17:14:51 +02:00
private DeviceInfo mDeviceInfo ;
2015-08-10 10:35:55 +02:00
GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo ( ) ;
2015-08-18 17:37:51 +02:00
GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo ( ) ;
2015-08-10 10:35:55 +02:00
2015-04-19 02:37:29 +02:00
public MiBandSupport ( ) {
addSupportedService ( MiBandService . UUID_SERVICE_MIBAND_SERVICE ) ;
2015-04-14 02:03:14 +02:00
}
2015-04-14 01:24:03 +02:00
@Override
2015-04-19 02:37:29 +02:00
protected TransactionBuilder initializeDevice ( TransactionBuilder builder ) {
2015-05-28 00:26:41 +02:00
builder . add ( new SetDeviceStateAction ( getDevice ( ) , State . INITIALIZING , getContext ( ) ) ) ;
2015-05-25 23:14:02 +02:00
pair ( builder )
. sendUserInfo ( builder )
2015-07-31 13:48:11 +02:00
. setWearLocation ( builder )
2015-07-29 18:49:16 +02:00
. setFitnessGoal ( builder )
2015-05-25 23:14:02 +02:00
. enableNotifications ( builder , true )
. setCurrentTime ( builder )
2015-05-28 00:26:41 +02:00
. requestBatteryInfo ( builder )
2015-08-14 23:37:47 +02:00
. requestDeviceInfo ( builder )
2015-05-28 00:26:41 +02:00
. setInitialized ( builder ) ;
2015-05-24 00:11:14 +02:00
2015-04-19 02:37:29 +02:00
return builder ;
2015-04-14 01:24:03 +02:00
}
2015-05-28 00:26:41 +02:00
/ * *
* Last action of initialization sequence . Sets the device to initialized .
* It is only invoked if all other actions were successfully run , so the device
* must be initialized , then .
2015-06-13 00:32:48 +02:00
*
2015-05-28 00:26:41 +02:00
* @param builder
* /
private void setInitialized ( TransactionBuilder builder ) {
builder . add ( new SetDeviceStateAction ( getDevice ( ) , State . INITIALIZED , getContext ( ) ) ) ;
}
2015-05-24 00:11:14 +02:00
// TODO: tear down the notifications on quit
private MiBandSupport enableNotifications ( TransactionBuilder builder , boolean enable ) {
builder . notify ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_NOTIFICATION ) , enable )
. notify ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_REALTIME_STEPS ) , enable )
2015-05-24 14:39:36 +02:00
. notify ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_ACTIVITY_DATA ) , enable )
2015-05-24 00:11:14 +02:00
. notify ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_BATTERY ) , enable )
. notify ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_SENSOR_DATA ) , enable ) ;
return this ;
}
2015-04-14 01:24:03 +02:00
@Override
2015-04-19 02:37:29 +02:00
public boolean useAutoConnect ( ) {
return true ;
}
2015-04-19 11:28:03 +02:00
2015-05-05 00:48:02 +02:00
@Override
public void pair ( ) {
for ( int i = 0 ; i < 5 ; i + + ) {
if ( connect ( ) ) {
return ;
}
}
}
2015-08-18 00:52:02 +02:00
public DeviceInfo getDeviceInfo ( ) {
return mDeviceInfo ;
}
2015-04-19 02:37:29 +02:00
private byte [ ] getDefaultNotification ( ) {
final int vibrateTimes = 1 ;
final long vibrateDuration = 250l ;
final int flashTimes = 1 ;
final int flashColour = 0xFFFFFFFF ;
final int originalColour = 0xFFFFFFFF ;
final long flashDuration = 250l ;
return getNotification ( vibrateDuration , vibrateTimes , flashTimes , flashColour , originalColour , flashDuration ) ;
}
2015-04-14 01:24:03 +02:00
2015-06-21 00:34:05 +02:00
private void sendDefaultNotification ( TransactionBuilder builder , short repeat , BtLEAction extraAction ) {
2015-04-19 02:37:29 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
2015-06-21 00:34:05 +02:00
LOG . info ( " Sending notification to MiBand: " + characteristic + " ( " + repeat + " times) " ) ;
byte [ ] defaultNotification = getDefaultNotification ( ) ;
for ( short i = 0 ; i < repeat ; i + + ) {
builder . write ( characteristic , defaultNotification ) ;
builder . add ( extraAction ) ;
}
builder . queue ( getQueue ( ) ) ;
2015-04-19 02:37:29 +02:00
}
2015-06-20 23:22:22 +02:00
/ * *
* Sends a custom notification to the Mi Band .
2015-06-30 12:09:29 +02:00
*
2015-06-20 23:22:22 +02:00
* @param vibrationProfile specifies how and how often the Band shall vibrate .
* @param flashTimes
* @param flashColour
* @param originalColour
* @param flashDuration
2015-06-30 12:09:29 +02:00
* @param extraAction an extra action to be executed after every vibration and flash sequence . Allows to abort the repetition , for example .
2015-06-20 23:22:22 +02:00
* @param builder
* /
private void sendCustomNotification ( VibrationProfile vibrationProfile , int flashTimes , int flashColour , int originalColour , long flashDuration , BtLEAction extraAction , TransactionBuilder builder ) {
BluetoothGattCharacteristic controlPoint = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
2015-06-21 00:34:05 +02:00
for ( short i = 0 ; i < vibrationProfile . getRepeat ( ) ; i + + ) {
2015-06-20 23:22:22 +02:00
int [ ] onOffSequence = vibrationProfile . getOnOffSequence ( ) ;
for ( int j = 0 ; j < onOffSequence . length ; j + + ) {
int on = onOffSequence [ j ] ;
on = Math . min ( 500 , on ) ; // longer than 500ms is not possible
builder . write ( controlPoint , startVibrate ) ;
builder . wait ( on ) ;
builder . write ( controlPoint , stopVibrate ) ;
if ( + + j < onOffSequence . length ) {
int off = Math . max ( onOffSequence [ j ] , 25 ) ; // wait at least 25ms
builder . wait ( off ) ;
}
if ( extraAction ! = null ) {
builder . add ( extraAction ) ;
}
}
}
LOG . info ( " Sending notification to MiBand: " + controlPoint ) ;
builder . queue ( getQueue ( ) ) ;
}
2015-05-17 22:57:37 +02:00
private void sendCustomNotification ( int vibrateDuration , int vibrateTimes , int pause , int flashTimes , int flashColour , int originalColour , long flashDuration , TransactionBuilder builder ) {
BluetoothGattCharacteristic controlPoint = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
int vDuration = Math . min ( 500 , vibrateDuration ) ; // longer than 500ms is not possible
for ( int i = 0 ; i < vibrateTimes ; i + + ) {
builder . write ( controlPoint , startVibrate ) ;
builder . wait ( vDuration ) ;
builder . write ( controlPoint , stopVibrate ) ;
if ( pause > 0 ) {
builder . wait ( pause ) ;
}
}
LOG . info ( " Sending notification to MiBand: " + controlPoint ) ;
builder . queue ( getQueue ( ) ) ;
}
2015-06-13 00:32:48 +02:00
private static final byte [ ] startVibrate = new byte [ ] { MiBandService . COMMAND_SEND_NOTIFICATION , 1 } ;
private static final byte [ ] stopVibrate = new byte [ ] { MiBandService . COMMAND_STOP_MOTOR_VIBRATE } ;
private static final byte [ ] reboot = new byte [ ] { MiBandService . COMMAND_REBOOT } ;
2015-05-17 22:57:37 +02:00
2015-04-19 02:37:29 +02:00
private byte [ ] getNotification ( long vibrateDuration , int vibrateTimes , int flashTimes , int flashColour , int originalColour , long flashDuration ) {
2015-05-21 18:19:07 +02:00
byte [ ] vibrate = new byte [ ] { MiBandService . COMMAND_SEND_NOTIFICATION , ( byte ) 1 } ;
2015-04-19 02:37:29 +02:00
byte r = 6 ;
byte g = 0 ;
byte b = 6 ;
boolean display = true ;
// byte[] flashColor = new byte[]{ 14, r, g, b, display ? (byte) 1 : (byte) 0 };
return vibrate ;
}
/ * *
* Part of device initialization process . Do not call manually .
2015-04-19 11:28:03 +02:00
*
2015-04-19 02:37:29 +02:00
* @param builder
* @return
* /
private MiBandSupport sendUserInfo ( TransactionBuilder builder ) {
2015-05-12 06:28:11 +02:00
LOG . debug ( " Writing User Info! " ) ;
2015-04-19 02:37:29 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_USER_INFO ) ;
2015-05-07 23:51:03 +02:00
builder . write ( characteristic , MiBandCoordinator . getAnyUserInfo ( getDevice ( ) . getAddress ( ) ) . getData ( ) ) ;
2015-04-19 02:37:29 +02:00
return this ;
}
2015-05-05 23:25:54 +02:00
private MiBandSupport requestBatteryInfo ( TransactionBuilder builder ) {
2015-05-12 06:28:11 +02:00
LOG . debug ( " Requesting Battery Info! " ) ;
2015-05-05 23:25:54 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_BATTERY ) ;
builder . read ( characteristic ) ;
return this ;
}
2015-08-14 23:37:47 +02:00
private MiBandSupport requestDeviceInfo ( TransactionBuilder builder ) {
LOG . debug ( " Requesting Device Info! " ) ;
BluetoothGattCharacteristic deviceInfo = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_DEVICE_INFO ) ;
builder . read ( deviceInfo ) ;
BluetoothGattCharacteristic deviceName = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_DEVICE_NAME ) ;
builder . read ( deviceName ) ;
return this ;
}
2015-04-19 02:37:29 +02:00
/ * *
* Part of device initialization process . Do not call manually .
2015-04-19 11:28:03 +02:00
*
2015-05-07 23:51:03 +02:00
* @param transaction
2015-04-19 02:37:29 +02:00
* @return
* /
private MiBandSupport pair ( TransactionBuilder transaction ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Attempting to pair MI device... " ) ;
2015-04-19 02:37:29 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_PAIR ) ;
if ( characteristic ! = null ) {
transaction . write ( characteristic , new byte [ ] { 2 } ) ;
} else {
2015-05-12 06:28:11 +02:00
LOG . info ( " Unable to pair MI device -- characteristic not available " ) ;
2015-04-19 02:37:29 +02:00
}
return this ;
}
2015-08-18 17:37:51 +02:00
2015-07-29 18:49:16 +02:00
/ * *
* Part of device initialization process . Do not call manually .
*
* @param transaction
* @return
* /
2015-04-19 02:37:29 +02:00
2015-07-29 18:49:16 +02:00
private MiBandSupport setFitnessGoal ( TransactionBuilder transaction ) {
LOG . info ( " Attempting to set Fitness Goal... " ) ;
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
if ( characteristic ! = null ) {
int fitnessGoal = MiBandCoordinator . getFitnessGoal ( getDevice ( ) . getAddress ( ) ) ;
transaction . write ( characteristic , new byte [ ] {
MiBandService . COMMAND_SET_FITNESS_GOAL ,
0 ,
2015-08-18 17:37:51 +02:00
( byte ) ( fitnessGoal & 0xff ) ,
2015-07-29 18:49:16 +02:00
( byte ) ( ( fitnessGoal > > > 8 ) & 0xff )
} ) ;
} else {
LOG . info ( " Unable to set Fitness Goal " ) ;
}
return this ;
}
2015-07-31 13:48:11 +02:00
/ * *
* Part of device initialization process . Do not call manually .
*
* @param transaction
* @return
* /
private MiBandSupport setWearLocation ( TransactionBuilder transaction ) {
LOG . info ( " Attempting to set wear location... " ) ;
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
if ( characteristic ! = null ) {
int location = MiBandCoordinator . getWearLocation ( getDevice ( ) . getAddress ( ) ) ;
transaction . write ( characteristic , new byte [ ] {
MiBandService . COMMAND_SET_WEAR_LOCATION ,
( byte ) location
} ) ;
} else {
LOG . info ( " Unable to set Wear Location " ) ;
}
return this ;
}
2015-06-21 00:34:05 +02:00
private void performDefaultNotification ( String task , short repeat , BtLEAction extraAction ) {
2015-04-19 02:37:29 +02:00
try {
TransactionBuilder builder = performInitialized ( task ) ;
2015-06-21 00:34:05 +02:00
sendDefaultNotification ( builder , repeat , extraAction ) ;
2015-04-19 02:37:29 +02:00
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to send notification to MI device " , ex ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-14 01:24:03 +02:00
}
2015-05-17 22:57:37 +02:00
// private void performCustomNotification(String task, int vibrateDuration, int vibrateTimes, int pause, int flashTimes, int flashColour, int originalColour, long flashDuration) {
// try {
// TransactionBuilder builder = performInitialized(task);
// sendCustomNotification(vibrateDuration, vibrateTimes, pause, flashTimes, flashColour, originalColour, flashDuration, builder);
// } catch (IOException ex) {
// LOG.error("Unable to send notification to MI device", ex);
// }
// }
2015-06-20 23:22:22 +02:00
private void performPreferredNotification ( String task , String notificationOrigin , BtLEAction extraAction ) {
2015-05-17 22:57:37 +02:00
try {
TransactionBuilder builder = performInitialized ( task ) ;
SharedPreferences prefs = PreferenceManager . getDefaultSharedPreferences ( getContext ( ) ) ;
int vibrateDuration = getPreferredVibrateDuration ( notificationOrigin , prefs ) ;
int vibratePause = getPreferredVibratePause ( notificationOrigin , prefs ) ;
2015-06-20 23:22:22 +02:00
int vibrateTimes = getPreferredVibrateCount ( notificationOrigin , prefs ) ;
VibrationProfile profile = getPreferredVibrateProfile ( notificationOrigin , prefs , vibrateTimes ) ;
2015-05-17 22:57:37 +02:00
int flashTimes = getPreferredFlashCount ( notificationOrigin , prefs ) ;
int flashColour = getPreferredFlashColour ( notificationOrigin , prefs ) ;
int originalColour = getPreferredOriginalColour ( notificationOrigin , prefs ) ;
int flashDuration = getPreferredFlashDuration ( notificationOrigin , prefs ) ;
2015-06-20 23:22:22 +02:00
sendCustomNotification ( profile , flashTimes , flashColour , originalColour , flashDuration , extraAction , builder ) ;
// sendCustomNotification(vibrateDuration, vibrateTimes, vibratePause, flashTimes, flashColour, originalColour, flashDuration, builder);
2015-05-17 22:57:37 +02:00
} catch ( IOException ex ) {
LOG . error ( " Unable to send notification to MI device " , ex ) ;
}
}
private int getPreferredFlashDuration ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_DURATION , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_DURATION ) ;
}
private int getPreferredOriginalColour ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_ORIGINAL_COLOUR , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR ) ;
}
private int getPreferredFlashColour ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_COLOUR , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_COLOUR ) ;
}
private int getPreferredFlashCount ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( FLASH_COUNT , notificationOrigin , prefs , DEFAULT_VALUE_FLASH_COUNT ) ;
}
private int getPreferredVibratePause ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_PAUSE , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_PAUSE ) ;
}
private int getPreferredVibrateCount ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_COUNT , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_COUNT ) ;
}
private int getPreferredVibrateDuration ( String notificationOrigin , SharedPreferences prefs ) {
return getNotificationPrefIntValue ( VIBRATION_DURATION , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_DURATION ) ;
}
2015-06-20 23:22:22 +02:00
private VibrationProfile getPreferredVibrateProfile ( String notificationOrigin , SharedPreferences prefs , int repeat ) {
String profileId = getNotificationPrefStringValue ( VIBRATION_PROFILE , notificationOrigin , prefs , DEFAULT_VALUE_VIBRATION_PROFILE ) ;
2015-06-21 00:34:05 +02:00
return VibrationProfile . getProfile ( profileId , ( byte ) ( repeat & 0xfff ) ) ;
2015-06-20 23:22:22 +02:00
}
2015-06-24 20:14:08 +02:00
@Override
2015-08-21 00:58:18 +02:00
public void onSetAlarms ( ArrayList < ? extends Alarm > alarms ) {
2015-06-24 20:14:08 +02:00
try {
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) ;
TransactionBuilder builder = performInitialized ( " Set alarm " ) ;
2015-08-03 23:09:49 +02:00
for ( Alarm alarm : alarms ) {
2015-06-25 14:34:21 +02:00
queueAlarm ( alarm , builder , characteristic ) ;
}
2015-06-24 20:14:08 +02:00
builder . queue ( getQueue ( ) ) ;
2015-06-25 14:48:46 +02:00
Toast . makeText ( getContext ( ) , getContext ( ) . getString ( R . string . user_feedback_miband_set_alarms_ok ) , Toast . LENGTH_SHORT ) . show ( ) ;
2015-06-24 20:14:08 +02:00
} catch ( IOException ex ) {
2015-06-25 14:48:46 +02:00
Toast . makeText ( getContext ( ) , getContext ( ) . getString ( R . string . user_feedback_miband_set_alarms_failed ) , Toast . LENGTH_LONG ) . show ( ) ;
2015-06-24 20:14:08 +02:00
LOG . error ( " Unable to set alarms on MI device " , ex ) ;
}
}
2015-04-14 01:24:03 +02:00
@Override
2015-04-19 02:37:29 +02:00
public void onSMS ( String from , String body ) {
2015-06-20 23:22:22 +02:00
performPreferredNotification ( " sms received " , ORIGIN_SMS , null ) ;
2015-04-19 02:37:29 +02:00
}
2015-04-14 01:24:03 +02:00
2015-04-19 02:37:29 +02:00
@Override
public void onEmail ( String from , String subject , String body ) {
2015-06-20 23:22:22 +02:00
performPreferredNotification ( " email received " , ORIGIN_K9MAIL , null ) ;
2015-04-14 01:24:03 +02:00
}
2015-05-13 21:55:22 +02:00
@Override
public void onGenericNotification ( String title , String details ) {
2015-06-20 23:22:22 +02:00
performPreferredNotification ( " generic notification received " , ORIGIN_GENERIC , null ) ;
2015-05-13 21:55:22 +02:00
}
2015-04-14 01:24:03 +02:00
@Override
2015-08-21 00:58:18 +02:00
public void onSetTime ( ) {
2015-04-27 21:43:57 +02:00
try {
TransactionBuilder builder = performInitialized ( " Set date and time " ) ;
setCurrentTime ( builder ) ;
builder . queue ( getQueue ( ) ) ;
} catch ( IOException ex ) {
2015-05-12 06:28:11 +02:00
LOG . error ( " Unable to set time on MI device " , ex ) ;
2015-04-27 21:43:57 +02:00
}
}
/ * *
* Sets the current time to the Mi device using the given builder .
2015-05-01 01:49:43 +02:00
*
2015-04-27 21:43:57 +02:00
* @param builder
* /
private MiBandSupport setCurrentTime ( TransactionBuilder builder ) {
2015-06-01 09:42:44 +02:00
Calendar now = GregorianCalendar . getInstance ( ) ;
2015-04-26 00:53:48 +02:00
byte [ ] time = new byte [ ] {
( byte ) ( now . get ( Calendar . YEAR ) - 2000 ) ,
( byte ) now . get ( Calendar . MONTH ) ,
( byte ) now . get ( Calendar . DATE ) ,
( byte ) now . get ( Calendar . HOUR_OF_DAY ) ,
( byte ) now . get ( Calendar . MINUTE ) ,
( byte ) now . get ( Calendar . SECOND ) ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f ,
( byte ) 0x0f
} ;
2015-04-27 21:43:57 +02:00
BluetoothGattCharacteristic characteristic = getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_DATE_TIME ) ;
if ( characteristic ! = null ) {
builder . write ( characteristic , time ) ;
} else {
2015-05-12 06:28:11 +02:00
LOG . info ( " Unable to set time -- characteristic not available " ) ;
2015-04-23 14:11:57 +02:00
}
2015-04-27 21:43:57 +02:00
return this ;
2015-04-14 01:24:03 +02:00
}
@Override
2015-08-03 23:09:49 +02:00
public void onSetCallState ( String number , String name , ServiceCommand command ) {
if ( ServiceCommand . CALL_INCOMING . equals ( command ) ) {
2015-06-20 23:22:22 +02:00
telephoneRinging = true ;
AbortTransactionAction abortAction = new AbortTransactionAction ( ) {
@Override
protected boolean shouldAbort ( ) {
return ! isTelephoneRinging ( ) ;
}
} ;
performPreferredNotification ( " incoming call " , MiBandConst . ORIGIN_INCOMING_CALL , abortAction ) ;
2015-08-03 23:09:49 +02:00
} else if ( ServiceCommand . CALL_START . equals ( command ) | | ServiceCommand . CALL_END . equals ( command ) ) {
2015-06-20 23:22:22 +02:00
telephoneRinging = false ;
2015-04-19 15:15:53 +02:00
}
2015-04-14 01:24:03 +02:00
}
2015-06-20 23:22:22 +02:00
private boolean isTelephoneRinging ( ) {
// don't synchronize, this is not really important
return telephoneRinging ;
}
2015-04-14 01:24:03 +02:00
@Override
public void onSetMusicInfo ( String artist , String album , String track ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
2015-05-17 22:57:37 +02:00
@Override
public void onReboot ( ) {
2015-06-06 00:40:16 +02:00
try {
TransactionBuilder builder = performInitialized ( " Reboot " ) ;
builder . write ( getCharacteristic ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT ) , reboot ) ;
builder . queue ( getQueue ( ) ) ;
} catch ( IOException ex ) {
LOG . error ( " Unable to reboot MI " , ex ) ;
}
}
2015-06-21 00:34:05 +02:00
@Override
public void onFindDevice ( boolean start ) {
isLocatingDevice = start ;
if ( start ) {
AbortTransactionAction abortAction = new AbortTransactionAction ( ) {
@Override
protected boolean shouldAbort ( ) {
return ! isLocatingDevice ;
}
} ;
performDefaultNotification ( " locating device " , ( short ) 255 , abortAction ) ;
}
}
2015-06-06 00:40:16 +02:00
@Override
2015-06-06 19:39:04 +02:00
public void onFetchActivityData ( ) {
2015-05-17 22:57:37 +02:00
try {
2015-08-18 00:08:22 +02:00
new FetchActivityOperation ( this ) . perform ( ) ;
2015-05-17 22:57:37 +02:00
} catch ( IOException ex ) {
2015-05-24 15:04:48 +02:00
LOG . error ( " Unable to fetch MI activity data " , ex ) ;
2015-05-17 22:57:37 +02:00
}
2015-05-24 15:04:48 +02:00
}
2015-05-17 22:57:37 +02:00
2015-07-20 21:43:23 +02:00
private byte [ ] getHighLatency ( ) {
int minConnectionInterval = 460 ;
int maxConnectionInterval = 500 ;
int latency = 0 ;
int timeout = 500 ;
int advertisementInterval = 0 ;
return getLatency ( minConnectionInterval , maxConnectionInterval , latency , timeout , advertisementInterval ) ;
}
private byte [ ] getLatency ( int minConnectionInterval , int maxConnectionInterval , int latency , int timeout , int advertisementInterval ) {
byte result [ ] = new byte [ 12 ] ;
2015-07-25 21:52:52 +02:00
result [ 0 ] = ( byte ) ( minConnectionInterval & 0xff ) ;
result [ 1 ] = ( byte ) ( 0xff & minConnectionInterval > > 8 ) ;
result [ 2 ] = ( byte ) ( maxConnectionInterval & 0xff ) ;
2015-07-20 21:43:23 +02:00
result [ 3 ] = ( byte ) ( 0xff & maxConnectionInterval > > 8 ) ;
2015-07-25 21:52:52 +02:00
result [ 4 ] = ( byte ) ( latency & 0xff ) ;
result [ 5 ] = ( byte ) ( 0xff & latency > > 8 ) ;
result [ 6 ] = ( byte ) ( timeout & 0xff ) ;
2015-07-20 21:43:23 +02:00
result [ 7 ] = ( byte ) ( 0xff & timeout > > 8 ) ;
result [ 8 ] = 0 ;
result [ 9 ] = 0 ;
2015-07-25 21:52:52 +02:00
result [ 10 ] = ( byte ) ( advertisementInterval & 0xff ) ;
result [ 11 ] = ( byte ) ( 0xff & advertisementInterval > > 8 ) ;
2015-07-20 21:43:23 +02:00
return result ;
}
private byte [ ] getLowLatency ( ) {
int minConnectionInterval = 39 ;
int maxConnectionInterval = 49 ;
int latency = 0 ;
int timeout = 500 ;
int advertisementInterval = 0 ;
return getLatency ( minConnectionInterval , maxConnectionInterval , latency , timeout , advertisementInterval ) ;
}
2015-06-06 23:24:00 +02:00
@Override
public void onInstallApp ( Uri uri ) {
2015-08-04 23:02:36 +02:00
try {
2015-08-18 00:52:02 +02:00
new UpdateFirmwareOperation ( uri , this ) . perform ( ) ;
2015-08-04 23:02:36 +02:00
} catch ( IOException ex ) {
GB . toast ( getContext ( ) , " Firmware cannot be installed: " + ex . getMessage ( ) , Toast . LENGTH_LONG , GB . ERROR ) ;
2015-07-28 19:15:25 +02:00
}
2015-06-06 23:24:00 +02:00
}
2015-04-14 01:24:03 +02:00
@Override
public void onAppInfoReq ( ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
2015-05-18 22:20:01 +02:00
@Override
public void onAppStart ( UUID uuid ) {
// not supported
}
2015-04-14 01:24:03 +02:00
@Override
2015-05-18 20:56:19 +02:00
public void onAppDelete ( UUID uuid ) {
2015-05-07 23:51:03 +02:00
// not supported
2015-04-14 01:24:03 +02:00
}
2015-06-24 00:23:38 +02:00
@Override
public void onScreenshotReq ( ) {
// not supported
}
2015-05-24 00:11:14 +02:00
@Override
public void onCharacteristicChanged ( BluetoothGatt gatt ,
2015-05-24 15:04:48 +02:00
BluetoothGattCharacteristic characteristic ) {
2015-05-24 00:11:14 +02:00
super . onCharacteristicChanged ( gatt , characteristic ) ;
UUID characteristicUUID = characteristic . getUuid ( ) ;
2015-08-18 00:08:22 +02:00
if ( MiBandService . UUID_CHARACTERISTIC_BATTERY . equals ( characteristicUUID ) ) {
2015-06-18 23:07:22 +02:00
handleBatteryInfo ( characteristic . getValue ( ) , BluetoothGatt . GATT_SUCCESS ) ;
} else if ( MiBandService . UUID_CHARACTERISTIC_NOTIFICATION . equals ( characteristicUUID ) ) {
2015-07-31 12:30:42 +02:00
handleNotificationNotif ( characteristic . getValue ( ) ) ;
2015-06-18 23:07:22 +02:00
}
2015-05-24 00:11:14 +02:00
}
2015-05-24 14:39:36 +02:00
2015-04-19 22:20:47 +02:00
@Override
public void onCharacteristicRead ( BluetoothGatt gatt ,
2015-04-20 11:58:59 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2015-04-19 22:20:47 +02:00
super . onCharacteristicRead ( gatt , characteristic , status ) ;
2015-04-19 22:31:09 +02:00
UUID characteristicUUID = characteristic . getUuid ( ) ;
if ( MiBandService . UUID_CHARACTERISTIC_DEVICE_INFO . equals ( characteristicUUID ) ) {
2015-04-19 22:20:47 +02:00
handleDeviceInfo ( characteristic . getValue ( ) , status ) ;
2015-08-10 10:35:55 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_DEVICE_NAME . equals ( characteristicUUID ) ) {
handleDeviceName ( characteristic . getValue ( ) , status ) ;
2015-04-19 22:31:09 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_BATTERY . equals ( characteristicUUID ) ) {
handleBatteryInfo ( characteristic . getValue ( ) , status ) ;
2015-04-19 22:20:47 +02:00
}
}
2015-04-19 02:37:29 +02:00
@Override
public void onCharacteristicWrite ( BluetoothGatt gatt ,
2015-04-19 11:28:03 +02:00
BluetoothGattCharacteristic characteristic , int status ) {
2015-04-19 02:37:29 +02:00
UUID characteristicUUID = characteristic . getUuid ( ) ;
if ( MiBandService . UUID_CHARACTERISTIC_PAIR . equals ( characteristicUUID ) ) {
handlePairResult ( characteristic . getValue ( ) , status ) ;
2015-04-19 15:11:50 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_USER_INFO . equals ( characteristicUUID ) ) {
handleUserInfoResult ( characteristic . getValue ( ) , status ) ;
2015-05-24 00:11:14 +02:00
} else if ( MiBandService . UUID_CHARACTERISTIC_CONTROL_POINT . equals ( characteristicUUID ) ) {
handleControlPointResult ( characteristic . getValue ( ) , status ) ;
2015-04-19 02:37:29 +02:00
}
}
2015-08-03 18:35:58 +02:00
/ * *
* Utility method that may be used to log incoming messages when we don ' t know how to deal with them yet .
2015-08-18 17:37:51 +02:00
*
2015-08-03 18:35:58 +02:00
* @param value
* /
2015-07-31 12:30:42 +02:00
private void logMessageContent ( byte [ ] value ) {
LOG . info ( " RECEIVED DATA WITH LENGTH: " + value . length ) ;
for ( byte b : value ) {
LOG . warn ( " DATA: " + String . format ( " 0x%2x " , b ) ) ;
}
}
2015-08-03 18:35:58 +02:00
/ * *
* React to unsolicited messages sent by the Mi Band to the MiBandService . UUID_CHARACTERISTIC_NOTIFICATION
* characteristic ,
* These messages appear to be always 1 byte long , with values that are listed in MiBandService .
* It is not excluded that there are further values which are still unknown .
2015-08-18 17:37:51 +02:00
* < p / >
2015-08-03 18:35:58 +02:00
* Upon receiving known values that request further action by GB , the appropriate method is called .
*
* @param value
* /
2015-07-31 12:30:42 +02:00
private void handleNotificationNotif ( byte [ ] value ) {
2015-08-18 17:37:51 +02:00
if ( value . length ! = 1 ) {
2015-07-31 12:30:42 +02:00
LOG . error ( " Notifications should be 1 byte long. " ) ;
LOG . info ( " RECEIVED DATA WITH LENGTH: " + value . length ) ;
for ( byte b : value ) {
LOG . warn ( " DATA: " + String . format ( " 0x%2x " , b ) ) ;
}
return ;
}
switch ( value [ 0 ] ) {
default :
for ( byte b : value ) {
2015-08-14 23:37:47 +02:00
LOG . warn ( " DATA: " + String . format ( " 0x%2x " , b ) ) ;
2015-07-31 12:30:42 +02:00
}
}
}
2015-08-18 00:52:02 +02:00
2015-04-19 22:31:09 +02:00
private void handleDeviceInfo ( byte [ ] value , int status ) {
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
2015-07-23 17:14:51 +02:00
mDeviceInfo = new DeviceInfo ( value ) ;
2015-08-10 10:35:55 +02:00
versionCmd . fwVersion = mDeviceInfo . getHumanFirmwareVersion ( ) ;
handleGBDeviceEvent ( versionCmd ) ;
}
}
private void handleDeviceName ( byte [ ] value , int status ) {
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
versionCmd . hwVersion = new String ( value ) ;
handleGBDeviceEvent ( versionCmd ) ;
2015-04-19 22:31:09 +02:00
}
}
2015-08-03 18:35:58 +02:00
/ * *
* Convert an alarm from the GB internal structure to a Mi Band message and put on the specified
* builder queue as a write message for the passed characteristic
*
* @param alarm
* @param builder
* @param characteristic
* /
2015-08-03 23:09:49 +02:00
private void queueAlarm ( Alarm alarm , TransactionBuilder builder , BluetoothGattCharacteristic characteristic ) {
2015-06-24 20:14:08 +02:00
Calendar alarmCal = alarm . getAlarmCal ( ) ;
2015-06-30 12:09:29 +02:00
byte [ ] alarmMessage = new byte [ ] {
2015-06-24 20:14:08 +02:00
( byte ) MiBandService . COMMAND_SET_TIMER ,
( byte ) alarm . getIndex ( ) ,
2015-06-30 12:09:29 +02:00
( byte ) ( alarm . isEnabled ( ) ? 1 : 0 ) ,
2015-06-24 20:14:08 +02:00
( byte ) ( alarmCal . get ( Calendar . YEAR ) - 2000 ) ,
( byte ) alarmCal . get ( Calendar . MONTH ) ,
( byte ) alarmCal . get ( Calendar . DATE ) ,
( byte ) alarmCal . get ( Calendar . HOUR_OF_DAY ) ,
( byte ) alarmCal . get ( Calendar . MINUTE ) ,
( byte ) alarmCal . get ( Calendar . SECOND ) ,
( byte ) ( alarm . isSmartWakeup ( ) ? 30 : 0 ) ,
( byte ) alarm . getRepetitionMask ( )
} ;
builder . write ( characteristic , alarmMessage ) ;
}
2015-06-01 09:42:44 +02:00
private void handleControlPointResult ( byte [ ] value , int status ) {
if ( status ! = BluetoothGatt . GATT_SUCCESS ) {
LOG . warn ( " Could not write to the control point. " ) ;
}
LOG . info ( " handleControlPoint got status: " + status ) ;
2015-06-06 00:40:16 +02:00
2015-06-20 23:22:22 +02:00
if ( value ! = null ) {
for ( byte b : value ) {
LOG . info ( " handleControlPoint GOT DATA: " + String . format ( " 0x%8x " , b ) ) ;
}
} else {
LOG . warn ( " handleControlPoint GOT null " ) ;
2015-05-24 00:11:14 +02:00
}
2015-06-06 00:40:16 +02:00
}
2015-04-19 22:31:09 +02:00
private void handleBatteryInfo ( byte [ ] value , int status ) {
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
BatteryInfo info = new BatteryInfo ( value ) ;
2015-08-18 17:37:51 +02:00
batteryCmd . level = ( ( short ) info . getLevelInPercent ( ) ) ;
2015-08-21 08:41:57 +02:00
batteryCmd . state = info . getState ( ) ;
2015-08-18 17:37:51 +02:00
batteryCmd . lastChargeTime = info . getLastChargeTime ( ) ;
batteryCmd . numCharges = info . getNumCharges ( ) ;
handleGBDeviceEvent ( batteryCmd ) ;
2015-04-19 22:31:09 +02:00
}
}
2015-04-19 15:11:50 +02:00
private void handleUserInfoResult ( byte [ ] value , int status ) {
// successfully transfered user info means we're initialized
if ( status = = BluetoothGatt . GATT_SUCCESS ) {
setConnectionState ( State . INITIALIZED ) ;
}
}
private void setConnectionState ( State newState ) {
getDevice ( ) . setState ( newState ) ;
getDevice ( ) . sendDeviceUpdateIntent ( getContext ( ) ) ;
}
2015-04-19 02:37:29 +02:00
private void handlePairResult ( byte [ ] pairResult , int status ) {
if ( status ! = BluetoothGatt . GATT_SUCCESS ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Pairing MI device failed: " + status ) ;
2015-04-19 02:37:29 +02:00
return ;
}
2015-05-12 20:32:34 +02:00
String value = null ;
2015-04-19 02:37:29 +02:00
if ( pairResult ! = null ) {
if ( pairResult . length = = 1 ) {
try {
2015-05-12 20:32:34 +02:00
if ( pairResult [ 0 ] = = 2 ) {
2015-05-12 06:28:11 +02:00
LOG . info ( " Successfully paired MI device " ) ;
2015-04-19 02:37:29 +02:00
return ;
}
} catch ( Exception ex ) {
2015-05-12 06:28:11 +02:00
LOG . warn ( " Error identifying pairing result " , ex ) ;
2015-04-19 02:37:29 +02:00
return ;
}
}
2015-05-12 20:32:34 +02:00
value = Arrays . toString ( pairResult ) ;
2015-04-19 02:37:29 +02:00
}
2015-05-12 06:28:11 +02:00
LOG . info ( " MI Band pairing result: " + value ) ;
2015-04-19 02:37:29 +02:00
}
2015-06-16 17:03:35 +02:00
}