Initial HERE Active Listening Device Support
parent
4b4e95bfe0
commit
ac07ddc932
|
@ -388,7 +388,10 @@
|
||||||
android:name=".activities.VibrationActivity"
|
android:name=".activities.VibrationActivity"
|
||||||
android:label="@string/title_activity_vibration"
|
android:label="@string/title_activity_vibration"
|
||||||
android:parentActivityName=".activities.ControlCenterv2" />
|
android:parentActivityName=".activities.ControlCenterv2" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.AudioActivity"
|
||||||
|
android:label="@string/title_audio_activity"
|
||||||
|
android:parentActivityName=".activities.ControlCenterv2" />
|
||||||
<provider
|
<provider
|
||||||
android:name=".contentprovider.PebbleContentProvider"
|
android:name=".contentprovider.PebbleContentProvider"
|
||||||
android:authorities="com.getpebble.android.provider"
|
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
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published
|
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
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
Gadgetbridge is distributed in the hope that it will be useful,
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
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/>. */
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
@ -42,10 +42,12 @@ import android.widget.Toast;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
|
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.AudioActivity;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
@ -84,27 +86,27 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||||
|
|
||||||
holder.container.setOnClickListener(new View.OnClickListener() {
|
holder.container.setOnClickListener(new View.OnClickListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (device.isInitialized() || device.isConnected()) {
|
if (device.isInitialized() || device.isConnected()) {
|
||||||
showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress);
|
showTransientSnackbar(R.string.controlcenter_snackbar_need_longpress);
|
||||||
} else {
|
} else {
|
||||||
showTransientSnackbar(R.string.controlcenter_snackbar_connecting);
|
showTransientSnackbar(R.string.controlcenter_snackbar_connecting);
|
||||||
GBApplication.deviceService().connect(device);
|
GBApplication.deviceService().connect(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
holder.container.setOnLongClickListener(new View.OnLongClickListener() {
|
holder.container.setOnLongClickListener(new View.OnLongClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View v) {
|
public boolean onLongClick(View v) {
|
||||||
if (device.getState() != GBDevice.State.NOT_CONNECTED) {
|
if (device.getState() != GBDevice.State.NOT_CONNECTED) {
|
||||||
showTransientSnackbar(R.string.controlcenter_snackbar_disconnecting);
|
showTransientSnackbar(R.string.controlcenter_snackbar_disconnecting);
|
||||||
GBApplication.deviceService().disconnect();
|
GBApplication.deviceService().disconnect();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.deviceImageView.setImageResource(R.drawable.level_list_device);
|
holder.deviceImageView.setImageResource(R.drawable.level_list_device);
|
||||||
//level-list does not allow negative values, hence we always add 100 to the key.
|
//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));
|
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() + "%");
|
holder.batteryStatusLabel.setText(device.getBatteryLevel() + "%");
|
||||||
BatteryState batteryState = device.getBatteryState();
|
BatteryState batteryState = device.getBatteryState();
|
||||||
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
|
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
|
||||||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
|
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
|
||||||
holder.batteryIcon.setImageLevel(device.getBatteryLevel() + 100);
|
holder.batteryIcon.setImageLevel(device.getBatteryLevel() + 100);
|
||||||
} else {
|
} else {
|
||||||
holder.batteryIcon.setImageLevel(device.getBatteryLevel());
|
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.fetchActivityDataBox.setVisibility((device.isInitialized() && coordinator.supportsActivityDataFetching()) ? View.VISIBLE : View.GONE);
|
||||||
holder.fetchActivityData.setOnClickListener(new View.OnClickListener()
|
holder.fetchActivityData.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
|
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
|
||||||
GBApplication.deviceService().onFetchActivityData();
|
GBApplication.deviceService().onFetchActivityData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
//take screenshot
|
//take screenshot
|
||||||
holder.takeScreenshotView.setVisibility((device.isInitialized() && coordinator.supportsScreenshots()) ? View.VISIBLE : View.GONE);
|
holder.takeScreenshotView.setVisibility((device.isInitialized() && coordinator.supportsScreenshots()) ? View.VISIBLE : View.GONE);
|
||||||
holder.takeScreenshotView.setOnClickListener(new View.OnClickListener()
|
holder.takeScreenshotView.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
showTransientSnackbar(R.string.controlcenter_snackbar_requested_screenshot);
|
showTransientSnackbar(R.string.controlcenter_snackbar_requested_screenshot);
|
||||||
GBApplication.deviceService().onScreenshotReq();
|
GBApplication.deviceService().onScreenshotReq();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//manage apps
|
//manage apps
|
||||||
holder.manageAppsView.setVisibility((device.isInitialized() && coordinator.supportsAppsManagement()) ? View.VISIBLE : View.GONE);
|
holder.manageAppsView.setVisibility((device.isInitialized() && coordinator.supportsAppsManagement()) ? View.VISIBLE : View.GONE);
|
||||||
holder.manageAppsView.setOnClickListener(new View.OnClickListener()
|
holder.manageAppsView.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
||||||
Class<? extends Activity> appsManagementActivity = coordinator.getAppsManagementActivity();
|
Class<? extends Activity> appsManagementActivity = coordinator.getAppsManagementActivity();
|
||||||
if (appsManagementActivity != null) {
|
if (appsManagementActivity != null) {
|
||||||
Intent startIntent = new Intent(context, appsManagementActivity);
|
Intent startIntent = new Intent(context, appsManagementActivity);
|
||||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||||
context.startActivity(startIntent);
|
context.startActivity(startIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//set alarms
|
//set alarms
|
||||||
holder.setAlarmsView.setVisibility(coordinator.supportsAlarmConfiguration() ? View.VISIBLE : View.GONE);
|
holder.setAlarmsView.setVisibility(coordinator.supportsAlarmConfiguration() ? View.VISIBLE : View.GONE);
|
||||||
holder.setAlarmsView.setOnClickListener(new View.OnClickListener()
|
holder.setAlarmsView.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent startIntent;
|
Intent startIntent;
|
||||||
startIntent = new Intent(context, ConfigureAlarms.class);
|
startIntent = new Intent(context, ConfigureAlarms.class);
|
||||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||||
context.startActivity(startIntent);
|
context.startActivity(startIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
//show graphs
|
//show graphs
|
||||||
holder.showActivityGraphs.setVisibility(coordinator.supportsActivityTracking() ? View.VISIBLE : View.GONE);
|
holder.showActivityGraphs.setVisibility(coordinator.supportsActivityTracking() ? View.VISIBLE : View.GONE);
|
||||||
holder.showActivityGraphs.setOnClickListener(new View.OnClickListener()
|
holder.showActivityGraphs.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent startIntent;
|
Intent startIntent;
|
||||||
startIntent = new Intent(context, ChartsActivity.class);
|
startIntent = new Intent(context, ChartsActivity.class);
|
||||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||||
context.startActivity(startIntent);
|
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());
|
ItemWithDetailsAdapter infoAdapter = new ItemWithDetailsAdapter(context, device.getDeviceInfos());
|
||||||
infoAdapter.setHorizontalAlignment(true);
|
infoAdapter.setHorizontalAlignment(true);
|
||||||
|
@ -222,95 +240,96 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||||
holder.deviceInfoBox.setActivated(detailsShown);
|
holder.deviceInfoBox.setActivated(detailsShown);
|
||||||
holder.deviceInfoBox.setVisibility(detailsShown ? View.VISIBLE : View.GONE);
|
holder.deviceInfoBox.setVisibility(detailsShown ? View.VISIBLE : View.GONE);
|
||||||
holder.deviceInfoView.setOnClickListener(new View.OnClickListener() {
|
holder.deviceInfoView.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
expandedDevicePosition = detailsShown ? -1 : position;
|
expandedDevicePosition = detailsShown ? -1 : position;
|
||||||
TransitionManager.beginDelayedTransition(parent);
|
TransitionManager.beginDelayedTransition(parent);
|
||||||
notifyDataSetChanged();
|
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()
|
holder.findDevice.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (device.getType() == DeviceType.VIBRATISSIMO) {
|
if (device.getType() == DeviceType.VIBRATISSIMO) {
|
||||||
Intent startIntent;
|
Intent startIntent;
|
||||||
startIntent = new Intent(context, VibrationActivity.class);
|
startIntent = new Intent(context, VibrationActivity.class);
|
||||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||||
context.startActivity(startIntent);
|
context.startActivity(startIntent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GBApplication.deviceService().onFindDevice(true);
|
GBApplication.deviceService().onFindDevice(true);
|
||||||
//TODO: extract string resource if we like this solution.
|
//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() {
|
Snackbar.make(parent, R.string.control_center_find_lost_device, Snackbar.LENGTH_INDEFINITE).setAction("Found it!", new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
GBApplication.deviceService().onFindDevice(false);
|
GBApplication.deviceService().onFindDevice(false);
|
||||||
}
|
}
|
||||||
}).setCallback(new Snackbar.Callback() {
|
}).setCallback(new Snackbar.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onDismissed(Snackbar snackbar, int event) {
|
public void onDismissed(Snackbar snackbar, int event) {
|
||||||
GBApplication.deviceService().onFindDevice(false);
|
GBApplication.deviceService().onFindDevice(false);
|
||||||
super.onDismissed(snackbar, event);
|
super.onDismissed(snackbar, event);
|
||||||
}
|
}
|
||||||
}).show();
|
}).show();
|
||||||
// ProgressDialog.show(
|
// ProgressDialog.show(
|
||||||
// context,
|
// context,
|
||||||
// context.getString(R.string.control_center_find_lost_device),
|
// context.getString(R.string.control_center_find_lost_device),
|
||||||
// context.getString(R.string.control_center_cancel_to_stop_vibration),
|
// context.getString(R.string.control_center_cancel_to_stop_vibration),
|
||||||
// true, true,
|
// true, true,
|
||||||
// new DialogInterface.OnCancelListener() {
|
// new DialogInterface.OnCancelListener() {
|
||||||
// @Override
|
// @Override
|
||||||
// public void onCancel(DialogInterface dialog) {
|
// public void onCancel(DialogInterface dialog) {
|
||||||
// GBApplication.deviceService().onFindDevice(false);
|
// GBApplication.deviceService().onFindDevice(false);
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
//remove device, hidden under details
|
//remove device, hidden under details
|
||||||
holder.removeDevice.setOnClickListener(new View.OnClickListener()
|
holder.removeDevice.setOnClickListener(new View.OnClickListener()
|
||||||
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setTitle(context.getString(R.string.controlcenter_delete_device_name, device.getName()))
|
.setTitle(context.getString(R.string.controlcenter_delete_device_name, device.getName()))
|
||||||
.setMessage(R.string.controlcenter_delete_device_dialogmessage)
|
.setMessage(R.string.controlcenter_delete_device_dialogmessage)
|
||||||
.setPositiveButton(R.string.Delete, new DialogInterface.OnClickListener() {
|
.setPositiveButton(R.string.Delete, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
try {
|
try {
|
||||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
||||||
if (coordinator != null) {
|
if (coordinator != null) {
|
||||||
coordinator.deleteDevice(device);
|
coordinator.deleteDevice(device);
|
||||||
}
|
}
|
||||||
DeviceHelper.getInstance().removeBond(device);
|
DeviceHelper.getInstance().removeBond(device);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
GB.toast(context, "Error deleting device: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
GB.toast(context, "Error deleting device: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex);
|
||||||
} finally {
|
} finally {
|
||||||
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
|
Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST);
|
||||||
LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent);
|
LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,6 +357,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||||
ImageView manageAppsView;
|
ImageView manageAppsView;
|
||||||
ImageView setAlarmsView;
|
ImageView setAlarmsView;
|
||||||
ImageView showActivityGraphs;
|
ImageView showActivityGraphs;
|
||||||
|
ImageView showAudioSettings;
|
||||||
|
|
||||||
ImageView deviceInfoView;
|
ImageView deviceInfoView;
|
||||||
//overflow
|
//overflow
|
||||||
|
@ -365,6 +385,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||||
manageAppsView = (ImageView) view.findViewById(R.id.device_action_manage_apps);
|
manageAppsView = (ImageView) view.findViewById(R.id.device_action_manage_apps);
|
||||||
setAlarmsView = (ImageView) view.findViewById(R.id.device_action_set_alarms);
|
setAlarmsView = (ImageView) view.findViewById(R.id.device_action_set_alarms);
|
||||||
showActivityGraphs = (ImageView) view.findViewById(R.id.device_action_show_activity_graphs);
|
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);
|
deviceInfoView = (ImageView) view.findViewById(R.id.device_info_image);
|
||||||
|
|
||||||
deviceInfoBox = (RelativeLayout) view.findViewById(R.id.device_item_infos_box);
|
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();
|
View snackbarView = snackbar.getView();
|
||||||
|
|
||||||
// change snackbar text color
|
// change snackbar text color
|
||||||
int snackbarTextId = android.support.design.R.id.snackbar_text;
|
int snackbarTextId = android.support.design.R.id.snackbar_text;
|
||||||
TextView textView = (TextView) snackbarView.findViewById(snackbarTextId);
|
TextView textView = (TextView) snackbarView.findViewById(snackbarTextId);
|
||||||
//textView.setTextColor();
|
//textView.setTextColor();
|
||||||
|
|
|
@ -148,6 +148,14 @@ public interface DeviceCoordinator {
|
||||||
*/
|
*/
|
||||||
boolean supportsActivityTracking();
|
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
|
* Returns true if activity data fetching is supported AND possible at this
|
||||||
* very moment. This will consider the device state (being connected/disconnected/busy...)
|
* very moment. This will consider the device state (being connected/disconnected/busy...)
|
||||||
|
|
|
@ -79,6 +79,8 @@ public interface EventHandler {
|
||||||
|
|
||||||
void onSetConstantVibration(int integer);
|
void onSetConstantVibration(int integer);
|
||||||
|
|
||||||
|
void onSetAudioProperty(int property, int volume);
|
||||||
|
|
||||||
void onScreenshotReq();
|
void onScreenshotReq();
|
||||||
|
|
||||||
void onEnableHeartRateSleepSupport(boolean enable);
|
void onEnableHeartRateSleepSupport(boolean enable);
|
||||||
|
|
|
@ -137,6 +137,11 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsScreenshots() {
|
public boolean supportsScreenshots() {
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return new HPlusHealthSampleProvider(device, session);
|
return new HPlusHealthSampleProvider(device, session);
|
||||||
|
|
|
@ -114,6 +114,11 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -74,6 +74,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -151,6 +151,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getManufacturer() {
|
public String getManufacturer() {
|
||||||
return "Xiaomi";
|
return "Xiaomi";
|
||||||
|
|
|
@ -98,6 +98,11 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return new No1F1SampleProvider(device, session);
|
return new No1F1SampleProvider(device, session);
|
||||||
|
|
|
@ -117,6 +117,11 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsScreenshots() {
|
public boolean supportsScreenshots() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -75,6 +75,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsAudioSettings() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -24,6 +24,9 @@ import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
// FIXME: REMOVE
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -46,6 +49,9 @@ import static nodomain.freeyourgadget.gadgetbridge.util.JavaExtensions.coalesce;
|
||||||
|
|
||||||
public class GBDeviceService implements DeviceService {
|
public class GBDeviceService implements DeviceService {
|
||||||
protected final Context mContext;
|
protected final Context mContext;
|
||||||
|
// FIXME: REMOVE
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
|
||||||
|
|
||||||
private final Class<? extends Service> mServiceClass;
|
private final Class<? extends Service> mServiceClass;
|
||||||
private final String[] transliterationExtras = new String[]{
|
private final String[] transliterationExtras = new String[]{
|
||||||
EXTRA_NOTIFICATION_PHONENUMBER,
|
EXTRA_NOTIFICATION_PHONENUMBER,
|
||||||
|
@ -298,6 +304,14 @@ public class GBDeviceService implements DeviceService {
|
||||||
invokeService(intent);
|
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
|
@Override
|
||||||
public void onScreenshotReq() {
|
public void onScreenshotReq() {
|
||||||
Intent intent = createIntent().setAction(ACTION_REQUEST_SCREENSHOT);
|
Intent intent = createIntent().setAction(ACTION_REQUEST_SCREENSHOT);
|
||||||
|
|
|
@ -53,6 +53,7 @@ public interface DeviceService extends EventHandler {
|
||||||
String ACTION_DISCONNECT = PREFIX + ".action.disconnect";
|
String ACTION_DISCONNECT = PREFIX + ".action.disconnect";
|
||||||
String ACTION_FIND_DEVICE = PREFIX + ".action.find_device";
|
String ACTION_FIND_DEVICE = PREFIX + ".action.find_device";
|
||||||
String ACTION_SET_CONSTANT_VIBRATION = PREFIX + ".action.set_constant_vibration";
|
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_SET_ALARMS = PREFIX + ".action.set_alarms";
|
||||||
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps";
|
||||||
String ACTION_REALTIME_SAMPLES = PREFIX + ".action.realtime_samples";
|
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_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
|
||||||
String EXTRA_FIND_START = "find_start";
|
String EXTRA_FIND_START = "find_start";
|
||||||
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
||||||
|
String EXTRA_AUDIO_PROPERTY = "audio_property";
|
||||||
String EXTRA_CALL_COMMAND = "call_command";
|
String EXTRA_CALL_COMMAND = "call_command";
|
||||||
String EXTRA_CALL_PHONENUMBER = "call_phonenumber";
|
String EXTRA_CALL_PHONENUMBER = "call_phonenumber";
|
||||||
String EXTRA_CALL_DISPLAYNAME = "call_displayname";
|
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),
|
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),
|
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),
|
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);
|
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled);
|
||||||
|
|
||||||
private final int key;
|
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_SETTIME;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_ALARMS;
|
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_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_START;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_STARTAPP;
|
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.ACTION_TEST_NEW_FUNCTION;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALARMS;
|
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_CONFIG;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
|
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_CONFIG;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME;
|
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_FIND_START;
|
||||||
|
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM;
|
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_ARTIST;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_DURATION;
|
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_DURATION;
|
||||||
|
@ -426,6 +429,12 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||||
mDeviceSupport.onSetConstantVibration(intensity);
|
mDeviceSupport.onSetConstantVibration(intensity);
|
||||||
break;
|
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:
|
case ACTION_CALLSTATE:
|
||||||
CallSpec callSpec = new CallSpec();
|
CallSpec callSpec = new CallSpec();
|
||||||
callSpec.command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED);
|
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.vibratissimo.VibratissimoSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.here.HereSupport;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
// delte both
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class DeviceSupportFactory {
|
public class DeviceSupportFactory {
|
||||||
|
// delete the following
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
|
||||||
private final BluetoothAdapter mBtAdapter;
|
private final BluetoothAdapter mBtAdapter;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
public DeviceSupportFactory(Context context) {
|
public DeviceSupportFactory(Context context) {
|
||||||
|
@ -134,6 +142,11 @@ public class DeviceSupportFactory {
|
||||||
case NO1F1:
|
case NO1F1:
|
||||||
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||||
break;
|
break;
|
||||||
|
case HERE:
|
||||||
|
// case HERER:
|
||||||
|
// case HEREL:
|
||||||
|
deviceSupport = new ServiceDeviceSupport(new HereSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||||
|
break;
|
||||||
case TECLASTH30:
|
case TECLASTH30:
|
||||||
deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -152,6 +152,11 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||||
delegate.onNotification(notificationSpec);
|
delegate.onNotification(notificationSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int intensity) {
|
||||||
|
delegate.onSetAudioProperty(property, intensity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeleteNotification(int id) {
|
public void onDeleteNotification(int id) {
|
||||||
delegate.onDeleteNotification(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
|
@Override
|
||||||
public void onSetTime() {
|
public void onSetTime() {
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,12 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,10 @@ public class LiveviewSupport extends AbstractSerialDeviceSupport {
|
||||||
//nothing to do ATM
|
//nothing to do ATM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
|
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
|
||||||
//nothing to do ATM
|
//nothing to do ATM
|
||||||
|
|
|
@ -452,6 +452,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
|
||||||
// not supported
|
// not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Part of device initialization process. Do not call manually.
|
* Part of device initialization process. Do not call manually.
|
||||||
*
|
*
|
||||||
|
|
|
@ -663,6 +663,12 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isAlarmClockRinging() {
|
private boolean isAlarmClockRinging() {
|
||||||
// don't synchronize, this is not really important
|
// don't synchronize, this is not really important
|
||||||
return alarmClockRinging;
|
return alarmClockRinging;
|
||||||
|
|
|
@ -355,6 +355,11 @@ public class No1F1Support extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeleteCalendarEvent(byte type, long id) {
|
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
|
@Override
|
||||||
public void onSetConstantVibration(int intensity) {
|
public void onSetConstantVibration(int intensity) {
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,11 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetAudioProperty(int property, int volume) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAppStart(UUID uuid, boolean start) {
|
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.no1f1.No1F1Coordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
|
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.Device;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes;
|
import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
@ -198,6 +199,7 @@ public class DeviceHelper {
|
||||||
result.add(new No1F1Coordinator());
|
result.add(new No1F1Coordinator());
|
||||||
result.add(new MakibesF68Coordinator());
|
result.add(new MakibesF68Coordinator());
|
||||||
result.add(new EXRIZUK8Coordinator());
|
result.add(new EXRIZUK8Coordinator());
|
||||||
|
result.add(new HereCoordinator());
|
||||||
result.add(new TeclastH30Coordinator());
|
result.add(new TeclastH30Coordinator());
|
||||||
|
|
||||||
return result;
|
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" />
|
card_view:srcCompat="@drawable/ic_activity_graphs" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/device_action_find"
|
android:id="@+id/device_action_show_audio_settings"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:layout_below="@id/device_image"
|
android:layout_below="@id/device_image"
|
||||||
android:layout_margin="4dp"
|
android:layout_margin="4dp"
|
||||||
android:layout_toEndOf="@id/device_action_show_activity_graphs"
|
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:clickable="true"
|
||||||
android:contentDescription="@string/controlcenter_find_device"
|
android:contentDescription="@string/controlcenter_find_device"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
|
|
|
@ -287,6 +287,7 @@
|
||||||
<string name="miband_prefs_fitness_goal">Target steps for each day</string>
|
<string name="miband_prefs_fitness_goal">Target steps for each day</string>
|
||||||
<string name="dbaccess_error_executing">Error executing \'%1$s\'</string>
|
<string name="dbaccess_error_executing">Error executing \'%1$s\'</string>
|
||||||
<string name="controlcenter_start_activitymonitor">Your activity (ALPHA)</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="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="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>
|
<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_open_on_phone">Open on phone</string>
|
||||||
<string name="_pebble_watch_mute">Mute</string>
|
<string name="_pebble_watch_mute">Mute</string>
|
||||||
<string name="_pebble_watch_reply">Reply</string>
|
<string name="_pebble_watch_reply">Reply</string>
|
||||||
|
<string name="title_audio_activity">Audio Settings</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue