1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-02 11:26:09 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java
José Rebelo 81aef0bf35 Add support for multiple weather locations
Introduce the concept of primary and secondary weathers:

* Primary weather keeps the same behavior as previously across all weather providers, so it's non-breaking. This location is not necessarily the current location, just the primary weather location set by the user.
* The GenericWeatherReceiver now has a new extra WeatherSecondaryJson, that receives a json list with secondary weather locations.

It's guaranteed that the primary weather always exists, so the list of WeatherSpecs provided to devices is never empty. Update all support classes accordingly.
2024-03-29 21:10:40 +00:00

315 lines
10 KiB
Java

/* Copyright (C) 2015-2024 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo,
Julien Pivotto, Steffen Liebergeld
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 <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.serial;
import android.location.Location;
import java.util.ArrayList;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
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.Reminder;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WorldClock;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
/**
* An abstract base class for devices speaking a serial protocol, like via
* an rfcomm bluetooth socket or a TCP socket.
* <p/>
* This class uses two helper classes to deal with that:
* - GBDeviceIoThread, which creates and maintains the actual socket connection and implements the transport layer
* - GBDeviceProtocol, which implements the encoding and decoding of messages, i.e. the actual device specific protocol
* <p/>
* Note that these two classes need to be implemented in a device specific way.
* <p/>
* This implementation implements all methods of {@link EventHandler}, calls the {@link GBDeviceProtocol device protocol}
* to create the device specific message for the respective events and sends them to the device via {@link #sendToDevice(byte[])}.
*/
public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport {
private GBDeviceProtocol gbDeviceProtocol;
protected GBDeviceIoThread gbDeviceIOThread;
/**
* Factory method to create the device specific GBDeviceProtocol instance to be used.
*/
protected abstract GBDeviceProtocol createDeviceProtocol();
/**
* Factory method to create the device specific GBDeviceIoThread instance to be used.
*/
protected abstract GBDeviceIoThread createDeviceIOThread();
@Override
public void dispose() {
// currently only one thread allowed
if (gbDeviceIOThread != null) {
gbDeviceIOThread.quit();
gbDeviceIOThread.interrupt();
gbDeviceIOThread = null;
}
}
/**
* Lazily creates and returns the GBDeviceProtocol instance to be used.
*/
protected synchronized GBDeviceProtocol getDeviceProtocol() {
if (gbDeviceProtocol == null) {
gbDeviceProtocol = createDeviceProtocol();
}
return gbDeviceProtocol;
}
/**
* Lazily creates and returns the GBDeviceIoThread instance to be used.
*/
public synchronized GBDeviceIoThread getDeviceIOThread() {
if (gbDeviceIOThread == null) {
gbDeviceIOThread = createDeviceIOThread();
}
return gbDeviceIOThread;
}
/**
* Sends the given message to the device. This implementation delegates the
* writing to the {@link #getDeviceIOThread device io thread}
*
* @param bytes the message to send to the device
*/
private void sendToDevice(byte[] bytes) {
if (bytes != null && gbDeviceIOThread != null) {
gbDeviceIOThread.write(bytes);
}
}
private void handleGBDeviceEvent(GBDeviceEventSendBytes sendBytes) {
sendToDevice(sendBytes.encodedBytes);
}
@Override
public void evaluateGBDeviceEvent(GBDeviceEvent deviceEvent) {
if (deviceEvent instanceof GBDeviceEventSendBytes) {
handleGBDeviceEvent((GBDeviceEventSendBytes) deviceEvent);
return;
}
super.evaluateGBDeviceEvent(deviceEvent);
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
byte[] bytes = gbDeviceProtocol.encodeNotification(notificationSpec);
sendToDevice(bytes);
}
@Override
public void onDeleteNotification(int id) {
byte[] bytes = gbDeviceProtocol.encodeDeleteNotification(id);
sendToDevice(bytes);
}
@Override
public void onSetTime() {
byte[] bytes = gbDeviceProtocol.encodeSetTime();
sendToDevice(bytes);
}
@Override
public void onSetCallState(CallSpec callSpec) {
byte[] bytes = gbDeviceProtocol.encodeSetCallState(callSpec.number, callSpec.name, callSpec.command);
sendToDevice(bytes);
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
byte[] bytes = gbDeviceProtocol.encodeSetCannedMessages(cannedMessagesSpec);
sendToDevice(bytes);
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
byte[] bytes = gbDeviceProtocol.encodeSetMusicState(stateSpec.state, stateSpec.position, stateSpec.playRate, stateSpec.shuffle, stateSpec.repeat);
sendToDevice(bytes);
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(musicSpec.artist, musicSpec.album, musicSpec.track, musicSpec.duration, musicSpec.trackCount, musicSpec.trackNr);
sendToDevice(bytes);
}
@Override
public void onSetPhoneVolume(final float volume) {
byte[] bytes = gbDeviceProtocol.encodeVolume(volume);
sendToDevice(bytes);
}
@Override
public void onAppInfoReq() {
byte[] bytes = gbDeviceProtocol.encodeAppInfoReq();
sendToDevice(bytes);
}
@Override
public void onAppStart(UUID uuid, boolean start) {
byte[] bytes = gbDeviceProtocol.encodeAppStart(uuid, start);
sendToDevice(bytes);
}
@Override
public void onAppDelete(UUID uuid) {
byte[] bytes = gbDeviceProtocol.encodeAppDelete(uuid);
sendToDevice(bytes);
}
@Override
public void onAppReorder(UUID[] uuids) {
byte[] bytes = gbDeviceProtocol.encodeAppReorder(uuids);
sendToDevice(bytes);
}
@Override
public void onFetchRecordedData(int dataTypes) {
byte[] bytes = gbDeviceProtocol.encodeSynchronizeActivityData();
sendToDevice(bytes);
}
@Override
public void onReset(int flags) {
byte[] bytes = gbDeviceProtocol.encodeReset(flags);
sendToDevice(bytes);
}
@Override
public void onFindDevice(boolean start) {
byte[] bytes = gbDeviceProtocol.encodeFindDevice(start);
sendToDevice(bytes);
}
@Override
public void onFindPhone(boolean start) {
byte[] bytes = gbDeviceProtocol.encodeFindPhone(start);
sendToDevice(bytes);
}
@Override
public void onScreenshotReq() {
byte[] bytes = gbDeviceProtocol.encodeScreenshotReq();
sendToDevice(bytes);
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeSteps(enable);
sendToDevice(bytes);
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable);
sendToDevice(bytes);
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeHeartRateMeasurement(enable);
sendToDevice(bytes);
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
byte[] bytes = gbDeviceProtocol.encodeAddCalendarEvent(calendarEventSpec);
sendToDevice(bytes);
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {
byte[] bytes = gbDeviceProtocol.encodeDeleteCalendarEvent(type, id);
sendToDevice(bytes);
}
@Override
public void onSendConfiguration(String config) {
byte[] bytes = gbDeviceProtocol.encodeSendConfiguration(config);
sendToDevice(bytes);
}
@Override
public void onTestNewFunction() {
byte[] bytes = gbDeviceProtocol.encodeTestNewFunction();
sendToDevice(bytes);
}
@Override
public void onSendWeather(ArrayList<WeatherSpec> weatherSpecs) {
WeatherSpec weatherSpec = weatherSpecs.get(0);
byte[] bytes = gbDeviceProtocol.encodeSendWeather(weatherSpec);
sendToDevice(bytes);
}
@Override
public void onSetFmFrequency(float frequency) {
byte[] bytes = gbDeviceProtocol.encodeFmFrequency(frequency);
sendToDevice(bytes);
}
@Override
public void onSetLedColor(int color) {
byte[] bytes = gbDeviceProtocol.encodeLedColor(color);
sendToDevice(bytes);
}
@Override
public void onPowerOff() {
byte[] bytes = gbDeviceProtocol.encodePowerOff();
sendToDevice(bytes);
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
byte[] bytes = gbDeviceProtocol.encodeSetAlarms(alarms);
sendToDevice(bytes);
}
@Override
public void onSetReminders(ArrayList<? extends Reminder> reminders) {
byte[] bytes = gbDeviceProtocol.encodeReminders(reminders);
sendToDevice(bytes);
}
@Override
public void onSetWorldClocks(ArrayList<? extends WorldClock> clocks) {
byte[] bytes = gbDeviceProtocol.encodeWorldClocks(clocks);
sendToDevice(bytes);
}
@Override
public void onSetGpsLocation(Location location) {
byte[] bytes = gbDeviceProtocol.encodeGpsLocation(location);
sendToDevice(bytes);
}
}