diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 000000000..1562f1318 --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,35 @@ +# This workflow will check the python code using black + +name: Python style check + +on: + push: + branches: [ master ] + paths: + - 'controller/**' + - 'policy/**' + - '.github/workflows/python.yml' + pull_request: + branches: [ master ] + paths: + - 'controller/**' + - 'policy/**' + - '.github/workflows/python.yml' + +jobs: + style-check: + name: Python style check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install dependencies + run: | + python -m pip install 'black[jupyter]' + - name: Check style + run: | + black --check . diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3949bdeba..2f0310600 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,41 +10,74 @@ 1. Submit an issue describing the changes you want to implement. If it's only minor changes/bug-fixes, you can skip to step 3. 2. After the scope was discussed in the issue, assign it to yourself. It should show up in the "To do" column in the OpenBot project. 3. Fork the project and clone it locally: - + `git clone https://github.com//OpenBot.git` 4. Create a branch: - `git checkout -b ` - + `git checkout -b ` + where `` concisely describes the scope of the work. - + 5. Do the work, write good commit messages, push your branch to the forked repository: - - ``` + + ```bash git add git commit -m git push --set-upstream origin ``` - + 6. Create a [pull request](https://github.com/intel-isl/OpenBot/pulls) in GitHub and link the issue to it. It should show up in the "In progress" column in the OpenBot project. 7. Work on any code review feedback you may receive and push it to your fork. The pull request gets updated automatically. 8. Get a cold drink of your choice to reward yourself for making the world a better place. ## Guidelines -- Use same style and formatting as rest of code. - - For the Android code you can run: - 1. `./gradlew checkStyle` --> returns java files with incorrect style. - 2. `./gradlew applyStyle` --> applies neccessary style changes to all java files. - - For the Arduino and Python code, just try to blend in. +- Use same style and formatting as rest of code. + - For the Java (Android) and Python code see [below](#Formatting). + - For any other code, just try to blend in. - Update documentation associated with code changes you made. -- If you want to include 3rd party dependencies, please discuss this in the issue first. +- If you want to include 3rd party dependencies, please discuss this in the issue first. - Pull requests should implement single features with as few changes as possible. - Make sure you don't include temporary or binary files (the gitignores should mostly take care of this). - Rebase/merge master into your branch before you submit the pull request. - If possible, test your code on Windows, Linux and OSX. +## Formatting + +### Java + +We use a gradle script for formatting java code. Make sure you are in the `android` directory. + +You can check your code with: + +```bash +./gradlew checkStyle +``` + +You can apply the style to all files by running: + +```bash +./gradlew applyStyle +``` + +### Python + +We use [black](https://pypi.org/project/black/) for formatting python code. + +You can check your code in the current directory with: + +```bash +black --check . +``` + +You can apply the style to all files in the current directory by running: + +```bash +black . +``` + +## Further resources If you are looking for more information about contributing to open-source projects, here are two good references: diff --git a/README.md b/README.md index 73517488b..71fce26e8 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ OpenBot leverages smartphones as brains for low-cost robots. We have designed a - Build your own [Robot Body](body/README.md) - Flash the [Arduino Firmware](firmware/README.md) - Install the [Android Apps](android/README.md) -- Control the robot via [Python](python/README.md) +- Try the [Python Controller](controller/README.md) - Train your own [Driving Policy](policy/README.md) ## Get the source code diff --git a/android/app/src/main/java/org/openbot/autopilot/AutopilotFragment.java b/android/app/src/main/java/org/openbot/autopilot/AutopilotFragment.java index f2f14f76d..ec08abf33 100644 --- a/android/app/src/main/java/org/openbot/autopilot/AutopilotFragment.java +++ b/android/app/src/main/java/org/openbot/autopilot/AutopilotFragment.java @@ -440,8 +440,11 @@ protected void handleDriveCommand(Control control) { vehicle.setControl(control); float left = vehicle.getLeftSpeed(); float right = vehicle.getRightSpeed(); - binding.controllerContainer.controlInfo.setText( - String.format(Locale.US, "%.0f,%.0f", left, right)); + requireActivity() + .runOnUiThread( + () -> + binding.controllerContainer.controlInfo.setText( + String.format(Locale.US, "%.0f,%.0f", left, right))); } @Override diff --git a/android/app/src/main/java/org/openbot/common/ControlsFragment.java b/android/app/src/main/java/org/openbot/common/ControlsFragment.java index 27c7584d5..8aaf50a98 100644 --- a/android/app/src/main/java/org/openbot/common/ControlsFragment.java +++ b/android/app/src/main/java/org/openbot/common/ControlsFragment.java @@ -168,6 +168,7 @@ private void handlePhoneControllerEvents() { switch (commandType) { case Constants.CMD_DRIVE: JSONObject driveValue = event.getJSONObject("driveCmd"); + vehicle.setControl( new Control( Float.parseFloat(driveValue.getString("l")), diff --git a/android/app/src/main/java/org/openbot/env/ControllerConfig.java b/android/app/src/main/java/org/openbot/env/ControllerConfig.java new file mode 100644 index 000000000..3dd1d83af --- /dev/null +++ b/android/app/src/main/java/org/openbot/env/ControllerConfig.java @@ -0,0 +1,124 @@ +package org.openbot.env; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; +import androidx.preference.PreferenceManager; +import org.openbot.utils.ConnectionUtils; + +public class ControllerConfig { + private static ControllerConfig _controllerConfig; + SharedPreferences preferences; + + enum VIDEO_SERVER_TYPE { + WEBRTC, + RTSP + } + + private final boolean isMirrored = true; + private final boolean mute = true; + + private String currentServerType; + + public static ControllerConfig getInstance() { + if (_controllerConfig == null) { + synchronized (PhoneController.class) { + if (_controllerConfig == null) _controllerConfig = new ControllerConfig(); + } + } + + return _controllerConfig; + } + + void init(Context context) { + preferences = PreferenceManager.getDefaultSharedPreferences(context); + currentServerType = get("video_server", "WEBRTC"); + monitorSettingUpdates(); + } + + private void set(String name, String value) { + SharedPreferences.Editor editor = preferences.edit(); + editor.putString(name, value); + editor.apply(); + } + + private String get(String name, String defaultValue) { + try { + return preferences.getString(name, defaultValue); + } catch (ClassCastException e) { + return defaultValue; + } + } + + private Boolean getBoolean(String name, Boolean defaultValue) { + try { + return preferences.getBoolean(name, defaultValue); + } catch (ClassCastException e) { + return defaultValue; + } + } + + private void setBoolean(String name, boolean value) { + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean(name, value); + editor.apply(); + } + + // specific settings + public boolean isMirrored() { + return getBoolean("MIRRORED", true); + } + + public void setMirrored(boolean mirrored) { + setBoolean("MIRRORED", mirrored); + } + + public boolean isMute() { + return getBoolean("MUTE", true); + } + + public void setMute(boolean mute) { + setBoolean("MUTE", mute); + } + + public String getVideoServerType() { + return get("video_server", "WEBRTC"); + } + + public void setVideoServerType(String type) { + set("video_server", type); + } + + private void monitorSettingUpdates() { + ControllerToBotEventBus.subscribe( + this.getClass().getSimpleName(), + event -> { + String commandType = event.getString("command"); + + switch (commandType) { + case "TOGGLE_MIRROR": + setMirrored(!isMirrored()); + + // inform the controller of current state + BotToControllerEventBus.emitEvent( + ConnectionUtils.createStatus("TOGGLE_MIRROR", isMirrored())); + break; + + case "TOGGLE_SOUND": + setMute(!isMute()); + + // inform the controller of current state + BotToControllerEventBus.emitEvent( + ConnectionUtils.createStatus("TOGGLE_SOUND", isMute())); + break; + } + }, + error -> { + Log.d(null, "Error occurred in monitorConnection: " + error); + }, + event -> + event.has("command") + && (event.getString("command").contains("TOGGLE_MIRROR") + || event.getString("command").contains("TOGGLE_SOUND"))); + } +} diff --git a/android/app/src/main/java/org/openbot/env/ControllerToBotEventBus.java b/android/app/src/main/java/org/openbot/env/ControllerToBotEventBus.java index 555a5f7c5..e7a8fc2e5 100644 --- a/android/app/src/main/java/org/openbot/env/ControllerToBotEventBus.java +++ b/android/app/src/main/java/org/openbot/env/ControllerToBotEventBus.java @@ -7,6 +7,7 @@ import io.reactivex.rxjava3.subjects.PublishSubject; import java.util.HashMap; import java.util.Map; +import org.json.JSONException; import org.json.JSONObject; public final class ControllerToBotEventBus { @@ -17,7 +18,15 @@ private ControllerToBotEventBus() {} private static final PublishSubject subject = PublishSubject.create(); - public static void emitEvent(JSONObject event) { + public static void emitEvent(String event) { + try { + emitEvent(new JSONObject(event)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + private static void emitEvent(JSONObject event) { subject.onNext(event); } diff --git a/android/app/src/main/java/org/openbot/env/ImageUtils.java b/android/app/src/main/java/org/openbot/env/ImageUtils.java index eac0e06b5..5dfa3a470 100755 --- a/android/app/src/main/java/org/openbot/env/ImageUtils.java +++ b/android/app/src/main/java/org/openbot/env/ImageUtils.java @@ -21,6 +21,7 @@ import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.RectF; +import android.os.Build; import android.view.Surface; import java.io.File; import java.io.FileOutputStream; @@ -227,7 +228,11 @@ public static Matrix getTransformationMatrix( } public static int getScreenOrientation(Activity activity) { - switch (activity.getDisplay().getRotation()) { + int rotation = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + ? activity.getDisplay().getRotation() + : activity.getWindowManager().getDefaultDisplay().getRotation(); + switch (rotation) { case Surface.ROTATION_270: return 270; case Surface.ROTATION_180: diff --git a/android/app/src/main/java/org/openbot/env/NearbyConnection.java b/android/app/src/main/java/org/openbot/env/NearbyConnection.java index 6d90abd79..96ae8b323 100755 --- a/android/app/src/main/java/org/openbot/env/NearbyConnection.java +++ b/android/app/src/main/java/org/openbot/env/NearbyConnection.java @@ -37,8 +37,6 @@ import java.util.Timer; import java.util.TimerTask; import org.jetbrains.annotations.NotNull; -import org.json.JSONException; -import org.json.JSONObject; import org.openbot.OpenBotApplication; import org.openbot.utils.ConnectionUtils; import timber.log.Timber; @@ -156,11 +154,7 @@ public void onConnectionResult(@NonNull String endpointId, ConnectionResolution pairedDeviceEndpointId = endpointId; isConnected = true; - try { - ControllerToBotEventBus.emitEvent(new JSONObject("{command: \"CONNECTED\"}")); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent("{command: \"CONNECTED\"}"); } else { Timber.i("onConnectionResult: connection failed"); isConnected = false; @@ -181,11 +175,7 @@ public void onDisconnected(@NonNull String endpointId) { Toast.LENGTH_LONG) .show(); Timber.i("onDisconnected: disconnected from the opponent"); - try { - ControllerToBotEventBus.emitEvent(new JSONObject("{command: \"DISCONNECTED\"}")); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent("{command: \"DISCONNECTED\"}"); } }; diff --git a/android/app/src/main/java/org/openbot/env/NetworkServiceConnection.java b/android/app/src/main/java/org/openbot/env/NetworkServiceConnection.java index 91207195f..b168ab46f 100644 --- a/android/app/src/main/java/org/openbot/env/NetworkServiceConnection.java +++ b/android/app/src/main/java/org/openbot/env/NetworkServiceConnection.java @@ -18,8 +18,6 @@ import java.util.Scanner; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import org.json.JSONException; -import org.json.JSONObject; import org.openbot.utils.ConnectionUtils; import timber.log.Timber; @@ -149,12 +147,7 @@ public void onServiceLost(NsdServiceInfo service) { ((Activity) context) .runOnUiThread( () -> { - try { - ControllerToBotEventBus.emitEvent( - new JSONObject("{command: \"DISCONNECTED\"}")); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent("{command: \"DISCONNECTED\"}"); }); } @@ -209,11 +202,7 @@ public void onServiceResolved(NsdServiceInfo serviceInfo) { ((Activity) context) .runOnUiThread( () -> { - try { - ControllerToBotEventBus.emitEvent(new JSONObject("{command: \"CONNECTED\"}")); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent("{command: \"CONNECTED\"}"); }); new Thread("Receiver Thread") { @@ -340,12 +329,7 @@ void close() { ((Activity) context) .runOnUiThread( () -> { - try { - ControllerToBotEventBus.emitEvent( - new JSONObject("{command: \"DISCONNECTED\"}")); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent("{command: \"DISCONNECTED\"}"); }); } catch (IOException e) { e.printStackTrace(); diff --git a/android/app/src/main/java/org/openbot/env/PhoneController.java b/android/app/src/main/java/org/openbot/env/PhoneController.java index 8b75260d3..e749ef31b 100644 --- a/android/app/src/main/java/org/openbot/env/PhoneController.java +++ b/android/app/src/main/java/org/openbot/env/PhoneController.java @@ -2,13 +2,10 @@ import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import androidx.preference.PreferenceManager; -import org.json.JSONException; import org.json.JSONObject; import org.openbot.R; import org.openbot.customview.AutoFitSurfaceGlView; @@ -23,12 +20,6 @@ public class PhoneController { private ConnectionSelector connectionSelector; private IVideoServer videoServer; private View view = null; - private Config config = new Config(); - - private enum VIDEO_SERVER_TYPE { - WEBRTC, - RTSP - }; public static PhoneController getInstance(Context context) { if (_phoneController == null) { // Check for the first time @@ -40,65 +31,24 @@ public static PhoneController getInstance(Context context) { } } - // The function bellow will attempt to switch server types if another is selected in settings. - // However, this is not reliable at the moment so it is not used here. - // The user can change the setting once when the app is started, then select FreeRoam fragment. - // Future changes will not be detected until app is restarted.. - - // _phoneController.config.checkForConfigChange(context); - return _phoneController; } - private class Config { - VIDEO_SERVER_TYPE currentServerType; - - void init(Context context) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - currentServerType = - preferences.getString("video_server", "WebRTC").equals("RTSP") - ? VIDEO_SERVER_TYPE.RTSP - : VIDEO_SERVER_TYPE.WEBRTC; - } - - private void checkForConfigChange(Context context) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - VIDEO_SERVER_TYPE serverType = - preferences.getString("video_server", "WebRTC").equals("RTSP") - ? VIDEO_SERVER_TYPE.RTSP - : VIDEO_SERVER_TYPE.WEBRTC; - if (serverType != currentServerType) { - currentServerType = serverType; - if (videoServer != null) { - videoServer.setCanStart(false); // stop old server - } - // we have a new video server, we must re-init the Phone Controller so the server will start - // properly. - PhoneController.this.init(context); - } - } - } - class DataReceived implements IDataReceived { @Override public void dataReceived(String commandStr) { - try { - ControllerToBotEventBus.emitEvent(new JSONObject(commandStr)); - } catch (JSONException e) { - e.printStackTrace(); - } + ControllerToBotEventBus.emitEvent(commandStr); } } private void init(Context context) { - config.init(context); - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - VIDEO_SERVER_TYPE serverType = - preferences.getString("video_server", "WebRTC").equals("RTSP") - ? VIDEO_SERVER_TYPE.RTSP - : VIDEO_SERVER_TYPE.WEBRTC; - videoServer = serverType == VIDEO_SERVER_TYPE.RTSP ? new RtspServer() : new WebRtcServer(); + ControllerConfig.getInstance().init(context); + + videoServer = + "RTSP".equals(ControllerConfig.getInstance().getVideoServerType()) + ? new RtspServer() + : new WebRtcServer(); + videoServer.init(context); videoServer.setCanStart(true); diff --git a/android/app/src/main/java/org/openbot/env/RtspServer.java b/android/app/src/main/java/org/openbot/env/RtspServer.java index 17dbca9ac..8ee83d11f 100644 --- a/android/app/src/main/java/org/openbot/env/RtspServer.java +++ b/android/app/src/main/java/org/openbot/env/RtspServer.java @@ -34,7 +34,6 @@ public class RtspServer private final String TAG = "RtspServerPedroOpenGL"; private RtspServerCamera1 rtspServerCamera1; private View view; - private final MirrorImageSetter mirror = new MirrorImageSetter(); private AndGate andGate; private Context context; private Size resolution = new Size(640, 360); @@ -184,7 +183,10 @@ private void startServer(Size resolution, int port) { resolution.getWidth(), resolution.getHeight(), 20, 1200 * 1024, 2, 0)) { rtspServerCamera1.startStream(""); - cameraControlHandler.disableAudio(); + boolean isMute = ControllerConfig.getInstance().isMute(); + if (isMute) { + cameraControlHandler.disableAudio(); + } // Delay starting the client for a second to make sure the server is started. Runnable action = () -> startClient(); @@ -199,18 +201,6 @@ public void setResolution(int w, int h) { andGate.set("resolution set", true); } - class MirrorImageSetter { - public boolean isMirrored() { - return isMirrored; - } - - public void setMirrored(boolean mirrored) { - isMirrored = mirrored; - } - - private boolean isMirrored = true; - } - // ConnectCheckerRtsp callbacks @Override public void onConnectionSuccessRtsp() { @@ -291,34 +281,14 @@ private void disableAudio() { } private void handleCameraControlEvents() { + ControllerConfig config = ControllerConfig.getInstance(); + ControllerToBotEventBus.subscribe( this.getClass().getSimpleName(), event -> { String commandType = event.getString("command"); - - switch (commandType) { - case "TOGGLE_SOUND": - Log.i(TAG, "TOGGLE_SOUND"); - - if (rtspServerCamera1.isAudioMuted()) rtspServerCamera1.enableAudio(); - else rtspServerCamera1.disableAudio(); - - // inform the controller of current state - BotToControllerEventBus.emitEvent( - ConnectionUtils.createStatus( - "TOGGLE_SOUND", !rtspServerCamera1.isAudioMuted())); - break; - - case "TOGGLE_MIRROR": - Log.i(TAG, "TOGGLE_MIRROR"); - - mirror.setMirrored(!mirror.isMirrored()); - - // inform the controller of current state - BotToControllerEventBus.emitEvent( - ConnectionUtils.createStatus("TOGGLE_MIRROR", mirror.isMirrored())); - break; - } + if (rtspServerCamera1.isAudioMuted()) rtspServerCamera1.enableAudio(); + else rtspServerCamera1.disableAudio(); }, error -> Log.d(null, "Error occurred in ControllerToBotEventBus: " + error), event -> diff --git a/android/app/src/main/java/org/openbot/env/SharedPreferencesManager.java b/android/app/src/main/java/org/openbot/env/SharedPreferencesManager.java index 8d24b1350..a5fa724e1 100644 --- a/android/app/src/main/java/org/openbot/env/SharedPreferencesManager.java +++ b/android/app/src/main/java/org/openbot/env/SharedPreferencesManager.java @@ -32,6 +32,7 @@ public class SharedPreferencesManager { private static final String NUM_THREAD = "NUM_THREAD"; private static final String CAMERA_SWITCH = "CAMERA_SWITCH"; private static final String SHEET_EXPANDED = "SHEET_EXPANDED"; + private static final String DELAY = "DELAY"; private final SharedPreferences preferences; @@ -145,4 +146,20 @@ public void setCameraSwitch(boolean isChecked) { public void setSheetExpanded(boolean expanded) { preferences.edit().putBoolean(SHEET_EXPANDED, expanded).apply(); } + + public void setSensorStatus(boolean status, String sensor) { + preferences.edit().putBoolean(sensor, status).apply(); + } + + public boolean getSensorStatus(String sensor) { + return preferences.getBoolean(sensor, false); + } + + public void setDelay(int delay) { + preferences.edit().putInt(DELAY, delay).apply(); + } + + public int getDelay() { + return preferences.getInt(DELAY, 200); + } } diff --git a/android/app/src/main/java/org/openbot/env/WebRtcServer.java b/android/app/src/main/java/org/openbot/env/WebRtcServer.java index 74e864f04..e3d6427e0 100644 --- a/android/app/src/main/java/org/openbot/env/WebRtcServer.java +++ b/android/app/src/main/java/org/openbot/env/WebRtcServer.java @@ -61,7 +61,6 @@ public class WebRtcServer implements IVideoServer { private final String TAG = "WebRtcPeer"; private SurfaceViewRenderer view; private Size resolution = new Size(640, 360); - private MirrorImageSetter mirror = new MirrorImageSetter(); public static final String VIDEO_TRACK_ID = "ARDAMSv0"; public static final int VIDEO_RESOLUTION_WIDTH = 640; @@ -205,7 +204,10 @@ private void startStreamingVideo() { mediaStream.addTrack(localAudioTrack); peerConnection.addStream(mediaStream); - cameraControlHandler.disableAudio(); + boolean isMute = ControllerConfig.getInstance().isMute(); + if (isMute) { + cameraControlHandler.disableAudio(); + } } private void stopStreamingVideo() { @@ -446,43 +448,19 @@ private void disableAudio() { } private void handleCameraControlEvents() { + ControllerConfig config = ControllerConfig.getInstance(); ControllerToBotEventBus.subscribe( this.getClass().getSimpleName(), event -> { String commandType = event.getString("command"); - switch (commandType) { - case "TOGGLE_SOUND": - if (mediaStream.audioTracks.size() > 0) { - AudioTrack audio = mediaStream.audioTracks.get(0); - audio.setEnabled(!audio.enabled()); - - // inform the controller of current state - BotToControllerEventBus.emitEvent( - ConnectionUtils.createStatus("TOGGLE_SOUND", audio.enabled())); - } - - break; - - case "TOGGLE_MIRROR": - mirror.setMirrored(!mirror.isMirrored()); - - // inform the controller of current state - BotToControllerEventBus.emitEvent( - ConnectionUtils.createStatus("TOGGLE_MIRROR", mirror.isMirrored())); - break; + if (mediaStream.audioTracks.size() > 0) { + AudioTrack audio = mediaStream.audioTracks.get(0); + audio.setEnabled(!audio.enabled()); } }, error -> Log.d(null, "Error occurred in ControllerToBotEventBus: " + error), - event -> - event.has("command") - && ("TOGGLE_SOUND".equals(event.getString("command")) - || "TOGGLE_MIRROR" - .equals( - event.getString( - "command"))) // filter out all but the "TOGGLE_SOUND" and - // "TOGGLE_MIRROR" commands.. - ); + event -> event.has("command") && "TOGGLE_SOUND".equals(event.getString("command"))); } } @@ -492,18 +470,6 @@ private void beep() { tg.startTone(ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE); } - class MirrorImageSetter { - public boolean isMirrored() { - return isMirrored; - } - - public void setMirrored(boolean mirrored) { - isMirrored = mirrored; - } - - private boolean isMirrored = true; - } - class SignalingHandler { void handleControllerWebRtcEvents() { ControllerToBotEventBus.subscribe( diff --git a/android/app/src/main/java/org/openbot/logging/LoggerFragment.java b/android/app/src/main/java/org/openbot/logging/LoggerFragment.java index 8932ec0dc..f56d9790b 100644 --- a/android/app/src/main/java/org/openbot/logging/LoggerFragment.java +++ b/android/app/src/main/java/org/openbot/logging/LoggerFragment.java @@ -87,6 +87,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat setControlMode(Enums.ControlMode.getByID(preferencesManager.getControlMode())); setDriveMode(Enums.DriveMode.getByID(preferencesManager.getDriveMode())); + binding.sensorDataButton.setOnClickListener( + v -> { + SensorsDialog sensorsDialog = new SensorsDialog(); + sensorsDialog.show(getChildFragmentManager(), sensorsDialog.getTag()); + }); binding.controllerContainer.controlMode.setOnClickListener( v -> { Enums.ControlMode controlMode = diff --git a/android/app/src/main/java/org/openbot/logging/SensorListAdapter.java b/android/app/src/main/java/org/openbot/logging/SensorListAdapter.java new file mode 100644 index 000000000..989c27179 --- /dev/null +++ b/android/app/src/main/java/org/openbot/logging/SensorListAdapter.java @@ -0,0 +1,55 @@ +package org.openbot.logging; + +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.CheckBox; +import androidx.recyclerview.widget.RecyclerView; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.openbot.databinding.ItemSensorBinding; +import org.openbot.env.SharedPreferencesManager; + +public class SensorListAdapter extends RecyclerView.Adapter { + + private final List names; + private final List status; + private final SharedPreferencesManager preferencesManager; + + public SensorListAdapter( + HashMap items, SharedPreferencesManager preferencesManager) { + names = new ArrayList<>(items.keySet()); + status = new ArrayList<>(items.values()); + this.preferencesManager = preferencesManager; + } + + @NotNull + @Override + public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) { + return new ViewHolder( + ItemSensorBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + holder.checkBox.setText(names.get(position)); + holder.checkBox.setChecked(status.get(position)); + holder.checkBox.setOnClickListener( + v -> preferencesManager.setSensorStatus(holder.checkBox.isChecked(), names.get(position))); + } + + @Override + public int getItemCount() { + return names.size(); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + public CheckBox checkBox; + + public ViewHolder(ItemSensorBinding binding) { + super(binding.getRoot()); + checkBox = binding.check; + } + } +} diff --git a/android/app/src/main/java/org/openbot/logging/SensorService.java b/android/app/src/main/java/org/openbot/logging/SensorService.java index f3c29f40f..2c2a714cf 100644 --- a/android/app/src/main/java/org/openbot/logging/SensorService.java +++ b/android/app/src/main/java/org/openbot/logging/SensorService.java @@ -10,12 +10,14 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Location; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.SystemClock; +import androidx.annotation.RequiresApi; import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationCallback; import com.google.android.gms.location.LocationRequest; @@ -27,6 +29,8 @@ import java.io.IOException; import org.openbot.R; import org.openbot.env.Logger; +import org.openbot.env.SharedPreferencesManager; +import org.openbot.utils.Enums; public class SensorService extends Service implements SensorEventListener { private SensorManager sensorManager; @@ -37,9 +41,9 @@ public class SensorService extends Service implements SensorEventListener { private Sensor lightSensor; private Sensor proximitySensor; private Sensor pressureSensor; + private Sensor temperatureSensor; private Sensor poseSensor; private Sensor motionSensor; - private Sensor stationarySensor; private BufferedWriter accelerometerLog; private BufferedWriter gyroscopeLog; @@ -48,6 +52,7 @@ public class SensorService extends Service implements SensorEventListener { private BufferedWriter lightLog; private BufferedWriter proximityLog; private BufferedWriter pressureLog; + private BufferedWriter temperatureLog; private BufferedWriter poseLog; private BufferedWriter motionLog; private BufferedWriter gpsLog; @@ -72,6 +77,9 @@ public class SensorService extends Service implements SensorEventListener { private static final Logger LOGGER = new Logger(); Messenger messenger = new Messenger(new SensorMessageHandler()); + private SharedPreferencesManager preferencesManager; + + @RequiresApi(api = Build.VERSION_CODES.N) @Override public final void onCreate() { super.onCreate(); @@ -83,11 +91,12 @@ public final void onCreate() { lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); + temperatureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); poseSensor = sensorManager.getDefaultSensor(Sensor.TYPE_POSE_6DOF); motionSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MOTION_DETECT); - stationarySensor = sensorManager.getDefaultSensor(Sensor.TYPE_STATIONARY_DETECT); // Initialize the FusedLocationClient. fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); + preferencesManager = new SharedPreferencesManager(this); } @Override @@ -103,44 +112,81 @@ public int onStartCommand(Intent intent, int flags, int startId) { logFolder = (String) extras.get("logFolder"); } - accelerometerLog = openLog(logFolder, "accelerometerLog.txt"); - // appendLog(mAccelerometerLog, mAccelerometer.getName()); - appendLog(accelerometerLog, "timestamp[ns],x[m/s^2],y[m/s^2],z[m/s^2]"); + int delay = (int) (preferencesManager.getDelay() * 1000); + if (preferencesManager.getSensorStatus(Enums.SensorType.ACCELEROMETER.getSensor()) + && accelerometerSensor != null) { + accelerometerLog = openLog(logFolder, "accelerometerLog.txt"); + appendLog(accelerometerLog, "timestamp[ns],x[m/s^2],y[m/s^2],z[m/s^2]"); + sensorManager.registerListener(this, accelerometerSensor, delay); + } + + if (preferencesManager.getSensorStatus(Enums.SensorType.GYROSCOPE.getSensor()) + && gyroscopeSensor != null) { + gyroscopeLog = openLog(logFolder, "gyroscopeLog.txt"); + appendLog(gyroscopeLog, "timestamp[ns],x[rad/s],y[rad/s],z[rad/s]"); + sensorManager.registerListener(this, gyroscopeSensor, delay); + } - gyroscopeLog = openLog(logFolder, "gyroscopeLog.txt"); - // appendLog(mGyroscopeLog, mGyroscope.getName()); - appendLog(gyroscopeLog, "timestamp[ns],x[rad/s],y[rad/s],z[rad/s]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.GRAVITY.getSensor()) + && gravitySensor != null) { + gravityLog = openLog(logFolder, "gravityLog.txt"); + appendLog(gravityLog, "timestamp[ns],x[m/s^2],y[m/s^2],z[m/s^2]"); + sensorManager.registerListener(this, gravitySensor, delay); + } - gravityLog = openLog(logFolder, "gravityLog.txt"); - // appendLog(mGravityLog, mGravity.getName()); - appendLog(gravityLog, "timestamp[ns],x[m/s^2],y[m/s^2],z[m/s^2]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.MAGNETIC.getSensor()) + && magneticSensor != null) { + magneticLog = openLog(logFolder, "magneticLog.txt"); + appendLog(magneticLog, "timestamp[ns],x[uT],y[uT],z[uT]"); + sensorManager.registerListener(this, magneticSensor, delay); + } - magneticLog = openLog(logFolder, "magneticLog.txt"); - // appendLog(mMagneticLog, mMagnetic.getName()); - appendLog(magneticLog, "timestamp[ns],x[uT],y[uT],z[uT]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.LIGHT.getSensor()) + && lightSensor != null) { + lightLog = openLog(logFolder, "lightLog.txt"); + appendLog(lightLog, "timestamp[ns],light[lux]"); + sensorManager.registerListener(this, lightSensor, delay); + } - lightLog = openLog(logFolder, "lightLog.txt"); - // appendLog(mLightLog, mLight.getName()); - appendLog(lightLog, "timestamp[ns],light[lux]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.PROXIMITY.getSensor()) + && proximitySensor != null) { + proximityLog = openLog(logFolder, "proximityLog.txt"); + appendLog(proximityLog, "timestamp[ns],proximity[cm]"); + sensorManager.registerListener(this, proximitySensor, delay); + } - proximityLog = openLog(logFolder, "proximityLog.txt"); - // appendLog(mProximityLog, mProximity.getName()); - appendLog(proximityLog, "timestamp[ns],proximity[cm]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.PRESSURE.getSensor()) + && pressureSensor != null) { + pressureLog = openLog(logFolder, "pressureLog.txt"); + appendLog(pressureLog, "timestamp[ns],pressure[hPa]"); + sensorManager.registerListener(this, pressureSensor, delay); + } - pressureLog = openLog(logFolder, "pressureLog.txt"); - // appendLog(mPressureLog, mPressure.getName()); - appendLog(pressureLog, "timestamp[ns],pressure[hPa]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.TEMPERATURE.getSensor()) + && temperatureSensor != null) { + temperatureLog = openLog(logFolder, "temperatureLog.txt"); + appendLog(temperatureLog, "timestamp[ns],temperature[degrees]"); + sensorManager.registerListener(this, temperatureSensor, delay); + } - poseLog = openLog(logFolder, "poseLog.txt"); - // appendLog(mPoseLog, mPose.getName()); - appendLog(poseLog, "timestamp[ns],x,y,z,w,x,y,z,dx,dy,dz,dw,dx,dy,dz,id"); + if (preferencesManager.getSensorStatus(Enums.SensorType.POSE.getSensor()) + && poseSensor != null) { + poseLog = openLog(logFolder, "poseLog.txt"); + appendLog(poseLog, "timestamp[ns],x,y,z,w,x,y,z,dx,dy,dz,dw,dx,dy,dz,id"); + sensorManager.registerListener(this, poseSensor, delay); + } - motionLog = openLog(logFolder, "motionLog.txt"); - // appendLog(mMotionLog, mMotion.getName()); - appendLog(motionLog, "timestamp[ns],motion"); + if (preferencesManager.getSensorStatus(Enums.SensorType.MOTION.getSensor()) + && motionSensor != null) { + motionLog = openLog(logFolder, "motionLog.txt"); + appendLog(motionLog, "timestamp[ns],motion"); + sensorManager.registerListener(this, motionSensor, delay); + } - gpsLog = openLog(logFolder, "gpsLog.txt"); - appendLog(gpsLog, "timestamp[ns],latitude,longitude,altitude[m],bearing,speed[m/s]"); + if (preferencesManager.getSensorStatus(Enums.SensorType.GPS.getSensor())) { + gpsLog = openLog(logFolder, "gpsLog.txt"); + appendLog(gpsLog, "timestamp[ns],latitude,longitude,altitude[m],bearing,speed[m/s]"); + } frameLog = openLog(logFolder, "rgbFrames.txt"); appendLog(frameLog, "timestamp[ns],frame"); @@ -154,28 +200,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { indicatorLog = openLog(logFolder, "indicatorLog.txt"); appendLog(indicatorLog, "timestamp[ns],signal"); - vehicleLog = openLog(logFolder, "vehicleLog.txt"); - appendLog(vehicleLog, "timestamp[ns],batteryVoltage,leftWheel,rightWheel,obstacle"); - - sensorManager.registerListener(this, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, gyroscopeSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, gravitySensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, magneticSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, lightSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, proximitySensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, pressureSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, poseSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, motionSensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); - sensorManager.registerListener( - this, stationarySensor, android.hardware.SensorManager.SENSOR_DELAY_NORMAL); + if (preferencesManager.getSensorStatus(Enums.SensorType.VEHICLE.getSensor())) { + vehicleLog = openLog(logFolder, "vehicleLog.txt"); + appendLog(vehicleLog, "timestamp[ns],batteryVoltage,leftWheel,rightWheel,obstacle"); + } locationCallback = new LocationCallback() { @@ -290,6 +318,10 @@ public final void onSensorChanged(SensorEvent event) { // Atmospheric pressure in mPa (millibar) appendLog(pressureLog, event.timestamp + "," + event.values[0]); break; + case Sensor.TYPE_AMBIENT_TEMPERATURE: + // Ambient temperature in degrees + appendLog(temperatureLog, event.timestamp + "," + event.values[0]); + break; case Sensor.TYPE_POSE_6DOF: // values[0]: x*sin(θ/2) // values[1]: y*sin(θ/2) @@ -395,6 +427,7 @@ public void onDestroy() { if (lightLog != null) closeLog(lightLog); if (proximityLog != null) closeLog(proximityLog); if (pressureLog != null) closeLog(pressureLog); + if (temperatureLog != null) closeLog(temperatureLog); if (poseLog != null) closeLog(poseLog); if (motionLog != null) closeLog(motionLog); if (gpsLog != null) closeLog(gpsLog); @@ -441,7 +474,7 @@ public void appendLog(BufferedWriter writer, String text) { writer.append(text); writer.newLine(); writer.flush(); - } catch (IOException e) { + } catch (Exception e) { e.printStackTrace(); } } diff --git a/android/app/src/main/java/org/openbot/logging/SensorsDialog.java b/android/app/src/main/java/org/openbot/logging/SensorsDialog.java new file mode 100644 index 000000000..38343a6ae --- /dev/null +++ b/android/app/src/main/java/org/openbot/logging/SensorsDialog.java @@ -0,0 +1,83 @@ +package org.openbot.logging; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.openbot.R; +import org.openbot.databinding.DialogSensorsBinding; +import org.openbot.env.SharedPreferencesManager; +import org.openbot.utils.Enums; + +public class SensorsDialog extends DialogFragment { + + public SensorsDialog() {} + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog); + } + + @Override + public View onCreateView( + @NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + SharedPreferencesManager preferencesManager = new SharedPreferencesManager(requireContext()); + DialogSensorsBinding binding = DialogSensorsBinding.inflate(LayoutInflater.from(getContext())); + SensorManager sensorManager = + (SensorManager) requireActivity().getSystemService(Context.SENSOR_SERVICE); + List sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL); + + HashMap list = new LinkedHashMap<>(); + list.put( + Enums.SensorType.VEHICLE.getSensor(), + preferencesManager.getSensorStatus(Enums.SensorType.VEHICLE.getSensor())); + + // Every modern phone has GPS + list.put( + Enums.SensorType.GPS.getSensor(), + preferencesManager.getSensorStatus(Enums.SensorType.GPS.getSensor())); + + for (Sensor sensor : sensorList) { + for (Enums.SensorType name : Enums.SensorType.values()) + if (sensor.getStringType().toLowerCase().contains(name.getSensor().toLowerCase())) { + list.put(name.getSensor(), preferencesManager.getSensorStatus(name.getSensor())); + break; + } + } + + SensorListAdapter adapter = new SensorListAdapter(list, preferencesManager); + + binding.listView.setLayoutManager(new LinearLayoutManager(requireContext())); + binding.listView.setAdapter(adapter); + binding.dismiss.setOnClickListener(v -> dismiss()); + + binding.delay.setText(String.valueOf(preferencesManager.getDelay())); + binding.delay.addTextChangedListener( + new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + if (!s.toString().isEmpty()) + preferencesManager.setDelay(Integer.parseInt(s.toString())); + } + }); + return binding.getRoot(); + } +} diff --git a/android/app/src/main/java/org/openbot/main/SettingsFragment.java b/android/app/src/main/java/org/openbot/main/SettingsFragment.java index aa2985780..6ed322a7f 100644 --- a/android/app/src/main/java/org/openbot/main/SettingsFragment.java +++ b/android/app/src/main/java/org/openbot/main/SettingsFragment.java @@ -7,8 +7,10 @@ import android.app.Activity; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.provider.Settings; import android.view.View; import android.widget.Toast; @@ -16,7 +18,9 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.lifecycle.ViewModelProvider; +import androidx.preference.ListPreference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreferenceCompat; import org.openbot.R; @@ -37,41 +41,40 @@ public class SettingsFragment extends PreferenceFragmentCompat { private final ActivityResultLauncher requestPermissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestMultiplePermissions(), - result -> { - result.forEach( - (permission, granted) -> { - switch (permission) { - case PERMISSION_CAMERA: - if (granted) camera.setChecked(true); - else { - camera.setChecked(false); - PermissionUtils.showCameraPermissionSettingsToast(requireActivity()); - } - break; - case PERMISSION_STORAGE: - if (granted) storage.setChecked(true); - else { - storage.setChecked(false); - PermissionUtils.showStoragePermissionSettingsToast(requireActivity()); - } - break; - case PERMISSION_LOCATION: - if (granted) location.setChecked(true); - else { - location.setChecked(false); - PermissionUtils.showLocationPermissionSettingsToast(requireActivity()); - } - break; - case PERMISSION_AUDIO: - if (granted) mic.setChecked(true); - else { - mic.setChecked(false); - PermissionUtils.showAudioPermissionSettingsToast(requireActivity()); - } - break; - } - }); - }); + result -> + result.forEach( + (permission, granted) -> { + switch (permission) { + case PERMISSION_CAMERA: + if (granted) camera.setChecked(true); + else { + camera.setChecked(false); + PermissionUtils.showCameraPermissionSettingsToast(requireActivity()); + } + break; + case PERMISSION_STORAGE: + if (granted) storage.setChecked(true); + else { + storage.setChecked(false); + PermissionUtils.showStoragePermissionSettingsToast(requireActivity()); + } + break; + case PERMISSION_LOCATION: + if (granted) location.setChecked(true); + else { + location.setChecked(false); + PermissionUtils.showLocationPermissionSettingsToast(requireActivity()); + } + break; + case PERMISSION_AUDIO: + if (granted) mic.setChecked(true); + else { + mic.setChecked(false); + PermissionUtils.showAudioPermissionSettingsToast(requireActivity()); + } + break; + } + })); @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -185,6 +188,41 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { return false; }); } + + ListPreference streamMode = findPreference("video_server"); + + if (streamMode != null) + streamMode.setOnPreferenceChangeListener( + (preference, newValue) -> { + AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); + builder.setTitle(R.string.confirm_title); + builder.setMessage(R.string.stream_change_body); + builder.setPositiveButton( + "Yes", + (dialog, id) -> { + streamMode.setValue(newValue.toString()); + restartApp(); + }); + builder.setNegativeButton( + "Cancel", (dialog, id) -> streamMode.setValue(streamMode.getEntry().toString())); + AlertDialog dialog = builder.create(); + dialog.show(); + return false; + }); + } + + private void restartApp() { + new Handler() + .postDelayed( + () -> { + final PackageManager pm = requireActivity().getPackageManager(); + final Intent intent = + pm.getLaunchIntentForPackage(requireActivity().getPackageName()); + requireActivity().finishAffinity(); // Finishes all activities. + requireActivity().startActivity(intent); // Start the launch activity + System.exit(0); // System finishes and automatically relaunches us. + }, + 100); } @Override diff --git a/android/app/src/main/java/org/openbot/objectNav/ObjectNavFragment.java b/android/app/src/main/java/org/openbot/objectNav/ObjectNavFragment.java index a9d73d315..c93bbf984 100644 --- a/android/app/src/main/java/org/openbot/objectNav/ObjectNavFragment.java +++ b/android/app/src/main/java/org/openbot/objectNav/ObjectNavFragment.java @@ -497,8 +497,11 @@ protected void handleDriveCommand(Control control) { vehicle.setControl(control); float left = vehicle.getLeftSpeed(); float right = vehicle.getRightSpeed(); - binding.controllerContainer.controlInfo.setText( - String.format(Locale.US, "%.0f,%.0f", left, right)); + requireActivity() + .runOnUiThread( + () -> + binding.controllerContainer.controlInfo.setText( + String.format(Locale.US, "%.0f,%.0f", left, right))); } protected Model getModel() { diff --git a/android/app/src/main/java/org/openbot/robot/FreeRoamFragment.java b/android/app/src/main/java/org/openbot/robot/FreeRoamFragment.java index 9acfe3006..7452e80d3 100644 --- a/android/app/src/main/java/org/openbot/robot/FreeRoamFragment.java +++ b/android/app/src/main/java/org/openbot/robot/FreeRoamFragment.java @@ -274,12 +274,14 @@ protected void processControllerKeyData(String commandType) { handleDriveCommand(); setControlMode(ControlMode.GAMEPAD); break; + case Constants.CMD_SPEED_DOWN: setSpeedMode( Enums.toggleSpeed( Enums.Direction.DOWN.getValue(), Enums.SpeedMode.getByID(preferencesManager.getSpeedMode()))); break; + case Constants.CMD_SPEED_UP: setSpeedMode( Enums.toggleSpeed( diff --git a/android/app/src/main/java/org/openbot/tflite/Autopilot.java b/android/app/src/main/java/org/openbot/tflite/Autopilot.java index 2c85ea6a4..d8ab9df6b 100644 --- a/android/app/src/main/java/org/openbot/tflite/Autopilot.java +++ b/android/app/src/main/java/org/openbot/tflite/Autopilot.java @@ -11,6 +11,7 @@ import java.nio.ByteOrder; import java.util.Arrays; import org.openbot.env.Control; +import timber.log.Timber; public abstract class Autopilot extends Network { @@ -24,8 +25,11 @@ public abstract class Autopilot extends Network { * @return A detector with the desired configuration. */ - /** A ByteBuffer to hold image data, to be feed into Tensorflow Lite as inputs. */ - protected ByteBuffer indicatorBuffer = null; + /** A ByteBuffer to hold data, to be feed into Tensorflow Lite as inputs. */ + protected ByteBuffer cmdBuffer = null; + + private int cmdIndex; + private int imgIndex; public static Autopilot create(Activity activity, Model model, Device device, int numThreads) throws IOException, IllegalArgumentException { @@ -36,25 +40,30 @@ public static Autopilot create(Activity activity, Model model, Device device, in protected Autopilot(Activity activity, Model model, Device device, int numThreads) throws IOException, IllegalArgumentException { super(activity, model, device, numThreads); - - tflite.getInputIndex("cmd_input"); + try { + cmdIndex = tflite.getInputIndex("serving_default_cmd_input:0"); + imgIndex = tflite.getInputIndex("serving_default_img_input:0"); + } catch (Exception e) { + cmdIndex = tflite.getInputIndex("cmd_input"); + imgIndex = tflite.getInputIndex("img_input"); + } if (!Arrays.equals( - tflite.getInputTensor(tflite.getInputIndex("img_input")).shape(), + tflite.getInputTensor(imgIndex).shape(), new int[] {1, getImageSizeY(), getImageSizeX(), 3})) throw new IllegalArgumentException("Invalid tensor dimensions"); - indicatorBuffer = ByteBuffer.allocateDirect(4); - indicatorBuffer.order(ByteOrder.nativeOrder()); + cmdBuffer = ByteBuffer.allocateDirect(4); + cmdBuffer.order(ByteOrder.nativeOrder()); - LOGGER.d("Created a Tensorflow Lite Autopilot."); + Timber.d("Created a Tensorflow Lite Autopilot."); } private void convertIndicatorToByteBuffer(int indicator) { - if (indicatorBuffer == null) { + if (cmdBuffer == null) { return; } - indicatorBuffer.rewind(); - indicatorBuffer.putFloat(indicator); + cmdBuffer.rewind(); + cmdBuffer.putFloat(indicator); } public Control recognizeImage(final Bitmap bitmap, final int indicator) { @@ -69,10 +78,10 @@ public Control recognizeImage(final Bitmap bitmap, final int indicator) { Trace.beginSection("runInference"); long startTime = SystemClock.elapsedRealtime(); Object[] inputArray; - if (tflite.getInputIndex("cmd_input") == 0) { - inputArray = new Object[] {indicatorBuffer, imgData}; + if (cmdIndex == 0) { + inputArray = new Object[] {cmdBuffer, imgData}; } else { - inputArray = new Object[] {imgData, indicatorBuffer}; + inputArray = new Object[] {imgData, cmdBuffer}; } float[][] predicted_ctrl = new float[1][2]; @@ -80,7 +89,7 @@ public Control recognizeImage(final Bitmap bitmap, final int indicator) { tflite.runForMultipleInputsOutputs(inputArray, outputMap); long endTime = SystemClock.elapsedRealtime(); Trace.endSection(); - LOGGER.v("Timecost to run model inference: " + (endTime - startTime)); + Timber.v("Timecost to run model inference: %s", (endTime - startTime)); Trace.endSection(); // "recognizeImage" return new Control(predicted_ctrl[0][0], predicted_ctrl[0][1]); diff --git a/android/app/src/main/java/org/openbot/tflite/Detector.java b/android/app/src/main/java/org/openbot/tflite/Detector.java index 0beddab7e..18eb3d00f 100755 --- a/android/app/src/main/java/org/openbot/tflite/Detector.java +++ b/android/app/src/main/java/org/openbot/tflite/Detector.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Locale; import java.util.PriorityQueue; +import timber.log.Timber; /** * Wrapper for frozen detection models trained using the Tensorflow Object Detection API: @@ -158,7 +159,7 @@ protected Detector(Activity activity, Model model, Device device, int numThreads labels = loadLabelList(activity); parseTflite(); - LOGGER.d("Created a Tensorflow Lite Detector."); + Timber.d("Created a Tensorflow Lite Detector."); } /** Reads label list from Assets. */ @@ -194,7 +195,7 @@ public List recognizeImage(final Bitmap bitmap, String className) runInference(); long endTime = SystemClock.elapsedRealtime(); Trace.endSection(); - LOGGER.v("Timecost to run model inference: " + (endTime - startTime)); + Timber.v("Timecost to run model inference: %s", (endTime - startTime)); Trace.endSection(); // "recognizeImage" return getRecognitions(className); diff --git a/android/app/src/main/java/org/openbot/utils/Enums.java b/android/app/src/main/java/org/openbot/utils/Enums.java index b5944ba23..ed9f5db95 100644 --- a/android/app/src/main/java/org/openbot/utils/Enums.java +++ b/android/app/src/main/java/org/openbot/utils/Enums.java @@ -4,6 +4,31 @@ import java.util.EnumSet; public class Enums { + public enum SensorType { + ACCELEROMETER("Accelerometer"), + GYROSCOPE("Gyroscope"), + PROXIMITY("Proximity"), + GRAVITY("Gravity"), + MAGNETIC("Magnetic"), + LIGHT("Light"), + PRESSURE("Pressure"), + TEMPERATURE("Temperature"), + GPS("Gps"), + VEHICLE("Vehicle"), + POSE("Pose"), + MOTION("Motion"); + + private String sensor; + + SensorType(String sensor) { + this.sensor = sensor; + } + + public String getSensor() { + return sensor; + } + } + public enum LogMode { ALL_IMGS(0), CROP_IMG(1), diff --git a/android/app/src/main/res/drawable/button_background_border.xml b/android/app/src/main/res/drawable/button_background_border.xml new file mode 100755 index 000000000..914ded38d --- /dev/null +++ b/android/app/src/main/res/drawable/button_background_border.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout-land/dialog_sensors.xml b/android/app/src/main/res/layout-land/dialog_sensors.xml new file mode 100644 index 000000000..1baefce29 --- /dev/null +++ b/android/app/src/main/res/layout-land/dialog_sensors.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +