From 3dd04d16791c59f5185337c29b807e16e18123cd Mon Sep 17 00:00:00 2001 From: Vladd11 Date: Sat, 9 Apr 2022 23:53:45 +0400 Subject: [PATCH] Released version. --- app/build.gradle | 7 +- app/src/main/AndroidManifest.xml | 3 + .../vladd11/app/robot/ConnectionManager.java | 11 +- .../com/vladd11/app/robot/MainActivity.java | 36 ++- .../com/vladd11/app/robot/PathFinder.java | 9 +- .../app/robot/StreamingController.java | 218 ++++++++---------- app/src/main/res/layout/activity_main.xml | 43 ++-- app/src/main/res/values/themes.xml | 2 +- app/src/main/res/xml/device_filter.xml | 41 +--- build.gradle | 8 +- firmware/firmware.ino | 10 +- gradle.properties | 1 + settings.gradle | 6 + 13 files changed, 191 insertions(+), 204 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 164d355..2ff4a17 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { defaultConfig { applicationId "com.vladd11.app.robot" - minSdk 28 - targetSdk 31 + minSdk 27 + targetSdk 27 versionCode 1 versionName "1.0" @@ -42,6 +42,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.1' + implementation project(path: ':libusbcamera') testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' @@ -54,4 +55,6 @@ dependencies { // RxJava is also required. implementation 'io.reactivex.rxjava2:rxjava:2.1.12' implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' + + //implementation 'com.github.vladd11:AndroidUSBCamera:7db7cc7e0e' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 59ea437..9d70f24 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,11 +4,14 @@ + + + webSocket.send(ByteString.of(buffer))); + try { + onFrameRequest.call(buffer -> webSocket.send(ByteString.of(buffer))); + } catch (CameraAccessException | InterruptedException e) { + e.printStackTrace(); + } } else if (text.startsWith("route")) { final List> rawCoordinates = gson.fromJson( text.replace("route ", ""), @@ -105,11 +110,11 @@ public interface OnLocationPointsAvailableListener { } public interface OnImageRequestReceivedListener { - void call(ImageReceivedListener listener); + void call(ImageReceivedListener listener) throws CameraAccessException, InterruptedException; } public interface ImageReceivedListener { - void received(ByteBuffer buffer) throws InterruptedException; + void received(byte[] buffer) throws InterruptedException; } public interface OnConnectedListener { diff --git a/app/src/main/java/com/vladd11/app/robot/MainActivity.java b/app/src/main/java/com/vladd11/app/robot/MainActivity.java index 1c0ff0d..21e0d00 100644 --- a/app/src/main/java/com/vladd11/app/robot/MainActivity.java +++ b/app/src/main/java/com/vladd11/app/robot/MainActivity.java @@ -9,6 +9,7 @@ import android.hardware.usb.UsbManager; import android.location.Location; import android.os.Bundle; +import android.os.Environment; import android.os.Looper; import android.util.Log; import android.view.View; @@ -48,9 +49,22 @@ public class MainActivity extends AppCompatActivity implements PathFinder.PathFi private TextView actionTextView; private TextView headingTextView; private TextView bearingTextView; + private TextView diffTextView; private SimpleBluetoothDeviceInterface deviceInterface; + @Override + protected void onStart() { + super.onStart(); + if (controller.mCameraHelper != null) controller.mCameraHelper.registerUSB(); + } + + @Override + protected void onStop() { + if (controller.mCameraHelper != null) controller.mCameraHelper.unregisterUSB(); + super.onStop(); + } + @SuppressLint("CheckResult") @Override protected void onCreate(Bundle savedInstanceState) { @@ -62,6 +76,7 @@ protected void onCreate(Bundle savedInstanceState) { actionTextView = findViewById(R.id.currentActionText); headingTextView = findViewById(R.id.headingTextView); bearingTextView = findViewById(R.id.bearingTextView); + diffTextView = findViewById(R.id.diffTextView); final Compass compass = new Compass(); pathFinder = new PathFinder(this); @@ -141,6 +156,7 @@ private void onConnected(BluetoothSerialDevice connectedDevice) { @Override public void whenActionChanged(Action action) { + if (deviceInterface != null) { switch (action) { case FORWARD: @@ -215,23 +231,20 @@ public void whenLocationAccurate() { connectionManager.connect(() -> { try { connectionManager.getLocationPoints(pathFinder.location, this::followPathOfPoints); - connectionManager.receiveImageRequests(listener -> { - try { - controller.send((buffer) -> { - Log.d(TAG, "received"); - listener.received(buffer); - }); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - }, (roadPosition, frameTime) -> { + connectionManager.receiveImageRequests(listener -> controller.send((buffer) -> { + Log.d(TAG, "received"); + listener.received(buffer); + }), (roadPosition, frameTime) -> { for (List cords : roadPosition) { double x = cords.get(0) - 0.5f; //double y = cords.get(1); if (x < -0.2f || x > 0.2) { if (pathFinder.isFrameValid(frameTime)) { - pathFinder.setAngleDiff((int) (x * 60)); + int diff = (int) (x * 60); + + diffTextView.setText(String.valueOf(diff)); + pathFinder.setAngleDiff(diff); } } } @@ -244,6 +257,7 @@ public void whenLocationAccurate() { @Override protected void onDestroy() { + controller.stop(); connectionManager.disconnect(); super.onDestroy(); } diff --git a/app/src/main/java/com/vladd11/app/robot/PathFinder.java b/app/src/main/java/com/vladd11/app/robot/PathFinder.java index 335be03..e4a30c6 100644 --- a/app/src/main/java/com/vladd11/app/robot/PathFinder.java +++ b/app/src/main/java/com/vladd11/app/robot/PathFinder.java @@ -40,11 +40,8 @@ public void start() { public void run() { if (isLocationAccurate && target != null) { float relativeBearing = location.bearingTo(target) - heading; - if (relativeBearing < 0) { - relativeBearing = 360 + relativeBearing; - } - if (!(almostEqual(relativeBearing, 0, 7) || almostEqual(relativeBearing, 360, 7))) { + if (!(almostEqual(relativeBearing, 0, 14))) { if (relativeBearing < 0) listener.whenActionChanged(Action.LEFT); else listener.whenActionChanged(Action.RIGHT); } else listener.whenActionChanged(Action.FORWARD); @@ -65,7 +62,7 @@ public void onLocationResult(@NonNull LocationResult locationResult) { } if (target != null) { - if (location.distanceTo(target) > location.getAccuracy()) { + if (location.distanceTo(target) > Math.max(location.getAccuracy(), 5)) { shouldForward = true; } else nextPoint(); @@ -109,7 +106,7 @@ public int getAngle() { } public void setAngleDiff(int angleDiff) { - this.diff = angleDiff; + //this.diff = angleDiff; } @Override diff --git a/app/src/main/java/com/vladd11/app/robot/StreamingController.java b/app/src/main/java/com/vladd11/app/robot/StreamingController.java index d8f6dfb..0d9d5e8 100644 --- a/app/src/main/java/com/vladd11/app/robot/StreamingController.java +++ b/app/src/main/java/com/vladd11/app/robot/StreamingController.java @@ -1,174 +1,136 @@ package com.vladd11.app.robot; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.ImageFormat; +import android.app.Activity; +import android.graphics.Bitmap; import android.hardware.camera2.CameraAccessException; -import android.hardware.camera2.CameraCaptureSession; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.CameraMetadata; -import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.TotalCaptureResult; -import android.media.Image; -import android.media.ImageReader; +import android.hardware.usb.UsbDevice; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import androidx.annotation.NonNull; +import com.jiangdg.usbcamera.UVCCameraHelper; +import com.serenegiant.usb.CameraDialog; +import com.serenegiant.usb.USBMonitor; +import com.serenegiant.usb.common.AbstractUVCCameraHandler; +import com.serenegiant.usb.widget.CameraViewInterface; +import com.serenegiant.usb.widget.UVCCameraTextureView; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -public class StreamingController { +public class StreamingController implements CameraDialog.CameraDialogParent { + private static final int WIDTH = 640; + private static final int HEIGHT = 480; private static final String TAG = "StreamingController"; - private CameraCaptureSession cameraSession; - private Surface imReaderSurface; - private ImageReader imageReader; - public final int width = 720; - public final int height = 1280; - private final Context context; - private final SurfaceView surfaceView; + private final Activity activity; + private final CameraViewInterface surfaceView; private final Handler handler; + public UVCCameraHelper mCameraHelper; + private boolean isPreview; + private boolean isRequest; + private boolean twice; - public StreamingController(Context context, SurfaceView surfaceView) { - this.context = context; + public StreamingController(Activity activity, UVCCameraTextureView surfaceView) { + this.activity = activity; this.surfaceView = surfaceView; this.handler = new Handler(Looper.myLooper()); } - public void send(OnImageReadyListener listener) throws CameraAccessException { - CaptureRequest.Builder builder = cameraSession.getDevice().createCaptureRequest( - CameraDevice.TEMPLATE_VIDEO_SNAPSHOT); - builder.addTarget(imReaderSurface); - cameraSession.capture(builder.build(), new CameraCaptureSession.CaptureCallback() { - @Override - public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { - imageReader.setOnImageAvailableListener(reader -> { - final Image image = imageReader.acquireLatestImage(); - if (image == null) return; - Log.d(TAG, "onCaptureCompleted"); - + public void send(OnImageReadyListener listener) throws InterruptedException { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - try { - listener.call(image.getPlanes()[0].getBuffer()); - } catch (InterruptedException e) { - e.printStackTrace(); - } + surfaceView.captureStillImage(WIDTH, HEIGHT).compress(Bitmap.CompressFormat.JPEG, 90, stream); + listener.call(stream.toByteArray()); + } - image.close(); - }, handler); - } - }, handler); + public void stop() { + if(mCameraHelper != null) { + mCameraHelper.unregisterUSB(); + mCameraHelper.stopPreview(); + } } public void start() throws CameraAccessException { - surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { + if(twice) return; + twice = true; + + mCameraHelper = UVCCameraHelper.getInstance(640, 480); + + surfaceView.setCallback(new CameraViewInterface.Callback() { @Override - public void surfaceCreated(@NonNull SurfaceHolder holder) { - imageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2); - -// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() - //final Surface previewSurface = surfaceView.getHolder().getSurface(); - imReaderSurface = imageReader.getSurface(); - final Surface[] targets = new Surface[]{imReaderSurface, surfaceView.getHolder().getSurface()}; - - try { - getBackCamera(device -> { - try { - device.createCaptureSession(Arrays.asList(targets), new CameraCaptureSession.StateCallback() { - @Override - public void onConfigured(@NonNull CameraCaptureSession session) { - cameraSession = session; - try { - CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest( - CameraDevice.TEMPLATE_PREVIEW); - captureRequest.addTarget(surfaceView.getHolder().getSurface()); - session.setRepeatingRequest(captureRequest.build(), null, handler); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - - @Override - public void onConfigureFailed(@NonNull CameraCaptureSession session) { - - } - }, handler); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - }); - } catch (CameraAccessException e) { - e.printStackTrace(); + public void onSurfaceCreated(CameraViewInterface view, Surface surface) { + if (!isPreview && mCameraHelper.isCameraOpened()) { + mCameraHelper.startPreview(surfaceView); + isPreview = true; } } @Override - public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { + public void onSurfaceChanged(CameraViewInterface view, Surface surface, int width, int height) { } @Override - public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + public void onSurfaceDestroy(CameraViewInterface view, Surface surface) { } }); - } - @SuppressLint("MissingPermission") - public void getBackCamera(OnCameraReadyListener listener) throws CameraAccessException { - final CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); - - final String[] preCameraIds = cameraManager.getCameraIdList(); - final List cameraIds = new ArrayList<>(); - // Get list of all compatible cameras - for (String id : - preCameraIds) { - try { - final CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); - final int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); - for (int capability : - capabilities) { - if (capability == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) { - cameraIds.add(id); - } + mCameraHelper.setOnPreviewFrameListener(nv21Yuv -> Log.d(TAG, "onPreviewResult: "+nv21Yuv.length)); + mCameraHelper.setDefaultPreviewSize(WIDTH, HEIGHT); + mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_MJPEG); +// set default preview size + /*m + mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_MJPEG);*/ +// set default frame format,defalut is UVCCameraHelper.FRAME_FORMAT_MJPEG +// if using mpeg can not record mp4,please try yuv +// mCameraHelper.setDefaultFrameFormat(UVCCameraHelper.FRAME_FORMAT_YUYV); + mCameraHelper.initUSBMonitor(activity, surfaceView, new UVCCameraHelper.OnMyDevConnectListener() { + @Override + public void onAttachDev(UsbDevice device) { + Log.d(TAG, "onAttachDev"); + if (!isRequest) { + isRequest = true; + mCameraHelper.requestPermission(0); } - } catch (CameraAccessException e) { - e.printStackTrace(); } - } - - // Iterate over the list of cameras and return the first one matching desired - // lens-facing configuration - for (String id : cameraIds) { - final CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); - if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK) { - cameraManager.openCamera(id, new CameraDevice.StateCallback() { - @Override - public void onOpened(@NonNull CameraDevice camera) { - listener.call(camera); - } - @Override - public void onDisconnected(@NonNull CameraDevice camera) { + @Override + public void onDettachDev(UsbDevice device) { + Log.d(TAG, "onDettachDev"); + if (isRequest) { + isRequest = false; + mCameraHelper.closeCamera(); + } + } - } + @Override + public void onConnectDev(UsbDevice device, boolean isConnected) { + if (isConnected) { + Log.d(TAG, "onConnectDev"); + } else Log.e(TAG, "onConnectDev: fail"); + } - @Override - public void onError(@NonNull CameraDevice camera, int error) { + @Override + public void onDisConnectDev(UsbDevice device) { - } - }, handler); } - } + }); + //handler.postDelayed(() -> mCameraHelper.requestPermission(0), 3000); + } + + @Override + public USBMonitor getUSBMonitor() { + return mCameraHelper.getUSBMonitor(); + } + + @Override + public void onDialogResult(boolean canceled) { + Log.d(TAG, "onDialogResult"); } public interface OnCameraReadyListener { @@ -176,6 +138,6 @@ public interface OnCameraReadyListener { } public interface OnImageReadyListener { - void call(ByteBuffer buffer) throws InterruptedException; + void call(byte[] buffer) throws InterruptedException; } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fe5e397..e472096 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,40 +8,51 @@ + + + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toEndOf="@+id/frameLayout" + app:layout_constraintTop_toBottomOf="@+id/diffTextView" /> + app:layout_constraintStart_toEndOf="@+id/frameLayout" + app:layout_constraintTop_toBottomOf="@+id/bearingTextView" /> - + app:layout_constraintTop_toTopOf="parent"> + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 8bc318b..aca5cb8 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,6 @@ -