1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-09 22:57:54 +02:00

Merge remote-tracking branch 'upstream/master' into black_whitelist_for_notifications

# Conflicts:
#	app/src/main/res/values/strings.xml
This commit is contained in:
abettenburg 2019-01-12 09:55:53 +01:00
commit 51399066a4
104 changed files with 2938 additions and 777 deletions

View File

@ -1,5 +1,17 @@
### Changelog
#### Version 0.32.0
* Initial support for Casio GB-6900B
* Increase number of alarms and store them per-device
* Support factory reset in debug activity (Mi Band 1/2/3, Bip, Cor)
* Filter out unicode control sequences (fixes problems with Telegram and probably others)
* Fix endless loop resulting in OOM when RTL support is enabled
* Recoginize p≡p as an email app
* No longer display Android paired devices in that were not a paired with Gadgetbridge
* Amazfit Bip: Allow flashing latest GPS firmware
* Pebble: Native support for M7S watch face
* No1 F1: Support for a Chinese clone
#### Version 0.31.3
* Pebble: Fix crash with DISMISS and OPEN actions

View File

@ -349,6 +349,7 @@ public class GBDaoGenerator {
private static void addAlarms(Schema schema, Entity user, Entity device) {
Entity alarm = addEntity(schema, "Alarm");
alarm.implementsInterface("nodomain.freeyourgadget.gadgetbridge.model.Alarm");
Property deviceId = alarm.addLongProperty("deviceId").notNull().getProperty();
Property userId = alarm.addLongProperty("userId").notNull().getProperty();
Property position = alarm.addIntProperty("position").notNull().getProperty();
@ -359,8 +360,11 @@ public class GBDaoGenerator {
indexUnique.makeUnique();
alarm.addIndex(indexUnique);
alarm.addBooleanProperty("enabled").notNull();
alarm.addBooleanProperty("smartAlarm").notNull();
alarm.addIntProperty("repetition").notNull();
alarm.addBooleanProperty("smartWakeup").notNull();
alarm.addIntProperty("repetition").notNull().codeBeforeGetter(
"public boolean isRepetitive() { return getRepetition() != ALARM_ONCE; } " +
"public boolean getRepetition(int dow) { return (this.repetition & dow) > 0; }"
);
alarm.addIntProperty("hour").notNull();
alarm.addIntProperty("minute").notNull();
alarm.addToOne(user, userId);

View File

@ -26,7 +26,8 @@ vendor's servers.
## Supported Devices
* Amazfit Bip [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip)
* Amazfit Cor (no maintainer) [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor)
* Amazfit Cor [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor)
* Casio GB-6900B (WIP)
* HPlus Devices (e.g. ZeBand) [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/HPlus)
* ID115 (WIP)
* Lenovo Watch 9 (WIP)
@ -42,23 +43,18 @@ vendor's servers.
* Vibratissimo (experimental)
* ZeTime (WIP)
## Features
Please see [FEATURES.md](https://github.com/Freeyourgadget/Gadgetbridge/blob/master/FEATURES.md)
## Getting Started (Pebble)
1. Pair your Pebble through the Android's Bluetooth Settings or Gadgetbridge. Pebble 2 MUST be paired though Gadgetbridge (tap on the + in Control Center)
2. Start Gadgetbridge, tap on the device you want to connect to
3. To test, choose "Debug" from the menu and play around
For more information read [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble-Getting-Started)
Please [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble-Getting-Started)
## How to use (Mi Band 1+2)
* When starting Gadgetbridge the first time, it will automatically
attempt to discover and pair your Mi Band. Alternatively you can invoke discovery
manually via the "+" button. It will ask you for some personal info that appears
* Invoke the discovery activity manually via the "+" button. It will ask you for some personal info that appears
to be needed for proper steps calculation on the band. If you do not provide these,
some hardcoded default "dummy" values will be used instead.
@ -95,7 +91,6 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/
* Daniele Gobbetti
### Additional device support
* João Paulo Barraca (HPlus)
* Vitaly Svyastyn (NO.1 F1)
* Sami Alaoui (Teclast H30)
@ -103,6 +98,7 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/
* Sebastian Kranz (ZeTime)
* Vadim Kaushan (ID115)
* "maxirnilian" (Lenovo Watch 9)
* Andreas Böhler (Casio GB-6900B)
## Contribute

View File

@ -25,8 +25,8 @@ android {
targetSdkVersion 27
// Note: always bump BOTH versionCode and versionName!
versionName "0.31.3"
versionCode 142
versionName "0.32.0"
versionCode 143
vectorDrawables.useSupportLibrary = true
}
buildTypes {
@ -88,6 +88,7 @@ dependencies {
implementation "org.cyanogenmod:platform.sdk:6.0"
implementation 'com.jaredrummler:colorpicker:1.0.2'
// implementation project(":DaoCore")
implementation 'com.github.wax911:android-emojify:0.1.7'
}
preBuild.dependsOn(":GBDaoGenerator:genSources")

View File

@ -27,12 +27,15 @@ import android.os.Build;
import android.widget.RemoteViews;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
@ -55,7 +58,8 @@ public class SleepAlarmWidget extends AppWidgetProvider {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.sleep_alarm_widget);
// Add our own click intent
Intent intent = new Intent(ACTION);
Intent intent = new Intent(context, SleepAlarmWidget.class);
intent.setAction(ACTION);
PendingIntent clickPI = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.sleepalarmwidget_text, clickPI);
@ -90,24 +94,41 @@ public class SleepAlarmWidget extends AppWidgetProvider {
// current timestamp
GregorianCalendar calendar = new GregorianCalendar();
// add preferred sleep duration
calendar.add(Calendar.HOUR_OF_DAY, userSleepDuration);
if (userSleepDuration > 0) {
calendar.add(Calendar.HOUR_OF_DAY, userSleepDuration);
} else { // probably testing
calendar.add(Calendar.MINUTE, 1);
}
// overwrite the first alarm and activate it, without
// overwrite the first alarm and activate it
/*
GBAlarm alarm = GBAlarm.createSingleShot(0,0, true, calendar); // FIXME!!!!
alarm.store();
*/
if (GBApplication.isRunningLollipopOrLater()) {
setAlarmViaAlarmManager(context, calendar.getTimeInMillis());
Context appContext = context.getApplicationContext();
if (appContext instanceof GBApplication) {
GBApplication gbApp = (GBApplication) appContext;
GBDevice selectedDevice = gbApp.getDeviceManager().getSelectedDevice();
if (selectedDevice == null || !selectedDevice.isInitialized()) {
GB.toast(context,
context.getString(R.string.appwidget_not_connected),
Toast.LENGTH_LONG, GB.WARN);
return;
}
}
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
GB.toast(context,
String.format(context.getString(R.string.appwidget_alarms_set), hours, minutes),
context.getString(R.string.appwidget_setting_alarm, hours, minutes),
Toast.LENGTH_SHORT, GB.INFO);
Alarm alarm = AlarmUtils.createSingleShot(0,true, calendar);
ArrayList<Alarm> alarms = new ArrayList<>(1);
alarms.add(alarm);
GBApplication.deviceService().onSetAlarms(alarms);
// if (GBApplication.isRunningLollipopOrLater()) {
// setAlarmViaAlarmManager(context, calendar.getTimeInMillis());
// }
}
}

View File

@ -26,14 +26,16 @@ import android.widget.TimePicker;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
public class AlarmDetails extends AbstractGBActivity {
private GBAlarm alarm;
private Alarm alarm;
private TimePicker timePicker;
private CheckedTextView cbSmartWakeup;
private CheckedTextView cbMonday;
@ -50,7 +52,7 @@ public class AlarmDetails extends AbstractGBActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm_details);
alarm = getIntent().getParcelableExtra("alarm");
alarm = (Alarm) getIntent().getSerializableExtra(nodomain.freeyourgadget.gadgetbridge.model.Alarm.EXTRA_ALARM);
device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
timePicker = findViewById(R.id.alarm_time_picker);
@ -109,17 +111,17 @@ public class AlarmDetails extends AbstractGBActivity {
timePicker.setCurrentHour(alarm.getHour());
timePicker.setCurrentMinute(alarm.getMinute());
cbSmartWakeup.setChecked(alarm.isSmartWakeup());
cbSmartWakeup.setChecked(alarm.getSmartWakeup());
int smartAlarmVisibility = supportsSmartWakeup() ? View.VISIBLE : View.GONE;
cbSmartWakeup.setVisibility(smartAlarmVisibility);
cbMonday.setChecked(alarm.getRepetition(GBAlarm.ALARM_MON));
cbTuesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_TUE));
cbWednesday.setChecked(alarm.getRepetition(GBAlarm.ALARM_WED));
cbThursday.setChecked(alarm.getRepetition(GBAlarm.ALARM_THU));
cbFriday.setChecked(alarm.getRepetition(GBAlarm.ALARM_FRI));
cbSaturday.setChecked(alarm.getRepetition(GBAlarm.ALARM_SAT));
cbSunday.setChecked(alarm.getRepetition(GBAlarm.ALARM_SUN));
cbMonday.setChecked(alarm.getRepetition(Alarm.ALARM_MON));
cbTuesday.setChecked(alarm.getRepetition(Alarm.ALARM_TUE));
cbWednesday.setChecked(alarm.getRepetition(Alarm.ALARM_WED));
cbThursday.setChecked(alarm.getRepetition(Alarm.ALARM_THU));
cbFriday.setChecked(alarm.getRepetition(Alarm.ALARM_FRI));
cbSaturday.setChecked(alarm.getRepetition(Alarm.ALARM_SAT));
cbSunday.setChecked(alarm.getRepetition(Alarm.ALARM_SUN));
}
@ -144,10 +146,11 @@ public class AlarmDetails extends AbstractGBActivity {
private void updateAlarm() {
alarm.setSmartWakeup(supportsSmartWakeup() && cbSmartWakeup.isChecked());
alarm.setRepetition(cbMonday.isChecked(), cbTuesday.isChecked(), cbWednesday.isChecked(), cbThursday.isChecked(), cbFriday.isChecked(), cbSaturday.isChecked(), cbSunday.isChecked());
int repetitionMask = AlarmUtils.createRepetitionMassk(cbMonday.isChecked(), cbTuesday.isChecked(), cbWednesday.isChecked(), cbThursday.isChecked(), cbFriday.isChecked(), cbSaturday.isChecked(), cbSunday.isChecked());
alarm.setRepetition(repetitionMask);
alarm.setHour(timePicker.getCurrentHour());
alarm.setMinute(timePicker.getCurrentMinute());
alarm.store();
DBHelper.store(alarm);
}
@Override

View File

@ -19,31 +19,33 @@ package nodomain.freeyourgadget.gadgetbridge.activities;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.MenuItem;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.GBAlarmListAdapter;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.entities.AlarmDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class ConfigureAlarms extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(ConfigureAlarms.class);
private static final int REQ_CONFIGURE_ALARM = 1;
@ -84,42 +86,46 @@ public class ConfigureAlarms extends AbstractGBActivity {
}
}
/**
* Reads the available alarms from the database and updates the view afterwards.
*/
private void updateAlarmsFromDB() {
Prefs prefs = GBApplication.getPrefs();
int reservedSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0);
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
int alarmSlots = coordinator.getAlarmSlotCount();
Long deviceId;
List<Alarm> allAlarms = null;
try (DBHandler db = GBApplication.acquireDB()) {
AlarmDao alarmDao = db.getDaoSession().getAlarmDao();
Device dbDevice = DBHelper.findDevice(gbDevice, db.getDaoSession());
deviceId = dbDevice.getId();
QueryBuilder<Alarm> qb = alarmDao.queryBuilder();
qb.where(AlarmDao.Properties.DeviceId.eq(deviceId)).orderAsc(AlarmDao.Properties.Position).limit(alarmSlots - reservedSlots);
allAlarms = qb.build().list();
} catch (Exception e) {
return;
List<Alarm> alarms = DBHelper.getAlarms(getGbDevice());
if (alarms.isEmpty()) {
alarms = AlarmUtils.readAlarmsFromPrefs(getGbDevice());
storeMigratedAlarms(alarms);
}
addMissingAlarms(alarms);
List<GBAlarm> gbAlarms = new ArrayList<>();
if (allAlarms != null) {
for (Alarm alarm : allAlarms) {
gbAlarms.add(new GBAlarm(deviceId, alarm.getPosition(), alarm.getEnabled(),
alarm.getSmartAlarm(), alarm.getRepetition(), alarm.getHour(), alarm.getMinute()));
mGBAlarmListAdapter.setAlarmList(alarms);
mGBAlarmListAdapter.notifyDataSetChanged();
}
private void storeMigratedAlarms(List<Alarm> alarms) {
for (Alarm alarm : alarms) {
DBHelper.store(alarm);
}
}
private void addMissingAlarms(List<Alarm> alarms) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(getGbDevice());
int supportedNumAlarms = coordinator.getAlarmSlotCount();
if (supportedNumAlarms > alarms.size()) {
try (DBHandler db = GBApplication.acquireDB()) {
DaoSession daoSession = db.getDaoSession();
Device device = DBHelper.getDevice(getGbDevice(), daoSession);
User user = DBHelper.getUser(daoSession);
while (supportedNumAlarms > alarms.size()) {
alarms.add(createDefaultAlarm(device, user, alarms.size()));
}
} catch (Exception e) {
LOG.error("Error accessing database", e);
}
}
int hour = 5;
while (gbAlarms.size() < alarmSlots) {
GBAlarm gbAlarm = new GBAlarm(deviceId, gbAlarms.size(), false, false, 31, hour++, 30);
gbAlarms.add(gbAlarm);
gbAlarm.store();
}
}
mGBAlarmListAdapter.setAlarmList(gbAlarms);
mGBAlarmListAdapter.notifyDataSetChanged();
private Alarm createDefaultAlarm(@NonNull Device device, @NonNull User user, int position) {
return new Alarm(device.getId(), user.getId(), position, false, false,0, 6, 30);
}
@Override
@ -133,10 +139,10 @@ public class ConfigureAlarms extends AbstractGBActivity {
return super.onOptionsItemSelected(item);
}
public void configureAlarm(GBAlarm alarm) {
public void configureAlarm(Alarm alarm) {
avoidSendAlarmsToDevice = true;
Intent startIntent = new Intent(getApplicationContext(), AlarmDetails.class);
startIntent.putExtra("alarm", alarm);
startIntent.putExtra(Alarm.EXTRA_ALARM, alarm);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, getGbDevice());
startActivityForResult(startIntent, REQ_CONFIGURE_ALARM);
}

View File

@ -28,7 +28,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.NavUtils;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
@ -59,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import static android.content.Intent.EXTRA_SUBJECT;
@ -183,9 +183,33 @@ public class DebugActivity extends AbstractGBActivity {
rebootButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GBApplication.deviceService().onReboot();
GBApplication.deviceService().onReset(GBDeviceProtocol.RESET_FLAGS_REBOOT);
}
});
Button factoryResetButton = findViewById(R.id.factoryResetButton);
factoryResetButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(DebugActivity.this)
.setCancelable(true)
.setTitle(R.string.debugactivity_really_factoryreset_title)
.setMessage(R.string.debugactivity_really_factoryreset)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
GBApplication.deviceService().onReset(GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET);
}
})
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
});
Button heartRateButton = findViewById(R.id.HeartRateButton);
heartRateButton.setOnClickListener(new View.OnClickListener() {
@Override
@ -278,14 +302,7 @@ public class DebugActivity extends AbstractGBActivity {
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String fileName = GBApplication.getLogPath();
if (fileName != null && fileName.length() > 0) {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("*/*");
emailIntent.putExtra(EXTRA_SUBJECT, "Gadgetbridge log file");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileName)));
startActivity(Intent.createChooser(emailIntent, "Share File"));
}
shareLog();
}
})
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {

View File

@ -25,7 +25,6 @@ import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.widget.Button;
@ -36,8 +35,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FindPhoneActivity extends AbstractGBActivity {
@ -100,9 +97,13 @@ public class FindPhoneActivity extends AbstractGBActivity {
mp.prepare();
mp.start();
} catch (IOException ignore) {
LOG.warn("problem playing ringtone");
}
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), AudioManager.FLAG_PLAY_SOUND);
if (mAudioManager != null) {
userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM), AudioManager.FLAG_PLAY_SOUND);
}
}
public void stopSound() {

View File

@ -74,7 +74,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
@Override
protected String getBalanceMessage(long balance, int targetValue) {
if (balance > 0) {
final long totalBalance = balance - (targetValue * TOTAL_DAYS);
final long totalBalance = balance - ((long)targetValue * TOTAL_DAYS);
if (totalBalance > 0)
return getString(R.string.overslept, getHM(totalBalance));
else

View File

@ -105,7 +105,7 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment {
@Override
protected String getBalanceMessage(long balance, int targetValue) {
if (balance > 0) {
final long totalBalance = balance - (targetValue * TOTAL_DAYS);
final long totalBalance = balance - ((long)targetValue * TOTAL_DAYS);
if (totalBalance > 0)
return getString(R.string.overstep, Math.abs(totalBalance));
else

View File

@ -35,33 +35,32 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
/**
* Adapter for displaying GBAlarm instances.
*/
public class GBAlarmListAdapter extends RecyclerView.Adapter<GBAlarmListAdapter.ViewHolder> {
private final Context mContext;
private List<GBAlarm> alarmList;
private ArrayList<Alarm> alarmList;
public GBAlarmListAdapter(Context context) {
this.mContext = context;
}
public void setAlarmList(List<GBAlarm> alarmList) {
this.alarmList = alarmList;
public void setAlarmList(List<Alarm> alarms) {
this.alarmList = new ArrayList<>(alarms);
}
public ArrayList getAlarmList() {
return (ArrayList) alarmList;
public ArrayList<Alarm> getAlarmList() {
return alarmList;
}
public void update(GBAlarm alarm) {
alarm.store();
private void updateInDB(Alarm alarm) {
DBHelper.store(alarm);
}
@NonNull
@ -74,7 +73,7 @@ public class GBAlarmListAdapter extends RecyclerView.Adapter<GBAlarmListAdapter.
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
final GBAlarm alarm = alarmList.get(position);
final Alarm alarm = alarmList.get(position);
holder.alarmDayMonday.setChecked(alarm.getRepetition(Alarm.ALARM_MON));
holder.alarmDayTuesday.setChecked(alarm.getRepetition(Alarm.ALARM_TUE));
@ -88,7 +87,7 @@ public class GBAlarmListAdapter extends RecyclerView.Adapter<GBAlarmListAdapter.
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
alarm.setEnabled(isChecked);
update(alarm);
updateInDB(alarm);
}
});
@ -98,16 +97,15 @@ public class GBAlarmListAdapter extends RecyclerView.Adapter<GBAlarmListAdapter.
((ConfigureAlarms) mContext).configureAlarm(alarm);
}
});
holder.alarmTime.setText(alarm.getTime());
holder.isEnabled.setChecked(alarm.isEnabled());
if (alarm.isSmartWakeup()) {
holder.alarmTime.setText(DateTimeUtils.formatTime(alarm.getHour(), alarm.getMinute()));
holder.isEnabled.setChecked(alarm.getEnabled());
if (alarm.getSmartWakeup()) {
holder.isSmartWakeup.setVisibility(TextView.VISIBLE);
} else {
holder.isSmartWakeup.setVisibility(TextView.GONE);
}
}
@Override
public int getItemCount() {
return alarmList.size();

View File

@ -196,7 +196,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
);
//set alarms
holder.setAlarmsView.setVisibility(coordinator.supportsAlarmConfiguration() ? View.VISIBLE : View.GONE);
holder.setAlarmsView.setVisibility(coordinator.getAlarmSlotCount() > 0 ? View.VISIBLE : View.GONE);
holder.setAlarmsView.setOnClickListener(new View.OnClickListener()
{

View File

@ -21,7 +21,6 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -33,6 +32,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -44,8 +44,11 @@ import de.greenrobot.dao.query.QueryBuilder;
import de.greenrobot.dao.query.WhereCondition;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.entities.ActivityDescription;
import nodomain.freeyourgadget.gadgetbridge.entities.ActivityDescriptionDao;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.entities.AlarmDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes;
@ -62,6 +65,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ValidByDate;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
/**
@ -362,6 +366,13 @@ public class DBHelper {
return true;
}
/**
* Finds the corresponding Device entity for the given GBDevice.
* @param gbDevice
* @param session
* @return the corresponding Device entity, or null if none
*/
@Nullable
public static Device findDevice(GBDevice gbDevice, DaoSession session) {
DeviceDao deviceDao = session.getDeviceDao();
Query<Device> query = deviceDao.queryBuilder().where(DeviceDao.Properties.Identifier.eq(gbDevice.getAddress())).build();
@ -561,6 +572,49 @@ public class DBHelper {
return tag;
}
/**
* Returns all user-configurable alarms for the given user and device. The list is sorted by
* {@link Alarm#position}. Calendar events that may also be modeled as alarms are not stored
* in the database and hence not returned by this method.
* @param gbDevice the device for which the alarms shall be loaded
* @return the list of alarms for the given device
*/
@NonNull
public static List<Alarm> getAlarms(@NonNull GBDevice gbDevice) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
Prefs prefs = GBApplication.getPrefs();
// TODO: this alarm reservation is a device dependent detail
int reservedSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0);
int alarmSlots = coordinator.getAlarmSlotCount();
try (DBHandler db = GBApplication.acquireDB()) {
DaoSession daoSession = db.getDaoSession();
User user = getUser(daoSession);
Device dbDevice = DBHelper.findDevice(gbDevice, daoSession);
if (dbDevice != null) {
AlarmDao alarmDao = daoSession.getAlarmDao();
Long deviceId = dbDevice.getId();
QueryBuilder<Alarm> qb = alarmDao.queryBuilder();
qb.where(
AlarmDao.Properties.UserId.eq(user.getId()),
AlarmDao.Properties.DeviceId.eq(deviceId)).orderAsc(AlarmDao.Properties.Position).limit(alarmSlots - reservedSlots);
return qb.build().list();
}
} catch (Exception e) {
LOG.warn("Error reading alarms from db", e);
}
return Collections.emptyList();
}
public static void store(Alarm alarm) {
try (DBHandler db = GBApplication.acquireDB()) {
DaoSession daoSession = db.getDaoSession();
daoSession.insertOrReplace(alarm);
} catch (Exception e) {
LOG.error("Error acquiring database", e);
}
}
public static void clearSession() {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
DaoSession session = dbHandler.getDaoSession();

View File

@ -149,4 +149,7 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
public int[] getColorPresets() {
return new int[0];
}
@Override
public boolean supportsUnicodeEmojis() { return false; }
}

View File

@ -181,15 +181,10 @@ public interface DeviceCoordinator {
*/
boolean supportsScreenshots();
/**
* Returns true if this device/coordinator supports setting alarms.
*
* @return
*/
boolean supportsAlarmConfiguration();
/**
* Returns the number of alarms this device/coordinator supports
* Shall return 0 also if it is not possible to set alarms via
* protocol, but only on the smart device itself.
*
* @return
*/
@ -278,4 +273,9 @@ public interface DeviceCoordinator {
*/
@NonNull
int[] getColorPresets();
/**
* Indicates whether the device supports unicode emojis.
*/
boolean supportsUnicodeEmojis();
}

View File

@ -69,7 +69,7 @@ public interface EventHandler {
void onFetchRecordedData(int dataTypes);
void onReboot();
void onReset(int flags);
void onHeartRateTest();

View File

@ -137,11 +137,6 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;

View File

@ -0,0 +1,87 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
import java.util.UUID;
public final class CasioGB6900Constants {
public static final UUID CASIO_VIRTUAL_SERVER_SERVICE = UUID.fromString("26eb0007-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_VIRTUAL_SERVER_FEATURES = UUID.fromString("26eb0008-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_A_NOT_W_REQ_NOT = UUID.fromString( "26eb0009-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_A_NOT_COM_SET_NOT = UUID.fromString( "26eb000a-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
// Alert
public static final UUID ALERT_SERVICE_UUID = UUID.fromString("26eb0000-b012-49a8-b1f8-394fb2032b0f");
public static final UUID ALERT_CHARACTERISTIC_UUID = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
public static final UUID ALERT_NOTIFICATION_CONTROL_POINT = UUID.fromString("00002a44-0000-1000-8000-00805f9b34fb");
// More Alert
public static final UUID MORE_ALERT_SERVICE_UUID = UUID.fromString("26eb001a-b012-49a8-b1f8-394fb2032b0f");
public static final UUID MORE_ALERT_UUID = UUID.fromString("26eb001b-b012-49a8-b1f8-394fb2032b0f");
public static final UUID MORE_ALERT_FOR_LONG_UUID = UUID.fromString("26eb001c-b012-49a8-b1f8-394fb2032b0f");
// Phone Alert
public static final UUID CASIO_PHONE_ALERT_STATUS_SERVICE = UUID.fromString("26eb0001-b012-49a8-b1f8-394fb2032b0f");
public static final UUID RINGER_CONTROL_POINT = UUID.fromString("00002a40-0000-1000-8000-00805f9b34fb");
// Phone Finder
public static final UUID CASIO_IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("26eb0005-b012-49a8-b1f8-394fb2032b0f");
public static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
// Current Time
public static final UUID CURRENT_TIME_SERVICE_UUID = UUID.fromString("26eb0002-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CURRENT_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
public static final UUID LOCAL_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a0f-0000-1000-8000-00805f9b34fb");
// Control Mode
public static final UUID WATCH_FEATURES_SERVICE_UUID = UUID.fromString("26eb000d-b012-49a8-b1f8-394fb2032b0f");
public static final UUID WATCH_CTRL_SERVICE_UUID = UUID.fromString("26eb0018-b012-49a8-b1f8-394fb2032b0f");
public static final UUID KEY_CONTAINER_CHARACTERISTIC_UUID = UUID.fromString("26eb0019-b012-49a8-b1f8-394fb2032b0f");
public static final UUID NAME_OF_APP_CHARACTERISTIC_UUID = UUID.fromString("26eb001d-b012-49a8-b1f8-394fb2032b0f");
public static final UUID FUNCTION_SWITCH_CHARACTERISTIC = UUID.fromString("26eb001e-b012-49a8-b1f8-394fb2032b0f");
public static final String MUSIC_MESSAGE = "Music";
// Link Loss
public static final UUID LINK_LOSS_SERVICE = UUID.fromString("00001803-0000-1000-8000-00805f9b34fb");
// TxPower
public static final UUID TX_POWER_SERVICE_UUID = UUID.fromString("00001804-0000-1000-8000-00805f9b34fb");
public static final UUID TX_POWER_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb");
// Settings
public static final UUID CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID = UUID.fromString("26eb000f-b012-49a8-b1f8-394fb2032b0f");
// Notification Types
public static final byte CALL_NOTIFICATION_ID = 3;
public static final byte MAIL_NOTIFICATION_ID = 1;
public static final byte CALENDAR_NOTIFICATION_ID = 7;
public static final byte SNS_NOTIFICATION_ID = 13;
public static final byte SMS_NOTIFICATION_ID = 5;
}

View File

@ -0,0 +1,153 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
public class CasioGB6900DeviceCoordinator extends AbstractDeviceCoordinator {
protected static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceCoordinator.class);
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
if (candidate.supportsService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE)) {
return DeviceType.CASIOGB6900;
}
String name = candidate.getDevice().getName();
if (name != null) {
if (name.startsWith("CASIO")) {
return DeviceType.CASIOGB6900;
}
}
return DeviceType.UNKNOWN;
}
@Override
public int getBondingStyle(GBDevice deviceCandidate){
return BONDING_STYLE_BOND;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return false;
}
@Override
public boolean supportsWeather() {
return false;
}
@Override
public boolean supportsFindDevice() {
return true;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.CASIOGB6900;
}
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return false;
}
@Override
public boolean supportsActivityTracking() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0; // 4 regular and one snooze but not yet implemented
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
}
@Override
public String getManufacturer() {
return "Casio";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
}

View File

@ -36,7 +36,6 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -143,11 +142,6 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value

View File

@ -35,7 +35,7 @@ public class MakibesF68Coordinator extends HPlusCoordinator {
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName();
if(name != null && name.startsWith("SPORT")){
if(name != null && name.startsWith("SPORT") && !name.startsWith("SPORTAGE")){
return DeviceType.MAKIBESF68;
}

View File

@ -103,11 +103,6 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 10;

View File

@ -149,6 +149,7 @@ public class HuamiService {
public static final byte[] COMMAND_DISTANCE_UNIT_METRIC = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x00 };
public static final byte[] COMMAND_DISTANCE_UNIT_IMPERIAL = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x01 };
public static final byte[] COMMAND_SET_LANGUAGE_NEW_TEMPLATE = new byte[]{ENDPOINT_DISPLAY, 0x17, 0x00, 0, 0, 0, 0, 0};
public static final byte[] COMMAND_FACTORY_RESET = new byte[]{ENDPOINT_DISPLAY, 0x0b, 0x00, 0x01};
// The third byte controls the threshold, in minutes
// The last 8 bytes represent 2 separate time intervals for the inactivity warnings

View File

@ -73,4 +73,7 @@ public class AmazfitCorCoordinator extends HuamiCoordinator {
public boolean supportsMusicInfo() {
return true;
}
@Override
public boolean supportsUnicodeEmojis() { return true; }
}

View File

@ -105,11 +105,6 @@ public class ID115Coordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;

View File

@ -26,9 +26,7 @@ import android.os.Build;
import android.os.ParcelUuid;
import android.support.annotation.NonNull;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -37,7 +35,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
import org.slf4j.Logger;
@ -144,14 +141,9 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value
return 3;
}
@Override

View File

@ -79,11 +79,6 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;

View File

@ -36,8 +36,6 @@ import java.util.Collections;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -131,11 +129,6 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3;

View File

@ -163,6 +163,8 @@ public class MiBandService {
public static final byte COMMAND_SEND_NOTIFICATION = 0x8;
public static final byte COMMAND_FACTORYRESET = 0x9;
public static final byte COMMAND_CONFIRM_ACTIVITY_DATA_TRANSFER_COMPLETE = 0xa;
public static final byte COMMAND_SYNC = 0xb;

View File

@ -32,7 +32,6 @@ import java.util.Collections;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -107,14 +106,9 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value
return 3;
}
@Override

View File

@ -116,11 +116,6 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;
@ -175,4 +170,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
public boolean supportsMusicInfo() {
return true;
}
@Override
public boolean supportsUnicodeEmojis() { return true; }
}

View File

@ -17,7 +17,6 @@
package nodomain.freeyourgadget.gadgetbridge.devices.roidmi;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
@ -83,11 +82,6 @@ public abstract class RoidmiCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;

View File

@ -23,8 +23,6 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
@ -80,11 +78,6 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 0;

View File

@ -112,11 +112,6 @@ public class Watch9DeviceCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value

View File

@ -86,14 +86,9 @@ public class XWatchCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value
return 0;
}
@Override

View File

@ -74,11 +74,6 @@ public class ZeTimeCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return true;
}
@Override
public int getAlarmSlotCount() {
return 3; // FIXME - check the real value

View File

@ -244,7 +244,7 @@ public class NotificationListener extends NotificationListenerService {
}
// Ignore too frequent notifications, according to user preference
long min_timeout = prefs.getInt("notifications_timeout", 0) * 1000;
long min_timeout = (long)prefs.getInt("notifications_timeout", 0) * 1000L;
long cur_time = System.currentTimeMillis();
if (notificationBurstPrevention.containsKey(source)) {
long last_time = notificationBurstPrevention.get(source);

View File

@ -1,215 +0,0 @@
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.impl;
import android.os.Parcel;
import android.support.annotation.NonNull;
import java.util.Calendar;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
public class GBAlarm implements Alarm {
private final int index;
private boolean enabled;
private boolean smartWakeup;
private int repetition;
private int hour;
private int minute;
private long deviceId;
public GBAlarm(Long deviceId, int index, boolean enabled, boolean smartWakeup, int repetition, int hour, int minute) {
this.deviceId = deviceId;
this.index = index;
this.enabled = enabled;
this.smartWakeup = smartWakeup;
this.repetition = repetition;
this.hour = hour;
this.minute = minute;
}
public static GBAlarm createSingleShot(long deviceId, int index, boolean smartWakeup, Calendar calendar) {
return new GBAlarm(deviceId, index, true, smartWakeup, Alarm.ALARM_ONCE, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE));
}
private static GBAlarm readFromParcel(Parcel pc) {
long deviceId = pc.readLong();
int index = pc.readInt();
boolean enabled = Boolean.parseBoolean(pc.readString());
boolean smartWakeup = Boolean.parseBoolean(pc.readString());
int repetition = pc.readInt();
int hour = pc.readInt();
int minute = pc.readInt();
return new GBAlarm(deviceId, index, enabled, smartWakeup, repetition, hour, minute);
}
@Override
public boolean equals(Object o) {
if (o instanceof GBAlarm) {
GBAlarm comp = (GBAlarm) o;
return comp.getIndex() == getIndex();
} else {
return false;
}
}
@Override
public int hashCode() {
return getIndex();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(deviceId);
dest.writeInt(this.index);
dest.writeString(String.valueOf(this.enabled));
dest.writeString(String.valueOf(this.smartWakeup));
dest.writeInt(this.repetition);
dest.writeInt(this.hour);
dest.writeInt(this.minute);
}
@Override
public int compareTo(@NonNull Alarm another) {
if (this.getIndex() < another.getIndex()) {
return -1;
} else if (this.getIndex() > another.getIndex()) {
return 1;
}
return 0;
}
@Override
public int getIndex() {
return this.index;
}
@Override
public String getTime() {
return String.format(Locale.US, "%02d", this.hour) + ":" + String.format(Locale.US, "%02d", this.minute);
}
public int getHour() {
return this.hour;
}
public int getMinute() {
return this.minute;
}
@Override
public Calendar getAlarmCal() {
Calendar alarm = Calendar.getInstance();
Calendar now = Calendar.getInstance();
alarm.set(Calendar.HOUR_OF_DAY, this.hour);
alarm.set(Calendar.MINUTE, this.minute);
if (now.after(alarm) && repetition == ALARM_ONCE) {
//if the alarm is in the past set it to tomorrow
alarm.add(Calendar.DATE, 1);
}
return alarm;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
@Override
public boolean isSmartWakeup() {
return this.smartWakeup;
}
@Override
public boolean getRepetition(int dow) {
return (this.repetition & dow) > 0;
}
@Override
public int getRepetitionMask() {
return this.repetition;
}
@Override
public boolean isRepetitive() {
return getRepetitionMask() != ALARM_ONCE;
}
public void setSmartWakeup(boolean smartWakeup) {
this.smartWakeup = smartWakeup;
}
public void setRepetition(boolean mon, boolean tue, boolean wed, boolean thu, boolean fri, boolean sat, boolean sun) {
this.repetition = (mon ? ALARM_MON : 0) |
(tue ? ALARM_TUE : 0) |
(wed ? ALARM_WED : 0) |
(thu ? ALARM_THU : 0) |
(fri ? ALARM_FRI : 0) |
(sat ? ALARM_SAT : 0) |
(sun ? ALARM_SUN : 0);
}
public void setHour(int hour) {
this.hour = hour;
}
public void setMinute(int minute) {
this.minute = minute;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void store() {
try (DBHandler db = GBApplication.acquireDB()) {
DaoSession daoSession = db.getDaoSession();
Long userId = DBHelper.getUser(daoSession).getId();
nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm = new nodomain.freeyourgadget.gadgetbridge.entities.Alarm(deviceId, userId, index, enabled, smartWakeup, repetition, hour, minute);
daoSession.insertOrReplace(alarm);
} catch (Exception e) {
// LOG.error("Error acquiring database", e);
}
}
public static final Creator CREATOR = new Creator() {
@Override
public GBAlarm createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
public GBAlarm[] newArray(int size) {
return new GBAlarm[size];
}
};
}

View File

@ -176,7 +176,7 @@ public class GBDeviceService implements DeviceService {
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
Intent intent = createIntent().setAction(ACTION_SET_ALARMS)
.putParcelableArrayListExtra(EXTRA_ALARMS, alarms);
.putExtra(EXTRA_ALARMS, alarms);
invokeService(intent);
}
@ -291,8 +291,9 @@ public class GBDeviceService implements DeviceService {
}
@Override
public void onReboot() {
Intent intent = createIntent().setAction(ACTION_REBOOT);
public void onReset(int flags) {
Intent intent = createIntent().setAction(ACTION_RESET)
.putExtra(EXTRA_RESET_FLAGS, flags);
invokeService(intent);
}

View File

@ -16,11 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.model;
import android.os.Parcelable;
import java.io.Serializable;
import java.util.Calendar;
public interface Alarm extends Serializable {
/**
* The {@link android.os.Bundle} name for transferring pacreled alarms.
*/
String EXTRA_ALARM = "alarm";
public interface Alarm extends Parcelable, Comparable<Alarm> {
byte ALARM_ONCE = 0;
byte ALARM_MON = 1;
byte ALARM_TUE = 2;
@ -30,19 +33,19 @@ public interface Alarm extends Parcelable, Comparable<Alarm> {
byte ALARM_SAT = 32;
byte ALARM_SUN = 64;
int getIndex();
int getPosition();
Calendar getAlarmCal();
boolean getEnabled();
String getTime();
boolean getSmartWakeup();
boolean isEnabled();
boolean isSmartWakeup();
int getRepetitionMask();
int getRepetition();
boolean isRepetitive();
boolean getRepetition(int dow);
}
int getHour();
int getMinute();
}

View File

@ -29,7 +29,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
public interface DeviceService extends EventHandler {
String PREFIX = "nodomain.freeyourgadget.gadgetbridge.devices";
String ACTION_MIBAND2_AUTH = PREFIX + ".action.miban2_auth";
String ACTION_START = PREFIX + ".action.start";
String ACTION_CONNECT = PREFIX + ".action.connect";
String ACTION_NOTIFICATION = PREFIX + ".action.notification";
@ -47,7 +46,7 @@ public interface DeviceService extends EventHandler {
String ACTION_APP_CONFIGURE = PREFIX + ".action.app_configure";
String ACTION_APP_REORDER = PREFIX + ".action.app_reorder";
String ACTION_INSTALL = PREFIX + ".action.install";
String ACTION_REBOOT = PREFIX + ".action.reboot";
String ACTION_RESET = PREFIX + ".action.reset";
String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test";
String ACTION_FETCH_RECORDED_DATA = PREFIX + ".action.fetch_activity_data";
String ACTION_DISCONNECT = PREFIX + ".action.disconnect";
@ -110,6 +109,7 @@ public interface DeviceService extends EventHandler {
String EXTRA_RECORDED_DATA_TYPES = "data_types";
String EXTRA_FM_FREQUENCY = "fm_frequency";
String EXTRA_LED_COLOR = "led_color";
String EXTRA_RESET_FLAGS = "reset_flags";
/**
* Use EXTRA_REALTIME_SAMPLE instead

View File

@ -51,6 +51,7 @@ public enum DeviceType {
WATCH9(100, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_watch9),
ROIDMI(110, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi),
ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3),
CASIOGB6900(120, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_casiogb6900),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
private final int key;

View File

@ -73,6 +73,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBAutoFetchReceiver;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.EmojiConverter;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -94,7 +95,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FI
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_HEARTRATE_TEST;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_INSTALL;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_NOTIFICATION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REBOOT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_RESET;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_APPINFO;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_DEVICEINFO;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REQUEST_SCREENSHOT;
@ -160,6 +161,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TYPE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_RECORDED_DATA_TYPES;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_RESET_FLAGS;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER;
@ -174,6 +176,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
private DeviceSupportFactory mFactory;
private GBDevice mGBDevice = null;
private DeviceSupport mDeviceSupport;
private DeviceCoordinator mCoordinator = null;
private PhoneCallReceiver mPhoneCallReceiver = null;
private SMSReceiver mSMSReceiver = null;
@ -225,8 +228,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
if (mGBDevice != null && mGBDevice.equals(device)) {
mGBDevice = device;
mCoordinator = DeviceHelper.getInstance().getCoordinator(device);
boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized());
setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), DeviceHelper.getInstance().getCoordinator(device));
setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), mCoordinator);
} else {
LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + device);
}
@ -351,6 +355,20 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
return START_STICKY;
}
/**
* @param text: original text
* @return 'text' or a new String without non supported chars like emoticons, etc.
*/
private String sanitizeNotifText(String text) {
if (text == null || text.length() == 0)
return text;
if (!mCoordinator.supportsUnicodeEmojis())
return EmojiConverter.convertUnicodeEmojiToAscii(text, getApplicationContext());
return text;
}
private void handleAction(Intent intent, String action, Prefs prefs) {
switch (action) {
case ACTION_REQUEST_DEVICEINFO:
@ -360,10 +378,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
int desiredId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
NotificationSpec notificationSpec = new NotificationSpec(desiredId);
notificationSpec.phoneNumber = intent.getStringExtra(EXTRA_NOTIFICATION_PHONENUMBER);
notificationSpec.sender = intent.getStringExtra(EXTRA_NOTIFICATION_SENDER);
notificationSpec.subject = intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT);
notificationSpec.title = intent.getStringExtra(EXTRA_NOTIFICATION_TITLE);
notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
notificationSpec.sender = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SENDER));
notificationSpec.subject = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT));
notificationSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE));
notificationSpec.body = sanitizeNotifText(intent.getStringExtra(EXTRA_NOTIFICATION_BODY));
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
notificationSpec.attachedActions = (ArrayList<NotificationSpec.Action>) intent.getSerializableExtra(EXTRA_NOTIFICATION_ACTIONS);
@ -404,9 +422,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
calendarEventSpec.type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1);
calendarEventSpec.timestamp = intent.getIntExtra(EXTRA_CALENDAREVENT_TIMESTAMP, -1);
calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1);
calendarEventSpec.title = intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE);
calendarEventSpec.description = intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION);
calendarEventSpec.location = intent.getStringExtra(EXTRA_CALENDAREVENT_LOCATION);
calendarEventSpec.title = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE));
calendarEventSpec.description = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION));
calendarEventSpec.location = sanitizeNotifText(intent.getStringExtra(EXTRA_CALENDAREVENT_LOCATION));
mDeviceSupport.onAddCalendarEvent(calendarEventSpec);
break;
}
@ -416,8 +434,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mDeviceSupport.onDeleteCalendarEvent(type, id);
break;
}
case ACTION_REBOOT: {
mDeviceSupport.onReboot();
case ACTION_RESET: {
int flags = intent.getIntExtra(EXTRA_RESET_FLAGS, 0);
mDeviceSupport.onReset(flags);
break;
}
case ACTION_HEARTRATE_TEST: {
@ -438,6 +457,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
setReceiversEnableState(false, false, null);
mGBDevice = null;
mDeviceSupport = null;
mCoordinator = null;
break;
}
case ACTION_FIND_DEVICE: {
@ -454,7 +474,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
CallSpec callSpec = new CallSpec();
callSpec.command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED);
callSpec.number = intent.getStringExtra(EXTRA_CALL_PHONENUMBER);
callSpec.name = intent.getStringExtra(EXTRA_CALL_DISPLAYNAME);
callSpec.name = sanitizeNotifText(intent.getStringExtra(EXTRA_CALL_DISPLAYNAME));
mDeviceSupport.onSetCallState(callSpec);
break;
case ACTION_SETCANNEDMESSAGES:
@ -471,9 +491,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
break;
case ACTION_SETMUSICINFO:
MusicSpec musicSpec = new MusicSpec();
musicSpec.artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST);
musicSpec.album = intent.getStringExtra(EXTRA_MUSIC_ALBUM);
musicSpec.track = intent.getStringExtra(EXTRA_MUSIC_TRACK);
musicSpec.artist = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ARTIST));
musicSpec.album = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_ALBUM));
musicSpec.track = sanitizeNotifText(intent.getStringExtra(EXTRA_MUSIC_TRACK));
musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0);
musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0);
musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0);
@ -528,7 +548,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
break;
case ACTION_SET_ALARMS:
ArrayList<Alarm> alarms = intent.getParcelableArrayListExtra(EXTRA_ALARMS);
ArrayList<? extends Alarm> alarms = (ArrayList<? extends Alarm>) intent.getSerializableExtra(EXTRA_ALARMS);
mDeviceSupport.onSetAlarms(alarms);
break;
case ACTION_ENABLE_REALTIME_STEPS: {
@ -593,9 +613,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mDeviceSupport.dispose();
mDeviceSupport = null;
mGBDevice = null;
mCoordinator = null;
}
mDeviceSupport = deviceSupport;
mGBDevice = mDeviceSupport != null ? mDeviceSupport.getDevice() : null;
mCoordinator = mGBDevice != null ? DeviceHelper.getInstance().getCoordinator(mGBDevice) : null;
}
private void start() {

View File

@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.CasioGB6900DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support;
@ -171,6 +172,9 @@ public class DeviceSupportFactory {
case ROIDMI3:
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case CASIOGB6900:
deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
}
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);

View File

@ -256,11 +256,11 @@ public class ServiceDeviceSupport implements DeviceSupport {
}
@Override
public void onReboot() {
if (checkBusy("reboot")) {
public void onReset(int flags) {
if (checkBusy("reset")) {
return;
}
delegate.onReboot();
delegate.onReset(flags);
}
@Override

View File

@ -110,9 +110,14 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
/**
* Send commands like this to the device:
* <p>
* <code>perform("sms notification").write(someCharacteristic, someByteArray).queue(getQueue());</code>
* <code>performInitialized("sms notification").write(someCharacteristic, someByteArray).queue(getQueue());</code>
* </p>
* TODO: support orchestration of multiple reads and writes depending on returned values
* This will asynchronously
* <ul>
* <li>connect to the device (if necessary)</li>
* <li>initialize the device (if necessary)</li>
* <li>execute the commands collected with the returned transaction builder</li>
* </ul>
*
* @see #performConnected(Transaction)
* @see #initializeDevice(TransactionBuilder)
@ -133,6 +138,11 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
}
/**
* Ensures that the device is connected and (only then) performs the actions of the given
* transaction builder.
*
* In contrast to {@link #performInitialized(String)}, no initialization sequence is performed
* with the device, only the actions of the given builder are executed.
* @param transaction
* @throws IOException
* @see {@link #performInitialized(String)}

View File

@ -0,0 +1,196 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
class CasioGATTServer extends BluetoothGattServerCallback {
private static final Logger LOG = LoggerFactory.getLogger(CasioGATTServer.class);
private Context mContext;
private BluetoothGattServer mBluetoothGattServer;
private CasioGB6900DeviceSupport mDeviceSupport = null;
private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl();
CasioGATTServer(Context context, CasioGB6900DeviceSupport deviceSupport) {
mContext = context;
mDeviceSupport = deviceSupport;
}
public void setContext(Context ctx) {
mContext = ctx;
}
boolean initialize() {
if(mContext == null) {
return false;
}
BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager == null) {
return false;
}
mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this);
if (mBluetoothGattServer == null) {
return false;
}
BluetoothGattService casioGATTService = new BluetoothGattService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic bluetoothgGATTCharacteristic = new BluetoothGattCharacteristic(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_WRITE);
bluetoothgGATTCharacteristic.setValue(new byte[0]);
BluetoothGattCharacteristic bluetoothgGATTCharacteristic2 = new BluetoothGattCharacteristic(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
bluetoothgGATTCharacteristic2.setValue(CasioGB6900Constants.MUSIC_MESSAGE.getBytes());
BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(CasioGB6900Constants.CCC_DESCRIPTOR_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothgGATTCharacteristic2.addDescriptor(bluetoothGattDescriptor);
casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic);
casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic2);
mBluetoothGattServer.addService(casioGATTService);
return true;
}
@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
if (!characteristic.getUuid().equals(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID)) {
LOG.warn("unexpected read request");
return;
}
LOG.info("will send response to read request from device: " + device.getAddress());
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, CasioGB6900Constants.MUSIC_MESSAGE.getBytes())) {
LOG.warn("error sending response");
}
}
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic,
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
if (!characteristic.getUuid().equals(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID)) {
LOG.warn("unexpected write request");
return;
}
if(mDeviceSupport == null) {
LOG.warn("mDeviceSupport is null, did initialization complete?");
return;
}
if((value[0] & 0x03) == 0) {
int button = value[1] & 0x0f;
LOG.info("Button pressed: " + button);
switch(button) {
case 3:
musicCmd.event = GBDeviceEventMusicControl.Event.NEXT;
break;
case 2:
musicCmd.event = GBDeviceEventMusicControl.Event.PREVIOUS;
break;
case 1:
musicCmd.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
break;
default:
LOG.warn("Unhandled button received: " + button);
return;
}
mDeviceSupport.evaluateGBDeviceEvent(musicCmd);
}
else {
LOG.info("received from device: " + value.toString());
}
}
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState);
if (newState == BluetoothGattServer.STATE_DISCONNECTED) {
}
if (newState == BluetoothGattServer.STATE_CONNECTED) {
GBDevice.State devState = mDeviceSupport.getDevice().getState();
Intent deviceCommunicationServiceIntent = new Intent(mContext, DeviceCommunicationService.class);
if (devState.equals(GBDevice.State.WAITING_FOR_RECONNECT) || devState.equals(GBDevice.State.NOT_CONNECTED)) {
LOG.info("Forcing re-connect because GATT server has been reconnected.");
deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT);
deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(deviceCommunicationServiceIntent);
//PendingIntent reconnectPendingIntent = PendingIntent.getService(mContext, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent);
}
}
}
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1));
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) {
LOG.warn("onDescriptorWriteRequest() error sending response!");
}
}
@Override
public void onServiceAdded(int status, BluetoothGattService service) {
LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid());
}
@Override
public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) {
LOG.info("onNotificationSent() status = " + status + " to device " + bluetoothDevice.getAddress());
}
void close() {
if (mBluetoothGattServer != null) {
mBluetoothGattServer.clearServices();
mBluetoothGattServer.close();
mBluetoothGattServer = null;
}
}
}

View File

@ -0,0 +1,72 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CasioGATTThread extends Thread {
CasioGATTServer mServer = null;
private static final Logger LOG = LoggerFactory.getLogger(CasioGATTThread.class);
private boolean mStopFlag = false;
private final Object waitObject = new Object();
public CasioGATTThread(Context context, CasioGB6900DeviceSupport deviceSupport)
{
mServer = new CasioGATTServer(context, deviceSupport);
}
public void setContext(Context ctx) {
mServer.setContext(ctx);
}
@Override
public void run() {
if (!mServer.initialize()) {
LOG.error("Error initializing CasioGATTServer. Has the context been set?");
return;
}
long waitTime = 60 * 1000;
while (!mStopFlag) {
synchronized (waitObject) {
try {
waitObject.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (mStopFlag) {
break;
}
}
mServer.close();
}
public void quit() {
mStopFlag = true;
synchronized (waitObject) {
waitObject.notify();
}
}
}

View File

@ -0,0 +1,634 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceSupport.class);
private ArrayList<BluetoothGattCharacteristic> mCasioCharacteristics = new ArrayList<BluetoothGattCharacteristic>();
private CasioGATTThread mThread;
private CasioHandlerThread mHandlerThread = null;
private MusicSpec mBufferMusicSpec = null;
private MusicStateSpec mBufferMusicStateSpec = null;
private BluetoothGatt mBtGatt = null;
public CasioGB6900DeviceSupport() {
super(LOG);
addSupportedService(GattService.UUID_SERVICE_IMMEDIATE_ALERT);
addSupportedService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE);
addSupportedService(CasioGB6900Constants.ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_IMMEDIATE_ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CURRENT_TIME_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.WATCH_FEATURES_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_PHONE_ALERT_STATUS_SERVICE);
addSupportedService(CasioGB6900Constants.MORE_ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.TX_POWER_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.LINK_LOSS_SERVICE);
mThread = new CasioGATTThread(getContext(), this);
}
@Override
public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
super.setContext(gbDevice, btAdapter, context);
mThread.setContext(context);
mThread.start();
}
@Override
public void dispose() {
LOG.info("Dispose");
close();
super.dispose();
}
private void close() {
if (mHandlerThread != null) {
mHandlerThread.quit();
mHandlerThread.interrupt();
mHandlerThread = null;
}
if(mThread != null) {
mThread.quit();
mThread.interrupt();
mThread = null;
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt) {
mBtGatt = gatt;
super.onServicesDiscovered(gatt);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
addCharacteristics();
builder.setGattCallback(this);
enableNotifications(builder, true);
configureWatch(builder);
LOG.info("Initialization Done");
return builder;
}
// FIXME: Replace hardcoded values by configuration
private void configureWatch(TransactionBuilder builder) {
if (mBtGatt == null)
return;
byte value[] = new byte[]{GattCharacteristic.MILD_ALERT};
BluetoothGattService llService = mBtGatt.getService(CasioGB6900Constants.LINK_LOSS_SERVICE);
BluetoothGattCharacteristic charact = llService.getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID);
builder.write(charact, value);
}
private void addCharacteristics() {
mCasioCharacteristics.clear();
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.FUNCTION_SWITCH_CHARACTERISTIC));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID));
mCasioCharacteristics.add(getCharacteristic(CasioGB6900Constants.RINGER_CONTROL_POINT));
}
public boolean enableNotifications(TransactionBuilder builder, boolean enable) {
for(BluetoothGattCharacteristic charact : mCasioCharacteristics) {
builder.notify(charact, enable);
}
return true;
}
public void readTxPowerLevel() {
try {
TransactionBuilder builder = performInitialized("readTxPowerLevel");
builder.read(getCharacteristic(CasioGB6900Constants.TX_POWER_LEVEL_CHARACTERISTIC_UUID));
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("readTxPowerLevel failed: " + e.getMessage());
}
}
private void writeCasioCurrentTime(TransactionBuilder builder) {
byte[] arr = new byte[10];
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
arr[0] = (byte)((year >>> 0) & 0xff);
arr[1] = (byte)((year >>> 8) & 0xff);
arr[2] = (byte)(1 + cal.get(Calendar.MONTH));
arr[3] = (byte)cal.get(Calendar.DAY_OF_MONTH);
arr[4] = (byte)cal.get(Calendar.HOUR_OF_DAY);
arr[5] = (byte)cal.get(Calendar.MINUTE);
arr[6] = (byte)(1 + cal.get(Calendar.SECOND));
byte dayOfWk = (byte)(cal.get(Calendar.DAY_OF_WEEK) - 1);
if(dayOfWk == 0)
dayOfWk = 7;
arr[7] = dayOfWk;
arr[8] = (byte)(int) TimeUnit.MILLISECONDS.toSeconds(256 * cal.get(Calendar.MILLISECOND));
arr[9] = 1; // or 0?
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CURRENT_TIME_CHARACTERISTIC_UUID);
if(charact != null) {
charact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
builder.write(charact, arr);
}
else {
LOG.warn("Characteristic not found: CURRENT_TIME_CHARACTERISTIC_UUID");
}
}
private void writeCasioLocalTimeInformation(TransactionBuilder builder) {
Calendar cal = Calendar.getInstance();
int zoneOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.ZONE_OFFSET));
int dstOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.DST_OFFSET));
byte byte0 = (byte)(zoneOffset / 15);
byte byte1 = (byte)(dstOffset / 15);
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.LOCAL_TIME_CHARACTERISTIC_UUID);
if(charact != null) {
builder.write(charact, new byte[]{byte0, byte1});
}
else {
LOG.warn("Characteristic not found: LOCAL_TIME_CHARACTERISTIC_UUID");
}
}
private void writeCasioVirtualServerFeature(TransactionBuilder builder) {
byte byte0 = (byte)0;
byte0 |= 1; // Casio Current Time Service
byte0 |= 2; // Casio Alert Notification Service
byte0 |= 4; // Casio Phone Alert Status Service
byte0 |= 8; // Casio Immediate Alert Service
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_FEATURES);
if(charact != null) {
builder.write(charact, new byte[]{byte0, 0x00});
}
else {
LOG.warn("Characteristic not found: CASIO_VIRTUAL_SERVER_FEATURES");
}
}
private boolean handleInitResponse(byte data) {
boolean handled = false;
switch(data)
{
case (byte) 1:
LOG.info("Initialization done, setting state to INITIALIZED");
if(mHandlerThread == null) {
mHandlerThread = new CasioHandlerThread(getDevice(), getContext(), this);
mHandlerThread.start();
}
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
handled = true;
break;
default:
LOG.warn("handleInitResponse: Error initializing device, received unexpected value: " + data);
gbDevice.setState(GBDevice.State.NOT_CONNECTED);
gbDevice.sendDeviceUpdateIntent(getContext());
handled = true;
break;
}
return handled;
}
private boolean handleTimeRequests(byte data) {
boolean handled = false;
switch(data) // Request Type
{
case (byte) 1:
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioCurrentTime");
writeCasioCurrentTime(builder);
performConnected(builder.getTransaction());
handled = true;
} catch (IOException e) {
LOG.warn("handleTimeRequests::writeCasioCurrentTime failed: " + e.getMessage());
}
break;
case (byte) 2:
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioLocalTimeInformation");
writeCasioLocalTimeInformation(builder);
performConnected(builder.getTransaction());
handled = true;
} catch (IOException e) {
LOG.warn("handleTimeRequests::writeCasioLocalTimeInformation failed: " + e.getMessage());
}
break;
}
return handled;
}
private boolean handleServerFeatureRequests(byte data) {
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioVirtualServerFeature");
writeCasioVirtualServerFeature(builder);
performConnected(builder.getTransaction());
} catch (IOException e) {
LOG.warn("handleServerFeatureRequests failed: " + e.getMessage());
}
return true;
}
private boolean handleCasioCom(byte[] data) {
boolean handled = false;
if(data.length < 3) {
LOG.warn("handleCasioCom failed: Received unexpected request (too short)");
return false;
}
switch(data[0]) // ServiceID
{
case 0:
handled = handleInitResponse(data[2]);
break;
case 2:
handled = handleTimeRequests(data[2]);
break;
case 7:
handled = handleServerFeatureRequests(data[2]);
break;
}
return handled;
}
@Override
public boolean onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if(data.length == 0)
return true;
if(characteristicUUID.equals(CasioGB6900Constants.TX_POWER_LEVEL_CHARACTERISTIC_UUID)) {
String str = "onCharacteristicRead: Received power level: ";
for(int i=0; i<data.length; i++) {
str += String.format("0x%1x ", data[i]);
}
LOG.info(str);
}
else {
return super.onCharacteristicRead(gatt, characteristic, status);
}
return true;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
boolean handled = false;
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
return true;
if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT)) {
handled = handleCasioCom(data);
}
if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT)) {
handled = handleCasioCom(data);
}
if(characteristicUUID.equals(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID)) {
GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
if(data[0] == 0x02) {
findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
}
else {
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
}
evaluateGBDeviceEvent(findPhoneEvent);
handled = true;
}
if(characteristicUUID.equals(CasioGB6900Constants.RINGER_CONTROL_POINT)) {
if(data[0] == 0x02)
{
LOG.info("Mute/ignore call event not yet supported by GB");
}
handled = true;
}
if(!handled) {
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
return super.onCharacteristicChanged(gatt, characteristic);
}
return true;
}
private void showNotification(byte icon, String title, String message) {
try {
TransactionBuilder builder = performInitialized("showNotification");
int len;
byte[] titleBytes = title.getBytes(StandardCharsets.US_ASCII);
len = titleBytes.length > 18 ? 18 : titleBytes.length;
byte[] msg = new byte[2 + len];
msg[0] = icon;
msg[1] = 1;
for(int i=0; i<len; i++)
{
msg[i + 2] = titleBytes[i];
}
builder.write(getCharacteristic(CasioGB6900Constants.ALERT_CHARACTERISTIC_UUID), msg);
LOG.info("Showing notification, title: " + title + " message (not sent): " + message);
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("showNotification failed: " + e.getMessage());
}
}
@Override
public boolean useAutoConnect() {
return true;
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
byte icon;
switch (notificationSpec.type) {
case GENERIC_SMS:
icon = CasioGB6900Constants.SMS_NOTIFICATION_ID;
break;
case GENERIC_CALENDAR:
icon = CasioGB6900Constants.CALENDAR_NOTIFICATION_ID;
break;
case GENERIC_EMAIL:
icon = CasioGB6900Constants.MAIL_NOTIFICATION_ID;
break;
default:
icon = CasioGB6900Constants.SNS_NOTIFICATION_ID;
break;
}
showNotification(icon, notificationTitle, notificationSpec.body);
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
}
@Override
public void onSetTime() {
try {
TransactionBuilder builder = performInitialized("SetTime");
writeCasioLocalTimeInformation(builder);
writeCasioCurrentTime(builder);
builder.queue(getQueue());
} catch(IOException e) {
LOG.warn("onSetTime failed: " + e.getMessage());
}
}
@Override
public void onSetCallState(CallSpec callSpec) {
switch (callSpec.command) {
case CallSpec.CALL_INCOMING:
showNotification(CasioGB6900Constants.CALL_NOTIFICATION_ID, callSpec.name, callSpec.number);
break;
default:
LOG.info("not sending CallSpec since only CALL_INCOMING is handled");
break;
}
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
if(stateSpec != mBufferMusicStateSpec)
{
mBufferMusicStateSpec = stateSpec;
sendMusicInfo();
}
}
private void sendMusicInfo()
{
try {
TransactionBuilder builder = performInitialized("sendMusicInfo");
String info = "";
if (mBufferMusicSpec.track != null && mBufferMusicSpec.track.length() > 0) {
info += mBufferMusicSpec.track;
}
if (mBufferMusicSpec.album != null && mBufferMusicSpec.album.length() > 0) {
info += mBufferMusicSpec.album;
}
if (mBufferMusicSpec.artist != null && mBufferMusicSpec.artist.length() > 0) {
info += mBufferMusicSpec.artist;
}
byte[] bInfo = info.getBytes(StandardCharsets.US_ASCII);
int len = bInfo.length > 17 ? 17 : bInfo.length;
byte[] arr = new byte[len + 3];
arr[0] = 0;
arr[1] = 10;
arr[2] = 1;
for(int i=0; i<len; i++)
{
arr[i+3] = bInfo[i];
}
builder.write(getCharacteristic(CasioGB6900Constants.MORE_ALERT_FOR_LONG_UUID), arr);
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("sendMusicInfo failed: " + e.getMessage());
}
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
if(musicSpec != mBufferMusicSpec)
{
mBufferMusicSpec = musicSpec;
sendMusicInfo();
}
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
}
@Override
public void onInstallApp(Uri uri) {
}
@Override
public void onAppInfoReq() {
}
@Override
public void onAppStart(UUID uuid, boolean start) {
}
@Override
public void onAppDelete(UUID uuid) {
}
@Override
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
}
@Override
public void onAppReorder(UUID[] uuids) {
}
@Override
public void onFetchRecordedData(int dataTypes) {
}
@Override
public void onReset(int flags) {
}
@Override
public void onHeartRateTest() {
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
}
@Override
public void onFindDevice(boolean start) {
if(start) {
showNotification(CasioGB6900Constants.SNS_NOTIFICATION_ID, "You found it!", "");
}
}
@Override
public void onSetConstantVibration(int integer) {
}
@Override
public void onScreenshotReq() {
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
}
@Override
public void onSetHeartRateMeasurementInterval(int seconds) {
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {
}
@Override
public void onSendConfiguration(String config) {
}
@Override
public void onTestNewFunction() {
}
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
}
}

View File

@ -0,0 +1,120 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
public class CasioHandlerThread extends GBDeviceIoThread {
private static final Logger LOG = LoggerFactory.getLogger(CasioHandlerThread.class);
private boolean mQuit = false;
private CasioGB6900DeviceSupport mDeviceSupport;
private final Object waitObject = new Object();
//private CasioGATTServer mServer = null;
private int TX_PERIOD = 60;
private Calendar mTxTime = GregorianCalendar.getInstance();
public CasioHandlerThread(GBDevice gbDevice, Context context, CasioGB6900DeviceSupport deviceSupport) {
super(gbDevice, context);
LOG.info("Initializing Casio Handler Thread");
mQuit = false;
//mServer = new CasioGATTServer(context, deviceSupport);
mDeviceSupport = deviceSupport;
}
@Override
public void run() {
mQuit = false;
/*
if(!mServer.initialize()) {
LOG.error("Error initializing CasioGATTServer. Has the context been set?");
return;
}
*/
long waitTime = TX_PERIOD * 1000;
while (!mQuit) {
if (waitTime > 0) {
synchronized (waitObject) {
try {
waitObject.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if (mQuit) {
break;
}
if (gbDevice.getState() == GBDevice.State.NOT_CONNECTED) {
quit();
}
Calendar now = GregorianCalendar.getInstance();
if (now.compareTo(mTxTime) > 0) {
requestTxPowerLevel();
}
now = GregorianCalendar.getInstance();
waitTime = mTxTime.getTimeInMillis() - now.getTimeInMillis();
}
}
public void requestTxPowerLevel() {
try {
mDeviceSupport.readTxPowerLevel();
} catch(Exception e) {
}
mTxTime = GregorianCalendar.getInstance();
mTxTime.add(Calendar.SECOND, TX_PERIOD);
synchronized (waitObject) {
waitObject.notify();
}
}
@Override
public void quit() {
LOG.info("CasioHandlerThread: Quit Handler Thread");
mQuit = true;
synchronized (waitObject) {
waitObject.notify();
}
}
}

View File

@ -58,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
@ -415,7 +416,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
setCurrentDate(builder);
setCurrentTime(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
}catch(IOException e){
}
@ -430,13 +431,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
for (Alarm alarm : alarms) {
if (!alarm.isEnabled())
if (!alarm.getEnabled())
continue;
if (alarm.isSmartWakeup()) //Not available
if (alarm.getSmartWakeup()) //Not available
continue;
Calendar t = alarm.getAlarmCal();
Calendar t = AlarmUtils.toCalendar(alarm);
setAlarm(builder, t);
builder.queue(getQueue());
@ -446,7 +447,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
setAlarm(builder, null);
performConnected(builder.getTransaction());
builder.queue(getQueue());
GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO);
}catch(Exception e){}
@ -526,13 +527,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
try {
getQueue().clear();
TransactionBuilder builder = performInitialized("Shutdown");
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN});
performConnected(builder.getTransaction());
builder.queue(getQueue());
}catch(Exception e){
}
@ -545,7 +546,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("HeartRateTest");
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ?
performConnected(builder.getTransaction());
builder.queue(getQueue());
}catch(Exception e){
}
@ -563,7 +564,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF;
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state});
performConnected(builder.getTransaction());
builder.queue(getQueue());
}catch(Exception e){
}
@ -575,7 +576,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("findMe");
setFindMe(builder, start);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error toggling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -591,10 +592,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER;
for (int i = 0; i < msg.length - 1; i++)
msg[i + 1] = (byte) "GadgetBridge".charAt(i);
msg[i + 1] = (byte) "Gadgetbridge".charAt(i);
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error setting Vibration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -711,7 +712,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
builder.write(ctrlCharacteristic, msg);
}
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error showing incoming call: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
@ -774,7 +775,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
msg[2] = (byte) remaining;
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error showing device Notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);

View File

@ -72,11 +72,11 @@ 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;
import nodomain.freeyourgadget.gadgetbridge.entities.AlarmDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
@ -112,6 +112,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.Ini
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
@ -613,7 +615,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("Set alarm");
boolean anyAlarmEnabled = false;
for (Alarm alarm : alarms) {
anyAlarmEnabled |= alarm.isEnabled();
anyAlarmEnabled |= alarm.getEnabled();
queueAlarm(alarm, builder, characteristic);
}
builder.queue(getQueue());
@ -829,13 +831,17 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
try {
TransactionBuilder builder = performInitialized("Reboot");
sendReboot(builder);
TransactionBuilder builder = performInitialized("Reset");
if ((flags & GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET) != 0) {
sendFactoryReset(builder);
} else {
sendReboot(builder);
}
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to reboot MI", ex);
LOG.error("Unable to reset", ex);
}
}
@ -844,6 +850,11 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
return this;
}
public HuamiSupport sendFactoryReset(TransactionBuilder builder) {
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_FACTORY_RESET);
return this;
}
@Override
public void onHeartRateTest() {
if (characteristicHRControlPoint == null) {
@ -1392,29 +1403,29 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
* @param characteristic
*/
private void queueAlarm(Alarm alarm, TransactionBuilder builder, BluetoothGattCharacteristic characteristic) {
Calendar calendar = alarm.getAlarmCal();
Calendar calendar = AlarmUtils.toCalendar(alarm);
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
int maxAlarms = coordinator.getAlarmSlotCount();
if (alarm.getIndex() >= maxAlarms) {
if (alarm.isEnabled()) {
if (alarm.getPosition() >= maxAlarms) {
if (alarm.getEnabled()) {
GB.toast(getContext(), "Only " + maxAlarms + " alarms are currently supported.", Toast.LENGTH_LONG, GB.WARN);
}
return;
}
int base = 0;
if (alarm.isEnabled()) {
if (alarm.getEnabled()) {
base = 128;
}
int daysMask = alarm.getRepetitionMask();
int daysMask = alarm.getRepetition();
if (!alarm.isRepetitive()) {
daysMask = 128;
}
byte[] alarmMessage = new byte[] {
(byte) 0x2, // TODO what is this?
(byte) (base + alarm.getIndex()), // 128 is the base, alarm slot is added
(byte) (base + alarm.getPosition()), // 128 is the base, alarm slot is added
(byte) calendar.get(Calendar.HOUR_OF_DAY),
(byte) calendar.get(Calendar.MINUTE),
(byte) daysMask,
@ -1467,14 +1478,6 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> mEvents = upcomingEvents.getCalendarEventList(getContext());
Long deviceId;
try (DBHandler handler = GBApplication.acquireDB()) {
DaoSession session = handler.getDaoSession();
deviceId = DBHelper.getDevice(getDevice(), session).getId();
} catch (Exception e) {
LOG.error("Could not acquire DB", e);
return this;
}
int iteration = 0;
for (CalendarEvents.CalendarEvent mEvt : mEvents) {
@ -1484,7 +1487,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
int slotToUse = 2 - iteration;
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(mEvt.getBegin());
Alarm alarm = GBAlarm.createSingleShot(deviceId, slotToUse, false, calendar);
Alarm alarm = AlarmUtils.createSingleShot(slotToUse, false, calendar);
queueAlarm(alarm, builder, characteristic);
iteration++;
}

View File

@ -27,27 +27,31 @@ import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
// gps detection is totally bogus, just the first 16 bytes
private static final byte[] GPS_HEADER = new byte[]{
(byte) 0xcb, 0x51, (byte) 0xc1, 0x30, 0x41, (byte) 0x9e, 0x5e, (byte) 0xd3,
0x51, 0x35, (byte) 0xdf, 0x66, (byte) 0xed, (byte) 0xd9, 0x5f, (byte) 0xa7
};
private static final byte[] GPS_HEADER2 = new byte[]{
0x10, 0x50, 0x26, 0x76, (byte) 0x8f, 0x4a, (byte) 0xa1, 0x49,
(byte) 0xa7, 0x26, (byte) 0xd0, (byte) 0xe6, 0x4a, 0x21, (byte) 0x88, (byte) 0xd4
};
private static final byte[] GPS_HEADER3 = new byte[]{
(byte) 0xeb, (byte) 0xfa, (byte) 0xc5, (byte) 0x89, (byte) 0xf0, 0x5c, 0x2e, (byte) 0xcc,
(byte) 0xfa, (byte) 0xf3, 0x62, (byte) 0xeb, (byte) 0x92, (byte) 0xc6, (byte) 0xa1, (byte) 0xbb
};
private static final byte[] GPS_HEADER4 = new byte[]{
0x0b, 0x61, 0x53, (byte) 0xed, (byte) 0x83, (byte) 0xac, 0x07, 0x21,
(byte) 0x8c, 0x36, 0x2e, (byte) 0x8c, (byte) 0x9c, 0x08, 0x54, (byte) 0xa6
};
private static final byte[] GPS_HEADER5 = new byte[]{
(byte) 0xec, 0x51, 0x73, 0x22 , 0x60 ,0x02 ,0x14, (byte) 0xb7,
(byte) 0xb5, (byte) 0xea, 0x4b, 0x22 , 0x5d, 0x23, (byte) 0xe5, 0x4f
private static final byte[][] GPS_HEADERS = {
new byte[]{
(byte) 0xcb, 0x51, (byte) 0xc1, 0x30, 0x41, (byte) 0x9e, 0x5e, (byte) 0xd3,
0x51, 0x35, (byte) 0xdf, 0x66, (byte) 0xed, (byte) 0xd9, 0x5f, (byte) 0xa7
},
new byte[]{
0x10, 0x50, 0x26, 0x76, (byte) 0x8f, 0x4a, (byte) 0xa1, 0x49,
(byte) 0xa7, 0x26, (byte) 0xd0, (byte) 0xe6, 0x4a, 0x21, (byte) 0x88, (byte) 0xd4
},
new byte[]{
(byte) 0xeb, (byte) 0xfa, (byte) 0xc5, (byte) 0x89, (byte) 0xf0, 0x5c, 0x2e, (byte) 0xcc,
(byte) 0xfa, (byte) 0xf3, 0x62, (byte) 0xeb, (byte) 0x92, (byte) 0xc6, (byte) 0xa1, (byte) 0xbb
},
new byte[]{
0x0b, 0x61, 0x53, (byte) 0xed, (byte) 0x83, (byte) 0xac, 0x07, 0x21,
(byte) 0x8c, 0x36, 0x2e, (byte) 0x8c, (byte) 0x9c, 0x08, 0x54, (byte) 0xa6
},
new byte[]{
(byte) 0xec, 0x51, 0x73, 0x22, 0x60, 0x02, 0x14, (byte) 0xb7,
(byte) 0xb5, (byte) 0xea, 0x4b, 0x22, 0x5d, 0x23, (byte) 0xe5, 0x4f
},
new byte[]{
0x73, 0x75, 0x68, (byte) 0xd0, 0x70, 0x73, (byte) 0xbb, 0x5a,
0x3e, (byte) 0xc3, (byte) 0xd3, 0x09, (byte) 0x9e, 0x1d, (byte) 0xd3, (byte) 0xc9
}
};
// this is the same as Cor
@ -132,6 +136,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(16716, "9565,dfbd8faf42,0");
crcToVersion.put(54154, "9567,8b05506,0,0,");
crcToVersion.put(15717, "15974,e61dd16,126");
crcToVersion.put(62532, "18344,eb2f43f,126");
// font
crcToVersion.put(61054, "8");
@ -150,9 +155,6 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
}
return HuamiFirmwareType.RES;
}
if (ArrayUtils.startsWith(bytes, GPS_HEADER) || ArrayUtils.startsWith(bytes, GPS_HEADER2) || ArrayUtils.startsWith(bytes, GPS_HEADER3) || ArrayUtils.startsWith(bytes, GPS_HEADER4) || ArrayUtils.startsWith(bytes, GPS_HEADER5)) {
return HuamiFirmwareType.GPS;
}
if (ArrayUtils.startsWith(bytes, GPS_ALMANAC_HEADER)) {
return HuamiFirmwareType.GPS_ALMANAC;
}
@ -175,6 +177,12 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
return HuamiFirmwareType.FONT_LATIN;
}
}
for (byte[] gpsHeader : GPS_HEADERS) {
if (ArrayUtils.startsWith(bytes, gpsHeader)) {
return HuamiFirmwareType.GPS;
}
}
return HuamiFirmwareType.INVALID;
}

View File

@ -85,9 +85,9 @@ public class AmazfitBipSupport extends HuamiSupport {
return;
}
String senderOrTiltle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
String senderOrTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
String message = StringUtils.truncate(senderOrTiltle, 32) + "\0";
String message = StringUtils.truncate(senderOrTitle, 32) + "\0";
if (notificationSpec.subject != null) {
message += StringUtils.truncate(notificationSpec.subject, 128) + "\n\n";
}

View File

@ -43,6 +43,7 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(9458, "1.0.7.52");
crcToVersion.put(51575, "1.0.7.88");
crcToVersion.put(6346, "1.2.5.00");
crcToVersion.put(24277, "1.2.7.20");
// resources
crcToVersion.put(46341, "RES 1.0.5.60");
@ -51,6 +52,7 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(60501, "RES 1.0.7.52-71");
crcToVersion.put(31263, "RES 1.0.7.77-91");
crcToVersion.put(20920, "RES 1.2.5.00-65");
crcToVersion.put(25397, "RES 1.2.7.20-69");
// font
crcToVersion.put(61054, "8");

View File

@ -53,6 +53,8 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(31330, "1.6.0.16");
crcToVersion.put(10930, "1.8.0.0");
crcToVersion.put(59800, "2.0.0.4");
crcToVersion.put(10023, "2.2.0.12");
crcToVersion.put(40344, "2.2.0.14");
// resources
crcToVersion.put(54724, "1.2.0.8");
@ -61,6 +63,7 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo {
crcToVersion.put(25278, "1.4.0.12-1.6.0.16");
crcToVersion.put(23249, "1.8.0.0");
crcToVersion.put(1815, "2.0.0.4");
crcToVersion.put(7225, "2.2.0.12-2.2.0.14");
// font
crcToVersion.put(19775, "1");

View File

@ -101,7 +101,7 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
try {
TransactionBuilder builder = performInitialized("time");
setTime(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(IOException e) {
LOG.warn("Unable to send current time", e);
}
@ -185,7 +185,7 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
try {
getQueue().clear();
@ -193,7 +193,7 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
builder.write(normalWriteCharacteristic, new byte[] {
ID115Constants.CMD_ID_DEVICE_RESTART, ID115Constants.CMD_KEY_REBOOT
});
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(Exception e) {
}
}
@ -350,7 +350,7 @@ public class ID115Support extends AbstractBTLEDeviceSupport {
ID115Constants.CMD_KEY_NOTIFY_STOP,
1
});
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(IOException e) {
LOG.warn("Unable to stop call notification", e);
}

View File

@ -45,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
@ -215,7 +216,7 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
}
builder.write(ctrlCharacteristic, currentPacket);
}
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn(e.getMessage());
}
@ -277,14 +278,14 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
default:
return;
}
Calendar cal = alarms.get(i).getAlarmCal();
Calendar cal = AlarmUtils.toCalendar(alarms.get(i));
builder.write(ctrlCharacteristic, commandWithChecksum(
cmd,
alarms.get(i).isEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1,
alarms.get(i).isEnabled() ? cal.get(Calendar.MINUTE) : -1
alarms.get(i).getEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1,
alarms.get(i).getEnabled() ? cal.get(Calendar.MINUTE) : -1
));
}
performConnected(builder.getTransaction());
builder.queue(getQueue());
GB.toast(getContext(), "Alarm settings applied - do note that the current device does not support day specification", Toast.LENGTH_LONG, GB.INFO);
} catch(IOException e) {
LOG.warn(e.getMessage());
@ -296,7 +297,7 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
try {
TransactionBuilder builder = performInitialized("SetTime");
syncDateAndTime(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(IOException e) {
LOG.warn(e.getMessage());
}
@ -367,13 +368,13 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
try {
TransactionBuilder builder = performInitialized("Reboot");
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_ACTION_REBOOT_DEVICE, 0, 0
));
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(Exception e) {
LOG.warn(e.getMessage());
}
@ -386,7 +387,7 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_ACTION_HEARTRATE_SWITCH, 0, 1
));
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(Exception e) {
LOG.warn(e.getMessage());
}
@ -400,7 +401,7 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
builder.write(ctrlCharacteristic, commandWithChecksum(
JYouConstants.CMD_SET_HEARTRATE_AUTO, 0, enable ? 1 : 0
));
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch(Exception e) {
LOG.warn(e.getMessage());
}

View File

@ -56,7 +56,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
@ -86,6 +85,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.UpdateFirmwareOperation;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
@ -270,6 +271,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
}
static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT};
static final byte[] factoryReset = new byte[]{MiBandService.COMMAND_FACTORYRESET};
static final byte[] startHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 1};
static final byte[] stopHeartMeasurementManual = new byte[]{0x15, MiBandService.COMMAND_SET_HR_MANUAL, 0};
@ -558,7 +560,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("Set alarm");
boolean anyAlarmEnabled = false;
for (Alarm alarm : alarms) {
anyAlarmEnabled |= alarm.isEnabled();
anyAlarmEnabled |= alarm.getEnabled();
queueAlarm(alarm, builder, characteristic);
}
builder.queue(getQueue());
@ -689,13 +691,17 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
try {
TransactionBuilder builder = performInitialized("Reboot");
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot);
TransactionBuilder builder = performInitialized("reset");
if ((flags & GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET) != 0) {
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), factoryReset);
} else {
builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), reboot);
}
builder.queue(getQueue());
} catch (IOException ex) {
LOG.error("Unable to reboot MI", ex);
LOG.error("Unable to reset", ex);
}
}
@ -1128,20 +1134,20 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
* @param characteristic
*/
private void queueAlarm(Alarm alarm, TransactionBuilder builder, BluetoothGattCharacteristic characteristic) {
byte[] alarmCalBytes = MiBandDateConverter.calendarToRawBytes(alarm.getAlarmCal());
byte[] alarmCalBytes = MiBandDateConverter.calendarToRawBytes(AlarmUtils.toCalendar(alarm));
byte[] alarmMessage = new byte[]{
MiBandService.COMMAND_SET_TIMER,
(byte) alarm.getIndex(),
(byte) (alarm.isEnabled() ? 1 : 0),
(byte) alarm.getPosition(),
(byte) (alarm.getEnabled() ? 1 : 0),
alarmCalBytes[0],
alarmCalBytes[1],
alarmCalBytes[2],
alarmCalBytes[3],
alarmCalBytes[4],
alarmCalBytes[5],
(byte) (alarm.isSmartWakeup() ? 30 : 0),
(byte) alarm.getRepetitionMask()
(byte) (alarm.getSmartWakeup() ? 30 : 0),
(byte) alarm.getRepetition()
};
builder.write(characteristic, alarmMessage);
}
@ -1225,15 +1231,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> mEvents = upcomingEvents.getCalendarEventList(getContext());
Long deviceId;
try (DBHandler handler = GBApplication.acquireDB()) {
DaoSession session = handler.getDaoSession();
deviceId = DBHelper.getDevice(getDevice(), session).getId();
} catch (Exception e) {
LOG.error("Could not acquire DB", e);
return;
}
int iteration = 0;
for (CalendarEvents.CalendarEvent mEvt : mEvents) {
if (iteration >= availableSlots || iteration > 2) {
@ -1242,7 +1239,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
int slotToUse = 2 - iteration;
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(mEvt.getBegin());
Alarm alarm = GBAlarm.createSingleShot(deviceId, slotToUse, false, calendar);
Alarm alarm = AlarmUtils.createSingleShot(slotToUse, false, calendar);
queueAlarm(alarm, builder, characteristic);
iteration++;
}

View File

@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAc
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -155,7 +156,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand1Operation {
} else if (updateCoordinator.needsReboot()) {
displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO);
GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext());
getSupport().onReboot();
getSupport().onReset(GBDeviceProtocol.RESET_FLAGS_REBOOT);
} else {
LOG.error("BUG: Successful firmware update without reboot???");
}

View File

@ -58,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import static org.apache.commons.lang3.math.NumberUtils.min;
@ -176,7 +177,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
try {
TransactionBuilder builder = performInitialized("setTime");
setTime(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error setting time: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -188,20 +189,20 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("Set alarm");
boolean anyAlarmEnabled = false;
for (Alarm alarm : alarms) {
anyAlarmEnabled |= alarm.isEnabled();
Calendar calendar = alarm.getAlarmCal();
anyAlarmEnabled |= alarm.getEnabled();
Calendar calendar = AlarmUtils.toCalendar(alarm);
int maxAlarms = 3;
if (alarm.getIndex() >= maxAlarms) {
if (alarm.isEnabled()) {
if (alarm.getPosition() >= maxAlarms) {
if (alarm.getEnabled()) {
GB.toast(getContext(), "Only 3 alarms are supported.", Toast.LENGTH_LONG, GB.WARN);
}
return;
}
int daysMask = 0;
if (alarm.isEnabled()) {
daysMask = alarm.getRepetitionMask();
if (alarm.getEnabled()) {
daysMask = alarm.getRepetition();
// Mask for this device starts from sunday and not from monday.
daysMask = (daysMask / 64) + (daysMask >> 1);
}
@ -210,11 +211,11 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
(byte) daysMask,
(byte) calendar.get(Calendar.HOUR_OF_DAY),
(byte) calendar.get(Calendar.MINUTE),
(byte) (alarm.isEnabled() ? 2 : 0), // vibration duration
(byte) (alarm.isEnabled() ? 10 : 0), // vibration count
(byte) (alarm.isEnabled() ? 2 : 0), // unknown
(byte) (alarm.getEnabled() ? 2 : 0), // vibration duration
(byte) (alarm.getEnabled() ? 10 : 0), // vibration count
(byte) (alarm.getEnabled() ? 2 : 0), // unknown
(byte) 0,
(byte) (alarm.getIndex() + 1)
(byte) (alarm.getPosition() + 1)
};
builder.write(ctrlCharacteristic, alarmMessage);
}
@ -296,7 +297,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
}
@Override
@ -308,7 +309,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
(byte) 0x11
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error starting heart rate measurement: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -500,7 +501,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
1
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("Unable to set vibration", e);
}
@ -514,7 +515,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
(byte) iconId
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error showing icon: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -546,7 +547,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
System.arraycopy(bytes, 0, msg, 2, length);
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error showing notificaton: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -560,7 +561,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
No1F1Constants.NOTIFICATION_STOP
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
LOG.warn("Unable to stop notification", e);
}
@ -578,7 +579,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
(byte) 0xfa
};
builder.write(ctrlCharacteristic, msg);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}

View File

@ -0,0 +1,162 @@
/* Copyright (C) 2018 Johann C. Rode, Sergio Lopez
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import android.util.Pair;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Locale;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
class AppMessageHandlerM7S extends AppMessageHandler {
private static final int CLEAR = 73;
private static final int CLOUDY = 34;
private static final int FOG = 60;
private static final int DRIZZLE = 45;
private static final int LIGHT_RAIN = 36;
private static final int RAIN = 36;
private static final int THUNDERSTORM = 70;
private static final int SNOW = 57;
private static final int HAIL = 51;
private static final int WIND = 66;
private static final int EXTREME_WIND = 66;
private static final int TORNADO = 88;
private static final int HURRICANE = 88;
private static final int EXTREME_COLD = 90;
private static final int EXTREME_HEAT = 93;
private static final int HAZE = 63;
private Integer KEY_LOCATION_NAME;
private Integer KEY_WEATHER_TEMP;
private Integer KEY_WEATHER_STRING_1;
private Integer KEY_WEATHER_STRING_2;
private Integer KEY_WEATHER_ICON;
private Integer KEY_WEATHER_DATA_TIME;
AppMessageHandlerM7S(UUID uuid, PebbleProtocol pebbleProtocol) {
super(uuid, pebbleProtocol);
try {
JSONObject appKeys = getAppKeys();
KEY_LOCATION_NAME = appKeys.getInt("KEY_LOCATION_NAME");
KEY_WEATHER_TEMP = appKeys.getInt("KEY_WEATHER_TEMP");
KEY_WEATHER_STRING_1 = appKeys.getInt("KEY_WEATHER_STRING_1");
KEY_WEATHER_STRING_2 = appKeys.getInt("KEY_WEATHER_STRING_2");
KEY_WEATHER_ICON = appKeys.getInt("KEY_WEATHER_ICON");
KEY_WEATHER_DATA_TIME = appKeys.getInt("KEY_WEATHER_DATA_TIME");
} catch (JSONException e) {
GB.toast("There was an error accessing the M7S watchface configuration.", Toast.LENGTH_LONG, GB.ERROR);
} catch (IOException ignore) {
}
}
private int getIconForConditionCode(int conditionCode) {
if (conditionCode == 800 || conditionCode == 951) {
return CLEAR;
} else if (conditionCode > 800 && conditionCode < 900) {
return CLOUDY;
} else if (conditionCode >= 300 && conditionCode < 313) {
return DRIZZLE;
} else if (conditionCode >= 313 && conditionCode < 400) {
return LIGHT_RAIN;
} else if (conditionCode >= 500 && conditionCode < 600) {
return RAIN;
} else if (conditionCode >= 700 && conditionCode < 732) {
return HAZE;
} else if (conditionCode == 741) {
return FOG;
} else if (conditionCode == 751 || conditionCode == 761 || conditionCode == 762 ) {
return HAZE;
} else if (conditionCode == 771) {
return WIND;
} else if (conditionCode == 781) {
return TORNADO;
} else if (conditionCode >= 200 && conditionCode < 300) {
return THUNDERSTORM;
} else if (conditionCode >= 600 && conditionCode < 700) {
return SNOW;
} else if (conditionCode == 906) {
return HAIL;
} else if (conditionCode >= 907 && conditionCode < 957) {
return WIND;
} else if (conditionCode == 905 || (conditionCode >= 957 && conditionCode < 900)) {
return EXTREME_WIND;
} else if (conditionCode == 900) {
return TORNADO;
} else if (conditionCode == 901 || conditionCode == 902 || conditionCode == 962) {
return HURRICANE;
} else if (conditionCode == 903) {
return EXTREME_COLD;
} else if (conditionCode == 904) {
return EXTREME_HEAT;
}
return 0;
}
private byte[] encodeM7SWeatherMessage(WeatherSpec weatherSpec) {
if (weatherSpec == null) {
return null;
}
String wString1 = String.format(Locale.ENGLISH, "%.0f / %.0f__C \n%.0f %s", (weatherSpec.todayMaxTemp-273.15), (weatherSpec.todayMinTemp-273.15), weatherSpec.windSpeed, "km/h");
String wString2 = String.format(Locale.ENGLISH, "%d %%", weatherSpec.currentHumidity);
ArrayList<Pair<Integer, Object>> pairs = new ArrayList<>(2);
pairs.add(new Pair<>(KEY_LOCATION_NAME, (Object) (weatherSpec.location)));
pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) ((int) Math.round(weatherSpec.currentTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_DATA_TIME, (Object) (weatherSpec.timestamp)));
pairs.add(new Pair<>(KEY_WEATHER_STRING_1, (Object) (wString1)));
pairs.add(new Pair<>(KEY_WEATHER_STRING_2, (Object) (wString2)));
pairs.add(new Pair<>(KEY_WEATHER_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode))));
byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs, null);
ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length);
buf.put(weatherMessage);
return buf.array();
}
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeM7SWeatherMessage(weatherSpec);
return new GBDeviceEvent[]{sendBytes};
}
@Override
public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) {
return encodeM7SWeatherMessage(weatherSpec);
}
}

View File

@ -142,7 +142,7 @@ class AppMessageHandlerObsidian extends AppMessageHandler {
return (!isNight) ? iconToLoad : iconToLoad.toUpperCase();
}
private byte[] encodeObisdianWeather(WeatherSpec weatherSpec) {
private byte[] encodeObsidianWeather(WeatherSpec weatherSpec) {
if (weatherSpec == null) {
return null;
@ -165,12 +165,12 @@ class AppMessageHandlerObsidian extends AppMessageHandler {
return new GBDeviceEvent[]{null};
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeObisdianWeather(weatherSpec);
sendBytes.encodedBytes = encodeObsidianWeather(weatherSpec);
return new GBDeviceEvent[]{sendBytes};
}
@Override
public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) {
return encodeObisdianWeather(weatherSpec);
return encodeObsidianWeather(weatherSpec);
}
}

View File

@ -0,0 +1,146 @@
/* Copyright (C) 2018 Johann C. Rode, Sergio Lopez
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import android.util.Pair;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Locale;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
class AppMessageHandlerRealWeather extends AppMessageHandler {
private static int CLEAR_DAY = 0;
private static int CLEAR_NIGHT = 1;
private static int WINDY = 2;
private static int COLD = 3;
private static int PARTLY_CLOUDY_DAY = 4;
private static int PARTLY_CLOUDY_NIGHT = 5;
private static int HAZE = 6;
private static int CLOUD = 7;
private static int RAIN = 8;
private static int SNOW = 9;
private static int HAIL = 10;
private static int CLOUDY = 11;
private static int STORM = 12;
private static int NA = 13;
private Integer KEY_WEATHER_ICON;
private Integer KEY_WEATHER_TEMP;
AppMessageHandlerRealWeather(UUID uuid, PebbleProtocol pebbleProtocol) {
super(uuid, pebbleProtocol);
try {
JSONObject appKeys = getAppKeys();
KEY_WEATHER_TEMP = appKeys.getInt("temperature");
KEY_WEATHER_ICON = appKeys.getInt("icon");
} catch (JSONException e) {
GB.toast("There was an error accessing the RealWeather watchface configuration.", Toast.LENGTH_LONG, GB.ERROR);
} catch (IOException ignore) {
}
}
private int getIconForConditionCode(int conditionCode, boolean isNight) {
if (conditionCode == 800 || conditionCode == 951) {
return isNight ? CLEAR_NIGHT : CLEAR_DAY;
} else if (conditionCode == 801 || conditionCode == 802) {
return isNight ? PARTLY_CLOUDY_NIGHT : PARTLY_CLOUDY_DAY;
} else if (conditionCode >= 300 && conditionCode < 313) {
return RAIN;
} else if (conditionCode >= 313 && conditionCode < 400) {
return RAIN;
} else if (conditionCode >= 500 && conditionCode < 600) {
return RAIN;
} else if (conditionCode >= 700 && conditionCode < 732) {
return CLOUDY;
} else if (conditionCode == 741 || conditionCode == 751 || conditionCode == 761 || conditionCode == 762 ) {
return HAZE;
} else if (conditionCode == 771) {
return WINDY;
} else if (conditionCode == 781) {
return STORM;
} else if (conditionCode >= 200 && conditionCode < 300) {
return STORM;
} else if (conditionCode == 600 || conditionCode == 601 || conditionCode == 602 ) {
return SNOW;
} else if (conditionCode == 611 || conditionCode == 612) {
return HAIL;
} else if (conditionCode == 615 || conditionCode == 616 || conditionCode == 620 || conditionCode == 621 || conditionCode == 622) {
return SNOW;
} else if (conditionCode == 906) {
return SNOW;
} else if (conditionCode == 803 || conditionCode == 804) {
return CLOUD;
} else if (conditionCode >= 907 && conditionCode < 957) {
return STORM;
} else if (conditionCode == 905 || (conditionCode >= 957 && conditionCode < 900)) {
return STORM;
} else if (conditionCode == 900) {
return STORM;
} else if (conditionCode == 901 || conditionCode == 902 || conditionCode == 962) {
return STORM;
} else if (conditionCode == 903) {
return COLD;
}
return 0;
}
private byte[] encodeRealWeatherMessage(WeatherSpec weatherSpec) {
if (weatherSpec == null) {
return null;
}
boolean isNight = false; // TODO
ArrayList<Pair<Integer, Object>> pairs = new ArrayList<>(2);
pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°", weatherSpec.currentTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight))));
byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs, null);
ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length);
buf.put(weatherMessage);
return buf.array();
}
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeRealWeatherMessage(weatherSpec);
return new GBDeviceEvent[]{sendBytes};
}
@Override
public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) {
return encodeRealWeatherMessage(weatherSpec);
}
}

View File

@ -0,0 +1,244 @@
/* Copyright (C) 2018 Johann C. Rode, Sergio Lopez
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
import android.util.Pair;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Locale;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
class AppMessageHandlerYWeather extends AppMessageHandler {
private static int CLEAR_DAY = 0;
private static int CLEAR_NIGHT = 1;
private static int WINDY = 2;
private static int COLD = 3;
private static int HOT = 4;
private static int PARTLY_CLOUDY_DAY = 5;
private static int PARTLY_CLOUDY_NIGHT = 6;
private static int FOG = 7;
private static int RAIN = 8;
private static int SNOW = 9;
private static int SLEET = 10;
private static int SNOW_SLEET = 11;
private static int RAIN_SLEET = 12;
private static int RAIN_SNOW = 13;
private static int CLOUDY = 14;
private static int STORM = 15;
private static int NA = 16;
private static int DRIZZLE = 17;
private Integer KEY_WEATHER_ICON;
private Integer KEY_WEATHER_TEMP;
private Integer KEY_LOCATION_NAME;
private Integer KEY_WEATHER_WIND_SPEED;
private Integer KEY_WEATHER_WIND_DIRECTION;
private Integer KEY_WEATHER_TODAY_MINTEMP;
private Integer KEY_WEATHER_TODAY_MAXTEMP;
private Integer KEY_WEATHER_D1_ICON;
private Integer KEY_WEATHER_D1_MINTEMP;
private Integer KEY_WEATHER_D1_MAXTEMP;
private Integer KEY_WEATHER_D2_ICON;
private Integer KEY_WEATHER_D2_MINTEMP;
private Integer KEY_WEATHER_D2_MAXTEMP;
private Integer KEY_WEATHER_D3_ICON;
private Integer KEY_WEATHER_D3_MINTEMP;
private Integer KEY_WEATHER_D3_MAXTEMP;
AppMessageHandlerYWeather(UUID uuid, PebbleProtocol pebbleProtocol) {
super(uuid, pebbleProtocol);
try {
JSONObject appKeys = getAppKeys();
KEY_LOCATION_NAME = appKeys.getInt("city");
KEY_WEATHER_TEMP = appKeys.getInt("temperature");
KEY_WEATHER_ICON = appKeys.getInt("icon");
KEY_WEATHER_WIND_SPEED = appKeys.getInt("wind");
KEY_WEATHER_WIND_DIRECTION = appKeys.getInt("wdirection");
KEY_WEATHER_TODAY_MINTEMP = appKeys.getInt("low");
KEY_WEATHER_TODAY_MAXTEMP = appKeys.getInt("high");
KEY_WEATHER_D1_ICON = appKeys.getInt("code1");
KEY_WEATHER_D1_MINTEMP = appKeys.getInt("low1");
KEY_WEATHER_D1_MAXTEMP = appKeys.getInt("high1");
KEY_WEATHER_D2_ICON = appKeys.getInt("code2");
KEY_WEATHER_D2_MINTEMP = appKeys.getInt("low2");
KEY_WEATHER_D2_MAXTEMP = appKeys.getInt("high2");
KEY_WEATHER_D3_ICON = appKeys.getInt("code3");
KEY_WEATHER_D3_MINTEMP = appKeys.getInt("low3");
KEY_WEATHER_D3_MAXTEMP = appKeys.getInt("high3");
} catch (JSONException e) {
GB.toast("There was an error accessing the YWeather watchface configuration.", Toast.LENGTH_LONG, GB.ERROR);
} catch (IOException ignore) {
}
}
private int getIconForConditionCode(int conditionCode, boolean isNight) {
if (conditionCode == 800 || conditionCode == 951) {
return isNight ? CLEAR_NIGHT : CLEAR_DAY;
} else if (conditionCode > 800 && conditionCode < 900) {
return isNight ? PARTLY_CLOUDY_NIGHT : PARTLY_CLOUDY_DAY;
} else if (conditionCode >= 300 && conditionCode < 313) {
return DRIZZLE;
} else if (conditionCode >= 313 && conditionCode < 400) {
return DRIZZLE;
} else if (conditionCode >= 500 && conditionCode < 600) {
return RAIN;
} else if (conditionCode >= 700 && conditionCode < 732) {
return CLOUDY;
} else if (conditionCode == 741 || conditionCode == 751 || conditionCode == 761 || conditionCode == 762 ) {
return FOG;
} else if (conditionCode == 771) {
return WINDY;
} else if (conditionCode == 781) {
return STORM;
} else if (conditionCode >= 200 && conditionCode < 300) {
return STORM;
} else if (conditionCode == 600 || conditionCode == 601 || conditionCode == 602 ) {
return SNOW;
} else if (conditionCode == 611 || conditionCode == 612) {
return SLEET;
} else if (conditionCode == 615 || conditionCode == 616 || conditionCode == 620 || conditionCode == 621 || conditionCode == 622) {
return RAIN_SNOW;
} else if (conditionCode == 906) {
return SLEET;
} else if (conditionCode >= 907 && conditionCode < 957) {
return STORM;
} else if (conditionCode == 905 || (conditionCode >= 957 && conditionCode < 900)) {
return STORM;
} else if (conditionCode == 900) {
return STORM;
} else if (conditionCode == 901 || conditionCode == 902 || conditionCode == 962) {
return STORM;
} else if (conditionCode == 903) {
return COLD;
} else if (conditionCode == 904) {
return HOT;
}
return 0;
}
private String formatWindDirection(float wdirection) {
if (Float.isNaN(wdirection)) {
return "n/a";
}
if (wdirection >= 348.75 || wdirection <= 11.25) {
return "N";
} else if ( wdirection > 11.25 && wdirection <= 33.75 ) {
return "NNE";
} else if ( wdirection > 33.75 && wdirection <= 56.25 ) {
return "NE";
} else if ( wdirection > 56.25 && wdirection <= 78.75 ) {
return "ENE";
} else if ( wdirection > 78.75 && wdirection <= 101.25 ) {
return "E";
} else if ( wdirection > 101.25 && wdirection <= 123.75 ) {
return "ESE";
} else if ( wdirection > 123.75 && wdirection <= 146.25 ) {
return "SE";
} else if ( wdirection > 146.25 && wdirection <= 168.75 ) {
return "SSE";
} else if ( wdirection > 168.75 && wdirection <= 191.25 ) {
return "S";
} else if ( wdirection > 191.25 && wdirection <= 213.75 ) {
return "SSW";
} else if ( wdirection > 213.75 && wdirection <= 236.25 ) {
return "SW";
} else if ( wdirection > 236.25 && wdirection <= 258.75 ) {
return "WSW";
} else if ( wdirection > 258.75 && wdirection <= 281.25 ) {
return "W";
} else if ( wdirection > 281.25 && wdirection <= 303.75 ) {
return "WNW";
} else if ( wdirection > 303.75 && wdirection <= 326.25 ) {
return "NW";
} else if ( wdirection > 326.25 && wdirection <= 348.75 ) {
return "NNW";
} else {
return "n/a";
}
}
private byte[] encodeYWeatherMessage(WeatherSpec weatherSpec) {
if (weatherSpec == null) {
return null;
}
boolean isNight = false; // TODO
ArrayList<Pair<Integer, Object>> pairs = new ArrayList<>(2);
pairs.add(new Pair<>(KEY_LOCATION_NAME, (Object) (weatherSpec.location)));
pairs.add(new Pair<>(KEY_WEATHER_TEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°", weatherSpec.currentTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_TODAY_MINTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", weatherSpec.todayMinTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_TODAY_MAXTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", weatherSpec.todayMaxTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_ICON, (Object) (getIconForConditionCode(weatherSpec.currentConditionCode, isNight))));
pairs.add(new Pair<>(KEY_WEATHER_WIND_SPEED, (Object) (String.format(Locale.ENGLISH, "%.0f", weatherSpec.windSpeed))));
pairs.add(new Pair<>(KEY_WEATHER_WIND_DIRECTION, (Object) (formatWindDirection(weatherSpec.windDirection))));
if (weatherSpec.forecasts.size() > 0) {
WeatherSpec.Forecast day1 = weatherSpec.forecasts.get(0);
pairs.add(new Pair<>(KEY_WEATHER_D1_ICON, (Object) (getIconForConditionCode(day1.conditionCode, false))));
pairs.add(new Pair<>(KEY_WEATHER_D1_MINTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day1.minTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_D1_MAXTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day1.maxTemp - 273.15))));
}
if (weatherSpec.forecasts.size() > 1) {
WeatherSpec.Forecast day2 = weatherSpec.forecasts.get(1);
pairs.add(new Pair<>(KEY_WEATHER_D2_ICON, (Object) (getIconForConditionCode(day2.conditionCode, false))));
pairs.add(new Pair<>(KEY_WEATHER_D2_MINTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day2.minTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_D2_MAXTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day2.maxTemp - 273.15))));
}
if (weatherSpec.forecasts.size() > 2) {
WeatherSpec.Forecast day3 = weatherSpec.forecasts.get(2);
pairs.add(new Pair<>(KEY_WEATHER_D3_ICON, (Object) (getIconForConditionCode(day3.conditionCode, false))));
pairs.add(new Pair<>(KEY_WEATHER_D3_MINTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day3.minTemp - 273.15))));
pairs.add(new Pair<>(KEY_WEATHER_D3_MAXTEMP, (Object) (String.format(Locale.ENGLISH, "%.0f°C", day3.maxTemp - 273.15))));
}
byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs, null);
ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length);
buf.put(weatherMessage);
return buf.array();
}
@Override
public GBDeviceEvent[] onAppStart() {
WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec();
if (weatherSpec == null) {
return new GBDeviceEvent[]{null};
}
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
sendBytes.encodedBytes = encodeYWeatherMessage(weatherSpec);
return new GBDeviceEvent[]{sendBytes};
}
@Override
public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) {
return encodeYWeatherMessage(weatherSpec);
}
}

View File

@ -396,6 +396,9 @@ public class PebbleProtocol extends GBDeviceProtocol {
private static final UUID UUID_ZALEWSZCZAK_TALLY = UUID.fromString("abb51965-52e2-440a-b93c-843eeacb697d");
private static final UUID UUID_OBSIDIAN = UUID.fromString("ef42caba-0c65-4879-ab23-edd2bde68824");
private static final UUID UUID_SIMPLY_LIGHT = UUID.fromString("04a6e68a-42d6-4738-87b2-1c80a994dee4");
private static final UUID UUID_M7S = UUID.fromString("03adc57a-569b-4669-9a80-b505eaea314d");
private static final UUID UUID_YWEATHER = UUID.fromString("35a28a4d-0c9f-408f-9c6d-551e65f03186");
private static final UUID UUID_REALWEATHER = UUID.fromString("1f0b0701-cc8f-47ec-86e7-7181397f9a52");
private static final UUID UUID_ZERO = new UUID(0, 0);
@ -422,6 +425,9 @@ public class PebbleProtocol extends GBDeviceProtocol {
mAppMessageHandlers.put(UUID_OBSIDIAN, new AppMessageHandlerObsidian(UUID_OBSIDIAN, PebbleProtocol.this));
mAppMessageHandlers.put(UUID_GBPEBBLE, new AppMessageHandlerGBPebble(UUID_GBPEBBLE, PebbleProtocol.this));
mAppMessageHandlers.put(UUID_SIMPLY_LIGHT, new AppMessageHandlerSimplyLight(UUID_SIMPLY_LIGHT, PebbleProtocol.this));
mAppMessageHandlers.put(UUID_M7S, new AppMessageHandlerM7S(UUID_M7S, PebbleProtocol.this));
mAppMessageHandlers.put(UUID_YWEATHER, new AppMessageHandlerRealWeather(UUID_YWEATHER, PebbleProtocol.this));
mAppMessageHandlers.put(UUID_REALWEATHER, new AppMessageHandlerRealWeather(UUID_REALWEATHER, PebbleProtocol.this));
}
}
@ -1468,7 +1474,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
@Override
public byte[] encodeReboot() {
public byte[] encodeReset(int flags) {
return encodeSimpleMessage(ENDPOINT_RESET, RESET_REBOOT);
}

View File

@ -197,7 +197,7 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
}

View File

@ -57,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.operations.InitOperation;
import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils;
public class Watch9DeviceSupport extends AbstractBTLEDeviceSupport {
@ -331,7 +332,7 @@ public class Watch9DeviceSupport extends AbstractBTLEDeviceSupport {
try {
TransactionBuilder builder = performInitialized("setAlarms");
for (Alarm alarm : alarms) {
setAlarm(alarm, alarm.getIndex() + 1, builder);
setAlarm(alarm, alarm.getPosition() + 1, builder);
}
builder.queue(getQueue());
} catch (IOException e) {
@ -352,14 +353,14 @@ public class Watch9DeviceSupport extends AbstractBTLEDeviceSupport {
private void setAlarm(Alarm alarm, int index, TransactionBuilder builder) {
// Shift the GB internal repetition mask to match the device specific one.
byte repetitionMask = (byte) ((alarm.getRepetitionMask() << 1) | (alarm.isRepetitive() ? 0x80 : 0x00));
byte repetitionMask = (byte) ((alarm.getRepetition() << 1) | (alarm.isRepetitive() ? 0x80 : 0x00));
repetitionMask |= (alarm.getRepetition(Alarm.ALARM_SUN) ? 0x01 : 0x00);
if (0 < index && index < 4) {
byte[] alarmValue = new byte[]{(byte) index,
Conversion.toBcd8(alarm.getAlarmCal().get(Calendar.HOUR_OF_DAY)),
Conversion.toBcd8(alarm.getAlarmCal().get(Calendar.MINUTE)),
Conversion.toBcd8(AlarmUtils.toCalendar(alarm).get(Calendar.HOUR_OF_DAY)),
Conversion.toBcd8(AlarmUtils.toCalendar(alarm).get(Calendar.MINUTE)),
repetitionMask,
(byte) (alarm.isEnabled() ? 0x01 : 0x00),
(byte) (alarm.getEnabled() ? 0x01 : 0x00),
0x00 // TODO: Unknown
};
builder.write(getCharacteristic(Watch9Constants.UUID_CHARACTERISTIC_WRITE),
@ -440,7 +441,7 @@ public class Watch9DeviceSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
}

View File

@ -233,7 +233,7 @@ public class XWatchSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
//Not supported
}
@ -455,7 +455,7 @@ public class XWatchSupport extends AbstractBTLEDeviceSupport {
try {
builder = performInitialized("fetchActivityData");
requestDetailedData(builder);
performConnected(builder.getTransaction());
builder.queue(getQueue());
} catch (IOException e) {
GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}

View File

@ -411,7 +411,7 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onReboot() {
public void onReset(int flags) {
}

View File

@ -106,14 +106,17 @@ public class GBMusicControlReceiver extends BroadcastReceiver {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
MediaSessionManager mediaSessionManager =
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
List<MediaController> controllers = mediaSessionManager.getActiveSessions(
new ComponentName(context, NotificationListener.class));
try {
MediaController controller = controllers.get(0);
audioPlayer = controller.getPackageName();
} catch (IndexOutOfBoundsException e) {
LOG.error("No media controller available", e);
List<MediaController> controllers = mediaSessionManager.getActiveSessions(
new ComponentName(context, NotificationListener.class));
try {
MediaController controller = controllers.get(0);
audioPlayer = controller.getPackageName();
} catch (IndexOutOfBoundsException e) {
LOG.error("No media controller available", e);
}
} catch (SecurityException e) {
LOG.warn("No permission to get media sessions - did not grant notification access?", e);
}
}
return audioPlayer;

View File

@ -186,8 +186,8 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
}
@Override
public void onReboot() {
byte[] bytes = gbDeviceProtocol.encodeReboot();
public void onReset(int flags) {
byte[] bytes = gbDeviceProtocol.encodeReset(flags);
sendToDevice(bytes);
}

View File

@ -28,6 +28,9 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
public abstract class GBDeviceProtocol {
public static final int RESET_FLAGS_REBOOT = 1;
public static final int RESET_FLAGS_FACTORY_RESET = 2;
private GBDevice mDevice;
protected GBDeviceProtocol(GBDevice device) {
@ -90,7 +93,7 @@ public abstract class GBDeviceProtocol {
return null;
}
public byte[] encodeReboot() {
public byte[] encodeReset(int flags) {
return null;
}

View File

@ -0,0 +1,131 @@
package nodomain.freeyourgadget.gadgetbridge.util;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
/**
* Some utility methods for dealing with Alarms.
*/
public class AlarmUtils {
/**
* Creates an auto-generated (not user-configurable), non-recurring alarm. This alarm
* may not be stored in the database. Some features are not available (e.g. device id, user id).
* @param index
* @param smartWakeup
* @param calendar
* @return
*/
public static nodomain.freeyourgadget.gadgetbridge.model.Alarm createSingleShot(int index, boolean smartWakeup, Calendar calendar) {
return new Alarm(-1, -1, index, true, smartWakeup, Alarm.ALARM_ONCE, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE));
}
/**
* Creates a Calendar object representing the time of the given alarm (not taking the
* day/date into account.
* @param alarm
* @return
*/
public static Calendar toCalendar(nodomain.freeyourgadget.gadgetbridge.model.Alarm alarm) {
Calendar result = Calendar.getInstance();
Calendar now = Calendar.getInstance();
result.set(Calendar.HOUR_OF_DAY, alarm.getHour());
result.set(Calendar.MINUTE, alarm.getMinute());
if (now.after(result) && alarm.getRepetition() == Alarm.ALARM_ONCE) {
//if the alarm is in the past set it to tomorrow
result.add(Calendar.DATE, 1);
}
return result;
}
/**
* Returns a repetition mask suitable for {@link Alarm#repetition}
* @param mon whether the alarm shall repeat every Monday
* @param tue whether the alarm shall repeat every Tuesday
* @param wed whether the alarm shall repeat every Wednesday
* @param thu whether the alarm shall repeat every Thursday
* @param fri whether the alarm shall repeat every Friday
* @param sat whether the alarm shall repeat every Saturday
* @param sun whether the alarm shall repeat every Sunday
* @return the created repetition mask
*/
public static int createRepetitionMassk(boolean mon, boolean tue, boolean wed, boolean thu, boolean fri, boolean sat, boolean sun) {
int repetitionMask = (mon ? Alarm.ALARM_MON : 0) |
(tue ? Alarm.ALARM_TUE : 0) |
(wed ? Alarm.ALARM_WED : 0) |
(thu ? Alarm.ALARM_THU : 0) |
(fri ? Alarm.ALARM_FRI : 0) |
(sat ? Alarm.ALARM_SAT : 0) |
(sun ? Alarm.ALARM_SUN : 0);
return repetitionMask;
}
/**
* Just for backward compatibility, do not call in new code.
* @param gbDevice
* @return
* @deprecated use {@link DBHelper#getAlarms(GBDevice)} instead
*/
@NonNull
public static List<Alarm> readAlarmsFromPrefs(GBDevice gbDevice) {
Prefs prefs = GBApplication.getPrefs();
Set<String> stringAlarms = prefs.getStringSet(MiBandConst.PREF_MIBAND_ALARMS, new HashSet<String>());
List<Alarm> alarms = new ArrayList<>(stringAlarms.size());
try {
DBHandler db = GBApplication.acquireDB();
DaoSession daoSession = db.getDaoSession();
User user = DBHelper.getUser(daoSession);
Device device = DBHelper.getDevice(gbDevice, daoSession);
for (String stringAlarm : stringAlarms) {
alarms.add(createAlarmFromPreference(stringAlarm, device, user));
}
Collections.sort(alarms, AlarmUtils.createComparator());
return alarms;
} catch (Exception e) {
GB.log("Error accessing database", GB.ERROR, e);
return Collections.emptyList();
}
}
private static Alarm createAlarmFromPreference(String fromPreferences, Device device, User user) {
String[] tokens = fromPreferences.split(",");
int index = Integer.parseInt(tokens[0]);
boolean enabled = Boolean.parseBoolean(tokens[1]);
boolean smartWakeup = Boolean.parseBoolean(tokens[2]);
int repetition = Integer.parseInt(tokens[3]);
int hour = Integer.parseInt(tokens[4]);
int minute = Integer.parseInt(tokens[5]);
return new Alarm(device.getId(), user.getId(), index, enabled, smartWakeup, repetition, hour, minute);
}
private static Comparator<Alarm> createComparator() {
return new Comparator<Alarm>() {
@Override
public int compare(Alarm o1, Alarm o2) {
int p1 = o1.getPosition();
int p2 = o2.getPosition();
return Integer.compare(p1, p2);
}
};
}
}

View File

@ -33,6 +33,7 @@ import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
public class DateTimeUtils {
private static SimpleDateFormat DAY_STORAGE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
@ -136,6 +137,11 @@ public class DateTimeUtils {
return HOURS_MINUTES_FORMAT.format(date);
}
public static String formatTime(int hours, int minutes) {
return String.format(Locale.US, "%02d", hours) + ":" + String.format(Locale.US, "%02d", minutes);
}
public static Date todayUTC() {
Calendar cal = getCalendarUTC();
return cal.getTime();

View File

@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator;
@ -132,12 +133,7 @@ public class DeviceHelper {
GB.toast(context, context.getString(R.string.bluetooth_is_disabled_), Toast.LENGTH_SHORT, GB.WARN);
}
List<GBDevice> dbDevices = getDatabaseDevices();
// these come first, as they have the most information already
availableDevices.addAll(dbDevices);
if (btAdapter != null) {
List<GBDevice> bondedDevices = getBondedDevices(btAdapter);
availableDevices.addAll(bondedDevices);
}
Prefs prefs = GBApplication.getPrefs();
String miAddr = prefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, "");
@ -221,6 +217,7 @@ public class DeviceHelper {
result.add(new Watch9DeviceCoordinator());
result.add(new Roidmi1Coordinator());
result.add(new Roidmi3Coordinator());
result.add(new CasioGB6900DeviceCoordinator());
return result;
}
@ -264,29 +261,6 @@ public class DeviceHelper {
return gbDevice;
}
private @NonNull List<GBDevice> getBondedDevices(@NonNull BluetoothAdapter btAdapter) {
Set<BluetoothDevice> pairedDevices = btAdapter.getBondedDevices();
if (pairedDevices == null) {
return Collections.emptyList();
}
List<GBDevice> result = new ArrayList<>(pairedDevices.size());
DeviceHelper deviceHelper = DeviceHelper.getInstance();
for (BluetoothDevice pairedDevice : pairedDevices) {
if (pairedDevice == null) {
continue; // just to be safe, see https://github.com/Freeyourgadget/Gadgetbridge/pull/1052
}
if (pairedDevice.getName() != null && (pairedDevice.getName().startsWith("Pebble-LE ") || pairedDevice.getName().startsWith("Pebble Time LE "))) {
continue; // ignore LE Pebble (this is part of the main device now (volatileAddress)
}
GBDevice device = deviceHelper.toSupportedDevice(pairedDevice);
if (device != null) {
result.add(device);
}
}
return result;
}
/**
* Attempts to removing the bonding with the given device. Returns true
* if bonding was supposedly successful and false if anything went wrong

View File

@ -0,0 +1,94 @@
/* Copyright (C) 2018 Andreas Shimokawa, Matthieu Baerts
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
import android.content.Context;
import io.wax911.emojify.EmojiManager;
import io.wax911.emojify.EmojiUtils;
public class EmojiConverter {
private static final String[][] simpleEmojiMapping = {
{"\uD83D\uDE00", ":-D"}, // grinning
{"\uD83D\uDE01", ":-D"}, // grinning_face_with_smiling_eyes
{"\uD83D\uDE02", ":'D"}, // face_with_tears_of_joy
{"\uD83D\uDE03", ":-D"}, // smiling_face_with_open_mouth
{"\uD83D\uDE04", ":-D"}, // smiling_face_with_open_mouth_and_smiling_eyes
{"\uD83D\uDE05", ":'D"}, // smiling_face_with_open_mouth_and_cold_sweat
{"\uD83D\uDE06", "X-D"}, // smiling_face_with_open_mouth_and_tightly-closed_eyes
{"\uD83D\uDE07", "O:-)"}, // innocent
{"\uD83D\uDE09", ";-)"}, // wink
{"\uD83D\uDE0A", ":-)"}, // blush
{"\uD83D\uDE0B", ":-p"}, // yum
{"\uD83D\uDE0E", "B-)"}, // sunglasses
{"\uD83D\uDE15", ":-/"}, // confused
{"\uD83D\uDE16", ":-S"}, // confounded_face
{"\uD83D\uDE17", ":*"}, // kissing_face
{"\uD83D\uDE18", ";-*"}, // face_throwing_a_kiss
{"\uD83D\uDE19", ":-*"}, // kissing_face_with_smiling_eyes
{"\uD83D\uDE1A", ":-*"}, // kissing_closed_eyes
{"\uD83D\uDE1B", ":-P"}, // stuck_out_tongue
{"\uD83D\uDE1C", ";-P"}, // stuck_out_tongue_winking_eye
{"\uD83D\uDE1D", "X-P"}, // stuck_out_tongue_and_tightly-closed_eyes
{"\uD83D\uDE1E", ":-S"}, // disappointed
{"\uD83D\uDE20", ":-@"}, // angry_face
{"\uD83D\uDE21", ":-@"}, // pouting_face
{"\uD83D\uDE22", ":'("}, // cry
{"\uD83D\uDE23", ":-("}, // preserving_face
{"\uD83D\uDE25", ":'("}, // disappointed_but_relieved_face
{"\uD83D\uDE2D", ":'("}, // loudly_crying_face
{"\uD83D\uDE2E", ":-O"}, // open_mouth
{"\uD83D\uDE32", "X-o"}, // astonished_face
{"\uD83D\uDE42", ":)"}, // slightly_smiling_face
{"\uD83D\uDE43", "(-:"}, // upside_down_face
{"\u2639", ":-("}, // frowning_face
{"\u2764", "<3"}, // heart
};
private static boolean isEmojiDataInitialised = false;
private static String convertSimpleEmojiToAscii(String text) {
for (String[] emojiMap : simpleEmojiMapping) {
text = text.replace(emojiMap[0], emojiMap[1]);
}
return text;
}
private static synchronized void initEmojiData(Context context) {
// Do a lazy initialisation not to slowdown the startup and when it is needed
if (!isEmojiDataInitialised) {
EmojiManager.initEmojiData(context);
isEmojiDataInitialised = true;
}
}
private static String convertAdvancedEmojiToAscii(String text, Context context) {
initEmojiData(context);
return EmojiUtils.shortCodify(text);
}
public static String convertUnicodeEmojiToAscii(String text, Context context) {
text = convertSimpleEmojiToAscii(text);
text = convertAdvancedEmojiToAscii(text, context);
return text;
}
}

View File

@ -79,8 +79,10 @@ public class LanguageUtils {
put('آ', "2"); put('ئ', "2"); put('إ', "2"); put('ؤ', "2"); put('أ', "2"); put('ء', "2");
// Persian(Farsi)
put('پ', "p"); put('چ', "ch"); put('ژ', "zh"); put('ک', "k"); put('گ', "g"); put('ی', "y");
put('؟', "?"); put('٪', "%"); put('؛', ";"); put('،', ",");
put('پ', "p"); put('چ', "ch"); put('ژ', "zh"); put('ک', "k"); put('گ', "g"); put('ی', "y"); put('', " ");
put('؟', "?"); put('٪', "%"); put('؛', ";"); put('،', ","); put('۱', "1"); put('۲', "2"); put('۳', "3");
put('۴', "4"); put('۵', "5"); put('۶', "6"); put('۷', "7"); put('۸', "8"); put('۹', "9"); put('۰', "0");
put('»', "<"); put('«', ">"); put('ِ', "e"); put('َ', "a"); put('ُ', "o"); put('ّ', "");
// Polish
put('Ł', "L"); put('ł', "l");

View File

@ -117,6 +117,14 @@
grid:layout_gravity="fill_horizontal"
android:text="reboot" />
<Button
android:id="@+id/factoryResetButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
grid:layout_columnSpan="2"
grid:layout_gravity="fill_horizontal"
android:text="factory reset" />
<Button
android:id="@+id/testNotificationButton"
android:layout_width="wrap_content"

View File

@ -425,7 +425,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Afegeix un giny</string>
<string name="activity_prefs_sleep_duration">Duració preferida del son en hores</string>
<string name="appwidget_alarms_set">S\'ha creat una alarma a les %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">S\'ha creat una alarma a les %1$02d:%2$02d</string>
<string name="device_hw">Revisió del maquinari: %1$s</string>
<string name="device_fw">Versió del microprogramari: %1$s</string>
<string name="error_creating_directory_for_logfiles">S\'ha produït un error al crear un directori per als fitxers de registre: %1$s</string>

View File

@ -313,7 +313,7 @@
<string name="appwidget_text">Spí...</string>
<string name="add_widget">Přidat widget</string>
<string name="activity_prefs_sleep_duration">Preferovaná doba spánku v hodinách</string>
<string name="appwidget_alarms_set">Budík nastaven na %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Budík nastaven na %1$02d:%2$02d</string>
<string name="device_hw">Revize HW: %1$s</string>
<string name="device_fw">Verze FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Chyba při vytváření adresáře pro logy: %1$s</string>

View File

@ -8,7 +8,7 @@
<string name="action_donate">Spenden</string>
<string name="controlcenter_fetch_activity_data">Synchronisieren</string>
<string name="controlcenter_start_sleepmonitor">Schlafaufzeichnung (ALPHA)</string>
<string name="controlcenter_find_device">Suche verlegtes Gerät</string>
<string name="controlcenter_find_device">Verlorenes Gerät finden</string>
<string name="controlcenter_take_screenshot">Bildschirmfoto machen</string>
<string name="controlcenter_disconnect">Trennen</string>
<string name="controlcenter_delete_device">Gerät löschen</string>
@ -34,8 +34,8 @@
<string name="appmanager_health_deactivate">Deaktivieren</string>
<string name="appmanager_hrm_activate">HRM aktivieren</string>
<string name="appmanager_hrm_deactivate">HRM deaktivieren</string>
<string name="appmanager_weather_activate">Wetter-App des Systems aktivieren</string>
<string name="appmanager_weather_deactivate">Wetter-App des Systems deaktivieren</string>
<string name="appmanager_weather_activate">System Wetter-App aktivieren</string>
<string name="appmanager_weather_deactivate">System Wetter-App deaktivieren</string>
<string name="appmanager_weather_install_provider">Installiere die Wetter-Benachrichtigungs-App</string>
<string name="app_configure">Konfigurieren</string>
<string name="app_move_to_top">Nach oben</string>
@ -240,7 +240,7 @@
<string name="alarm_fri_short">Fr</string>
<string name="alarm_sat_short">Sa</string>
<string name="alarm_smart_wakeup">Intelligenter Wecker </string>
<string name="user_feedback_miband_set_alarms_failed">Beim Stellen der Wecker ist ein Fehler aufgetreten. Bitte erneut versuchen!</string>
<string name="user_feedback_miband_set_alarms_failed">Beim Stellen der Wecker ist ein Fehler aufgetreten. Bitte erneut versuchen.</string>
<string name="user_feedback_miband_set_alarms_ok">Wecker wurden an das Gerät gesendet!</string>
<string name="chart_no_data_synchronize">Keine Daten. Gerät synchronisieren?</string>
<string name="user_feedback_miband_activity_data_transfer">%1$s an Daten werden übertragen, beginnend mit %2$s</string>
@ -288,7 +288,7 @@
<string name="pref_summary_dont_ack_transfers">Wenn der Transfer der Aktivitätsdaten nicht bestätigt wird, werden die Daten nicht auf dem MiBand gelöscht. Das ist sinnvoll, wenn neben Gadgetbridge noch andere Apps auf das MiBand zugreifen.</string>
<string name="pref_summary_keep_data_on_device">Aktivitätsdaten verbleiben auf dem MiBand, auch nach der Synchronisierung. Hilfreich, wenn das MiBand mit weiteren Apps verwendet wird.</string>
<string name="pref_title_low_latency_fw_update">Benutze Modus mit niedriger Latenz für Firmware-Updates</string>
<string name="pref_summary_low_latency_fw_update">Dies kann bei Geräten helfen, bei denen Firmwareupdates fehlschlagen</string>
<string name="pref_summary_low_latency_fw_update">Dies kann bei Geräten helfen, bei denen Firmwareupdates fehlschlagen.</string>
<string name="live_activity_steps_history">Schritteverlauf</string>
<string name="live_activity_current_steps_per_minute">Akt. Schritte pro Minute</string>
<string name="live_activity_total_steps">Schritte insgesamt</string>
@ -335,7 +335,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Widget hinzufügen</string>
<string name="activity_prefs_sleep_duration">Gewünschte Schlafdauer in Stunden</string>
<string name="appwidget_alarms_set">Ein Wecker wurde auf %1$02d:%2$02d gestellt</string>
<string name="appwidget_setting_alarm">Ein Wecker wurde auf %1$02d:%2$02d gestellt</string>
<string name="device_hw">Hardwarerevision: %1$s</string>
<string name="device_fw">Firmware-Version: %1$s</string>
<string name="error_creating_directory_for_logfiles">Fehler beim Erstellen des Verzeichnisses für Logdateien: %1$s</string>
@ -422,7 +422,7 @@
<string name="mi2_prefs_button_press_count_max_delay">Maximale Verzögerung zwischen den Tastendrücken</string>
<string name="mi2_prefs_button_press_count_max_delay_summary">Maximale Verzögerung zwischen den Tastendrücken in Millisekunden</string>
<string name="mi2_prefs_button_press_count_match_delay">Verzögerung nach Tastenaktion</string>
<string name="_pebble_watch_open_on_phone">Auf dem Smartphone öffnen</string>
<string name="_pebble_watch_open_on_phone">Auf Android Gerät öffnen</string>
<string name="_pebble_watch_mute">Stummschalten</string>
<string name="_pebble_watch_reply">Antwort</string>
<string name="controlcenter_connect">Verbinden</string>
@ -433,13 +433,13 @@
<string name="traditional_chinese">Traditionelles Chinesisch</string>
<string name="english">Englisch</string>
<string name="fw_upgrade_notice_amazfitcor">Es soll die Firmware %s auf das Amazfit Cor gespielt werden.
\n
\nBitte stelle sicher, dass du zuerst die .res-Datei und im Anschluss die .fw-Datei installierst. Nach der Installation der .fw-Datei startet deine Uhr neu.
\n
\nHinweis: Die .res-Datei muss nicht neu installiert werden, falls die exakt gleiche Datei bereits bei einer vorherigen Version installiert wurde.
\n
\nNICHT GETESTET, DIES KÖNNTE DEIN GERÄT UNBENUTZBAR MACHEN, INSTALLATION AUF EIGENE GEFAHR!</string>
<string name="fw_upgrade_notice_amazfitcor">Es soll die Firmware %s auf das Amazfit Cor gespielt werden.
\n
\nBitte stelle sicher, dass du zuerst die .res-Datei und im Anschluss die .fw-Datei installierst. Nach der Installation der .fw-Datei startet deine Uhr neu.
\n
\nHinweis: Die .res-Datei muss nicht neu installiert werden, falls die exakt gleiche Datei bereits bei einer vorherigen Version installiert wurde.
\n
\nFORTFAHREN AUF EIGENE GEFAHR!</string>
<string name="mi2_prefs_button_press_count">Anzahl an Tastendrücken</string>
<string name="pref_title_charts_swipe">Seitwärts-Wischgesten in der Statistik-Anzeige aktivieren</string>
@ -609,4 +609,18 @@
<string name="mi3_night_mode_sunset">Bei Sonnenuntergang</string>
<string name="pref_rtl_max_line_length">Maximale Zeilenlänge von rechts nach links</string>
</resources>
<string name="overslept">Verschlafen: %1$s</string>
<string name="menuitem_notifications">Benachrichtigungen</string>
<string name="menuitem_music">Musik</string>
<string name="menuitem_more">Mehr</string>
<string name="warning">Warnung!</string>
<string name="no_data">Keine Daten</string>
<string name="preferences_led_color">LED Farbe</string>
<string name="preferences_fm_frequency">FM Frequenz</string>
<string name="pref_invalid_frequency_title">Ungültige Frequenz</string>
<string name="pref_invalid_frequency_message">Bitte eine Frequenz zwischen 87,5 und 108,0 eingeben</string>
<string name="language_and_region_prefs">Sprach- und Gebietseinstellungen</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="app_name">"Gadgetbridge "</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
@ -437,7 +437,7 @@
<string name="appwidget_text">Ζzz</string>
<string name="add_widget">Πρόσθεση widget</string>
<string name="activity_prefs_sleep_duration">Προτιμώμενος χρόνος ύπνου (σε ώρες)</string>
<string name="appwidget_alarms_set">Ένα ξυπνητήρι έχει οριστεί για τις %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Ένα ξυπνητήρι έχει οριστεί για τις %1$02d:%2$02d</string>
<string name="device_hw">Έκδοση υλικού: %1$s</string>
<string name="device_fw">Έκδοση λογισμικού: %1$s</string>
<string name="error_creating_directory_for_logfiles">Σφάλμα κατά τη δημιουργία φακέλου για τα αρχεία καταγραφής: %1$s</string>
@ -706,4 +706,6 @@
<string name="no_data">Χωρίς δεδομένα</string>
<string name="language_and_region_prefs">Ρυθμίσεις γλώσσας και περιοχής</string>
</resources>
<string name="debugactivity_really_factoryreset_title">Επαναφορά εργοστασιακών ρυθμίσεων;</string>
<string name="debugactivity_really_factoryreset">Κάνοντας επαναφορά εργοστασιακών ρυθμίσεων όλα τα δεδομένα θα διαγραφούν απο την συνδεδεμένη συσκευή (αν υποστηρίζεται). Στις συσκευές Xiaomi/Huami η διεύθυνση MAC θα αλλάξει, έτσι θα εμφανιστούν ως καινούργια συσκευή στην εφαρμογή.</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
@ -8,7 +8,7 @@
<string name="action_donate">Hacer donación</string>
<string name="controlcenter_fetch_activity_data">Sincronizar</string>
<string name="controlcenter_start_sleepmonitor">Monitor de sueño (ALPHA)</string>
<string name="controlcenter_find_device">Encontrar dispositivo perdido</string>
<string name="controlcenter_find_device">Localiza dispositivo perdido</string>
<string name="controlcenter_take_screenshot">Capturar pantalla</string>
<string name="controlcenter_disconnect">Desconectar</string>
<string name="controlcenter_delete_device">Borrar Dispositivo</string>
@ -35,10 +35,10 @@
<string name="appmanager_hrm_activate">Activar Monitor de Ritmo Cardíaco</string>
<string name="appmanager_hrm_deactivate">Desactivar Monitor de Ritmo Cardíaco</string>
<string name="appmanager_weather_activate">Activar la aplicación del tiempo del sistema</string>
<string name="appmanager_weather_deactivate">Desactivar la aplicación del tiempo del sistema</string>
<string name="appmanager_weather_install_provider">Instalar la aplicación de notificación del tiempo</string>
<string name="appmanager_weather_deactivate">Desactivar la aplicación interna del Tiempo</string>
<string name="appmanager_weather_install_provider">Instalar la aplicación de notificación del Tiempo</string>
<string name="app_configure">Configurar</string>
<string name="app_move_to_top">Mover a la parte de arriba</string>
<string name="app_move_to_top">Mover arriba</string>
<!--Strings related to AppBlacklist-->
<string name="title_activity_appblacklist">Lista negra de notificaciones</string>
<!--Strings related to CalBlacklist-->
@ -336,7 +336,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Añadir widget</string>
<string name="activity_prefs_sleep_duration">Horas de sueño deseadas</string>
<string name="appwidget_alarms_set">Una alarma ha sido fijada para %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Una alarma ha sido fijada para %1$02d:%2$02d</string>
<string name="device_hw">Revisión de hardware: %1$s</string>
<string name="device_fw">Versión de firmware: %1$s</string>
<string name="error_creating_directory_for_logfiles">Error creando un directorio para los archivos de registro: %1$s</string>
@ -395,7 +395,7 @@
<string name="mi2_dnd_automatic">Automático (detección de sueño)</string>
<string name="mi2_dnd_scheduled">Programado (intervalo de tiempo)</string>
<string name="discovery_attempting_to_pair">Intento de emparejamiento con %1$s</string>
<string name="discovery_bonding_failed_immediately">El enlace con %1$s ha fallado.</string>
<string name="discovery_bonding_failed_immediately">El enlace con %1$s ha fallado inmediatamente.</string>
<string name="discovery_trying_to_connect_to">Intentando conectar con: %1$s</string>
<string name="discovery_enable_bluetooth">Activa el Bluetooth para encontrar dispositivos.</string>
<string name="discovery_successfully_bonded">Enlazado con %1$s.</string>
@ -474,7 +474,7 @@
<string name="kind_gps_almanac">Almanaque GPS</string>
<string name="kind_gps_cep">Corrección de error del GPS</string>
<string name="kind_resources">Recursos</string>
<string name="kind_watchface">Watchface</string>
<string name="kind_watchface">Esfera</string>
<string name="devicetype_unknown">Dispositivo desconocido</string>
<string name="devicetype_test">Dispositivo de prueba</string>
@ -503,4 +503,31 @@
<string name="notification_channel_name">Notificaciones de Gadgetbridge</string>
<string name="devicetype_xwatch">XWatch</string>
</resources>
<string name="controlcenter_change_led_color">Cambiar color del LED</string>
<string name="controlcenter_change_fm_frequency">Cambiar frecuencia de FM</string>
<string name="controlcenter_calibrate_device">Calibrar dispositivo</string>
<string name="pref_auto_fetch">Extracción automatica de datos de actividad</string>
<string name="pref_auto_fetch_limit_fetches">Tiempo minimo entre extracciones</string>
<string name="pref_auto_fetch_limit_fetches_summary">Extraer datos cada %d minutos</string>
<string name="activity_type_light_sleep">Sueño ligero</string>
<string name="activity_type_deep_sleep">Sueño profundo</string>
<string name="activity_summaries">Actividades</string>
<string name="menuitem_shortcut_weather">Tiempo (Shortcut)</string>
<string name="menuitem_notifications">Notificaciones</string>
<string name="menuitem_weather">Tiempo</string>
<string name="menuitem_alarm">Alarma</string>
<string name="menuitem_timer">Temporizador</string>
<string name="menuitem_compass">Brújula</string>
<string name="menuitem_settings">Ajustes</string>
<string name="menuitem_music">Musica</string>
<string name="menuitem_more">Mas</string>
<string name="watch9_time_minutes">Minutos:</string>
<string name="watch9_time_hours">Horas:</string>
<string name="watch9_time_seconds">Segundos:</string>
<string name="watch9_calibration_button">Calibrar</string>
<string name="watch9_pairing_tap_hint">Cuando el dispositivo vibre, agítelo o pulse un botón.</string>
</resources>

View File

@ -337,7 +337,7 @@
<string name="add_widget">Ajouter un widget</string>
<string name="activity_prefs_sleep_duration">
Temps de sommeil préféré en heures</string>
<string name="appwidget_alarms_set">Une alarme a été enregistré pour %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Une alarme a été enregistré pour %1$02d:%2$02d</string>
<string name="device_hw">Révision matérielle : %1$s</string>
<string name="device_fw">Version du micrologiciel : %1$s</string>
<string name="error_creating_directory_for_logfiles">Erreur à la création de votre fichier log : %1$s</string>

View File

@ -391,7 +391,7 @@
<string name="appwidget_text">ZzZ</string>
<string name="add_widget">Engadir widget</string>
<string name="activity_prefs_sleep_duration">Duración de sono preferida en horas</string>
<string name="appwidget_alarms_set">Unha alarma foi definida para %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Unha alarma foi definida para %1$02d:%2$02d</string>
<string name="device_hw">Revisión de hardware: %1$s</string>
<string name="device_fw">Versión de firmware: %1$s</string>
<string name="error_creating_directory_for_logfiles">Erro creando un cartafol para os arquivos de rexistro: %1$s</string>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
@ -299,7 +299,7 @@
<string name="appwidget_text">חררר</string>
<string name="add_widget">הוספת וידג׳ט</string>
<string name="activity_prefs_sleep_duration">משך השינה המועדף בשעות</string>
<string name="appwidget_alarms_set">הוגדרה התראה לשעה %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">הוגדרה התראה לשעה %1$02d:%2$02d</string>
<string name="device_hw">מהדורת חומרה: %1$s</string>
<string name="device_fw">מהדורת קושחה: %1$s</string>
<string name="error_creating_directory_for_logfiles">אירעה שגיאה ביצירת תיקייה לקובצי הרישום: %1$s</string>
@ -635,7 +635,7 @@
<string name="pref_title_rtl">מימין לשמאל</string>
<string name="pref_summary_rtl">יש להפעיל אפשרות זו אם ההתקן לא יכול להציג שפות מימין לשמאל</string>
<string name="pref_rtl_max_line_length">גודל שורה מרבי מימין לשמאל</string>
<string name="pref_rtl_max_line_length_summary">קיצור או הארכת השורות של טקסט מימין לשמאל</string>
<string name="pref_rtl_max_line_length_summary">קיצור או הארכת השורות שאליהן מופרד טקסט מימין לשמאל</string>
<string name="pref_title_contextual_arabic">ערבית מחוברת</string>
<string name="pref_summary_contextual_arabic">יש להפעיל אפשרות זו כדי לתמוך בערבית מחוברת</string>
@ -648,4 +648,7 @@
<string name="no_data">אין נתונים</string>
<string name="live_activity_max_heart_rate">דופק נוכחי / מרבי: %1$d / %2$d</string>
</resources>
<string name="debugactivity_really_factoryreset_title">לאפס להגדרות יצרן\?</string>
<string name="debugactivity_really_factoryreset">איפוס להגדרות יצרן ימחק את כל הנתונים מההתקן המקושר (אם יש תמיכה בזה). מכשירים מבית Xiaomi/Huami גם משנים את כתובת החומרה (MAC), כך שבעיני Gadgetbridge הם נחשבים למכשירים חדשים.</string>
<string name="devicetype_casiogb6900">Casio GB-6900</string>
</resources>

View File

@ -330,7 +330,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Widget hozzáadása</string>
<string name="activity_prefs_sleep_duration">Preferált alvási idő órában</string>
<string name="appwidget_alarms_set">Ébresztő beállítva: %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Ébresztő beállítva: %1$02d:%2$02d</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Hiba a könyvtár létrehozásakor a naplófájlok számára: %1$s</string>

View File

@ -34,9 +34,9 @@
<string name="appmanager_health_deactivate">Disattiva</string>
<string name="appmanager_hrm_activate">Attiva il monitor del battito cardiaco</string>
<string name="appmanager_hrm_deactivate">Disattiva il monitor del battito cardiaco</string>
<string name="appmanager_weather_activate">Attiva l\'applicazione meteo</string>
<string name="appmanager_weather_deactivate">Disattiva l\'applicazione meteo</string>
<string name="appmanager_weather_install_provider">Installa l\'applicazione \"notifiche meteo\"</string>
<string name="appmanager_weather_activate">Attiva l\'Applicazione Meteo</string>
<string name="appmanager_weather_deactivate">Disattiva l\'Applicazione Meteo</string>
<string name="appmanager_weather_install_provider">Installa l\'applicazione per le notifiche meteo</string>
<string name="app_configure">Configura</string>
<string name="app_move_to_top">Sposta in cima</string>
<!--Strings related to AppBlacklist-->
@ -51,18 +51,18 @@
<string name="miband_firmware_unknown_warning">Questo firmware non è stato testato e potrebbe non essere compatibile con Gadgetbridge.
\n
\nNON è consigliato installarlo sulla Mi Band!</string>
<string name="miband_firmware_suggest_whitelist">Se si procede e tutto continua a funzionare ti invitiamo a contattare gli sviluppatori e dire loro di aggiungere il firmware %s all\'elenco di quelli testati</string>
<string name="miband_firmware_suggest_whitelist">Se si procede e tutto continua a funzionare ti invitiamo a contattare gli sviluppatori e dire loro di aggiungere il firmware %s all\'elenco di quelli compatibili.</string>
<!--Strings related to Settings-->
<string name="title_activity_settings">Impostazioni</string>
<string name="pref_header_general">Impostazioni</string>
<string name="pref_title_general_autoconnectonbluetooth">Collegati al dispositivo quando il bluetooth viene acceso</string>
<string name="pref_title_general_autoconnectonbluetooth">Collegati al dispositivo Gadgetbridge quando il bluetooth viene acceso</string>
<string name="pref_title_general_autostartonboot">Avvio automatico</string>
<string name="pref_title_general_autoreconnect">Riconnessione automatica</string>
<string name="pref_title_audio_player">Applicazione musicale preferita</string>
<string name="pref_default">Default</string>
<string name="pref_header_datetime">Data e ora</string>
<string name="pref_title_datetime_syctimeonconnect">Sincronizza l\'ora</string>
<string name="pref_summary_datetime_syctimeonconnect">Sincronizza l\'orario al collegamento e quando viene cambiato il fuso orario da Android</string>
<string name="pref_summary_datetime_syctimeonconnect">Sincronizza l\'orario al collegamento con il dispositivo Gadgetbridge e quando viene cambiato l\'orario o il fuso orario nel dispositivo Android</string>
<string name="pref_title_theme">Tema</string>
<string name="pref_theme_light">Chiaro</string>
<string name="pref_theme_dark">Scuro</string>
@ -71,15 +71,15 @@
<string name="pref_summary_minimize_priority_off">L\'icona nella barra di stato e la notifica nella schermata di blocco vengono mostrate</string>
<string name="pref_summary_minimize_priority_on">L\'icona nella barra di stato e la notifica nella schermata di blocco non vengono mostrate</string>
<string name="pref_header_notifications">Notifiche</string>
<string name="pref_title_notifications_repetitions">Ripetizioni</string>
<string name="pref_title_notifications_repetitions">Ripetute</string>
<string name="pref_title_notifications_call">Chiamate telefoniche</string>
<string name="pref_title_notifications_sms">SMS</string>
<string name="pref_title_notifications_pebblemsg">Messaggi Pebble</string>
<string name="pref_summary_notifications_pebblemsg">Supporto per applicazioni che inviano le notifiche a Pebble usando PebbleKit.</string>
<string name="pref_title_notifications_generic">Notifiche generiche</string>
<string name="pref_title_whenscreenon"> anche se lo schermo è acceso</string>
<string name="pref_title_notifications_generic">Supporto Notifiche Generiche</string>
<string name="pref_title_whenscreenon">…anche se lo schermo è acceso</string>
<string name="pref_title_notification_filter">Non disturbare</string>
<string name="pref_summary_notification_filter">Non inviare notifiche nei periodi configurati come \"Non Disturbare\"</string>
<string name="pref_summary_notification_filter">Notifiche indesiderate sono bloccate in questa modalità</string>
<string name="pref_title_transliteration">Traslitterazione</string>
<string name="pref_summary_transliteration">Abilita questa opzione se il tuo dispositivo non supporta tutti i caratteri della tua lingua</string>
<string name="always">Sempre</string>
@ -108,7 +108,7 @@
<string name="pref_title_enable_outgoing_call">Mostra le chiamate in uscita</string>
<string name="pref_summary_enable_outgoing_call">Disabilitando questa funzionalità impedirá la vibrazione del Pebble 2 quando si effettua una chiamata</string>
<string name="pref_title_enable_pebblekit">Consenti accesso ad altre applicazioni</string>
<string name="pref_summary_enable_pebblekit">Attiva l\'accesso sperimentale ad applicazioni Android che usano PebbleKit</string>
<string name="pref_summary_enable_pebblekit">Attiva il supporto sperimentale ad applicazioni Android con PebbleKit</string>
<string name="pref_title_sunrise_sunset">Alba e tramonto</string>
<string name="pref_summary_sunrise_sunset">Mostra gli orari calcolati per l\'alba e il tramonto sulla timeline</string>
<string name="pref_title_enable_calendar_sync">Sincronizza calendario</string>
@ -127,14 +127,14 @@
<string name="toast_enable_networklocationprovider">Per cortesia abilita la localizzazione utilizzando la rete</string>
<string name="toast_aqurired_networklocation">posizione acquisita</string>
<string name="pref_title_pebble_forceprotocol">Forza protocollo delle notifiche</string>
<string name="pref_summary_pebble_forceprotocol">Questa opzione forza l\'utilizzo della versione più recente delle notifiche in dipendenza del firmware del tuo dispositivo. ABILITALO SOLO SE SAI COSA STAI FACENDO!</string>
<string name="pref_summary_pebble_forceprotocol">Questa opzione forza l\'utilizzo della versione più recente del protocollo delle notifiche legato al firmware del tuo dispositivo. ABILITALA SOLO SE SAI COSA STAI FACENDO!</string>
<string name="pref_title_pebble_forceuntested">Abilita funzionalità non testate</string>
<string name="pref_summary_pebble_forceuntested">Abilita funzionalità non testate. ABILITARE SOLO SE SI SA QUELLO CHE SI STA FACENDO!</string>
<string name="pref_summary_pebble_forceuntested">Abilita funzionalità non testate. SAI QUELLO CHE STAI FACENDO!</string>
<string name="pref_title_pebble_forcele">Usa sempre BLE</string>
<string name="pref_summary_pebble_forcele">Utilizza il supporto sperimentale Pebble LE per tutti i Pebble in alternativa al BT. Questo richiede un nuovo accoppiamento a LE prima e poi al Pebble LE</string>
<string name="pref_title_pebble_mtu_limit">Limita la MTU del Pebble 2/LE</string>
<string name="pref_summary_pebble_mtu_limit">Se il tuo Pebble 2/LE non funziona come dovrebbe, prova a impostare un limite alla MTU (range valido 20-512)</string>
<string name="pref_title_pebble_enable_applogs">Abilita il log delle applicazioni che girano su Pebble</string>
<string name="pref_title_pebble_enable_applogs">Abilita il log delle applicazioni eseguite su Pebble</string>
<string name="pref_summary_pebble_enable_applogs">Il log delle applicazioni che girano su Pebble verrà aggiunto a quello di Gadgetbridge (richiede riconessione)</string>
<string name="pref_title_pebble_always_ack_pebblekit">Invia l\'ACK dei messaggi PebbleKit prematuramente</string>
<string name="pref_summary_pebble_always_ack_pebblekit">I messaggi inviati alle applicazioni di terze parti verranno immediatamente confermate</string>
@ -219,7 +219,7 @@
<string name="pref_screen_notification_profile_generic">Notifiche generiche</string>
<string name="pref_screen_notification_profile_email">Notifiche Email</string>
<string name="pref_screen_notification_profile_incoming_call">Notifiche chiamata in arrivo</string>
<string name="pref_screen_notification_profile_generic_chat">Cha</string>
<string name="pref_screen_notification_profile_generic_chat">Chat</string>
<string name="pref_screen_notification_profile_generic_navigation">Navigazione</string>
<string name="pref_screen_notification_profile_generic_social">Social Network</string>
<string name="stats_title">Zone di velocità</string>
@ -239,8 +239,8 @@
<string name="alarm_fri_short">Ven</string>
<string name="alarm_sat_short">Sab</string>
<string name="alarm_smart_wakeup">Sveglia intelligente</string>
<string name="user_feedback_miband_set_alarms_failed">Errore nell\'impostazione delle sveglie, riprova di nuovo!</string>
<string name="user_feedback_miband_set_alarms_ok">Sveglie sincronizzate con il dispositivo!</string>
<string name="user_feedback_miband_set_alarms_failed">Errore nell\'impostazione delle sveglie, riprova di nuovo.</string>
<string name="user_feedback_miband_set_alarms_ok">Sveglie inviate al dispositivo.</string>
<string name="chart_no_data_synchronize">Non ci sono dati. Hai effettuato la sincronizzazione con il device?</string>
<string name="user_feedback_miband_activity_data_transfer">Vengono trasferiti %1$s a partire dal %2$s</string>
<string name="miband_prefs_fitness_goal">Traguardo giornaliero di passi</string>
@ -288,7 +288,7 @@
<string name="pref_summary_dont_ack_transfers">Se il trasferimento non viene confermato, i dati rimangono memorizzati sulla Mi Band. Utile se GB è usato insieme ad altre app.</string>
<string name="pref_summary_keep_data_on_device">Conserva i dati delle attività sulla Mi Band anche dopo averli sincronizzati. Utile se GB è usato insieme ad altre app.</string>
<string name="pref_title_low_latency_fw_update">Utilizza la modalità a bassa latenza per gli aggiornamenti del firmware</string>
<string name="pref_summary_low_latency_fw_update">Può essere utile quando l\'aggiornamento del firmware fallisce</string>
<string name="pref_summary_low_latency_fw_update">Può essere utile quando l\'aggiornamento del firmware fallisce.</string>
<string name="live_activity_steps_history">Storico dei passi</string>
<string name="live_activity_current_steps_per_minute">Passi/minuto</string>
<string name="live_activity_total_steps">Passi totali</string>
@ -306,7 +306,7 @@
<string name="miband_prefs_reserve_alarm_calendar">Sveglie da riservare per i prossimi eventi del calendario</string>
<string name="miband_prefs_hr_sleep_detection">Utilizza il sensore del battito cardiaco per migliorare il riconoscimento del sonno</string>
<string name="miband_prefs_device_time_offset_hours">Sfasamento dell\'orario per il device (per consentire il rilevamento del sonno per chi lavora a turni)</string>
<string name="miband2_prefs_dateformat">Mi Band 2: Formato Data</string>
<string name="miband2_prefs_dateformat">Formato Data</string>
<string name="dateformat_time">Ora</string>
<string name="dateformat_date_time">Ora e data</string>
<string name="mi2_prefs_goal_notification">Notifica raggiungimento obbiettivi</string>
@ -320,7 +320,7 @@
<string name="mi2_prefs_inactivity_warnings">Allarmi inattività</string>
<string name="mi2_prefs_inactivity_warnings_summary">La band vibrerà dopo un determinato periodo di inattività</string>
<string name="mi2_prefs_inactivity_warnings_threshold">Limite inattività (in minuti)</string>
<string name="mi2_prefs_inactivity_warnings_dnd_summary">Disabilita allarmi inattività per un dato intervallo di tempo</string>
<string name="mi2_prefs_inactivity_warnings_dnd_summary">Disabilita allarmi inattività per un intervallo di tempo</string>
<string name="mi2_prefs_do_not_disturb_start">Ora inizio</string>
<string name="mi2_prefs_do_not_disturb_end">Ora fine</string>
<string name="FetchActivityOperation_about_to_transfer_since">Vengono trasferiti dati a partire dal %1$s</string>
@ -335,7 +335,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Aggiungi widget</string>
<string name="activity_prefs_sleep_duration">Ore di sonno preferite</string>
<string name="appwidget_alarms_set">Impostata sveglia per %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Impostata sveglia per %1$02d:%2$02d</string>
<string name="device_hw">Versione Hardware: %1$s</string>
<string name="device_fw">Versione Firmware: %1$s</string>
<string name="error_creating_directory_for_logfiles">Errore durante la creazione della directory per i file di log: %1$s</string>
@ -345,7 +345,7 @@
<string name="charts_legend_heartrate">Battito cardiaco</string>
<string name="live_activity_heart_rate">Battito cardiaco</string>
<string name="pref_title_pebble_health_store_raw">Memorizza i record grezzi nel database</string>
<string name="pref_summary_pebble_health_store_raw">Se selezionato, i dati vengono memorizzati come trasmessi dal Pebble, in modo da avere più dati in futuro. NB: Il database sarà più grande!</string>
<string name="pref_summary_pebble_health_store_raw">Salva i dati \"as is\", aumentando l\'utilizzo del database in modo da permettere interrogazioni successive.</string>
<string name="action_db_management">Gestione del database</string>
<string name="title_activity_db_management">Gestione del database</string>
<string name="activity_db_management_import_export_explanation">Le operazioni sul database utilizzano il percorso qui sotto. \nQuesto percorso è accessibile ad altre applicazioni ed al tuo computer. \nDopo l\'esportazione il database si troverà qui (oppure copia qui il database che desideri importare):</string>
@ -374,7 +374,7 @@
<string name="title_activity_vibration">Vibrazione</string>
<!--Strings related to Pebble Pairing Activity-->
<string name="title_activity_pebble_pairing">Pairing del Pebble</string>
<string name="pebble_pairing_hint">Un avviso di accoppiamento dovrebbe comparire sul tuo dispositivo Android. Se non succedesse controlla l\'area delle notifiche ed accetta la richiesta. Dopo fai lo stesso sul tuo Pebble</string>
<string name="pebble_pairing_hint">Un avviso di accoppiamento dovrebbe comparire sul tuo dispositivo Android. Se non succedesse controlla l\'area delle notifiche ed accetta la richiesta. Dopo fai lo stesso sul tuo Pebble.</string>
<string name="weather_notification_label">Assicurati che questa skin sia abilitata nell\'applicazione \"Notifiche Meteo\" per ricevere informazioni meteo sul tuo Pebble.\n\nNon è necessaria nessuna ulteriore configurazione.\n\nPuoi abilitare l\'applicazione Meteo di sistema sul tuo Pebble dalla gestione applicazioni.\n\nLe watchfaces che lo supportano mostreranno il meteo automaticamente.</string>
<string name="pref_title_setup_bt_pairing">Abilita il pairing bluetooth</string>
<string name="pref_summary_setup_bt_pairing">Disattiva se hai problemi di connession</string>
@ -468,7 +468,7 @@
<string name="discovery_pair_title">Accoppiare con %1$s?</string>
<string name="discovery_pair_question">Seleziona \'Accoppia\' per accoppiare i tuoi dispositivi. Se dovesse fallire riprova di nuova senza effettuare l\'accoppiamento.</string>
<string name="discovery_yes_pair">Accoppia</string>
<string name="_pebble_watch_open_on_phone">Apri nel telefono</string>
<string name="_pebble_watch_open_on_phone">Apri nel dispositivo Android</string>
<string name="_pebble_watch_mute">Muto</string>
<string name="_pebble_watch_reply">Rispondi</string>
@ -546,18 +546,18 @@
<string name="menuitem_compass">Bussola</string>
<string name="menuitem_settings">Impostazioni</string>
<string name="menuitem_alipay">Alipay</string>
<string name="controlcenter_change_led_color">Cambia il colore dei LED</string>
<string name="controlcenter_change_led_color">Cambio colore LED</string>
<string name="controlcenter_change_fm_frequency">Cambia frequenza FM</string>
<string name="controlcenter_calibrate_device">Calibra Dispositivo</string>
<string name="pref_title_notifications_timeout">Tempo minimo tra 2 notifiche</string>
<string name="pref_title_notifications_timeout">Tempo minimo tra le notifiche</string>
<string name="pref_title_rtl">Da destra a sinistra</string>
<string name="pref_summary_rtl">Abilita questo se il tuo dispositivo non mostra le lingue lette da destra a sinistra</string>
<string name="pref_rtl_max_line_length">Lunghezza massima delle linee</string>
<string name="pref_rtl_max_line_length_summary">Definisci il numero di caratteri da visualizzare per le lingue lette da destra a sinistra</string>
<string name="pref_rtl_max_line_length_summary">Aumenta o diminuisci il numero di righe con cui separare il testo da destra a sinistra</string>
<string name="preferences_id115_settings">"impostazioni per ID115 "</string>
<string name="preferences_id115_settings">"Impostazioni per ID115 "</string>
<string name="prefs_screen_orientation">Orientamento dello schermo</string>
<string name="pref_auto_fetch_limit_fetches">Tempo minimo tra le sincronizzazioni</string>
@ -569,4 +569,76 @@
<string name="watch9_pairing_tap_hint">Quando il tuo orologio vibra, scuotilo o premi il tasto.</string>
<string name="notif_battery_low">%1$s batteria scarica</string>
<string name="pref_auto_fetch_limit_fetches_summary">Sincronizza ogni %d minuti</string>
<string name="no_limit">Nessun limite</string>
<string name="seconds_5">5 secondi</string>
<string name="seconds_10">10 secondi</string>
<string name="seconds_20">20 secondi</string>
<string name="seconds_30">30 secondi</string>
<string name="minutes_1">1 minuto</string>
<string name="minutes_5">5 minuti</string>
<string name="minutes_10">10 minuti</string>
<string name="minutes_30">30 minuti</string>
<string name="you_slept">Hai dormito dalle %1$s alle %2$s</string>
<string name="you_did_not_sleep">Non hai dormito</string>
<string name="mi3_prefs_band_screen_unlock_summary">Swipe up per sbloccare lo schermo della band</string>
<string name="mi3_prefs_night_mode">Modalità notturna</string>
<string name="norwegian_bokmal">Norvegese Bokmål</string>
<string name="russian">Russo</string>
<string name="german">Tedesco</string>
<string name="italian">Italiano</string>
<string name="french">Francese</string>
<string name="polish">Polacco</string>
<string name="korean">Coreano</string>
<string name="japanese">Giapponese</string>
<string name="activity_prefs_chart_max_heart_rate">Frequenza cardiaca massima</string>
<string name="activity_prefs_chart_min_heart_rate">Frequenza cardiaca minima</string>
<string name="ok">OK</string>
<string name="mi3_night_mode_sunset">Al tramonto</string>
<string name="devicetype_mykronoz_zetime">MyKronoz ZeTime</string>
<string name="devicetype_id115">ID115</string>
<string name="devicetype_roidmi3">Roidmi 3</string>
<string name="menuitem_notifications">Notifiche</string>
<string name="menuitem_music">Musica</string>
<string name="watch9_time_minutes">Minuti:</string>
<string name="watch9_time_hours">Ore:</string>
<string name="watch9_time_seconds">Secondi:</string>
<string name="watch9_calibration_button">Calibra</string>
<string name="title_activity_watch9_pairing">accoppiamento Watch 9</string>
<string name="title_activity_watch9_calibration">calibrazione Watch 9</string>
<string name="preferences_rtl_settings">Supporto da destra a sinistra</string>
<string name="share_log">Condividi log</string>
<string name="notif_battery_low_extended">%1$s batteria scarica: %2$s</string>
<string name="lack_of_sleep">Debito di sonno: %1$s</string>
<string name="mi3_prefs_night_mode_summary">Riduci automaticamente la luminosità dello schermo della band di notte</string>
<string name="activity_prefs_charts">Impostazioni grafici</string>
<string name="devicetype_watch9">Watch 9</string>
<string name="devicetype_roidmi">Roidmi</string>
<string name="menuitem_more">Altro</string>
<string name="pref_title_contextual_arabic">Arabo Contestuale</string>
<string name="warning">Attenzione!</string>
<string name="no_data">Nessun dato</string>
<string name="preferences_led_color">Colore LED</string>
<string name="preferences_fm_frequency">Frequenze FM</string>
<string name="pref_invalid_frequency_title">Frequenza non valida</string>
<string name="pref_invalid_frequency_message">Per favore introdurre una frequenza compresa tra 87.5 e 108.0</string>
<string name="language_and_region_prefs">Impostazioni lingua e regione</string>
<string name="live_activity_max_heart_rate">Attuale / Massima frequenza cardiaca: %1$d / %2$d</string>
<string name="mi3_prefs_band_screen_unlock">Sblocca lo schermo della band</string>
<string name="watch9_calibration_hint">Imposta l\'orario che il tuo dispositivo ti sta mostrando adesso.</string>
<string name="pref_summary_contextual_arabic">Abilita questo per supportare la lingua Araba</string>
<string name="share_log_warning">Tieni presente che i file di log di Gadgetbridge possono contenere molte informazioni personali, inclusi, a titolo esemplificativo, dati sulla salute, identificatori univoci (come l\'indirizzo MAC del dispositivo), preferenze musicali, ecc. Modifica il file per rimuovere queste informazioni prima di inviare il file in una segnalazione di errore visibile pubblicamente.</string>
<string name="overslept">Sonno extra: %1$s</string>
<string name="lack_of_step">Mancanza di passi: %1$d</string>
<string name="overstep">Passo extra: %1$d</string>
</resources>

View File

@ -336,7 +336,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">ウィジェットを追加</string>
<string name="activity_prefs_sleep_duration">好ましい睡眠時間(時間)</string>
<string name="appwidget_alarms_set">%1$02d:%2$02d のアラームを設定しました</string>
<string name="appwidget_setting_alarm">%1$02d:%2$02d のアラームを設定しました</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">ログファイルのディレクトリを作成中にエラー: %1$s</string>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">가젯브릿지</string>
<string name="title_activity_controlcenter">가젯브릿지</string>
@ -28,17 +28,17 @@
<string name="miband_firmware_unknown_warning">이 펌웨어는 테스트를 거치지 않았고 가젯브릿지와 호환되지 않을 수 있습니다.
\n
\nMi Band에 설치하지 않는 것이 좋습니다!</string>
<string name="miband_firmware_suggest_whitelist">여전히 계속 진행하기를 원하고 이후에도 정상적으로 작동하기를 원한다면, %s 화이트리스트 펌웨어 버전을 가젯브릿지 개발자들에게 알려주세요</string>
<string name="miband_firmware_suggest_whitelist">여전히 계속 진행하기를 원하고 이후에도 정상적으로 작동하기를 원한다면, %s 펌웨어가 정상적으로 동작한다고 가젯브릿지 개발자들에게 알려주세요.</string>
<!--Strings related to Settings-->
<string name="title_activity_settings">설정</string>
<string name="pref_header_general">일반 설정</string>
<string name="pref_title_general_autoconnectonbluetooth">블루투스가 켜지면 기기에 접속하기</string>
<string name="pref_title_general_autoconnectonbluetooth">블루투스가 켜지면 가젯브릿지 기기에 접속하기</string>
<string name="pref_title_general_autoreconnect">자동으로 재연결</string>
<string name="pref_title_audio_player">선호하는 오디오 플레이어</string>
<string name="pref_default">기본값</string>
<string name="pref_header_datetime">날짜와 시간</string>
<string name="pref_title_datetime_syctimeonconnect">시간 동기화</string>
<string name="pref_summary_datetime_syctimeonconnect">시간이 바뀌거나, 시간대가 바뀌거나, 접속을 할 때 기기와 시간을 동기화</string>
<string name="pref_summary_datetime_syctimeonconnect">시간이 바뀌거나, 시간대가 바뀌거나, 접속을 할 때 가젯브릿지 기기와 시간을 동기화</string>
<string name="pref_title_theme">테마</string>
<string name="pref_theme_light">밝음</string>
<string name="pref_theme_dark">어두움</string>
@ -61,9 +61,9 @@
<string name="pref_title_enable_pebblekit">제 3자 안드로이드 앱 접근을 허용</string>
<string name="pref_summary_enable_pebblekit">PebbleKit을 사용하는 안드로이드 앱을 실험적으로 지원</string>
<string name="pref_title_pebble_forceprotocol">강제 알림 프로토콜</string>
<string name="pref_summary_pebble_forceprotocol">이 옵션은 펌웨어 버전에 따라 최신 알림 프로토콜을 강제로 사용하도록 합니다. 자세한 내용을 알 경우에만 이 옵션을 사용하세요.</string>
<string name="pref_summary_pebble_forceprotocol">이 옵션은 펌웨어 버전에 따라 최신 알림 프로토콜을 강제로 사용하도록 합니다. 자세한 내용을 알 경우에만 이 옵션을 사용하세요!</string>
<string name="pref_title_pebble_forceuntested">테스트를 거치지 않은 기능 사용</string>
<string name="pref_summary_pebble_forceuntested">사용하려는 기능은 테스트를 거치지 않았습니다. 확실하게 아는 경우에만 사용하세요!</string>
<string name="pref_summary_pebble_forceuntested">테스트를 거치지 않은 기능을 활성화합니다. 확실하게 아는 경우에만 사용하세요!</string>
<string name="pref_title_pebble_reconnect_attempts">재접속 시도</string>
<string name="not_connected">연결되지 않음</string>
<string name="connecting">연결 중</string>
@ -78,10 +78,10 @@
<string name="tap_connected_device_for_app_mananger">연결된 기기를 선택해 앱 관리자 실행</string>
<string name="tap_a_device_to_connect">기기를 선택해 연결</string>
<string name="cannot_connect_bt_address_invalid_">연결할 수 없습니다. 블루투스 주소가 올바르지 않은 것 같습니다.</string>
<string name="gadgetbridge_running">가젯브릿지 실행중입니다.</string>
<string name="gadgetbridge_running">가젯브릿지 실행중</string>
<string name="installing_binary_d_d">바이너리 설치중: %1$d/%2$d</string>
<string name="installation_failed_">설치 실패!</string>
<string name="installation_successful">설치 성공!</string>
<string name="installation_failed_">설치 실패</string>
<string name="installation_successful">설치</string>
<string name="firmware_install_warning">펌웨어를 설치하려고 합니다. 위험에 대한 책임을 지고 진행하세요.\n\n\n 이 펌웨어는 다음 하드웨어 리비전에 사용됩니다: %s</string>
<string name="app_install_info">지금 다음 앱을 설치하려고 합니다:\n\n\n%1$s 버전 %2$s , 다음으로부터: %3$s\n</string>
<string name="n_a">N/A</string>
@ -144,8 +144,8 @@
<string name="alarm_fri_short">금요일</string>
<string name="alarm_sat_short">토요일</string>
<string name="alarm_smart_wakeup">스마트한 알람</string>
<string name="user_feedback_miband_set_alarms_failed">알람 설정 오류가 발생했습니다. 다시 시도하세요!</string>
<string name="user_feedback_miband_set_alarms_ok">알람을 기기로 보냈습니다!</string>
<string name="user_feedback_miband_set_alarms_failed">알람 설정 오류가 발생했습니다. 다시 시도하세요.</string>
<string name="user_feedback_miband_set_alarms_ok">알람을 기기로 보냈습니다.</string>
<string name="chart_no_data_synchronize">데이터가 없습니다. 기기와 동기화할까요?</string>
<string name="user_feedback_miband_activity_data_transfer">%2$s 에서부터 %1$s 데이터를 전송하려고 합니다</string>
<string name="miband_prefs_fitness_goal">일일 목표 걸음 수</string>
@ -155,7 +155,7 @@
<string name="installer_activity_unable_to_find_handler">이 파일을 설치하는 데 필요한 핸들러를 찾을 수 없습니다.</string>
<string name="pbw_install_handler_unable_to_install">주어진 파일을 설치할 수 없습니다: %1$s</string>
<string name="pbw_install_handler_hw_revision_mismatch">주어진 펌웨어를 설치할 수 없습니다. Pebble의 하드웨어 리비전과 일치하지 않습니다.</string>
<string name="installer_activity_wait_while_determining_status">설치 상태를 파악하는 동안 기다려주세요...</string>
<string name="installer_activity_wait_while_determining_status">설치 상태를 파악하는 동안 기다려주세요</string>
<string name="notif_battery_low_title">기기의 배터리 잔량이 적습니다!</string>
<string name="notif_battery_low_percent">%1$s 배터리 남음: %2$s%%</string>
<string name="notif_battery_low_bigtext_last_charge_time">마지막 충전: %s</string>
@ -176,12 +176,12 @@
<string name="updatefirmwareoperation_updateproblem_do_not_reboot">펌웨어 전송에 문제가 있습니다. Mi Band를 재부팅하지 마세요!</string>
<string name="updatefirmwareoperation_metadata_updateproblem">펌웨어 메타데이터 전송에 문제가 있습니다</string>
<string name="updatefirmwareoperation_update_complete">펌웨어 설치 완료</string>
<string name="updatefirmwareoperation_update_complete_rebooting">펌웨어 설치 완료. 기기를 재부팅합니다...</string>
<string name="updatefirmwareoperation_write_failed">펌웨어 기록 실패</string>
<string name="updatefirmwareoperation_update_complete_rebooting">펌웨어 설치 완료, 기기를 재부팅합니다…</string>
<string name="updatefirmwareoperation_write_failed">펌웨어 플래싱 실패</string>
<string name="chart_steps">걸음 수</string>
<string name="liveactivity_live_activity">실시간 활동</string>
<string name="weeksteps_today_steps_description">오늘 걸음 수, 목표: %1$s</string>
<string name="pref_title_dont_ack_transfer">활동 데이터 전송 확인하지 않음</string>
<string name="pref_title_dont_ack_transfer">활동 데이터 전송 완료를 확인하지 않음</string>
<string name="pref_summary_dont_ack_transfers">만약 활동 데이터가 밴드에 확인되지 않았다면, 지워지지 않을 것입니다. 가젯브릿지가 다른 앱들과 같이 사용될 때 유용합니다.</string>
<string name="pref_summary_keep_data_on_device">동기화 이후에도 Mi Band의 활동 데이터가 유지될 것입니다. 가젯브릿지가 다른 앱들과 같이 사용될 때 유용합니다.</string>
<string name="live_activity_steps_history">걸음 수 기록</string>
@ -193,7 +193,7 @@
<string name="abstract_chart_fragment_kind_light_sleep">얕은 수면</string>
<string name="abstract_chart_fragment_kind_deep_sleep">깊은 수면</string>
<string name="abstract_chart_fragment_kind_not_worn">착용하지 않음</string>
<string name="device_not_connected">연결되지 않음</string>
<string name="device_not_connected">연결되지 않음.</string>
<string name="user_feedback_all_alarms_disabled">모든 알람 해제됨</string>
<string name="pref_title_keep_data_on_device">기기에 활동 데이터 유지</string>
<string name="miband_fwinstaller_incompatible_version">호환되지 않는 펌웨어</string>
@ -211,12 +211,12 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">위젯 추가</string>
<string name="activity_prefs_sleep_duration">선호하는 수면 시간</string>
<string name="appwidget_alarms_set">%1$02d:%2$02d 알람이 설정되었습니다</string>
<string name="appwidget_setting_alarm">%1$02d:%2$02d 알람이 설정되었습니다</string>
<string name="device_hw">하드웨어: %1$s</string>
<string name="device_fw">펌웨어: %1$s</string>
<string name="error_creating_directory_for_logfiles">기록 파일을 저장할 디렉토리 생성 오류: %1$s</string>
<string name="DEVINFO_HR_VER">심박수:</string>
<string name="updatefirmwareoperation_update_in_progress">펌웨어 업데이트 진행</string>
<string name="DEVINFO_HR_VER">"심박수: "</string>
<string name="updatefirmwareoperation_update_in_progress">펌웨어 플래싱</string>
<string name="updatefirmwareoperation_firmware_not_sent">펌웨어가 전송되지 않음</string>
<string name="charts_legend_heartrate">심박수</string>
<string name="live_activity_heart_rate">심박수</string>
@ -249,12 +249,12 @@
<string name="title_activity_calblacklist">비활성화 된 캘린더들</string>
<string name="fw_upgrade_notice_amazfitbip">지금 기존의 Amazfit Bip에 %s 펌웨어를 설치하려고 합니다.
<string name="fw_upgrade_notice_amazfitbip">지금 당신의 Amazfit Bip에 %s 펌웨어를 설치하려고 합니다.
\n
\n.gps 펌웨어를 설치하고 나서 .res 파일을, 그리고 최종적으로 .fw 파일을 설치하세요. .fw 파일을 설치한 후 기기가 재시작 됩니다.
\n.fw 파일을 설치하고 .res 파일을 설치한 후 최종적으로 .gps 파일을 설치하세요. .fw 파일을 설치한 후 기기가 재시작 됩니다.
\n
\n비고: 만약 전에 설치되어 있던 .res 파일과 .gps 파일을 그대로 사용한다면 각 파일을 다시 설치 할 필요는 없습니다.
\n
\n
\n본인의 책임 하에 진행하세요!</string>
<string name="pref_title_general_autostartonboot">자동으로 시작</string>
<string name="pref_title_language">언어</string>
@ -265,7 +265,7 @@
<string name="pref_summary_notifications_pebblemsg">PebbleKit를 사용하여 페블에 알림을 보내는 앱을 지원합니다.</string>
<string name="pref_title_notification_filter">방해 금지 모드</string>
<string name="pref_summary_notification_filter">방해 금지 모드가 활성화 되었을 때 원하지 않는 알림을 보내지 않습니다</string>
<string name="pref_summary_notification_filter">이 모드를 켜면 원하지 않는 알림이 울리지 않습니다</string>
<string name="pref_title_transliteration">표기법 변환</string>
<string name="pref_summary_transliteration">만약 장치가 당신이 사용하는 언어의 폰트를 지원하지 않는다면 이 옵션을 활성화 하세요</string>
@ -328,16 +328,16 @@
<string name="pref_title_unit_system">단위</string>
<string name="pref_title_timeformat">시간 형식</string>
<string name="controlcenter_connect">연결</string>
<string name="fw_upgrade_notice_amazfitcor">%s 펌웨어를 Amazfit Cor에 설치하려고 합니다.
\n
\n.res 파일을 먼저 설치하고 .fw 파일을 설치하십시오. .fw 파일이 설치된 이후에 기기가 재시작될 것입니다.
\n
\n기존의 .res 파일과 설치하려는 .res 파일이 동일하다면, .res 파일을 다시 설치할 필요는 없습니다.
\n
\n테스트되지 않은 기능입니다. 기기가 고장날 가능성이 있습니다. 본인의 책임 하에 진행하십시오!</string>
<string name="fw_upgrade_notice_amazfitcor">지금 당신의 Amazfit Cor에 %s 펌웨어를 설치하려고 합니다.
\n
\n.fw 파일을 설치하고 .res 파일을 설치한 후 최종적으로 .gps 파일을 설치하세요. .fw 파일을 설치한 후 기기가 재시작 됩니다.
\n
\n비고: 만약 전에 설치되어 있던 .res 파일과 .gps 파일을 그대로 사용한다면 각 파일을 다시 설치 할 필요는 없습니다.
\n
\n본인의 책임 하에 진행하세요!</string>
<string name="pref_title_charts_swipe">활동 차트에서 좌/우 밀어넘기기 사용</string>
<string name="pref_title_weather_location">날씨 위치</string>
<string name="pref_title_weather_location">날씨 위치 (CM/리니지 OS)</string>
<string name="pref_title_pebble_enable_bgjs">백그라운드 JS 사용하기</string>
<string name="pref_summary_pebble_enable_bgjs">활성화할 경우 시계 화면에 날씨와 배터리 정보 등을 표시합니다.</string>
@ -378,11 +378,11 @@
<string name="clock">시계</string>
<string name="heart_rate">심박수</string>
<string name="battery">배터리</string>
<string name="pref_title_low_latency_fw_update">펌웨어 업데이트에 낮은 지연 모드 사용</string>
<string name="pref_summary_low_latency_fw_update">펌웨어 업데이트가 실패할 경우 이 기능이 도움이 될 수 있습니다</string>
<string name="pref_title_low_latency_fw_update">펌웨어 플래싱에 낮은 지연 모드 사용</string>
<string name="pref_summary_low_latency_fw_update">펌웨어 업데이트가 실패하는 장치에 이 기능이 도움이 될 수 있습니다.</string>
<string name="miband_prefs_device_time_offset_hours">기기 시간을 상쇄하기(교대 근무자의 수면 측정을 위해)</string>
<string name="miband2_prefs_dateformat">Mi2: 날짜 형식</string>
<string name="miband2_prefs_dateformat">날짜 형식</string>
<string name="dateformat_time">시간</string>
<string name="dateformat_date_time">시간 &amp; 날짜</string>
<string name="mi2_prefs_button_actions">버튼 동작</string>
@ -410,7 +410,7 @@
<string name="mi2_prefs_inactivity_warnings">부동 자세 경고</string>
<string name="mi2_prefs_inactivity_warnings_summary">정해진 시간 동안 동작이 감지되지 않을 경우 진동으로 알림</string>
<string name="mi2_prefs_inactivity_warnings_threshold">경고 기준 시간</string>
<string name="mi2_prefs_inactivity_warnings_dnd_summary">부동 기간 설정</string>
<string name="mi2_prefs_inactivity_warnings_dnd_summary">일정 시간동안 움직이지 않음 경고를 비활성화</string>
<string name="mi2_prefs_do_not_disturb_start">시작 시간</string>
<string name="mi2_prefs_do_not_disturb_end">종료 시간</string>
@ -422,12 +422,12 @@
<string name="FetchActivityOperation_about_to_transfer_since">%1$s 이후의 데이터 전송 중</string>
<string name="pref_title_pebble_health_store_raw">데이터베이스에 미가공 데이터 저장</string>
<string name="pref_summary_pebble_health_store_raw">이 기능을 사용할 경우 데이터는 가공되지 않은 상태로 저장됩니다 (데이터 용량이 비교적 클 수 있습니다)!</string>
<string name="pref_summary_pebble_health_store_raw">이 기능을 사용할 경우 데이터는 가공되지 않은 상태로 저장되어, 추후 데이터 분석을 가능하게 하기 위해 데이터베이스에서 더 큰 용량을 차지합니다.</string>
<string name="action_db_management">데이터베이스 관리</string>
<string name="title_activity_db_management">데이터베이스 관리</string>
<string name="activity_db_management_import_export_explanation">데이터베이스 작업은 다음 경로를 사용합니다.
\n이 경로는 다른 컴퓨터 및 안드로이드 앱에서 접근 가능합니다.
\n내보낸 데이터베이스 및 가져올 데이터베이스를 여기서 확인하세요:</string>
<string name="activity_db_management_import_export_explanation">데이터베이스 작업은 다음 경로를 사용합니다.
\n이 경로는 다른 컴퓨터 및 안드로이드 앱에서 접근 가능합니다.
\n내보낸 데이터베이스를 여기서 확인하세요 (혹은 가져올 데이터베이스를 여기에 저장하세요):</string>
<string name="activity_db_management_merge_old_title">남겨진 데이터베이스 삭제</string>
<string name="dbmanagementactivvity_cannot_access_export_path">내보낼 경로에 접근할 수 없습니다. 개발자에게 연락하세요.</string>
<string name="dbmanagementactivity_exported_to">%1$s 로 내보냈음</string>
@ -440,18 +440,18 @@
<string name="dbmanagementactivity_error_importing_shared">설정 가져오기 오류: %1$s</string>
<string name="dbmanagementactivity_delete_activity_data_title">활동 데이터를 삭제할까요?</string>
<string name="dbmanagementactivity_really_delete_entire_db">정말로 모든 데이터베이스를 삭제할까요? 모든 활동 데이터 및 기기 정보가 소실됩니다.</string>
<string name="dbmanagementactivity_database_successfully_deleted">데이터 삭제 성공.</string>
<string name="dbmanagementactivity_database_successfully_deleted">데이터 삭제.</string>
<string name="dbmanagementactivity_db_deletion_failed">데이터베이스 삭제 실패.</string>
<string name="dbmanagementactivity_delete_old_activity_db">오래된 활동 데이터베이스를 삭제할까요?</string>
<string name="dbmanagementactivity_delete_old_activitydb_confirmation">정말로 오래된 활동 데이터베이스를 삭제할까요? 내보내지 않은 활동 데이터는 소실됩니다.</string>
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">오래된 활동 데이터 삭제 성공.</string>
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">오래된 활동 데이터 삭제.</string>
<string name="dbmanagementactivity_old_activity_db_deletion_failed">오래된 활동 데이터 삭제 실패.</string>
<string name="dbmanagementactivity_overwrite">덮어쓰기</string>
<string name="Cancel">취소</string>
<string name="title_activity_vibration">진동</string>
<string name="title_activity_pebble_pairing">Pebble 페어링</string>
<string name="pebble_pairing_hint">페어링 화면이 안드로이드 기기에 나타날 것입니다. 그렇지 않다면, 알림 서랍을 열어서 페어링 요청을 수락하세요. 그리고 나서 Pebble에서 페어링 요청을 수락하세요</string>
<string name="pebble_pairing_hint">페어링 화면이 안드로이드 기기에 나타날 것입니다. 그렇지 않다면, 알림 서랍을 열어서 페어링 요청을 수락하세요. 그리고 나서 Pebble에서 페어링 요청을 수락하세요.</string>
<string name="weather_notification_label">Pebble에 날씨 정보를 얻기 위해 이 스킨이 Weather Notification 앱에서 활성화되었는지를 확인하세요.
\n
@ -487,13 +487,13 @@
<string name="discovery_bonding_failed_immediately">%1$s에 연결 생성 실패.</string>
<string name="discovery_trying_to_connect_to">연결 시도하는 중: %1$s</string>
<string name="discovery_enable_bluetooth">기기를 발견하기 위해 블루투스를 활성화하세요.</string>
<string name="discovery_successfully_bonded">%1$s에 연결 생성 성공.</string>
<string name="discovery_successfully_bonded">%1$s에 연결 성공.</string>
<string name="discovery_pair_title">%1$s 와 페어링할까요?</string>
<string name="discovery_pair_question">기기에 페어링하려면 \'페어링\'을 선택하세요. 만약 실패한다면 페어링 없이 다시 시도하세요.</string>
<string name="discovery_yes_pair">페어링</string>
<string name="discovery_dont_pair">페어링하지 않기</string>
<string name="_pebble_watch_open_on_phone">스마트폰에서 열기</string>
<string name="_pebble_watch_open_on_phone">안드로이드 장치에서 열기</string>
<string name="_pebble_watch_mute">음소거</string>
<string name="_pebble_watch_reply">답장</string>
@ -545,7 +545,7 @@
<string name="notification_channel_name">가젯브릿지 알림</string>
<!-- Menus on the smart device -->
<string name="menuitem_shortcut_alipay">Alipay (Shortcut)</string>
<string name="menuitem_shortcut_alipay">Alipay (바로가기)</string>
<string name="menuitem_shortcut_weather">날씨 (Shortcut)</string>
<string name="menuitem_status">상태</string>
<string name="menuitem_activity">활동</string>
@ -576,7 +576,7 @@
<string name="pref_title_rtl">오른쪽에서 왼쪽으로</string>
<string name="pref_summary_rtl">만약 당신의 디바이스에서 좌우 언어가 보이지 않는다면 이것을 활성화하세요</string>
<string name="pref_rtl_max_line_length">좌우 최대 라인 길이</string>
<string name="pref_rtl_max_line_length_summary">텍스트 좌우 길이 조정은 분리된</string>
<string name="pref_rtl_max_line_length_summary">오른쪽에서 왼쪽으로 쓰는 문자가 구분되는 줄을 늘이거나 줄입니</string>
<string name="pref_title_weather">날씨</string>
<string name="pref_title_pebble_gatt_clientonly">"GATT client 만 "</string>
@ -652,4 +652,27 @@
<string name="menuitem_notifications">알람</string>
<string name="language_and_region_prefs">언어 및 지역 설정</string>
</resources>
<string name="debugactivity_really_factoryreset_title">정말로 공장 초기화를 실행할까요\?</string>
<string name="debugactivity_really_factoryreset">공장 초기화를 하면 (지원되는) 연결된 장치에서 데이터를 모두 삭제합니다. Xiaomi/Huami 장치들은 블루투스 MAC 주소도 변경하므로, Gadgetbridge에서 새 장치로 나타나게 됩니다.</string>
<string name="devicetype_id115">ID115</string>
<string name="menuitem_music">음악</string>
<string name="menuitem_more">더 보기</string>
<string name="watch9_time_minutes">분:</string>
<string name="watch9_time_hours">시간:</string>
<string name="watch9_time_seconds">초:</string>
<string name="watch9_calibration_hint">장치에 보이는 시간을 설정하세요.</string>
<string name="watch9_calibration_button">보정</string>
<string name="title_activity_watch9_pairing">Watch 9 페어링</string>
<string name="title_activity_watch9_calibration">Watch 9 보정</string>
<string name="pref_title_contextual_arabic">문맥 아랍어 커닝 켜기</string>
<string name="pref_summary_contextual_arabic">문맥 아랍어 처리 지원을 활성화 합니다</string>
<string name="preferences_rtl_settings">오른쪽에서 왼쪽으로 쓰는 문자 지원</string>
<string name="share_log">기록 공유</string>
<string name="share_log_warning">Gadgetbridge 기록은 건강 데이터, 개인을 식별할 수 있는 정보 (예를 들어서 장치의 MAC 주소), 음악 설정 등을 포함하는 많은 개인정보를 포함하고 있을 수 있습니다. 공개 이슈 트레커에 파일을 보내기 전에 파일을 편집해 이러한 정보를 제거하는 것을 고려하세요.</string>
<string name="warning">경고!</string>
<string name="no_data">데이터 없음</string>
<string name="preferences_led_color">LED 색상</string>
<string name="preferences_fm_frequency">FM 주파수</string>
<string name="pref_invalid_frequency_title">올바르지 않은 주파수</string>
<string name="pref_invalid_frequency_message">87.5와 108.0 사이의 주파수를 입력하세요</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="action_settings">Innstillinger</string>
<string name="action_debug">Feilretting</string>
<string name="action_quit">Avslutt</string>
@ -407,7 +407,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Legg til miniprogram</string>
<string name="activity_prefs_sleep_duration">Foretrukket søvn i timer</string>
<string name="appwidget_alarms_set">En alarm ble satt for %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">En alarm ble satt for %1$02d:%2$02d</string>
<string name="device_hw">Maskinvarerevisjon: %1$s</string>
<string name="device_fw">Fastvareversjon: %1$s</string>
<string name="error_creating_directory_for_logfiles">Feil under opprettelse av mappe for loggfiler: %1$s</string>
@ -678,7 +678,7 @@
<string name="pref_title_rtl">Høyre-til-venstre</string>
<string name="pref_summary_rtl">Skru på dette hvis din enhet ikke kan vise høyre til venstre-språk</string>
<string name="pref_rtl_max_line_length">Maksimal linjelengde for høyre-til-venstre</string>
<string name="pref_rtl_max_line_length_summary">Strekker eller korter ned linjene høyre-til-venstre tekst inndeles i</string>
<string name="pref_rtl_max_line_length_summary">Strekker eller korter ned linjene høyre-til-venstre -tekst inndeles i</string>
<string name="notif_battery_low">%1$s batteri snart tomt</string>
<string name="notif_battery_low_extended">%1$s batteri snart tomt: %2$s</string>
@ -697,4 +697,7 @@
<string name="no_data">Ingen data</string>
<string name="language_and_region_prefs">Språk og stedsinnstillinger</string>
</resources>
<string name="debugactivity_really_factoryreset_title">Vil du virkelig gjenopprette fabrikkinnstillinger\?</string>
<string name="debugactivity_really_factoryreset">Gjenoppretting til fabrikkinnstillinger vil slette all data fra den tilkoblede enheten (hvis støttet). Xiaomi/Huami-enheter endrer også MAC-adressen for Blåtann, og vil dermed framtre som nye enheter i Gadgetbridge.</string>
<string name="devicetype_casiogb6900">Casio GB-6900</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
@ -41,7 +41,7 @@
<string name="title_activity_appblacklist">Notificatie zwarte lijst</string>
<string name="fw_upgrade_notice">U staat op het punt om firmware %s te installeren in plaats van die nu op uw Mi Band staat.</string>
<string name="fw_upgrade_notice">U staat op het punt om firmware %s te installeren in plaats van de huidig geinstalleerde op uw Mi Band.</string>
<string name="fw_upgrade_notice_amazfitbip">U staat op het punt om de firmware %s te installeren op uw Amazfit Bip.
\n
\nZorg ervoor dat u het .fw-bestand installeert, daarna het .res-bestand en als laatste het .gps-bestand. Uw horloge wordt opnieuw gestart na het installeren van het .fw-bestand.
@ -54,12 +54,12 @@
<string name="miband_firmware_unknown_warning">Deze firmware is niet getest en is mogelijk niet compatibel met Gadgetbridge.
\n
\nU wordt AFGERADEN om het op uw Mi Band te installeren!</string>
<string name="miband_firmware_suggest_whitelist">Als u nog steeds wilt doorgaan en uw apparaat blijft goed functioneren, vertel dan alstublieft de ontwikkelaars van Gadgetbridge om de %s firmware-versie op de goedgekeurde lijst te zetten</string>
<string name="miband_firmware_suggest_whitelist">Als u nog steeds wilt doorgaan en uw apparaat blijft goed functioneren, vertel dan alstublieft de ontwikkelaars van Gadgetbridge om de %s firmware-versie op de goedgekeurde lijst te zetten.</string>
<string name="title_activity_settings">Instellingen</string>
<string name="pref_header_general">Algemene instellingen</string>
<string name="pref_title_general_autoconnectonbluetooth">Verbind met apparaat wanneer Bluetooth is ingeschakeld</string>
<string name="pref_title_general_autoconnectonbluetooth">Verbind met Gadgetbridge apparaat wanneer Bluetooth is ingeschakeld</string>
<string name="pref_title_general_autostartonboot">Start automatisch</string>
<string name="pref_title_general_autoreconnect">Verbind automatisch opnieuw</string>
<string name="pref_title_audio_player">Gewenste Audiospeler</string>
@ -67,7 +67,7 @@
<string name="pref_header_datetime">Datum en tijd</string>
<string name="pref_title_datetime_syctimeonconnect">Sync tijd</string>
<string name="pref_summary_datetime_syctimeonconnect">Synchroniseer de tijd naar het apparaat wanneer u verbinding maakt en wanneer de tijd of tijdzone op Android verandert</string>
<string name="pref_summary_datetime_syctimeonconnect">Synchroniseer de tijd naar het Gadgetbridge apparaat wanneer u verbinding maakt en wanneer de tijd of tijdzone op uw Android toestel verandert</string>
<string name="pref_title_theme">Thema</string>
<string name="pref_theme_light">Licht</string>
@ -84,11 +84,11 @@
<string name="pref_title_notifications_call">Telefoongesprekken</string>
<string name="pref_title_notifications_sms">SMS</string>
<string name="pref_title_notifications_pebblemsg">Pebble Berichten</string>
<string name="pref_summary_notifications_pebblemsg">Ondersteuning voor applicaties die meldingen versturen naar de Pebble via PebbleKit sturen.</string>
<string name="pref_summary_notifications_pebblemsg">Ondersteuning voor apps die meldingen versturen naar de Pebble via PebbleKit sturen.</string>
<string name="pref_title_notifications_generic">Algemene melding ondersteuning</string>
<string name="pref_title_whenscreenon">... ook als het scherm is ingeschakeld</string>
<string name="pref_title_notification_filter">Niet Storen</string>
<string name="pref_summary_notification_filter">Voorkom dat ongewenste meldingen worden verzonden in Niet Storen modus</string>
<string name="pref_summary_notification_filter">Ongewenste meldingen worden tegengehouden in deze modus</string>
<string name="pref_title_transliteration">Transliteratie</string>
<string name="pref_summary_transliteration">Schakel dit in als uw apparaat geen ondersteuning heeft voor het lettertype van uw taal</string>
@ -127,7 +127,7 @@
<string name="pref_summary_enable_outgoing_call">Als u dit uitschakelt, zal de Pebble 2/LE ook stoppen met trillen bij uitgaande gesprekken</string>
<string name="pref_title_enable_pebblekit">Toegang tot de Android App van 3de partij toestaan</string>
<string name="pref_summary_enable_pebblekit">De experimentele ondersteuning voor Android Apps inschakelen met PebbleKit</string>
<string name="pref_summary_enable_pebblekit">De experimentele ondersteuning inschakelen voor Android apps die van PebbleKit gebruik maken</string>
<string name="pref_header_pebble_timeline">Pebble tijdlijn</string>
<string name="pref_title_sunrise_sunset">zonsopkomst en zonsondergang</string>
@ -155,7 +155,7 @@
<string name="discovery_yes_pair">Koppel</string>
<string name="discovery_dont_pair">Niet Koppelen</string>
<string name="_pebble_watch_open_on_phone">Open op telefoon</string>
<string name="_pebble_watch_open_on_phone">Open op Android toestel</string>
<string name="_pebble_watch_mute">Dempen</string>
<string name="_pebble_watch_reply">Antwoord</string>
<string name="installhandler_firmware_name">%1$s: %2$s %3$s</string>
@ -172,7 +172,7 @@
\n
\nOpmerking: U hoeft de .res niet te installeren als deze exact dezelfde is als degene die ervoor al was geïnstalleerd.
\n
\nNOG NIET GETEST, KAN UW TOESTEL ONBRUIKBAAR MAKEN, GA VERDER OP EIGEN RISICO!</string>
\nGA VERDER OP EIGEN RISICO!</string>
<string name="pref_title_charts_swipe">Activeer links/rechts vegen in grafiek schermen</string>
<string name="pref_title_weather">Weer</string>
@ -184,14 +184,14 @@
<string name="pref_pebble_privacy_mode_content">Schuif de meldingstekst van het scherm</string>
<string name="pref_header_cannned_messages">Voorgedefinieerde berichten</string>
<string name="pref_title_pebble_forceprotocol">Verplicht notificatie protocol</string>
<string name="pref_summary_pebble_forceprotocol">Deze optie verplicht het gebruik van het laatste notificatie protocol, afhankelijk van de firmware versie. SCHAKEL DIT ALLEEN IN ALS JE WEET WAAR JE MEE BEZIG BENT!</string>
<string name="pref_summary_pebble_forceprotocol">Deze optie verplicht het gebruik van het laatste notificatie protocol, afhankelijk van de firmware versie. WEET WAAR JE MEE BEZIG BENT!</string>
<string name="pref_title_pebble_forceuntested">Schakel niet geteste features in</string>
<string name="pref_summary_pebble_forceuntested">Schakel features in die nog niet getest zijn. SCHAKEL DIT ALLEEN IN ALS JE WEET WAAR JE MEE BEZIG BENT!</string>
<string name="pref_summary_pebble_forceuntested">Schakel ongeteste features in. WEET WAAR JE MEE BEZIG BENT!</string>
<string name="pref_title_pebble_forcele">Geef altijd de voorkeur aan BLE</string>
<string name="pref_summary_pebble_forcele">Gebruik experimentele Pebble LE-ondersteuning voor alle Pebbels in plaats van BT classic. Dit vereist het eerst paren met een niet LE-versie en daarna met een Pebble LE</string>
<string name="pref_title_pebble_mtu_limit">Pebble 2/LE GATT MTU limiet</string>
<string name="pref_summary_pebble_mtu_limit">Als uw Pebble 2/Pebble LE niet werkt zoals verwacht, probeer deze instelling om de MTU te limiteren (geldig bereik: 20-512)</string>
<string name="pref_title_pebble_enable_applogs">logging van horlogeapp inschakelen</string>
<string name="pref_title_pebble_enable_applogs">Logging van de horlogeapp inschakelen</string>
<string name="pref_summary_pebble_enable_applogs">Zorgt ervoor dat logs van horloge-apps gelogd zullen worden door Gadgetbridge (opnieuw verbinden vereist)</string>
<string name="pref_title_pebble_always_ack_pebblekit">Vroegtijdige ACK PebbleKit</string>
<string name="pref_summary_pebble_always_ack_pebblekit">Zorgt ervoor dat berichten die door apps van derden worden verzonden, altijd en onmiddellijk herkend zullen worden</string>
@ -321,8 +321,8 @@
<string name="alarm_fri_short">Vrij</string>
<string name="alarm_sat_short">Zat</string>
<string name="alarm_smart_wakeup">Slim ontwaken</string>
<string name="user_feedback_miband_set_alarms_failed">Er is een fout opgetreden bij het instellen van de alarmen. Probeer het opnieuw!</string>
<string name="user_feedback_miband_set_alarms_ok">Alarmen verzonden naar het apparaat!</string>
<string name="user_feedback_miband_set_alarms_failed">Er is een fout opgetreden bij het instellen van de alarmen, probeer het opnieuw.</string>
<string name="user_feedback_miband_set_alarms_ok">Alarmen verzonden naar het apparaat.</string>
<string name="chart_no_data_synchronize">Geen data. Toestel synchroniseren?</string>
<string name="user_feedback_miband_activity_data_transfer">Klaar om %1$s data over te dragen van %2$s</string>
<string name="miband_prefs_fitness_goal">Dagelijkse stappen doel</string>
@ -369,7 +369,7 @@
<string name="pref_summary_dont_ack_transfers">Als de activiteitsdata niet geacked wordt naar de armband, zal deze niet gewist worden. Dit is nuttig als GB gebruikt wordt samen met andere apps.</string>
<string name="pref_summary_keep_data_on_device">Wij zullen activiteitsdata op de Mi Band houden zelfs na synchronisatie. Dit is nuttig als GB gebruikt wordt samen met andere apps.</string>
<string name="pref_title_low_latency_fw_update">Gebruik lage vertragingstijd modus voor Firmware flashen</string>
<string name="pref_summary_low_latency_fw_update">Dit kan helpen op toestellen waar firmware flashen faalt</string>
<string name="pref_summary_low_latency_fw_update">Dit kan helpen op toestellen waar firmware flashen faalt.</string>
<string name="live_activity_steps_history">Stappen geschiedenis</string>
<string name="live_activity_current_steps_per_minute">Huidige stappen/min</string>
@ -388,7 +388,7 @@
<string name="miband_prefs_reserve_alarm_calendar">Alarmen voor te behouden voor toekomstige evenementen</string>
<string name="miband_prefs_hr_sleep_detection">Gebruik hartslag sensor om slaap detectie te verbeteren</string>
<string name="miband_prefs_device_time_offset_hours">Toestel tijdsverschuiving in uren (voor het detecteren van slaap van ploegarbeiders)</string>
<string name="miband2_prefs_dateformat">Mi2: Datum formaat</string>
<string name="miband2_prefs_dateformat">Datum formaat</string>
<string name="dateformat_time">Tijd</string>
<string name="dateformat_date_time">Tijd &amp; datum</string>
<string name="mi2_prefs_button_actions">Knoppen acties</string>
@ -441,7 +441,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Widget toevoegen</string>
<string name="activity_prefs_sleep_duration">Gewenste slaapduur in uren</string>
<string name="appwidget_alarms_set">Een alarm is ingesteld voor %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Een alarm is ingesteld voor %1$02d:%2$02d</string>
<string name="device_hw">Hardware revisie: %1$s</string>
<string name="device_fw">Firmware versie: %1$s</string>
<string name="error_creating_directory_for_logfiles">Fout tijdens het aanmaken van map voor logbestanden: %1$s</string>
@ -452,11 +452,11 @@
<string name="live_activity_heart_rate">Hartslag</string>
<string name="pref_title_pebble_health_store_raw">Bewaar onbewerkte record in de database</string>
<string name="pref_summary_pebble_health_store_raw">Indien aangevinkt, wordt de data opgeslagen \"zoals ze is\" en zijn ze beschikbaar voor latere interpretatie. NB: De database zal groter zijn in dit geval!</string>
<string name="pref_summary_pebble_health_store_raw">Slaat de data op \"zoals ze is\", dit vergroot het gebruik van de database om latere interpretatie mogelijk te maken.</string>
<string name="action_db_management">Database beheer</string>
<string name="title_activity_db_management">Database beheer</string>
<string name="activity_db_management_import_export_explanation">De database operaties gebruiken het volgende pad op uw toestel.
\nDit pad is toegankelijk voor andere Android applicaties en uw computer.
<string name="activity_db_management_import_export_explanation">De database operaties gebruiken het volgende pad op uw toestel.
\nDit pad is toegankelijk voor andere Android apps en uw computer.
\nVerwacht uw geëxporteerde database daar (of plaats de database die u wil importeren) terug te vinden:</string>
<string name="activity_db_management_merge_old_title">Legacy-database verwijderen</string>
<string name="dbmanagementactivvity_cannot_access_export_path">Export pad is niet toegankelijk. Neem contact op met de ontwikkelaars.</string>
@ -483,7 +483,7 @@
<string name="title_activity_vibration">Vibratie</string>
<string name="title_activity_pebble_pairing">Pebble koppelen</string>
<string name="pebble_pairing_hint">Er zal een koppelingsdialoogvenster verschijnen op uw Android-apparaat. Als dat niet gebeurt, kijk dan tussen uw notificaties en accepteer het koppelingsverzoek. Accepteer het daarna ook op uw Pebble</string>
<string name="pebble_pairing_hint">Er zal een koppelingsdialoogvenster verschijnen op uw Android-apparaat. Als dat niet gebeurt, kijk dan tussen uw notificaties en accepteer het koppelingsverzoek. Accepteer het daarna ook op uw Pebble.</string>
<string name="weather_notification_label">Zorg ervoor dat deze skin is ingeschakeld in de Weermeldingen app om informatie over het weer op uw Pebble te krijgen.
\n
@ -631,4 +631,61 @@
<string name="devicetype_mykronoz_zetime">MyKronoz ZeTime</string>
<string name="menuitem_notifications">Notificaties</string>
</resources>
<string name="controlcenter_change_led_color">Wijzig LED kleur</string>
<string name="controlcenter_change_fm_frequency">Wijzig FM frequentie</string>
<string name="debugactivity_really_factoryreset_title">Zeker dat u de fabrieksinstellingen wil terugzetten\?</string>
<string name="debugactivity_really_factoryreset">Het terugzetten van de fabrieksinstellingen zal alle data van het verbonden toestel verwijderen (indien ondersteud). Xiaomi/Huami toestellen veranderen ook van Bluetooth MAC address, zodat deze zullen verschijnen als nieuwe toestellen in Gadgetbridge.</string>
<string name="pref_title_notifications_timeout">Minimum tijd tussen meldingen</string>
<string name="pref_title_rtl">Van rechts naar links</string>
<string name="pref_summary_rtl">Schakel dit in als uw toestel geen talen van rechts naar links kan weergeven</string>
<string name="pref_rtl_max_line_length">Rechts naar links maximum lijn lengte</string>
<string name="pref_rtl_max_line_length_summary">Verlengt of verkort de lijnen waarin tekst van rechts naar links verdeeld wordt</string>
<string name="notif_battery_low">%1$s batterijniveau laag</string>
<string name="notif_battery_low_extended">%1$s batterijniveau laag: %2$s</string>
<string name="lack_of_sleep">Tekort aan slaap: %1$s</string>
<string name="overslept">Overslapen: %1$s</string>
<string name="no_limit">Geen limiet</string>
<string name="seconds_5">5 seconden</string>
<string name="seconds_10">10 seconden</string>
<string name="seconds_20">20 seconden</string>
<string name="seconds_30">30 seconden</string>
<string name="minutes_1">1 minuut</string>
<string name="minutes_5">5 minuten</string>
<string name="minutes_10">10 minuten</string>
<string name="minutes_30">30 minuten</string>
<string name="lack_of_step">Tekort aan stappen: %1$d</string>
<string name="overstep">Boven aantal stappen: %1$d</string>
<string name="live_activity_max_heart_rate">Huidige / Max hartslag: %1$d / %2$d</string>
<string name="mi3_prefs_night_mode">Nacht modus</string>
<string name="mi3_prefs_night_mode_summary">Verlaag automatisch de scherm helderheid \'s nachts</string>
<string name="activity_prefs_charts">Grafiek instellingen</string>
<string name="activity_prefs_chart_max_heart_rate">Max harstslag</string>
<string name="activity_prefs_chart_min_heart_rate">Min hartslag</string>
<string name="ok">OK</string>
<string name="mi3_night_mode_sunset">Bij zonsondergang</string>
<string name="devicetype_id115">ID115</string>
<string name="devicetype_watch9">Horloge 9</string>
<string name="devicetype_roidmi">Roidmi</string>
<string name="devicetype_roidmi3">Roidmi 3</string>
<string name="menuitem_music">Muziek</string>
<string name="menuitem_more">Meer</string>
<string name="watch9_time_minutes">Minuten:</string>
<string name="watch9_time_hours">Uren:</string>
<string name="watch9_time_seconds">Seconden:</string>
<string name="watch9_calibration_hint">Stel de tijd in die uw toestel op dit moment weergeeft.</string>
<string name="watch9_calibration_button">Kalibreer</string>
<string name="title_activity_watch9_pairing">Horloge 9 koppelen</string>
<string name="title_activity_watch9_calibration">Horloge 9 kalibratie</string>
<string name="pref_title_contextual_arabic">Contextueel Arabisch</string>
<string name="pref_summary_contextual_arabic">Schakel dit in om contextueel Arabisch te ondersteunen</string>
<string name="preferences_rtl_settings">Rechts naar links ondersteuning</string>
<string name="share_log">Deel log</string>
<string name="share_log_warning">Hou er alsjeblieft rekening mee dat Gadgetbridge bestanden logt die veel persoonlijke informatie kunnen bevatten, inclusief maar niet gelimiteerd tot gezondheidsgegevens, unieke identificatiegegevens (zoals het MAC adres van een toestel), Muziek voorkeuren, enz. Overweeg het manueel verwijderen van deze gegevens uit dit bestand vooraleer het te verzenden naar een publiek foutrapport.</string>
<string name="warning">Waarschuwing!</string>
<string name="no_data">Geen data</string>
<string name="preferences_led_color">LED Kleur</string>
<string name="preferences_fm_frequency">FM frequentie</string>
<string name="pref_invalid_frequency_title">Ongeldige frequentie</string>
<string name="pref_invalid_frequency_message">Voer een frequentie in tussen 87,5 en 108,0</string>
<string name="language_and_region_prefs">Taal en regio instellingen</string>
</resources>

View File

@ -299,7 +299,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Adicionar widget</string>
<string name="activity_prefs_sleep_duration">Preferir duração de sono em horas</string>
<string name="appwidget_alarms_set">Um alarme foi definido para %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Um alarme foi definido para %1$02d:%2$02d</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Erro ao criar o diretório para arquivos de log: %1$s</string>

View File

@ -326,7 +326,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Adicionar widget</string>
<string name="activity_prefs_sleep_duration">Preferir duração de sono em horas</string>
<string name="appwidget_alarms_set">Alarme definido para as %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Alarme definido para as %1$02d:%2$02d</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">Versão FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Erro ao criar o diretório para os ficheiros de registo: %1$s</string>

View File

@ -318,7 +318,7 @@
<string name="appwidget_text">Хрр</string>
<string name="add_widget">Добавить виджет</string>
<string name="activity_prefs_sleep_duration">Желаемая продолжительность сна</string>
<string name="appwidget_alarms_set">Будильник был установлен на %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Будильник был установлен на %1$02d:%2$02d</string>
<string name="device_hw">Версия устройства: %1$s</string>
<string name="device_fw">Версия прошивки: %1$s</string>
<string name="error_creating_directory_for_logfiles">Ошибка создания каталога для лог-файлов: %1$s</string>

View File

@ -436,7 +436,7 @@
<string name="appwidget_text">Spí...</string>
<string name="add_widget">Pridať miniaplikáciu</string>
<string name="activity_prefs_sleep_duration">Preferovaná doba spánku v hodinách</string>
<string name="appwidget_alarms_set">Budík nastavený na %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Budík nastavený na %1$02d:%2$02d</string>
<string name="device_hw">Revízia HW: %1$s</string>
<string name="device_fw">Verzia FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Chyba pri vytváraní adresára pre logy: %1$s</string>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources><string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
@ -411,7 +411,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">添加小部件</string>
<string name="activity_prefs_sleep_duration">睡眠时间目标</string>
<string name="appwidget_alarms_set">已设置一个闹钟为 %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">已设置一个闹钟为 %1$02d:%2$02d</string>
<string name="device_hw">硬件版本:%1$s</string>
<string name="device_fw">固件版本:%1$s</string>
<string name="error_creating_directory_for_logfiles">为日志文件创建目录时出错:%1$s</string>
@ -691,4 +691,6 @@
<string name="pref_invalid_frequency_title">不正确的频率</string>
<string name="pref_invalid_frequency_message">请输入一个在 87.5 至 108.0 之间的频率</string>
<string name="language_and_region_prefs">语言和区域设置</string>
</resources>
<string name="debugactivity_really_factoryreset_title">确定要恢复出厂设置?</string>
<string name="debugactivity_really_factoryreset">恢复出厂设置将会删除您已连接设备上的所有数据如果支持的话。小米华米设备将会同时改变蓝牙MAC地址对于Gadgetbrige来说这将是一个新的设备。</string>
</resources>

View File

@ -1,4 +1,4 @@
<?xml version='1.0' encoding='UTF-8'?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Gadgetbridge</string>
@ -26,7 +26,10 @@
<string name="controlcenter_calibrate_device">Calibrate Device</string>
<!-- Strings related to Debug Activity -->
<string name="title_activity_debug">Debug</string>
<string name="debugactivity_really_factoryreset_title">Really factory reset?</string>
<string name="debugactivity_really_factoryreset">Doing a factory reset will delete all data from the connected device (if supported). Xiaomi/Huami devices also change Bluetooth MAC address, so they appear as a new devices to Gadgetbrige.</string>
<!-- Strings related to AppManager -->
<string name="title_activity_appmanager">App Manager</string>
@ -109,7 +112,7 @@
<string name="pref_title_rtl">Right-To-Left</string>
<string name="pref_summary_rtl">Enable this if your device can not show right-to-left languages</string>
<string name="pref_rtl_max_line_length">Right-To-Left Max Line Length</string>
<string name="pref_rtl_max_line_length_summary">Lengthens or shortens the lines Right-To-Left text is seperated into</string>
<string name="pref_rtl_max_line_length_summary">Lengthens or shortens the lines Right-To-Left text is separated into</string>
<string name="always">Always</string>
<string name="when_screen_off">When screen is off</string>
@ -491,7 +494,7 @@
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Add widget</string>
<string name="activity_prefs_sleep_duration">Preferred sleep duration in hours</string>
<string name="appwidget_alarms_set">An alarm was set for %1$02d:%2$02d</string>
<string name="appwidget_setting_alarm">Setting alarm for %1$02d:%2$02d</string>
<string name="device_hw">Hardware revision: %1$s</string>
<string name="device_fw">Firmware version: %1$s</string>
<string name="error_creating_directory_for_logfiles">Error creating directory for log files: %1$s</string>
@ -625,6 +628,7 @@
<string name="devicetype_watch9">Watch 9</string>
<string name="devicetype_roidmi">Roidmi</string>
<string name="devicetype_roidmi3">Roidmi 3</string>
<string name="devicetype_casiogb6900">Casio GB-6900</string>
<string name="choose_auto_export_location">Choose export location</string>
<string name="notification_channel_name">Gadgetbridge notifications</string>
@ -683,4 +687,5 @@
<string name="filter_mode">Filter Mode</string>
<string name="mode_configuration">Mode Configuration</string>
<string name="save_configuration">Save Configuration</string>
</resources>
<string name="appwidget_not_connected">Not connected, alarm not set.</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More