Initial HERE Active Listening Device Support

master
Nicolò Balzarotti 2017-10-08 00:14:43 +02:00
parent 4b4e95bfe0
commit ac07ddc932
34 changed files with 1109 additions and 155 deletions

View File

@ -388,7 +388,10 @@
android:name=".activities.VibrationActivity"
android:label="@string/title_activity_vibration"
android:parentActivityName=".activities.ControlCenterv2" />
<activity
android:name=".activities.AudioActivity"
android:label="@string/title_audio_activity"
android:parentActivityName=".activities.ControlCenterv2" />
<provider
android:name=".contentprovider.PebbleContentProvider"
android:authorities="com.getpebble.android.provider"

View File

@ -0,0 +1,67 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
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.activities;
import android.os.Bundle;
import android.widget.SeekBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.devices.here.HereConstants;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
public class AudioActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(AudioActivity.class);
private SeekBar seekBar;
private int volume;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.audio_settings);
LOG.info("Create");
seekBar = (SeekBar) findViewById(R.id.volume_seekbar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int position, boolean fromUser) {
// HERE's volume range is from 0xe3 (-22dB on their app) to 0xff (+6dB)
// 0xff - 0xe3 = 28 -> seekbar max value
// volume = seekbar + Min_volume (= 0xe3 = 227)
volume = position + 227;
LOG.debug("Volume = " + (byte)volume + " = " + volume + "= " + position);
GBApplication.deviceService().onSetAudioProperty(0, volume);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}

View File

@ -5,17 +5,17 @@
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.
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.
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.adapter;
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.adapter;
import android.app.Activity;
import android.app.AlertDialog;
@ -42,10 +42,12 @@ import android.widget.Toast;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.AudioActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -84,27 +86,27 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
holder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (device.isInitialized() || device.isConnected()) {
showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress);
} else {
showTransientSnackbar(R.string.controlcenter_snackbar_connecting);
GBApplication.deviceService().connect(device);
}
}
});
@Override
public void onClick(View v) {
if (device.isInitialized() || device.isConnected()) {
showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress);
} else {
showTransientSnackbar(R.string.controlcenter_snackbar_connecting);
GBApplication.deviceService().connect(device);
}
}
});
holder.container.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (device.getState() != GBDevice.State.NOT_CONNECTED) {
showTransientSnackbar(R.string.controlcenter_snackbar_disconnecting);
GBApplication.deviceService().disconnect();
}
return true;
}
});
@Override
public boolean onLongClick(View v) {
if (device.getState() != GBDevice.State.NOT_CONNECTED) {
showTransientSnackbar(R.string.controlcenter_snackbar_disconnecting);
GBApplication.deviceService().disconnect();
}
return true;
}
});
holder.deviceImageView.setImageResource(R.drawable.level_list_device);
//level-list does not allow negative values, hence we always add 100 to the key.
holder.deviceImageView.setImageLevel(device.getType().getKey() + 100 + (device.isInitialized() ? 100 : 0));
@ -128,7 +130,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
holder.batteryStatusLabel.setText(device.getBatteryLevel() + "%");
BatteryState batteryState = device.getBatteryState();
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
holder.batteryIcon.setImageLevel(device.getBatteryLevel() + 100);
} else {
holder.batteryIcon.setImageLevel(device.getBatteryLevel());
@ -139,76 +141,92 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
holder.fetchActivityDataBox.setVisibility((device.isInitialized() && coordinator.supportsActivityDataFetching()) ? View.VISIBLE : View.GONE);
holder.fetchActivityData.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
GBApplication.deviceService().onFetchActivityData();
}
}
);
{
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
GBApplication.deviceService().onFetchActivityData();
}
}
);
//take screenshot
holder.takeScreenshotView.setVisibility((device.isInitialized() && coordinator.supportsScreenshots()) ? View.VISIBLE : View.GONE);
holder.takeScreenshotView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.controlcenter_snackbar_requested_screenshot);
GBApplication.deviceService().onScreenshotReq();
}
}
);
{
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.controlcenter_snackbar_requested_screenshot);
GBApplication.deviceService().onScreenshotReq();
}
}
);
//manage apps
holder.manageAppsView.setVisibility((device.isInitialized() && coordinator.supportsAppsManagement()) ? View.VISIBLE : View.GONE);
holder.manageAppsView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
Class<? extends Activity> appsManagementActivity = coordinator.getAppsManagementActivity();
if (appsManagementActivity != null) {
Intent startIntent = new Intent(context, appsManagementActivity);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
}
);
{
@Override
public void onClick(View v) {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
Class<? extends Activity> appsManagementActivity = coordinator.getAppsManagementActivity();
if (appsManagementActivity != null) {
Intent startIntent = new Intent(context, appsManagementActivity);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
}
);
//set alarms
holder.setAlarmsView.setVisibility(coordinator.supportsAlarmConfiguration() ? View.VISIBLE : View.GONE);
holder.setAlarmsView.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ConfigureAlarms.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ConfigureAlarms.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
//show graphs
holder.showActivityGraphs.setVisibility(coordinator.supportsActivityTracking() ? View.VISIBLE : View.GONE);
holder.showActivityGraphs.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ChartsActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
{
@Override
public void onClick(View v) {
Intent startIntent;
startIntent = new Intent(context, ChartsActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
// audio settings
holder.showAudioSettings.setVisibility(device.isInitialized() && coordinator.supportsAudioSettings() ? View.VISIBLE : View.GONE);
holder.showAudioSettings.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
showTransientSnackbar(R.string.controlcenter_snackbar_requested_screenshot);
Intent startIntent;
startIntent = new Intent(context, AudioActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
}
);
ItemWithDetailsAdapter infoAdapter = new ItemWithDetailsAdapter(context, device.getDeviceInfos());
infoAdapter.setHorizontalAlignment(true);
@ -222,95 +240,96 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
holder.deviceInfoBox.setActivated(detailsShown);
holder.deviceInfoBox.setVisibility(detailsShown ? View.VISIBLE : View.GONE);
holder.deviceInfoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
expandedDevicePosition = detailsShown ? -1 : position;
TransitionManager.beginDelayedTransition(parent);
notifyDataSetChanged();
}
}
@Override
public void onClick(View v) {
expandedDevicePosition = detailsShown ? -1 : position;
TransitionManager.beginDelayedTransition(parent);
notifyDataSetChanged();
}
}
);
);
holder.findDevice.setVisibility(device.isInitialized() ? View.VISIBLE : View.GONE);
holder.findDevice.setVisibility((device.isInitialized() &&
device.getType() != DeviceType.HERE) ? View.VISIBLE : View.GONE);
holder.findDevice.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
if (device.getType() == DeviceType.VIBRATISSIMO) {
Intent startIntent;
startIntent = new Intent(context, VibrationActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
return;
}
GBApplication.deviceService().onFindDevice(true);
//TODO: extract string resource if we like this solution.
Snackbar.make(parent, R.string.control_center_find_lost_device, Snackbar.LENGTH_INDEFINITE).setAction("Found it!", new View.OnClickListener() {
@Override
public void onClick(View v) {
GBApplication.deviceService().onFindDevice(false);
}
}).setCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
GBApplication.deviceService().onFindDevice(false);
super.onDismissed(snackbar, event);
}
}).show();
// ProgressDialog.show(
// context,
// context.getString(R.string.control_center_find_lost_device),
// context.getString(R.string.control_center_cancel_to_stop_vibration),
// true, true,
// new DialogInterface.OnCancelListener() {
// @Override
// public void onCancel(DialogInterface dialog) {
// GBApplication.deviceService().onFindDevice(false);
// }
// });
}
}
{
@Override
public void onClick(View v) {
if (device.getType() == DeviceType.VIBRATISSIMO) {
Intent startIntent;
startIntent = new Intent(context, VibrationActivity.class);
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
return;
}
GBApplication.deviceService().onFindDevice(true);
//TODO: extract string resource if we like this solution.
Snackbar.make(parent, R.string.control_center_find_lost_device, Snackbar.LENGTH_INDEFINITE).setAction("Found it!", new View.OnClickListener() {
@Override
public void onClick(View v) {
GBApplication.deviceService().onFindDevice(false);
}
}).setCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
GBApplication.deviceService().onFindDevice(false);
super.onDismissed(snackbar, event);
}
}).show();
// ProgressDialog.show(
// context,
// context.getString(R.string.control_center_find_lost_device),
// context.getString(R.string.control_center_cancel_to_stop_vibration),
// true, true,
// new DialogInterface.OnCancelListener() {
// @Override
// public void onCancel(DialogInterface dialog) {
// GBApplication.deviceService().onFindDevice(false);
// }
// });
}
}
);
);
//remove device, hidden under details
holder.removeDevice.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v) {
new AlertDialog.Builder(context)
{
@Override
public void onClick(View v) {
new AlertDialog.Builder(context)
.setCancelable(true)
.setTitle(context.getString(R.string.controlcenter_delete_device_name, device.getName()))
.setMessage(R.string.controlcenter_delete_device_dialogmessage)
.setPositiveButton(R.string.Delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
if (coordinator != null) {
coordinator.deleteDevice(device);
}
DeviceHelper.getInstance().removeBond(device);
} catch (Exception ex) {
GB.toast(context, "Error deleting device: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
} finally {
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent);
}
}
})
@Override
public void onClick(DialogInterface dialog, int which) {
try {
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
if (coordinator != null) {
coordinator.deleteDevice(device);
}
DeviceHelper.getInstance().removeBond(device);
} catch (Exception ex) {
GB.toast(context, "Error deleting device: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
} finally {
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent);
}
}
})
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// do nothing
}
})
@Override
public void onClick(DialogInterface dialog, int which) {
// do nothing
}
})
.show();
}
});
}
});
}
@ -338,6 +357,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
ImageView manageAppsView;
ImageView setAlarmsView;
ImageView showActivityGraphs;
ImageView showAudioSettings;
ImageView deviceInfoView;
//overflow
@ -365,6 +385,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
manageAppsView = (ImageView) view.findViewById(R.id.device_action_manage_apps);
setAlarmsView = (ImageView) view.findViewById(R.id.device_action_set_alarms);
showActivityGraphs = (ImageView) view.findViewById(R.id.device_action_show_activity_graphs);
showAudioSettings = (ImageView) view.findViewById(R.id.device_action_show_audio_settings);
deviceInfoView = (ImageView) view.findViewById(R.id.device_info_image);
deviceInfoBox = (RelativeLayout) view.findViewById(R.id.device_item_infos_box);
@ -429,7 +450,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
View snackbarView = snackbar.getView();
// change snackbar text color
// change snackbar text color
int snackbarTextId = android.support.design.R.id.snackbar_text;
TextView textView = (TextView) snackbarView.findViewById(snackbarTextId);
//textView.setTextColor();

View File

@ -148,6 +148,14 @@ public interface DeviceCoordinator {
*/
boolean supportsActivityTracking();
/**
* Returns true if audio settings is supported by the device
* (with this coordinator). Only HERE is supported right now
*
* @return
*/
boolean supportsAudioSettings();
/**
* Returns true if activity data fetching is supported AND possible at this
* very moment. This will consider the device state (being connected/disconnected/busy...)

View File

@ -79,6 +79,8 @@ public interface EventHandler {
void onSetConstantVibration(int integer);
void onSetAudioProperty(int property, int volume);
void onScreenshotReq();
void onEnableHeartRateSleepSupport(boolean enable);

View File

@ -137,6 +137,11 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public boolean supportsScreenshots() {
return false;

View File

@ -0,0 +1,25 @@
package nodomain.freeyourgadget.gadgetbridge.devices.here;
/*
* @author Nicolò Balzarotti &lt;nicolo@nixo.xyz&gt;
*/
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
public final class HereConstants {
public static final String BASE_HERE_UUID = "d973f2%s-b19e-11e2-9e96-0800200c9a66";
public static final UUID UUID_CHARACTERISTIC_BATTERY = UUID.fromString(String.format(BASE_UUID, "2A19"));
public static final UUID UUID_BATTERY_VALUE = UUID.fromString(String.format(BASE_UUID, "180F"));
public static final UUID UUID_AUDIO_SETTINGS = UUID.fromString(String.format(BASE_HERE_UUID, "e0"));
public static final UUID UUID_CHARACTERISTIC_INFO = UUID.fromString(String.format(BASE_UUID, "180A"));
public static final UUID UUID_FW_VERSION = UUID.fromString(String.format(BASE_UUID, "2A26"));
public static final UUID UUID_VOLUME = UUID.fromString(String.format(BASE_HERE_UUID, "e7"));
// TODO: Put device in sleep mode, (byte)0x00 sleep, (byte)0x01 wake
public static final UUID UUID_SLEEP = UUID.fromString(String.format(BASE_HERE_UUID, "eb"));
}

View File

@ -0,0 +1,152 @@
/* Copyright (C) 2016-2017 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.devices.here;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AudioActivity;
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;
import java.util.regex.Pattern; // Regex match name
import java.util.regex.Matcher; // Regex match name
public class HereCoordinator extends AbstractDeviceCoordinator {
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
String name = candidate.getDevice().getName();
Pattern pat = Pattern.compile("H([LR])[A-F0-9]+|HERE-([LR])-[A-F0-9]+");
Matcher mat = pat.matcher(name);
// Identify if device is Left or Right
if (mat.matches()) {
// get device type (L or R)
// disabled until we find differences we need to handle
// if (mat.group(1).equals("L")) {
// return DeviceType.HEREL;
// } else if (mat.group(1).equals("R")) {
// return DeviceType.HERER;
// } else {
// Could not detect if L or R
return DeviceType.HERE;
// }
}
return DeviceType.UNKNOWN;
}
@Override
public DeviceType getDeviceType() {
return DeviceType.HERE;
}
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public Class<? extends Activity> getPrimaryActivity() {
return AudioActivity.class;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return null;
}
@Override
public boolean supportsActivityDataFetching() {
return false;
}
@Override
public boolean supportsActivityTracking() {
return false;
}
@Override
public boolean supportsAudioSettings() {
return true;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;
}
@Override
public boolean supportsScreenshots() {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
}
@Override
public String getManufacturer() {
return "Doppler Labs";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
public boolean supportsCalendarEvents() {
return false;
}
@Override
public boolean supportsRealtimeData() {
return false; // hmmm well, it has a temperature sensor :D
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
// nothing to delete, yet
}
}

View File

@ -128,6 +128,11 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return new HPlusHealthSampleProvider(device, session);

View File

@ -114,6 +114,11 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;

View File

@ -74,6 +74,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;

View File

@ -151,6 +151,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public String getManufacturer() {
return "Xiaomi";

View File

@ -98,6 +98,11 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return new No1F1SampleProvider(device, session);

View File

@ -117,6 +117,11 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
return true;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public boolean supportsScreenshots() {
return true;

View File

@ -75,6 +75,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator {
return false;
}
@Override
public boolean supportsAudioSettings() {
return false;
}
@Override
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
return null;

View File

@ -24,6 +24,9 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.annotation.Nullable;
// FIXME: REMOVE
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.UUID;
@ -46,6 +49,9 @@ import static nodomain.freeyourgadget.gadgetbridge.util.JavaExtensions.coalesce;
public class GBDeviceService implements DeviceService {
protected final Context mContext;
// FIXME: REMOVE
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
private final Class<? extends Service> mServiceClass;
private final String[] transliterationExtras = new String[]{
EXTRA_NOTIFICATION_PHONENUMBER,
@ -298,6 +304,14 @@ public class GBDeviceService implements DeviceService {
invokeService(intent);
}
@Override
public void onSetAudioProperty(int property, int intensity) {
// volume = 0
Intent intent = createIntent().setAction(ACTION_SET_AUDIO_PROPERTY)
.putExtra(EXTRA_AUDIO_PROPERTY, intensity);
invokeService(intent);
}
@Override
public void onScreenshotReq() {
Intent intent = createIntent().setAction(ACTION_REQUEST_SCREENSHOT);

View File

@ -53,6 +53,7 @@ public interface DeviceService extends EventHandler {
String ACTION_DISCONNECT = PREFIX + ".action.disconnect";
String ACTION_FIND_DEVICE = PREFIX + ".action.find_device";
String ACTION_SET_CONSTANT_VIBRATION = PREFIX + ".action.set_constant_vibration";
String ACTION_SET_AUDIO_PROPERTY = PREFIX + ".action.set_audio_property";
String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms";
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
String ACTION_REALTIME_SAMPLES = PREFIX + ".action.realtime_samples";
@ -76,6 +77,7 @@ public interface DeviceService extends EventHandler {
String EXTRA_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
String EXTRA_FIND_START = "find_start";
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
String EXTRA_AUDIO_PROPERTY = "audio_property";
String EXTRA_CALL_COMMAND = "call_command";
String EXTRA_CALL_PHONENUMBER = "call_phonenumber";
String EXTRA_CALL_DISPLAYNAME = "call_displayname";

View File

@ -40,6 +40,11 @@ public enum DeviceType {
EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled),
TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled),
// Single device made of L + R (71, 71)
// but each earbut can be controlled independently
HERE(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled),
// HERER(71, R.drawable.ic_device_here, R.drawable.ic_device_here_disabled),
// HEREL(72, R.drawable.ic_device_here, R.drawable.ic_device_here_disabled),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled);
private final int key;

View File

@ -100,10 +100,12 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SE
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETTIME;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_ALARMS;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_CONSTANT_VIBRATION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_AUDIO_PROPERTY;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_START;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_STARTAPP;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_TEST_NEW_FUNCTION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALARMS;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_AUDIO_PROPERTY;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
@ -123,6 +125,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAN
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONFIG;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_DURATION;
@ -426,6 +429,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mDeviceSupport.onSetConstantVibration(intensity);
break;
}
case ACTION_SET_AUDIO_PROPERTY: {
LOG.info(mDeviceSupport.getDevice().toString());
int volume = intent.getIntExtra(EXTRA_AUDIO_PROPERTY, 0);
mDeviceSupport.onSetAudioProperty(0, volume);
break;
}
case ACTION_CALLSTATE:
CallSpec callSpec = new CallSpec();
callSpec.command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED);

View File

@ -38,10 +38,18 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.here.HereSupport;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
// delte both
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DeviceSupportFactory {
// delete the following
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
private final BluetoothAdapter mBtAdapter;
private final Context mContext;
public DeviceSupportFactory(Context context) {
@ -134,6 +142,11 @@ public class DeviceSupportFactory {
case NO1F1:
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case HERE:
// case HERER:
// case HEREL:
deviceSupport = new ServiceDeviceSupport(new HereSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case TECLASTH30:
deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;

View File

@ -152,6 +152,11 @@ public class ServiceDeviceSupport implements DeviceSupport {
delegate.onNotification(notificationSpec);
}
@Override
public void onSetAudioProperty(int property, int intensity) {
delegate.onSetAudioProperty(property, intensity);
}
@Override
public void onDeleteNotification(int id) {
delegate.onDeleteNotification(id);

View File

@ -0,0 +1,512 @@
/* Copyright (C) 2017 Niclò Balzarotti
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.here;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
import android.net.Uri;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.devices.here.HereConstants;
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.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class HereSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(HereSupport.class);
public BluetoothGattCharacteristic batteryCharacteristic = null;
public BluetoothGattCharacteristic infoCharacteristic = null;
public BluetoothGattCharacteristic fwCharacteristic = null;
public BluetoothGattCharacteristic volumeCharacteristic = null;
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
public HereSupport() {
super(LOG);
addSupportedService(HereConstants.UUID_BATTERY_VALUE);
addSupportedService(HereConstants.UUID_CHARACTERISTIC_INFO);
addSupportedService(HereConstants.UUID_AUDIO_SETTINGS);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
batteryCharacteristic = getCharacteristic(HereConstants.UUID_CHARACTERISTIC_BATTERY);
infoCharacteristic = getCharacteristic(HereConstants.UUID_CHARACTERISTIC_INFO);
fwCharacteristic = getCharacteristic(HereConstants.UUID_CHARACTERISTIC_INFO);
volumeCharacteristic = getCharacteristic(HereConstants.UUID_VOLUME);
builder.setGattCallback(this);
builder.notify(batteryCharacteristic, true);
builder.read(fwCharacteristic);
syncSettings(builder);
// fw = builder.read(infoCharacteristic);
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
LOG.info("Initialization Done");
return builder;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
return true;
if (HereConstants.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) {
LOG.info("Battery is: " + String.format("%s", data[0]));
batteryCmd.level = ((short) data[0]);
handleGBDeviceEvent(batteryCmd);
return true;
} else if (HereConstants.UUID_FW_VERSION.equals(characteristicUUID)) {
LOG.info("Device info: " + (short)data[0]);
LOG.info("Device info: " + data);
versionCmd.fwVersion = String.format("%s", data);
handleGBDeviceEvent(versionCmd);
} else {
return true;
}
return true;
}
private void syncDateAndTime(TransactionBuilder builder) {
// Calendar cal = Calendar.getInstance();
// String strYear = String.valueOf(cal.get(Calendar.YEAR));
// byte year1 = (byte)Integer.parseInt(strYear.substring(0, 2));
// byte year2 = (byte)Integer.parseInt(strYear.substring(2, 4));
// byte month = (byte)cal.get(Calendar.MONTH);
// byte day = (byte)cal.get(Calendar.DAY_OF_MONTH);
// byte hour = (byte)cal.get(Calendar.HOUR_OF_DAY);
// byte minute = (byte)cal.get(Calendar.MINUTE);
// byte second = (byte)cal.get(Calendar.SECOND);
// byte weekDay = (byte)cal.get(Calendar.DAY_OF_WEEK);
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_DATE_AND_TIME,
// (year1 << 24) | (year2 << 16) | (month << 8) | day,
// (hour << 24) | (minute << 16) | (second << 8) | weekDay
// ));
}
private void syncSettings(TransactionBuilder builder) {
// syncDateAndTime(builder);
// // TODO: unhardcode and separate stuff
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_HEARTRATE_WARNING_VALUE, 0, 152
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_TARGET_STEPS, 0, 10000
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_GET_STEP_COUNT, 0, 0
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_GET_SLEEP_TIME, 0, 0
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_NOON_TIME, 12 * 60 * 60, 14 * 60 * 60 // 12:00 - 14:00
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_SLEEP_TIME, 21 * 60 * 60, 8 * 60 * 60 // 21:00 - 08:00
// ));
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_INACTIVITY_WARNING_TIME, 0, 0
// ));
// // do not disturb and a couple more features
// byte dndStartHour = 22;
// byte dndStartMin = 0;
// byte dndEndHour = 8;
// byte dndEndMin = 0;
// boolean dndToggle = false;
// boolean vibrationToggle = true;
// boolean wakeOnRaiseToggle = true;
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_DND_SETTINGS,
// (dndStartHour << 24) | (dndStartMin << 16) | (dndEndHour << 8) | dndEndMin,
// ((dndToggle ? 0 : 1) << 2) | ((vibrationToggle ? 1 : 0) << 1) | (wakeOnRaiseToggle ? 1 : 0)
// ));
}
private void showNotification(byte icon, String title, String message) {
// try {
// TransactionBuilder builder = performInitialized("ShowNotification");
// byte[] titleBytes = stringToUTF8Bytes(title, 16);
// byte[] messageBytes = stringToUTF8Bytes(message, 80);
// for (int i = 1; i <= 7; i++)
// {
// byte[] currentPacket = new byte[20];
// currentPacket[0] = HereConstants.CMD_ACTION_SHOW_NOTIFICATION;
// currentPacket[1] = 7;
// currentPacket[2] = (byte)i;
// switch(i) {
// case 1:
// currentPacket[4] = icon;
// break;
// case 2:
// if (titleBytes != null) {
// System.arraycopy(titleBytes, 0, currentPacket, 3, 6);
// System.arraycopy(titleBytes, 6, currentPacket, 10, 10);
// }
// break;
// default:
// if (messageBytes != null) {
// System.arraycopy(messageBytes, 16 * (i - 3), currentPacket, 3, 6);
// System.arraycopy(messageBytes, 6 + 16 * (i - 3), currentPacket, 10, 10);
// }
// break;
// }
// builder.write(ctrlCharacteristic, currentPacket);
// }
// performConnected(builder.getTransaction());
// } catch (IOException e) {
// LOG.warn(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 = HereConstants.ICON_SMS;
// break;
// case FACEBOOK:
// case FACEBOOK_MESSENGER:
// icon = HereConstants.ICON_FACEBOOK;
// break;
// case TWITTER:
// icon = HereConstants.ICON_TWITTER;
// break;
// case WHATSAPP:
// icon = HereConstants.ICON_WHATSAPP;
// break;
// default:
// icon = HereConstants.ICON_LINE;
// break;
// }
// showNotification(icon, notificationTitle, notificationSpec.body);
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
// try {
// TransactionBuilder builder = performInitialized("SetAlarms");
// for (int i = 0; i < alarms.size(); i++)
// {
// byte cmd;
// switch (i) {
// case 0:
// cmd = HereConstants.CMD_SET_ALARM_1;
// break;
// case 1:
// cmd = HereConstants.CMD_SET_ALARM_2;
// break;
// case 2:
// cmd = HereConstants.CMD_SET_ALARM_3;
// break;
// default:
// return;
// }
// Calendar cal = alarms.get(i).getAlarmCal();
// 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
// ));
// }
// performConnected(builder.getTransaction());
// 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());
// }
}
@Override
public void onSetTime() {
// try {
// TransactionBuilder builder = performInitialized("SetTime");
// syncDateAndTime(builder);
// performConnected(builder.getTransaction());
// } catch(IOException e) {
// LOG.warn(e.getMessage());
// }
}
@Override
public void onSetCallState(CallSpec callSpec) {
// switch (callSpec.command) {
// case CallSpec.CALL_INCOMING:
// showNotification(HereConstants.ICON_CALL, callSpec.name, callSpec.number);
// break;
// }
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetAudioProperty(int property, int volume) {
TransactionBuilder builder = new TransactionBuilder("volume");
LOG.info("setting audio");
switch (property) {
case 0: // volume
LOG.info("setting volume to "+volume);
builder.write(volumeCharacteristic, new byte[]{(byte) volume});
builder.queue(getQueue());
break;
}
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
}
@Override
public void onEnableRealtimeSteps(boolean enable) {
onEnableRealtimeHeartRateMeasurement(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) {
}
@Override
public void onAppReorder(UUID[] uuids) {
}
@Override
public void onFetchActivityData() {
}
@Override
public void onReboot() {
// try {
// TransactionBuilder builder = performInitialized("Reboot");
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_ACTION_REBOOT_DEVICE, 0, 0
// ));
// performConnected(builder.getTransaction());
// } catch(Exception e) {
// LOG.warn(e.getMessage());
// }
}
@Override
public void onHeartRateTest() {
// try {
// TransactionBuilder builder = performInitialized("HeartRateTest");
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_ACTION_HEARTRATE_SWITCH, 0, 1
// ));
// performConnected(builder.getTransaction());
// } catch(Exception e) {
// LOG.warn(e.getMessage());
// }
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
// // TODO: test
// try {
// TransactionBuilder builder = performInitialized("RealTimeHeartMeasurement");
// builder.write(ctrlCharacteristic, commandWithChecksum(
// HereConstants.CMD_SET_HEARTRATE_AUTO, 0, enable ? 1 : 0
// ));
// performConnected(builder.getTransaction());
// } catch(Exception e) {
// LOG.warn(e.getMessage());
// }
}
@Override
public void onFindDevice(boolean start) {
}
@Override
public void onSetConstantVibration(int integer) {
}
@Override
public void onScreenshotReq() {
}
@Override
public void onEnableHeartRateSleepSupport(boolean enable) {
}
@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) {
}
// private byte[] commandWithChecksum(byte cmd, int argSlot1, int argSlot2)
// {
// // ByteBuffer buf = ByteBuffer.allocate(10);
// // buf.put(cmd);
// // buf.putInt(argSlot1);
// // buf.putInt(argSlot2);
// // byte[] bytesToWrite = buf.array();
// // byte checksum = 0;
// // for (byte b : bytesToWrite) {
// // checksum += b;
// // }
// // bytesToWrite[9] = checksum;
// ByteBuffer buf = ByteBuffer.allocate(10);
// return buf;
// }
private byte[] stringToUTF8Bytes(String src, int byteCount) {
// try {
// if (src == null)
// return null;
// for (int i = src.length(); i > 0; i--) {
// String sub = src.substring(0, i);
// byte[] subUTF8 = sub.getBytes("UTF-8");
// if (subUTF8.length == byteCount) {
// return subUTF8;
// }
// if (subUTF8.length < byteCount) {
// byte[] largerSubUTF8 = new byte[byteCount];
// System.arraycopy(subUTF8, 0, largerSubUTF8, 0, subUTF8.length);
// return largerSubUTF8;
// }
// }
// } catch (UnsupportedEncodingException e) {
// LOG.warn(e.getMessage());
// }
return null;
}
}

View File

@ -431,6 +431,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onSetTime() {

View File

@ -316,6 +316,12 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {

View File

@ -105,6 +105,10 @@ public class LiveviewSupport extends AbstractSerialDeviceSupport {
//nothing to do ATM
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
//nothing to do ATM

View File

@ -452,6 +452,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
// not supported
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
/**
* Part of device initialization process. Do not call manually.
*

View File

@ -663,6 +663,12 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
private boolean isAlarmClockRinging() {
// don't synchronize, this is not really important
return alarmClockRinging;

View File

@ -355,6 +355,11 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onDeleteCalendarEvent(byte type, long id) {

View File

@ -127,6 +127,11 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onSetConstantVibration(int intensity) {

View File

@ -185,6 +185,11 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport {
}
@Override
public void onSetAudioProperty(int property, int volume) {
}
@Override
public void onAppStart(UUID uuid, boolean start) {

View File

@ -51,6 +51,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.here.HereCoordinator;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@ -198,6 +199,7 @@ public class DeviceHelper {
result.add(new No1F1Coordinator());
result.add(new MakibesF68Coordinator());
result.add(new EXRIZUK8Coordinator());
result.add(new HereCoordinator());
result.add(new TeclastH30Coordinator());
return result;

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="false"
android:layout_centerInParent="false"
android:layout_centerVertical="false"
android:weightSum="1">
<SeekBar
android:id="@+id/volume_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="28"
android:scaleY="4.0" />
</LinearLayout>

View File

@ -231,12 +231,27 @@
card_view:srcCompat="@drawable/ic_activity_graphs" />
<ImageView
android:id="@+id/device_action_find"
android:id="@+id/device_action_show_audio_settings"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_below="@id/device_image"
android:layout_margin="4dp"
android:layout_toEndOf="@id/device_action_show_activity_graphs"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:contentDescription="@string/controlcenter_start_audiosetting"
android:padding="4dp"
android:scaleType="fitXY"
android:tint="@color/secondarytext"
card_view:srcCompat="@drawable/ic_settings" />
<ImageView
android:id="@+id/device_action_find"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_below="@id/device_image"
android:layout_margin="4dp"
android:layout_toEndOf="@id/device_action_show_audio_settings"
android:clickable="true"
android:contentDescription="@string/controlcenter_find_device"
android:padding="4dp"

View File

@ -287,6 +287,7 @@
<string name="miband_prefs_fitness_goal">Target steps for each day</string>
<string name="dbaccess_error_executing">Error executing \'%1$s\'</string>
<string name="controlcenter_start_activitymonitor">Your activity (ALPHA)</string>
<string name="controlcenter_start_audiosetting">Audio Settings</string>
<string name="cannot_connect">Cannot connect: %1$s</string>
<string name="installer_activity_unable_to_find_handler">Unable to find a handler to install this file.</string>
<string name="pbw_install_handler_unable_to_install">Unable to install the given file: %1$s</string>
@ -474,4 +475,5 @@
<string name="_pebble_watch_open_on_phone">Open on phone</string>
<string name="_pebble_watch_mute">Mute</string>
<string name="_pebble_watch_reply">Reply</string>
<string name="title_audio_activity">Audio Settings</string>
</resources>