Initial HERE Active Listening Device Support
parent
4b4e95bfe0
commit
ac07ddc932
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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...)
|
||||
|
|
|
@ -79,6 +79,8 @@ public interface EventHandler {
|
|||
|
||||
void onSetConstantVibration(int integer);
|
||||
|
||||
void onSetAudioProperty(int property, int volume);
|
||||
|
||||
void onScreenshotReq();
|
||||
|
||||
void onEnableHeartRateSleepSupport(boolean enable);
|
||||
|
|
|
@ -137,6 +137,11 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAudioSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package nodomain.freeyourgadget.gadgetbridge.devices.here;
|
||||
|
||||
/*
|
||||
* @author Nicolò Balzarotti <nicolo@nixo.xyz>
|
||||
*/
|
||||
|
||||
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"));
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -151,6 +151,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAudioSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Xiaomi";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -117,6 +117,11 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAudioSettings() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return true;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -431,6 +431,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAudioProperty(int property, int volume) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
|
||||
|
|
|
@ -316,6 +316,12 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAudioProperty(int property, int volume) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -127,6 +127,11 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAudioProperty(int property, int volume) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int intensity) {
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue