From ab8982e7f27be31d563d80ab30034f722e463c6a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 2 Sep 2015 08:00:26 +0200 Subject: [PATCH] WIP: support for live display of activity data --- .../activities/charts/ChartsActivity.java | 4 +- .../charts/LiveActivityFragment.java | 154 ++++++++++++++++++ .../charts/WeekStepsChartFragment.java | 1 - .../gadgetbridge/devices/EventHandler.java | 2 + .../devices/miband/MiBandService.java | 7 +- .../gadgetbridge/impl/GBDeviceService.java | 7 + .../gadgetbridge/model/DeviceService.java | 5 +- .../service/DeviceCommunicationService.java | 6 + .../service/ServiceDeviceSupport.java | 8 + .../service/devices/miband/MiBandSupport.java | 29 +++- .../serial/AbstractSerialDeviceSupport.java | 6 + .../service/serial/GBDeviceProtocol.java | 5 + .../res/layout/fragment_live_activity.xml | 20 +++ app/src/main/res/values/strings.xml | 2 + .../service/TestDeviceSupport.java | 5 + 15 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java create mode 100644 app/src/main/res/layout/fragment_live_activity.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java index 787918d8a..c93e6f0f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java @@ -208,6 +208,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts return new SleepChartFragment(); case 2: return new WeekStepsChartFragment(); + case 3: + return new LiveActivityFragment(); } return null; @@ -216,7 +218,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts @Override public int getCount() { // Show 3 total pages. - return 3; + return 4; } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java new file mode 100644 index 000000000..d6610a84f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -0,0 +1,154 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.charts; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.github.mikephil.charting.charts.BarLineChartBase; +import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.charts.PieChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.components.YAxis; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; + +public class LiveActivityFragment extends AbstractChartFragment { + private static final Logger LOG = LoggerFactory.getLogger(LiveActivityFragment.class); + + private BarLineChartBase mStepsPerMinuteHistoryChart; + private PieChart mStepsPerMinuteCurrentChart; + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + switch (action) { + case DeviceService.ACTION_REALTIME_STEPS: + int steps = intent.getIntExtra(DeviceService.EXTRA_REALTIME_STEPS, 0); + updateCurrentSteps(steps); + break; + } + } + }; + + private void updateCurrentSteps(int steps) { + LOG.warn("STEPS: " + steps); + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + IntentFilter filterLocal = new IntentFilter(); + filterLocal.addAction(DeviceService.ACTION_ENABLE_REALTIME_STEPS); + LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filterLocal); + + View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false); + + mStepsPerMinuteHistoryChart = (BarLineChartBase) rootView.findViewById(R.id.livechart_steps_per_minute_history); + mStepsPerMinuteCurrentChart = (PieChart) rootView.findViewById(R.id.livechart_steps_per_minute_current); + + setupHistoryChart(mStepsPerMinuteHistoryChart); + setupCurrentChart(mStepsPerMinuteCurrentChart); + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); + GBApplication.deviceService().onEnableRealtimeSteps(true); + } + + @Override + public void onStop() { + super.onStop(); + GBApplication.deviceService().onEnableRealtimeSteps(false); + } + + @Override + public void onDestroyView() { + LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver); + super.onDestroyView(); + } + + private void setupCurrentChart(PieChart chart) { + chart.setBackgroundColor(BACKGROUND_COLOR); + chart.setDescriptionColor(DESCRIPTION_COLOR); + chart.setDescription(""); + chart.setNoDataTextDescription(""); + chart.setNoDataText(""); + } + + private void setupHistoryChart(BarLineChartBase chart) { + chart.setBackgroundColor(BACKGROUND_COLOR); + chart.setDescriptionColor(DESCRIPTION_COLOR); + chart.setDescription(""); + + configureBarLineChartDefaults(chart); + + XAxis x = chart.getXAxis(); + x.setDrawLabels(true); + x.setDrawGridLines(false); + x.setEnabled(true); + x.setTextColor(CHART_TEXT_COLOR); + x.setDrawLimitLinesBehindData(true); + + YAxis y = chart.getAxisLeft(); + y.setDrawGridLines(false); + y.setDrawTopYLabelEntry(false); + y.setTextColor(CHART_TEXT_COLOR); + + y.setEnabled(true); + + YAxis yAxisRight = chart.getAxisRight(); + yAxisRight.setDrawGridLines(false); + yAxisRight.setEnabled(false); + yAxisRight.setDrawLabels(false); + yAxisRight.setDrawTopYLabelEntry(false); + yAxisRight.setTextColor(CHART_TEXT_COLOR); + } + + @Override + public String getTitle() { + return getContext().getString(R.string.liveactivity_live_activity); + } + + @Override + protected void refreshInBackground(DBHandler db, GBDevice device) { + + } + + @Override + protected void renderCharts() { + mStepsPerMinuteCurrentChart.animateXY(50, 50); + mStepsPerMinuteHistoryChart.invalidate(); + } + + @Override + protected List getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) { + throw new UnsupportedOperationException("no db access supported for live activity"); + } + + @Override + protected void setupLegend(Chart chart) { + // no legend + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java index 71382dfa0..df612c053 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java @@ -185,7 +185,6 @@ public class WeekStepsChartFragment extends AbstractChartFragment { yAxisRight.setDrawLabels(false); yAxisRight.setDrawTopYLabelEntry(false); yAxisRight.setTextColor(CHART_TEXT_COLOR); - } protected void setupLegend(Chart chart) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index b2d6f90d1..07f146432 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -29,6 +29,8 @@ public interface EventHandler { void onSetMusicInfo(String artist, String album, String track); + void onEnableRealtimeSteps(boolean enable); + void onInstallApp(Uri uri); void onAppInfoReq(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java index 9addf457d..cefc36395 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java @@ -150,16 +150,17 @@ public class MiBandService { public static final byte COMMAND_STOP_MOTOR_VIBRATE = 0x13; + public static final byte COMMAND_SET_REALTIME_STEPS_NOTIFICATION = 0x3; + + public static final byte COMMAND_SET_REALTIME_STEP = 0x10; + /* FURTHER COMMANDS: unchecked therefore left commented - public static final COMMAND_SET_REALTIME_STEPS_NOTIFICATION = 0x3t public static final byte COMMAND_FACTORY_RESET = 0x9t; public static final int COMMAND_SET_COLOR_THEME = et; - public static final COMMAND_SET_REALTIME_STEP = 0x10t - public static final COMMAND_STOP_SYNC_DATA = 0x11t public static final byte COMMAND_GET_SENSOR_DATA = 0x12t diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 72e90bf20..5fd1efb79 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -185,4 +185,11 @@ public class GBDeviceService implements DeviceService { Intent intent = createIntent().setAction(ACTION_REQUEST_SCREENSHOT); invokeService(intent); } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_STEPS) + .putExtra(EXTRA_ENABLE_REALTIME_STEPS, enable); + invokeService(intent); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index 63e651117..41b345d52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -30,6 +30,8 @@ public interface DeviceService extends EventHandler { static final String ACTION_DISCONNECT = PREFIX + ".action.disconnect"; static final String ACTION_FIND_DEVICE = PREFIX + ".action.find_device"; static final String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms"; + static final String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps"; + static final String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps"; static final String EXTRA_DEVICE_ADDRESS = "device_address"; static final String EXTRA_NOTIFICATION_TITLE = "notification_title"; @@ -47,7 +49,8 @@ public interface DeviceService extends EventHandler { static final String EXTRA_URI = "uri"; static final String EXTRA_ALARMS = "alarms"; static final String EXTRA_PERFORM_PAIR = "perform_pair"; - + static final String EXTRA_ENABLE_REALTIME_STEPS = "enable_realtime_steps"; + static final String EXTRA_REALTIME_STEPS = "realtime_steps"; void start(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 72046b8e5..4f4a2f1a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -34,6 +34,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CA import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_INSTALL; @@ -54,6 +55,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ENABLE_REALTIME_STEPS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; @@ -263,6 +265,10 @@ public class DeviceCommunicationService extends Service { ArrayList alarms = intent.getParcelableArrayListExtra(EXTRA_ALARMS); mDeviceSupport.onSetAlarms(alarms); break; + case ACTION_ENABLE_REALTIME_STEPS: + boolean enable = intent.getBooleanExtra(EXTRA_ENABLE_REALTIME_STEPS, false); + mDeviceSupport.onEnableRealtimeSteps(enable); + break; } return START_STICKY; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 6639b8cce..d7bda277c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -231,4 +231,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onSetAlarms(alarms); } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + if (checkBusy("enable realtime steps: " + enable)) { + return; + } + delegate.onEnableRealtimeSteps(enable); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index aeeb8c5ee..0d04ee842 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -2,9 +2,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.preference.PreferenceManager; +import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; import org.slf4j.Logger; @@ -26,7 +28,9 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -207,9 +211,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private static final byte[] startVibrate = new byte[]{MiBandService.COMMAND_SEND_NOTIFICATION, 1}; private static final byte[] stopVibrate = new byte[]{MiBandService.COMMAND_STOP_MOTOR_VIBRATE}; private static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT}; + private static final byte[] startRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 1}; + private static final byte[] stopRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 0}; private byte[] getNotification(long vibrateDuration, int vibrateTimes, int flashTimes, int flashColour, int originalColour, long flashDuration) { - byte[] vibrate = new byte[]{MiBandService.COMMAND_SEND_NOTIFICATION, (byte) 1}; + byte[] vibrate = startVibrate; byte r = 6; byte g = 0; byte b = 6; @@ -514,6 +520,17 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + @Override + public void onEnableRealtimeSteps(boolean enable) { + try { + BluetoothGattCharacteristic controlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications") + .write(controlPoint, enable ? startRealTimeStepsNotifications : stopRealTimeStepsNotifications).queue(getQueue()); + } catch (IOException e) { + LOG.error("Unable to change realtime steps notification to: " + enable, e); + } + } + private byte[] getHighLatency() { int minConnectionInterval = 460; int maxConnectionInterval = 500; @@ -591,9 +608,19 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS); } else if (MiBandService.UUID_CHARACTERISTIC_NOTIFICATION.equals(characteristicUUID)) { handleNotificationNotif(characteristic.getValue()); + } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { + handleRealtimeSteps(characteristic.getValue()); } } + private void handleRealtimeSteps(byte[] value) { + int steps = 0xff & value[0] | (0xff & value[1]) << 8; + LOG.debug("realtime steps: " + steps); + Intent intent = new Intent(DeviceService.ACTION_REALTIME_STEPS) + .putExtra(DeviceService.EXTRA_REALTIME_STEPS, steps); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 448f699f3..819a187e3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -182,4 +182,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport byte[] bytes = gbDeviceProtocol.encodeScreenshotReq(); sendToDevice(bytes); } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeSteps(enable); + sendToDevice(bytes); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 90cf1d488..22c23063f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -63,7 +63,12 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeEnableRealtimeSteps(boolean enable) { + return null; + } + public GBDeviceEvent[] decodeResponse(byte[] responseData) { return null; } + } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_live_activity.xml b/app/src/main/res/layout/fragment_live_activity.xml new file mode 100644 index 000000000..8d7b5a2f0 --- /dev/null +++ b/app/src/main/res/layout/fragment_live_activity.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c37decc7..5ee3c13a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -185,4 +185,6 @@ Firmware installation complete, rebooting deviceā€¦ Firmware write failed Steps + Live Activity + Steps today, target: %1$s diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 61de0b47d..b0abafec0 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -121,4 +121,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport { public void onScreenshotReq() { } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } }