diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 85bb980a..957a6be6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,9 @@ - + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 56fa1215..1ea4e746 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -340,13 +340,23 @@ public class ControlCenter extends Activity { } SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - String miAddr = sharedPrefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, null); - if (miAddr != null && miAddr.length() > 0) { + String miAddr = sharedPrefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, ""); + if (miAddr.length() > 0) { GBDevice miDevice = new GBDevice(miAddr, "MI", DeviceType.MIBAND); if (!availableDevices.contains(miDevice)) { availableDevices.add(miDevice); } } + + String pebbleEmuAddr = sharedPrefs.getString("pebble_emu_addr", ""); + String pebbleEmuPort = sharedPrefs.getString("pebble_emu_port", ""); + if (pebbleEmuAddr.length() >= 7 && pebbleEmuPort.length() > 0) { + GBDevice pebbleEmuDevice = new GBDevice(pebbleEmuAddr + ":" + pebbleEmuPort, "Pebble qemu", DeviceType.PEBBLE); + if (!availableDevices.contains(pebbleEmuDevice)) { + availableDevices.add(pebbleEmuDevice); + } + } + deviceList.retainAll(availableDevices); for (GBDevice dev : availableDevices) { if (!deviceList.contains(dev)) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 6f43ed43..735b68af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; +import android.support.v4.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; @@ -35,5 +36,37 @@ public class SettingsActivity extends AbstractSettingsActivity { } }); + final Preference pebbleEmuAddr = findPreference("pebble_emu_addr"); + pebbleEmuAddr.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(refreshIntent); + preference.setSummary(newVal.toString()); + return true; + } + + }); + + final Preference pebbleEmuPort = findPreference("pebble_emu_port"); + pebbleEmuPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(refreshIntent); + preference.setSummary(newVal.toString()); + return true; + } + + }); } + + @Override + protected String[] getPreferenceKeysWithSummary() { + return new String[]{ + "pebble_emu_addr", + "pebble_emu_port" + }; + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 081fc7fa..97ac915f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -483,7 +483,7 @@ public abstract class AbstractChartFragment extends Fragment { } protected List getSamples(DBHandler db, GBDevice device) { - return getAllSamples(db, device, getTSStart(), getTSEnd()); + return getSamples(db, device, getTSStart(), getTSEnd()); } private int getTSEnd() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index f29efe12..bd3b1bfb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -25,11 +25,16 @@ public class DeviceSupportFactory { } public synchronized DeviceSupport createDeviceSupport(String deviceAddress) throws GBException { - DeviceSupport deviceSupport = createBTDeviceSupport(deviceAddress); + DeviceSupport deviceSupport; + if (deviceAddress.indexOf(":") == deviceAddress.lastIndexOf(":")) { // only one colon + deviceSupport = createTCPDeviceSupport(deviceAddress); + } else { + deviceSupport = createBTDeviceSupport(deviceAddress); + } + if (deviceSupport != null) { return deviceSupport; } - // support for other kinds of transports // no device found, check transport availability and warn checkBtAvailability(); @@ -68,4 +73,16 @@ public class DeviceSupportFactory { } return null; } + + private DeviceSupport createTCPDeviceSupport(String deviceAddress) throws GBException { + try { + GBDevice gbDevice = new GBDevice(deviceAddress, "Pebble qemu", DeviceType.PEBBLE); //FIXME, do not hardcode + DeviceSupport deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + deviceSupport.setContext(gbDevice, mBtAdapter, mContext); + return deviceSupport; + } catch (Exception e) { + throw new GBException("cannot connect to " + deviceAddress, e); // FIXME: localize + } + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 4721ca33..44a113c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -16,6 +16,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.zip.ZipInputStream; @@ -37,14 +39,17 @@ public class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; + private boolean mIsTCP = false; private BluetoothAdapter mBtAdapter = null; private BluetoothSocket mBtSocket = null; + private Socket mTCPSocket = null; // for emulator private InputStream mInStream = null; private OutputStream mOutStream = null; private boolean mQuit = false; private boolean mIsConnected = false; private boolean mIsInstalling = false; private int mConnectionAttempts = 0; + private PBWReader mPBWReader = null; private int mAppInstallToken = -1; private ZipInputStream mZis = null; @@ -66,14 +71,25 @@ public class PebbleIoThread extends GBDeviceIoThread { @Override protected boolean connect(String btDeviceAddress) { - BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); - ParcelUuid uuids[] = btDevice.getUuids(); GBDevice.State originalState = gbDevice.getState(); try { - mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); - mBtSocket.connect(); - mInStream = mBtSocket.getInputStream(); - mOutStream = mBtSocket.getOutputStream(); + // contains only one ":"? then it is addr:port + int firstColon = btDeviceAddress.indexOf(":"); + if (firstColon == btDeviceAddress.lastIndexOf(":")) { + mIsTCP = true; + InetAddress serverAddr = InetAddress.getByName(btDeviceAddress.substring(0, firstColon)); + mTCPSocket = new Socket(serverAddr, Integer.parseInt(btDeviceAddress.substring(firstColon + 1))); + mInStream = mTCPSocket.getInputStream(); + mOutStream = mTCPSocket.getOutputStream(); + } else { + mIsTCP = false; + BluetoothDevice btDevice = mBtAdapter.getRemoteDevice(btDeviceAddress); + ParcelUuid uuids[] = btDevice.getUuids(); + mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); + mBtSocket.connect(); + mInStream = mBtSocket.getInputStream(); + mOutStream = mBtSocket.getOutputStream(); + } } catch (IOException e) { e.printStackTrace(); gbDevice.setState(originalState); @@ -187,11 +203,14 @@ public class PebbleIoThread extends GBDeviceIoThread { break; } } + if (mIsTCP) { + mInStream.skip(6); + } int bytes = mInStream.read(buffer, 0, 4); + if (bytes < 4) { continue; } - ByteBuffer buf = ByteBuffer.wrap(buffer); buf.order(ByteOrder.BIG_ENDIAN); short length = buf.getShort(); @@ -214,6 +233,10 @@ public class PebbleIoThread extends GBDeviceIoThread { bytes += mInStream.read(buffer, bytes + 4, length - bytes); } + if (mIsTCP) { + mInStream.skip(2); + } + GBDeviceEvent deviceEvent = mPebbleProtocol.decodeResponse(buffer); if (deviceEvent == null) { LOG.info("unhandled message to endpoint " + endpoint + " (" + length + " bytes)"); @@ -261,15 +284,31 @@ public class PebbleIoThread extends GBDeviceIoThread { gbDevice.sendDeviceUpdateIntent(getContext()); } + private void write_real(byte[] bytes) { + try { + if (mIsTCP) { + ByteBuffer buf = ByteBuffer.allocate(bytes.length + 8); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) 0xfeed); + buf.putShort((short) 1); + buf.putShort((short) bytes.length); + buf.put(bytes); + buf.putShort((short) 0xbeef); + mOutStream.write(buf.array()); + mOutStream.flush(); + } else { + mOutStream.write(bytes); + mOutStream.flush(); + } + } catch (IOException e) { + } + } + @Override synchronized public void write(byte[] bytes) { // block writes if app installation in in progress if (mIsConnected && (!mIsInstalling || mInstallState == PebbleAppInstallState.WAIT_SLOT)) { - try { - mOutStream.write(bytes); - mOutStream.flush(); - } catch (IOException e) { - } + write_real(bytes); } } @@ -371,13 +410,8 @@ public class PebbleIoThread extends GBDeviceIoThread { if (!mIsInstalling) { return; } - int length = bytes.length; - LOG.info("got " + length + "bytes for writeInstallApp()"); - try { - mOutStream.write(bytes); - mOutStream.flush(); - } catch (IOException e) { - } + LOG.info("got " + bytes.length + "bytes for writeInstallApp()"); + write_real(bytes); } public void installApp(Uri uri, int appId) { @@ -411,7 +445,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (mPebbleProtocol.isFw3x) { if (appId == 0) { // only install metadata - not the binaries - write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getIconId())); + write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId())); GB.toast("To finish installation please start the watchapp on your Pebble", 5, GB.INFO); } else { // this came from an app fetch request, so do the real stuff diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index cf661913..4b38b3d0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -503,7 +503,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } - public byte[] encodeInstallMetadata(UUID uuid, String appName, short appVersion, short sdkVersion, int iconId) { + public byte[] encodeInstallMetadata(UUID uuid, String appName, short appVersion, short sdkVersion, int flags, int iconId) { // Calculate length first final short BLOBDB_LENGTH = 23; final short METADATA_LENGTH = 126; @@ -534,6 +534,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putLong(uuid.getMostSignificantBits()); // watchapp uuid buf.putLong(uuid.getLeastSignificantBits()); buf.order(ByteOrder.LITTLE_ENDIAN); + buf.putInt(flags); buf.putInt(iconId); buf.putShort(appVersion); buf.putShort(sdkVersion); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 7e6e3cee..6dfe88c3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -77,6 +77,16 @@ android:key="pebble_force_untested" android:title="@string/pref_title_pebble_forceuntested" android:summary="@string/pref_summary_pebble_forceuntested" /> + +