package com.example.carcontroller.SLAM; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; import androidx.preference.PreferenceManager; import com.example.carcontroller.Empty; import com.example.carcontroller.SlamControlGrpc; import com.example.carcontroller.SlamDetails; import com.example.carcontroller.SlamLocation; import com.google.protobuf.ByteString; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; public class SlamView extends SurfaceView implements SlamUpdater.MapChangedListener { private SlamUpdater slam; private Thread mapThread; private Context context; private int width; private SurfaceHolder surfaceHolder; private Paint paint; private SlamControlGrpc.SlamControlStub stub; private ManagedChannel channel; private int mapSizePixels; private int mapSizeMeters; public SlamView(Context context) { super(context); this.context = context; init(); } public SlamView(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; init(); } public SlamView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; init(); } private void init() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String host = prefs.getString("host", "10.0.0.53"); String port = prefs.getString("zmqPort", "5050"); String gRPCPort = prefs.getString("port", "50051"); mapSizePixels = Integer.parseInt(prefs.getString("MAPSIZEPIXELS", "540")); mapSizeMeters = Integer.parseInt(prefs.getString("MAPSIZEMETRES", "10")); slam = new ZmqSlamUpdater(host, port); slam.addMapChangedListener(this); surfaceHolder = getHolder(); paint = new Paint(); paint.setColor(Color.BLUE); mapThread = new Thread(slam); channel = ManagedChannelBuilder.forAddress(host, Integer.parseInt(gRPCPort)).usePlaintext().build(); stub = SlamControlGrpc.newStub(channel); } /** * Called by MainActivity.onResume() to start a thread. */ public void resume() { StreamObserver response = new StreamObserver() { @Override public void onNext(Empty value) { mapThread.start(); } @Override public void onError(Throwable t) { // TODO: close the activity, System.out.println(t.getMessage()); } @Override public void onCompleted() { // Don't care. } }; // use async grpc method, ZMQ doesn't need to connect straight away. stub.startMapStreaming(SlamDetails.newBuilder().setMapSizePixels(mapSizePixels).setMapSizeMeters(mapSizeMeters).build(), response); } public void stop() { // Use grpc to tell the loader to stop. Interrupt the loader thread as well. slam.stop(); try { mapThread.join(); } catch (InterruptedException e) { } } @Override public void mapChanged(ByteString map, SlamLocation location) { if (surfaceHolder.getSurface().isValid()) { Canvas canvas = surfaceHolder.lockCanvas(); canvas.save(); canvas.drawColor(Color.WHITE); // Using width as we want square. Bitmap bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8); for (int i = 0; i < width; i++) { for (int j = 0; j < width; j++) { // 0-255 is appropriate for the config used. bitmap.setPixel(i, j, map.byteAt(i * width + j)); } } canvas.drawBitmap(bitmap, 0, 0, paint); canvas.restore(); surfaceHolder.unlockCanvasAndPost(canvas); } } @Override protected void onSizeChanged(int w, int b, int oldW, int oldB) { super.onSizeChanged(w, b, oldW, oldB); width = w; } }