Sample Code

Learn how to implement Lens in an Android application

Here are some examples of implementing Lens in an Android application.

Basic Setup

XML layout contains two views: LensCameraView for preview and AppCompatButtton to initiate image capture. Java/Kotlin containsbasic logic to enable camera and initiate capture.

<xml version="1.0" encoding="utf-8">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- Camera View -->
    <com.truepic.lenssdk.LensCameraView
        android:id="@+id/lens_camera"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

    <!-- Capture Button -->
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/capture_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Take Picture"
        android:layout_margin="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
</xml>
public class CameraActivityExample extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        LensCameraView lensCamera = findViewById(R.id.lens_camera);
        AppCompatButton captureButton = findViewById(R.id.capture_button);

        lensCamera.setApiKey("api key here", new LensHandler() {
            @Override
            public void onError(LensError lensError) {
                handleError(lensError);
            }

            @Override
            public void enableCapture(boolean enabled) {
                captureButton.setEnabled(enabled);

                if(!enabled) {
                    // optional: notify user
                }
            }
        }, false);

        captureButton.setOnClickListener(view -> lensCamera.startCapture(new LensPhotoCaptureHandler() {
            @Override
            public void onTruepicCaptured(UUID uuid, Truepic truepic) {
                // save C2PA image
            }

            @Override
            public void onThumbnailCaptured(UUID uuid, Truepic truepic) {
                // optional
                // save thumbnail while C2PA image is being created
            }
        }));

    }

    private void handleError(LensError error) {
        switch (error.errorType()) {
            case Env_PlayStoreServices:
            case Env_AirplaneMode:
            case Env_DevMode:
            case Env_Network:
            case Permissions_Location:
            case Permissions_Camera:
            case Enrollment:
            case Attestation:
            case Camera:
            case KeyGeneration:
            case Native:
            default:
                Log.d("Camera", error.errorMessage())
        }
    }

}
class CameraActivityExample : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_camera)

        val lensCamera: LensCameraView = findViewById(R.id.lens_camera)
        val captureButton: AppCompatButton = findViewById(R.id.capture_button)

        lensCamera.setApiKey("api key here", object : LensHandler {
            override fun onError(error: LensError) {
                handleError(error)
            }

            override fun enableCapture(enable: Boolean) {
                captureButton.isEnabled = enabled

                if(!enabled) {
                    // optional: notify user
                }
            }

        })

        captureButton.setOnClickLenstener {
            lensCamera.startCapture(object : LensPhotoCaptureHandler {
                override fun onTruepicCaptured(uuid: UUID, truepic: Truepic) {
                    // save C2PA image
                }

                override fun onThumbnailCaptured(uuid: UUID, truepic: Truepic) {
                    // optional
                    // save thumbnail while C2PA image is being created
                }

            })
        }
    }

    private fun handleError(error: LensError) {
        when (error.errorType()) {
            ErrorType.Env_PlayStoreServices,
            ErrorType.Env_AirplaneMode,
            ErrorType.Env_DevMode,
            ErrorType.Env_Network,
            ErrorType.Permissions_Location,
            ErrorType.Permissions_Camera,
            ErrorType.Enrollment,
            ErrorType.Attestation,
            ErrorType.Camera,
            ErrorType.KeyGeneration,
            ErrorType.Native,
            else ->
                Log.d("Camera", error.errorMessage())
        }
    }

}

Touch listener for camera gestures

This is a sample implementation of pinch to zoom (scale) and tap to focus gestures that could be added to the camera preview by utilizing lensCameraView.setOnTouchListener.

public class CameraTouchListener implements View.OnTouchListener, ScaleGestureDetector.OnScaleGestureListener {
    private final ScaleGestureDetector gestureScale;
    float scaleFactor = 1;
    boolean isScaling = false;
    LensCamera camera;
    ImageView focusReticle;

    public CameraTouchListener(Context context, LensCamera camera, ImageView focusReticle) {
        this.camera = camera;
        gestureScale = new ScaleGestureDetector(context, this);
        this.focusReticle = focusReticle;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // pinch to zoom
        scaleFactor *= detector.getScaleFactor();
        scaleFactor = scaleFactor = Math.max(scaleFactor, 1);
        scaleFactor = ((float)((int)(scaleFactor * 100))) / 100;
        camera.setZoomRatio(scaleFactor);
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        isScaling = true;
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        isScaling = false;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        gestureScale.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                return true;
            case MotionEvent.ACTION_UP:
                if (!isScaling)
										// focusing
                    focusReticle.setX(event.getX() - focusReticle.getWidth() / 2.0F);
                    focusReticle.setY(event.getY() - focusReticle.getHeight() / 2.0F);
                    focusReticle.setVisibility(View.VISIBLE);
                    camera.focusMeteringOnTap(event, LensCamera.MODE_AE | LensCamera.MODE_AF | LensCamera.MODE_AWB, () -> {
                        if ((focusReticle != null)&&(focusReticle.getVisibility() == View.VISIBLE)) {
                            focusReticle.setVisibility(View.INVISIBLE);
                        }
                    });
                v.performClick();
                return true;
            default:
                return false;

        }
    }
}
class CameraTouchListenerKt(context: Context, private var camera: LensCamera, private var focusReticle: ImageView) : View.OnTouchListener,
    ScaleGestureDetector.OnScaleGestureListener {
    private val gestureScale: ScaleGestureDetector = ScaleGestureDetector(context, this)
    private var scaleFactor = 1f
    private var isScaling = false

    override fun onScale(detector: ScaleGestureDetector): Boolean {
        // pinch to zoom
        scaleFactor *= detector.scaleFactor
        scaleFactor = scaleFactor.coerceAtLeast(1f)
        scaleFactor = (scaleFactor * 100).toInt().toFloat() / 100
        camera.setZoomRatio(scaleFactor)
        return true
    }

    override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
        isScaling = true
        return true
    }

    override fun onScaleEnd(detector: ScaleGestureDetector) {
        isScaling = false
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouch(v: View, event: MotionEvent): Boolean {
        gestureScale.onTouchEvent(event)
        return when (event.action) {
            MotionEvent.ACTION_DOWN -> true
            MotionEvent.ACTION_UP -> {
                if (!isScaling) {
                  // focusing
                  focusReticle.x = event.x - focusReticle.width / 2.0f
                	focusReticle.y = event.y - focusReticle.height / 2.0f
                	focusReticle.visibility = View.VISIBLE
                	camera.focusMeteringOnTap(event, LensCamera.MODE_AE or LensCamera.MODE_AF or LensCamera.MODE_AWB) {
                    	if (focusReticle.visibility == View.VISIBLE) {
                     	   focusReticle.visibility = View.INVISIBLE
                    	}
                	}
                }
                v.performClick()
                true
            }
            else -> false
        }
    }
}