Merge remote-tracking branch 'origin/master' into low_battery_notification

This commit is contained in:
Daniele Gobbetti 2015-08-28 10:40:46 +02:00
commit 2f1aa45445
8 changed files with 296 additions and 112 deletions

View File

@ -1,5 +1,8 @@
###Changelog
####next version
* Pebble: support "dismiss all" action also on Pebble Time/FW 3.x notifications
####Version 0.5.1
* Pebble: support taking screenshot from Pebble Time
* Fix broken "find lost device" which was broken in 0.5.0

View File

@ -33,7 +33,7 @@ public class GadgetbridgePblSupport {
mPebbleProtocol = pebbleProtocol;
}
public GBDeviceEvent handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
public GBDeviceEvent[] handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
int timestamp = 0;
for (Pair<Integer, Object> pair : pairs) {
switch (pair.first) {
@ -75,6 +75,6 @@ public class GadgetbridgePblSupport {
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(uuid, mPebbleProtocol.last_id);
return sendBytes;
return new GBDeviceEvent[]{sendBytes};
}
}

View File

@ -5,7 +5,6 @@ import android.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
@ -41,7 +40,6 @@ public class MorpheuzSupport {
public static final UUID uuid = UUID.fromString("5be44f1d-d262-4ea6-aa30-ddbec1e3cab2");
private final PebbleProtocol mPebbleProtocol;
private boolean sent_to_gadgetbridge = false;
// data received from Morpheuz in native format
private int smartalarm_from = -1; // time in minutes relative from 0:00 for smart alarm (earliest)
private int smartalarm_to = -1;// time in minutes relative from 0:00 for smart alarm (latest)
@ -57,21 +55,15 @@ public class MorpheuzSupport {
private byte[] encodeMorpheuzMessage(int key, int value) {
ArrayList<Pair<Integer, Object>> pairs = new ArrayList<>();
pairs.add(new Pair<Integer, Object>(key, value));
byte[] ackMessage = mPebbleProtocol.encodeApplicationMessageAck(uuid, mPebbleProtocol.last_id);
byte[] testMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);
ByteBuffer buf = ByteBuffer.allocate(ackMessage.length + testMessage.length);
// encode ack and put in front of push message (hack for acknowledging the last message)
buf.put(ackMessage);
buf.put(testMessage);
return buf.array();
return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);
}
public GBDeviceEvent handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
public GBDeviceEvent[] handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
int ctrl_message = 0;
GBDeviceEventSleepMonitorResult sleepMonitorResult = null;
for (Pair<Integer, Object> pair : pairs) {
int ctrl_message = 0;
switch (pair.first) {
case KEY_TRANSMIT:
case KEY_GONEOFF:
@ -79,22 +71,11 @@ public class MorpheuzSupport {
alarm_gone_off = (int) pair.second;
LOG.info("got gone off: " + alarm_gone_off / 60 + ":" + alarm_gone_off % 60);
}
/* super-ugly hack: if if did not notice GadgetBridge yet, do so and delay confirmation so Morpheuz
* will resend gone off data. The second time, we acknowledge it.
*
* this can be fixed by allowing to return multiple GBDeviceCommands
*/
if (sent_to_gadgetbridge) {
ctrl_message = MorpheuzSupport.CTRL_VERSION_DONE | MorpheuzSupport.CTRL_GONEOFF_DONE | MorpheuzSupport.CTRL_TRANSMIT_DONE | MorpheuzSupport.CTRL_SET_LAST_SENT;
} else {
GBDeviceEventSleepMonitorResult sleepMonitorResult = new GBDeviceEventSleepMonitorResult();
sleepMonitorResult.smartalarm_from = smartalarm_from;
sleepMonitorResult.smartalarm_to = smartalarm_to;
sleepMonitorResult.alarm_gone_off = alarm_gone_off;
sleepMonitorResult.recording_base_timestamp = recording_base_timestamp;
sent_to_gadgetbridge = true;
return sleepMonitorResult;
}
sleepMonitorResult = new GBDeviceEventSleepMonitorResult();
sleepMonitorResult.smartalarm_from = smartalarm_from;
sleepMonitorResult.smartalarm_to = smartalarm_to;
sleepMonitorResult.alarm_gone_off = alarm_gone_off;
sleepMonitorResult.recording_base_timestamp = recording_base_timestamp;
break;
case KEY_POINT:
if (recording_base_timestamp == -1) {
@ -140,7 +121,6 @@ public class MorpheuzSupport {
case KEY_VERSION:
LOG.info("got version: " + ((float) ((int) pair.second) / 10.0f));
ctrl_message = MorpheuzSupport.CTRL_VERSION_DONE | MorpheuzSupport.CTRL_SET_LAST_SENT;
sent_to_gadgetbridge = false;
break;
case KEY_BASE:
// fix timestamp
@ -156,14 +136,20 @@ public class MorpheuzSupport {
LOG.info("unhandled key: " + pair.first);
break;
}
if (ctrl_message > 0) {
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeMorpheuzMessage(MorpheuzSupport.KEY_CTRL, ctrl_message);
return sendBytes;
}
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(uuid, mPebbleProtocol.last_id);
return sendBytes;
// always ack
GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes();
sendBytesAck.encodedBytes = mPebbleProtocol.encodeApplicationMessageAck(uuid, mPebbleProtocol.last_id);
// sometimes send control message
GBDeviceEventSendBytes sendBytesCtrl = null;
if (ctrl_message > 0) {
sendBytesCtrl = new GBDeviceEventSendBytes();
sendBytesCtrl.encodedBytes = encodeMorpheuzMessage(MorpheuzSupport.KEY_CTRL, ctrl_message);
}
// ctrl and sleep monitor might be null, thats okay
return new GBDeviceEvent[]{sendBytesAck, sendBytesCtrl, sleepMonitorResult};
}
}

View File

@ -237,12 +237,17 @@ public class PebbleIoThread extends GBDeviceIoThread {
mInStream.skip(2);
}
GBDeviceEvent deviceEvent = mPebbleProtocol.decodeResponse(buffer);
if (deviceEvent == null) {
GBDeviceEvent deviceEvents[] = mPebbleProtocol.decodeResponse(buffer);
if (deviceEvents == null) {
LOG.info("unhandled message to endpoint " + endpoint + " (" + length + " bytes)");
} else {
if (!evaluateGBDeviceEventPebble(deviceEvent)) {
mPebbleSupport.evaluateGBDeviceEvent(deviceEvent);
for (GBDeviceEvent deviceEvent : deviceEvents) {
if (deviceEvent == null) {
continue;
}
if (!evaluateGBDeviceEventPebble(deviceEvent)) {
mPebbleSupport.evaluateGBDeviceEvent(deviceEvent);
}
}
}
try {

View File

@ -95,6 +95,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final byte MUSICCONTROL_VOLUMEDOWN = 7;
static final byte MUSICCONTROL_GETNOWPLAYING = 7;
static final byte NOTIFICATIONACTION_ACK = 0;
static final byte NOTIFICATIONACTION_NACK = 1;
static final byte NOTIFICATIONACTION_INVOKE = 0x02;
static final byte NOTIFICATIONACTION_RESPONSE = 0x11;
static final byte TIME_GETTIME = 0;
static final byte TIME_SETTIME = 2;
static final byte TIME_SETTIME_UTC = 3;
@ -524,21 +529,22 @@ public class PebbleProtocol extends GBDeviceProtocol {
private byte[] encodeBlobdbNotification(int timestamp, String title, String subtitle, String body, byte type) {
String[] parts = {title, subtitle, body};
int icon_id = 1;
int icon_id = 0x80000000 | 1;
switch (type) {
case NOTIFICATION_EMAIL:
icon_id = 19;
icon_id = 0x80000000 | 19;
break;
case NOTIFICATION_SMS:
icon_id = 45;
icon_id = 0x80000000 | 45;
}
// Calculate length first
final short NOTIFICATION_PIN_LENGTH = 46;
final short ACTIONS_LENGTH = 17;
byte attributes_count = 1; // icon
byte actions_count = 1; // dismiss
short attributes_length = 7; // icon
short attributes_length = 7 + ACTIONS_LENGTH; // icon
if (parts != null) {
for (String s : parts) {
if (s == null || s.equals("")) {
@ -549,13 +555,6 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
}
byte actions_count = 0;
if (mForceProtocol) {
actions_count = 1;
attributes_length += ACTIONS_LENGTH;
}
UUID uuid = UUID.randomUUID();
short pin_length = (short) (NOTIFICATION_PIN_LENGTH + attributes_length);
@ -598,20 +597,39 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.putShort((short) 4); // length of int
buf.putInt(icon_id);
if (mForceProtocol) {
// ACTION
buf.put((byte) 0x01); // id
buf.put((byte) 0x04); // dismiss action
buf.put((byte) 0x01); // number attributes
buf.put((byte) 0x01); // attribute id (title)
String actionstring = "dismiss all";
buf.putShort((short) actionstring.length());
buf.put(actionstring.getBytes());
}
// ACTION
buf.put((byte) 0x01); // id
buf.put((byte) 0x02); // generic action, dismiss did not do anything
buf.put((byte) 0x01); // number attributes
buf.put((byte) 0x01); // attribute id (title)
String actionstring = "dismiss all";
buf.putShort((short) actionstring.length());
buf.put(actionstring.getBytes());
return encodeBlobdb(UUID.randomUUID(), BLOBDB_INSERT, BLOBDB_NOTIFICATION, buf.array());
}
public byte[] encodeActionResponse(UUID uuid) {
short length = 38;
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
buf.putInt(0x80000033); // icon id
buf.put((byte) 2); // title
buf.putShort((short) 9); // length
buf.put("Dismissed".getBytes());
return buf.array();
}
public byte[] encodeInstallMetadata(UUID uuid, String appName, short appVersion, short sdkVersion, int flags, int iconId) {
final short METADATA_LENGTH = 126;
@ -1059,15 +1077,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
return null;
}
private GBDeviceEventDismissNotification decodeNotificationAction(ByteBuffer buf) {
private GBDeviceEventDismissNotification decodeNotificationAction2x(ByteBuffer buf) {
buf.order(ByteOrder.LITTLE_ENDIAN);
byte command = buf.get();
if (command == 0x02) { // dismiss notification ?
if (isFw3x) {
buf.getLong(); // skip 8 bytes of UUID
buf.getInt(); // skip 4 bytes of UUID
}
int id = buf.getInt();
short action = buf.getShort(); // at least the low byte should be the action - or not?
if (action == 0x0001) {
@ -1081,6 +1095,29 @@ public class PebbleProtocol extends GBDeviceProtocol {
return null;
}
private GBDeviceEvent[] decodeNotificationAction3x(ByteBuffer buf) {
buf.order(ByteOrder.LITTLE_ENDIAN);
byte command = buf.get();
if (command == NOTIFICATIONACTION_INVOKE) {
buf.order(ByteOrder.BIG_ENDIAN);
long uuid_high = buf.getLong();
long uuid_low = buf.getLong();
int id = (int) (uuid_low & 0xffff);
byte action = buf.get();
if (action == 0x01) {
GBDeviceEventDismissNotification dismissNotification = new GBDeviceEventDismissNotification();
dismissNotification.notificationID = id;
GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes();
sendBytesAck.encodedBytes = encodeActionResponse(new UUID(uuid_high, uuid_low));
return new GBDeviceEvent[]{sendBytesAck, dismissNotification};
}
LOG.info("unexpected action: " + action);
}
return null;
}
private GBDeviceEventSendBytes decodePing(ByteBuffer buf) {
byte command = buf.get();
if (command == PING_PING) {
@ -1114,26 +1151,31 @@ public class PebbleProtocol extends GBDeviceProtocol {
private GBDeviceEventSendBytes decodeDatalog(ByteBuffer buf, short length) {
byte command = buf.get();
byte id = buf.get();
if (command == DATALOG_TIMEOUT) {
LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring");
return null;
}
if (command == 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));
} else if (command == 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);
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;
}
LOG.info("sending ACK (0x85)");
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
@ -1142,12 +1184,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
@Override
public GBDeviceEvent decodeResponse(byte[] responseData) {
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
ByteBuffer buf = ByteBuffer.wrap(responseData);
buf.order(ByteOrder.BIG_ENDIAN);
short length = buf.getShort();
short endpoint = buf.getShort();
GBDeviceEvent devEvt = null;
GBDeviceEvent devEvts[] = null;
byte pebbleCmd = -1;
switch (endpoint) {
case ENDPOINT_MUSICCONTROL:
@ -1178,7 +1220,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
default:
break;
}
devEvt = musicCmd;
devEvts = new GBDeviceEvent[]{musicCmd};
break;
case ENDPOINT_PHONECONTROL:
pebbleCmd = buf.get();
@ -1191,7 +1233,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
LOG.info("Unknown PHONECONTROL event" + pebbleCmd);
break;
}
devEvt = callCmd;
devEvts = new GBDeviceEvent[]{callCmd};
break;
case ENDPOINT_FIRMWAREVERSION:
pebbleCmd = buf.get();
@ -1213,7 +1255,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
} else if (hwRev == -3) { // basalt emulator
versionCmd.hwVersion = "dvt";
}
devEvt = versionCmd;
devEvts = new GBDeviceEvent[]{versionCmd};
break;
case ENDPOINT_APPMANAGER:
pebbleCmd = buf.get();
@ -1253,12 +1295,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
break;
}
}
devEvt = appInfoCmd;
devEvts = new GBDeviceEvent[]{appInfoCmd};
break;
case APPMANAGER_GETUUIDS:
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeSimpleMessage(ENDPOINT_APPMANAGER, APPMANAGER_GETAPPBANKSTATUS);
devEvt = sendBytes;
devEvts = new GBDeviceEvent[]{sendBytes};
tmpUUIDS.clear();
slotsUsed = buf.getInt();
for (int i = 0; i < slotsUsed; i++) {
@ -1282,7 +1324,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
deleteRes.event = GBDeviceEventAppManagement.Event.FAILURE;
break;
}
devEvt = deleteRes;
devEvts = new GBDeviceEvent[]{deleteRes};
break;
default:
LOG.info("Unknown APPMANAGER event" + pebbleCmd);
@ -1303,7 +1345,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
installRes.event = GBDeviceEventAppManagement.Event.FAILURE;
break;
}
devEvt = installRes;
devEvts = new GBDeviceEvent[]{installRes};
break;
case ENDPOINT_APPLICATIONMESSAGE:
pebbleCmd = buf.get();
@ -1317,13 +1359,13 @@ public class PebbleProtocol extends GBDeviceProtocol {
LOG.info("got APPLICATIONMESSAGE PUSH from UUID " + uuid);
if (WeatherNeatSupport.uuid.equals(uuid)) {
ArrayList<Pair<Integer, Object>> dict = decodeDict(buf);
devEvt = mWeatherNeatSupport.handleMessage(dict);
devEvts = mWeatherNeatSupport.handleMessage(dict);
} else if (MorpheuzSupport.uuid.equals(uuid)) {
ArrayList<Pair<Integer, Object>> dict = decodeDict(buf);
devEvt = mMorpheuzSupport.handleMessage(dict);
devEvts = mMorpheuzSupport.handleMessage(dict);
} else if (GadgetbridgePblSupport.uuid.equals(uuid)) {
ArrayList<Pair<Integer, Object>> dict = decodeDict(buf);
devEvt = mGadgetbridgePblSupport.handleMessage(dict);
devEvts = mGadgetbridgePblSupport.handleMessage(dict);
}
break;
case APPLICATIONMESSAGE_ACK:
@ -1346,33 +1388,35 @@ public class PebbleProtocol extends GBDeviceProtocol {
LOG.info("Pebble asked for Phone/App Version - repLYING!");
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodePhoneVersion(PHONEVERSION_REMOTE_OS_ANDROID);
devEvt = sendBytes;
devEvts = new GBDeviceEvent[]{sendBytes};
break;
default:
break;
}
break;
case ENDPOINT_DATALOG:
devEvt = decodeDatalog(buf, length);
devEvts = new GBDeviceEvent[]{decodeDatalog(buf, length)};
break;
case ENDPOINT_SCREENSHOT:
devEvt = decodeScreenshot(buf, length);
devEvts = new GBDeviceEvent[]{decodeScreenshot(buf, length)};
break;
case ENDPOINT_EXTENSIBLENOTIFS:
devEvts = new GBDeviceEvent[]{decodeNotificationAction2x(buf)};
break;
case ENDPOINT_NOTIFICATIONACTION:
devEvt = decodeNotificationAction(buf);
devEvts = decodeNotificationAction3x(buf);
break;
case ENDPOINT_PING:
devEvt = decodePing(buf);
devEvts = new GBDeviceEvent[]{decodePing(buf)};
break;
case ENDPOINT_APPFETCH:
devEvt = decodeAppFetch(buf);
devEvts = new GBDeviceEvent[]{decodeAppFetch(buf)};
break;
default:
break;
}
return devEvt;
return devEvts;
}
public void setForceProtocol(boolean force) {

View File

@ -48,9 +48,9 @@ public class WeatherNeatSupport {
return buf.array();
}
public GBDeviceEvent handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
public GBDeviceEvent[] handleMessage(ArrayList<Pair<Integer, Object>> pairs) {
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeWeatherNeatMessage("Berlin", "22 C", "cloudy", 0);
return sendBytes;
return new GBDeviceEvent[] {sendBytes};
}
}

View File

@ -63,7 +63,7 @@ public abstract class GBDeviceProtocol {
return null;
}
public GBDeviceEvent decodeResponse(byte[] responseData) {
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
return null;
}
}

View File

@ -0,0 +1,146 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
<string name="action_settings">Paramètre</string>
<string name="action_debug">Débugger</string>
<string name="action_quit">Quitter</string>
<string name="controlcenter_fetch_activity_data">Synchroniser</string>
<string name="controlcenter_start_sleepmonitor">Moniteur de sommeil (ALPHA)</string>
<string name="controlcenter_find_device">Trouver l\'appareil</string>
<string name="controlcenter_take_screenshot">Prendre une capture d\'écran</string>
<string name="controlcenter_disconnect">Déconnexion</string>
<string name="title_activity_debug">Débugger</string>
<!--Strings related to AppManager-->
<string name="appmananger_app_delete">Supprimer</string>
<!--Strings related to FwAppInstaller-->
<string name="fw_upgrade_notice">Vous êtes sur le point d\'installer le firmware %s à la place de celui qui est actuellement sur votre MiBand.</string>
<string name="miband_firmware_known">Ce firmware a été testé et est compatible with GadgetBridge</string>
<string name="miband_firmware_unknown_warning">Ce firmware n\'est pas testé and peut ne pas être compatible avec GadgetBridge. Il ne vous est pas conseillé de flasher votre MiBand</string>
<!--Strings related to Settings-->
<string name="title_activity_settings">Paramètre</string>
<string name="pref_header_general">Paramètres généraux</string>
<string name="pref_title_general_autoconnectonbluetooth">Connecter votre appareil quand le Bluetooth est mise en marche</string>
<string name="pref_header_datetime">Date et heure</string>
<string name="pref_title_datetime_syctimeonconnect">Synchroniser l\'horloge</string>
<string name="pref_summary_datetime_syctimeonconnect">Synchroniser l\'horloge lors de la connexion et quand l\'heure ou lorsque le fuseau horaire change sur Android</string>
<string name="pref_header_notifications">Notifications</string>
<string name="pref_title_notifications_repetitions">Répétitions</string>
<string name="pref_title_notifications_sms">SMS</string>
<string name="pref_title_notifications_incoming_call">Appel entrant</string>
<string name="pref_title_notifications_k9mail">K9-Email</string>
<string name="pref_title_notifications_pebblemsg">Message Pebble</string>
<string name="pref_title_notifications_generic">Support des notififactions génériques</string>
<string name="pref_title_whenscreenon">même lorsque l\'écran est allumé</string>
<string name="always">toujours</string>
<string name="when_screen_off">quand l\'écran est éteint</string>
<string name="never">jamais</string>
<string name="pref_header_development">Options développeur</string>
<string name="pref_title_development_miaddr">Adresse Mi Band</string>
<string name="pref_title_pebble_settings">Paramètres Pebble</string>
<string name="pref_title_pebble_forceprotocol">Protocole des notifications en vigueur</string>
<string name="pref_summary_pebble_forceprotocol">Cette option force l\'utilisant du dernier protocole de notification qui dépend de la verrsion du firmware. ACTIVER LA UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES!</string>
<string name="pref_title_pebble_forceuntested">Activités les fonctionnalités non testé</string>
<string name="pref_summary_pebble_forceuntested">Activer les fonctionnalités non testés</string>
<string name="not_connected">non connecté</string>
<string name="connecting">en train de se connecter</string>
<string name="connected">connecté</string>
<string name="unknown_state">état inconnu</string>
<string name="connectionstate_hw_fw">HW: %1$s FW: %2$s</string>
<string name="connectionstate_fw">FW: %1$s</string>
<string name="_unknown_">(inconnu)</string>
<string name="test">Test</string>
<string name="test_notification">Notification de test</string>
<string name="this_is_a_test_notification_from_gadgetbridge">Ceci est un test de notification venant de Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Le bluetooth n\'est pas supporté</string>
<string name="bluetooth_is_disabled_">Le bluetooth est désactivé</string>
<string name="tap_a_device_to_connect">Tapper sur l\'appareil pour le connecter</string>
<string name="cannot_connect_bt_address_invalid_">Ne peut être connecter. Ladresse bluetooth est invalide? </string>
<string name="gadgetbridge_running">Gadgetbridge est en fonctionnement</string>
<string name="battery_low">faible</string>
<string name="battery_medium">moyen</string>
<string name="battery_full">plein</string>
<string name="battery_not_charging">Ne charge pas</string>
<string name="installing_binary_d_d">Installation du binaire %1$d/%2$d</string>
<string name="installation_failed_">installation en échec</string>
<string name="installation_successful">installation réalisé</string>
<string name="firmware_install_warning">VOUS ÊTES EN TRAIN D\'INSTALLER UN FIRMWARE, PROCEDEZ À VOS PROPRES RISQUES. CE FIRMWARE EST POUR LA VERSION HW: %s</string>
<string name="n_a">N/A</string>
<string name="initialized">initialisé</string>
<string name="appversion_by_creator">%1$s par %2$s</string>
<string name="title_activity_discovery">Décourir les appareils</string>
<string name="discovery_stop_scanning">Arreter de scanner</string>
<string name="discovery_start_scanning">Démarrer le scan</string>
<string name="action_discover">Découvrir l\'appareil</string>
<string name="device_with_rssi">%1$s (%2$s)</string>
<string name="title_activity_android_pairing">Coupler l\'appareil</string>
<string name="android_pairing_hint">Utiliser l\'appareillage bluetouth d\'android pour coupler l\'appareil</string>
<string name="title_activity_mi_band_pairing">Coupler votre Mi Band</string>
<string name="miband_pairing">Coupler avec %s...</string>
<string name="message_cannot_pair_no_mac">Aucune adresse mac fournis, ne peut être couplé</string>
<string name="preferences_category_device_specific_settings">Paramètres spécifique à l\'appareil </string>
<string name="preferences_miband_settings">Paramètres Mi Band</string>
<string name="male">homme</string>
<string name="female">femme</string>
<string name="other">autre</string>
<string name="left">gauche</string>
<string name="right">droite</string>
<string name="miband_pairing_using_dummy_userdata">Aucune donnée utilisateur valide fournis, utilisation des données fictives pour le moment</string>
<string name="miband_pairing_tap_hint">Quand votre Mi Band vibre et clignote, appuyez dessus plusieurs fois d\'affilée.</string>
<string name="appinstaller_install">Installer</string>
<string name="discovery_connected_devices_hint">Rendre votre appareil découvrable. Actuellement les appareils connectés ne seront pas découvert.</string>
<string name="discovery_note">Note:</string>
<string name="miband_prefs_about_you">A propos de vous</string>
<string name="miband_prefs_alias">Nom/Pseudo</string>
<string name="miband_prefs_year_birth">Année de naissance</string>
<string name="miband_prefs_gender">Genre</string>
<string name="miband_prefs_height_cm">Taille en cm</string>
<string name="miband_prefs_weight_kg">Poids en kg</string>
<string name="pref_header_vibration_count">Nombre de vibration</string>
<string name="title_activity_sleepmonitor">Moniteur de sommeil</string>
<string name="pref_write_logfiles">Ecrire le fichier de logs (besoin de redemarrer)</string>
<string name="initializing">iniitialisation</string>
<string name="sleep_activity_date_range">De %1$s à %2$s</string>
<string name="miband_prefs_wearside">Port main gauche ou droite?</string>
<string name="pref_screen_vibration_profile">Profil de vibration</string>
<string name="vibration_profile_staccato">Saccadé</string>
<string name="vibration_profile_short">Court</string>
<string name="vibration_profile_medium">Moyen</string>
<string name="vibration_profile_long">Long</string>
<string name="vibration_profile_waterdrop">Goute d\'eau</string>
<string name="vibration_profile_ring">Cycle</string>
<string name="vibration_profile_alarm_clock">Réveil</string>
<string name="miband_prefs_vibration">Vibration</string>
<string name="pref_screen_notification_profile_sms">Notification SMS</string>
<string name="pref_header_vibration_settings">Paramètres des vibrations</string>
<string name="pref_screen_notification_profile_generic">Notification génériques</string>
<string name="pref_screen_notification_profile_pebblemsg">Notification Pebble</string>
<string name="pref_screen_notification_profile_k9mail">Notification des emails K9</string>
<string name="pref_screen_notification_profile_incoming_call">Notification des appels entrant</string>
<string name="control_center_find_lost_device">Trouver l\'appareil perdu</string>
<string name="control_center_cancel_to_stop_vibration">Annuler pour arreter les vibrations</string>
<string name="title_activity_charts">Votre activité</string>
<string name="title_activity_set_alarm">Configurer les réveils</string>
<string name="controlcenter_start_configure_alarms">Configurer les réveils</string>
<string name="title_activity_alarm_details">Détails des alarmes</string>
<string name="alarm_sun_short">Dim</string>
<string name="alarm_mon_short">Lun</string>
<string name="alarm_tue_short">Mar</string>
<string name="alarm_wed_short">Mer</string>
<string name="alarm_thu_short">Jeu</string>
<string name="alarm_fri_short">Ven</string>
<string name="alarm_sat_short">Sam</string>
<string name="alarm_smart_wakeup">réveil intelligent</string>
<string name="user_feedback_miband_set_alarms_failed">Il y avais une erreur lors du paramétrage des alarmes, s\'il vous plaît essayer à nouveau!</string>
<string name="user_feedback_miband_set_alarms_ok">Alarmes envoyer à l\'appareil</string>
<string name="chart_no_data_synchronize">Aucune donnée. Synchroniser l\'appareil?</string>
<string name="user_feedback_miband_activity_data_transfer">A propos du transférer %1$s de données à partir de %2$s</string>
<string name="miband_prefs_fitness_goal">Objectif de pas par jour</string>
<string name="dbaccess_error_executing">Erreur dexécution %1$s\'</string>
<string name="controlcenter_start_activitymonitor">Votre activité (ALPHA)</string>
<string name="cannot_connect">Impossible de se connecter: %1$s</string>
<string name="installer_activity_unable_to_find_handler">Impossible de trouver un gestionnaire pour installer ce fichier.</string>
<string name="pbw_install_handler_unable_to_install">Impossible d\'installer le fichier donné: $1%s</string>
<string name="pbw_install_handler_hw_revision_mismatch">Impossible d\'installer le firmware donnée: il ne correspond pas à la version du matériel de votre Pebble.</string>
<string name="installer_activity_wait_while_determining_status">S\'il vous plaît patienter pendant la détermination de l\'état de l\'installation ...</string>
</resources>