diff --git a/build.gradle b/build.gradle
index 9157aa4..a6f7177 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.2'
+ classpath 'com.android.tools.build:gradle:3.1.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.4'
diff --git a/mobile/src/main/res/drawable-hdpi/banner_screenshot.png b/mobile/src/main/res/drawable-hdpi/banner_screenshot.png
new file mode 100644
index 0000000..6a184e6
Binary files /dev/null and b/mobile/src/main/res/drawable-hdpi/banner_screenshot.png differ
diff --git a/mobile/src/main/res/drawable-mdpi/banner_screenshot.png b/mobile/src/main/res/drawable-mdpi/banner_screenshot.png
new file mode 100644
index 0000000..ea562f8
Binary files /dev/null and b/mobile/src/main/res/drawable-mdpi/banner_screenshot.png differ
diff --git a/mobile/src/main/res/drawable-xhdpi/banner_screenshot.png b/mobile/src/main/res/drawable-xhdpi/banner_screenshot.png
new file mode 100644
index 0000000..1150cab
Binary files /dev/null and b/mobile/src/main/res/drawable-xhdpi/banner_screenshot.png differ
diff --git a/mobile/src/main/res/drawable-xxhdpi/banner_screenshot.png b/mobile/src/main/res/drawable-xxhdpi/banner_screenshot.png
new file mode 100644
index 0000000..c6f297a
Binary files /dev/null and b/mobile/src/main/res/drawable-xxhdpi/banner_screenshot.png differ
diff --git a/mobile/src/main/res/drawable-xxxhdpi/banner_screenshot.png b/mobile/src/main/res/drawable-xxxhdpi/banner_screenshot.png
new file mode 100644
index 0000000..82f3117
Binary files /dev/null and b/mobile/src/main/res/drawable-xxxhdpi/banner_screenshot.png differ
diff --git a/things/src/main/AndroidManifest.xml b/things/src/main/AndroidManifest.xml
index a113772..67a983e 100644
--- a/things/src/main/AndroidManifest.xml
+++ b/things/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
diff --git a/things/src/main/java/com/example/androidthings/lantern/ChannelsRegistry.kt b/things/src/main/java/com/example/androidthings/lantern/ChannelsRegistry.kt
index ff4e04d..c0f4c96 100644
--- a/things/src/main/java/com/example/androidthings/lantern/ChannelsRegistry.kt
+++ b/things/src/main/java/com/example/androidthings/lantern/ChannelsRegistry.kt
@@ -23,6 +23,12 @@ object ChannelsRegistry {
Uri.parse("android.resource://com.example.androidthings.lantern/drawable/banner_clock"),
customizable = true
)),
+ Pair(::ScreenShot, ChannelInfo(
+ "ScreenShot",
+ "Take a picture",
+ "Take a picture of something that is under lantern and project it to any surface.",
+ Uri.parse("android.resource://com.example.androidthings.lantern/drawable/banner_screenshot")
+ )),
Pair(::NowPlayingChannel, ChannelInfo(
"now-playing",
"Now playing",
diff --git a/things/src/main/java/com/example/androidthings/lantern/channels/ScreenShot.kt b/things/src/main/java/com/example/androidthings/lantern/channels/ScreenShot.kt
new file mode 100644
index 0000000..cb5af4a
--- /dev/null
+++ b/things/src/main/java/com/example/androidthings/lantern/channels/ScreenShot.kt
@@ -0,0 +1,61 @@
+package com.example.androidthings.lantern.channels
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Matrix
+import android.media.ImageReader
+import android.os.Bundle
+import android.os.Handler
+import android.os.HandlerThread
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.example.androidthings.lantern.Channel
+import com.example.androidthings.lantern.hardware.Camera
+
+
+/**
+ * Makes a picture with the camera and projects it afterwards
+ */
+class ScreenShot : Channel() {
+
+ val TAG = this::class.java.simpleName
+
+ private lateinit var view: ImageView
+
+ private val handler: Handler = Handler()
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ this.view = ImageView(context)
+ this.view.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ val mCameraThread = HandlerThread("CameraBackground")
+ mCameraThread.start()
+ val mCameraHandler = Handler(mCameraThread.looper)
+
+ val mCamera = Camera.getInstance()
+ mCamera.initializeCamera(this.activity, mCameraHandler, imageAvailableListener)
+
+ handler.postDelayed({ mCamera.takePicture() }, 3000)
+ return view
+ }
+
+ private val imageAvailableListener = ImageReader.OnImageAvailableListener { reader ->
+ Log.d(TAG, "ImageAvailable!")
+ val image = reader.acquireLatestImage()
+ val imageBuffer = image.planes[0].buffer
+ val imageBytes = ByteArray(imageBuffer.remaining())
+ imageBuffer.get(imageBytes)
+ image.close()
+ val bitmap = getBitmapFromByteArray(imageBytes)
+ handler.post({ this.view.setImageBitmap(bitmap) })
+ }
+
+ private fun getBitmapFromByteArray(imageBytes: ByteArray): Bitmap {
+ val bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, Matrix(), true)
+ }
+}
\ No newline at end of file
diff --git a/things/src/main/java/com/example/androidthings/lantern/hardware/Camera.java b/things/src/main/java/com/example/androidthings/lantern/hardware/Camera.java
new file mode 100644
index 0000000..496b59c
--- /dev/null
+++ b/things/src/main/java/com/example/androidthings/lantern/hardware/Camera.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.androidthings.lantern.hardware;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+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.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Size;
+
+import java.util.Collections;
+
+import static android.content.Context.CAMERA_SERVICE;
+
+/**
+ * Helper class to deal with methods to deal with images from the camera.
+ */
+public class Camera {
+ private static final String TAG = Camera.class.getSimpleName();
+
+ private static final int IMAGE_WIDTH = 1280;
+ private static final int IMAGE_HEIGHT = 720;
+ private static final int MAX_IMAGES = 1;
+
+ private CameraDevice mCameraDevice;
+
+ private CameraCaptureSession mCaptureSession;
+
+ /**
+ * An {@link ImageReader} that handles still image capture.
+ */
+ private ImageReader mImageReader;
+
+ // Lazy-loaded singleton, so only one instance of the camera is created.
+ private Camera() {
+ }
+
+ private static class InstanceHolder {
+ private static Camera mCamera = new Camera();
+ }
+
+ public static Camera getInstance() {
+ return InstanceHolder.mCamera;
+ }
+
+ /**
+ * Initialize the camera device
+ */
+ public void initializeCamera(Context context,
+ Handler backgroundHandler,
+ ImageReader.OnImageAvailableListener imageAvailableListener) {
+ // Discover the camera instance
+ CameraManager manager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
+ String[] camIds = {};
+ try {
+ camIds = manager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Cam access exception getting IDs", e);
+ }
+ if (camIds.length < 1) {
+ Log.e(TAG, "No cameras found");
+ return;
+ }
+ String id = camIds[0];
+ Log.d(TAG, "Using camera id " + id);
+
+ // Initialize the image processor
+ mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT,
+ ImageFormat.JPEG, MAX_IMAGES);
+ mImageReader.setOnImageAvailableListener(
+ imageAvailableListener, backgroundHandler);
+
+ // Open the camera resource
+ try {
+ manager.openCamera(id, mStateCallback, backgroundHandler);
+ } catch (CameraAccessException cae) {
+ Log.d(TAG, "Camera access exception", cae);
+ }
+ }
+
+ /**
+ * Callback handling device state changes
+ */
+ private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
+ @Override
+ public void onOpened(CameraDevice cameraDevice) {
+ Log.d(TAG, "Opened camera.");
+ mCameraDevice = cameraDevice;
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice cameraDevice) {
+ Log.d(TAG, "Camera disconnected, closing.");
+ cameraDevice.close();
+ }
+
+ @Override
+ public void onError(CameraDevice cameraDevice, int i) {
+ Log.d(TAG, "Camera device error, closing.");
+ cameraDevice.close();
+ }
+
+ @Override
+ public void onClosed(CameraDevice cameraDevice) {
+ Log.d(TAG, "Closed camera, releasing");
+ mCameraDevice = null;
+ }
+ };
+
+ /**
+ * Begin a still image capture
+ */
+ public void takePicture() {
+ if (mCameraDevice == null) {
+ Log.e(TAG, "Cannot capture image. Camera not initialized.");
+ return;
+ }
+
+ // Here, we create a CameraCaptureSession for capturing still images.
+ try {
+ mCameraDevice.createCaptureSession(
+ Collections.singletonList(mImageReader.getSurface()),
+ mSessionCallback,
+ null);
+ } catch (CameraAccessException cae) {
+ Log.e(TAG, "access exception while preparing pic", cae);
+ }
+ }
+
+ /**
+ * Callback handling session state changes
+ */
+ private CameraCaptureSession.StateCallback mSessionCallback =
+ new CameraCaptureSession.StateCallback() {
+ @Override
+ public void onConfigured(CameraCaptureSession cameraCaptureSession) {
+ // The camera is already closed
+ if (mCameraDevice == null) {
+ return;
+ }
+
+ // When the session is ready, we start capture.
+ mCaptureSession = cameraCaptureSession;
+ triggerImageCapture();
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
+ Log.e(TAG, "Failed to configure camera");
+ }
+ };
+
+ /**
+ * Execute a new capture request within the active session
+ */
+ private void triggerImageCapture() {
+ try {
+ final CaptureRequest.Builder captureBuilder =
+ mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ captureBuilder.addTarget(mImageReader.getSurface());
+ captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
+ Log.d(TAG, "Session initialized.");
+ mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null);
+ } catch (CameraAccessException cae) {
+ Log.e(TAG, "camera capture exception", cae);
+ }
+ }
+
+ /**
+ * Callback handling capture session events
+ */
+ private final CameraCaptureSession.CaptureCallback mCaptureCallback =
+ new CameraCaptureSession.CaptureCallback() {
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session,
+ CaptureRequest request,
+ CaptureResult partialResult) {
+ Log.d(TAG, "Partial result");
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session,
+ CaptureRequest request,
+ TotalCaptureResult result) {
+ if (session != null) {
+ session.close();
+ mCaptureSession = null;
+ Log.d(TAG, "CaptureSession closed");
+ }
+ }
+ };
+
+
+ /**
+ * Close the camera resources
+ */
+ public void shutDown() {
+ if (mCameraDevice != null) {
+ mCameraDevice.close();
+ }
+ }
+
+ /**
+ * Helpful debugging method: Dump all supported camera formats to log. You don't need to run
+ * this for normal operation, but it's very helpful when porting this code to different
+ * hardware.
+ */
+ public static void dumpFormatInfo(Context context) {
+ CameraManager manager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
+ String[] camIds = {};
+ try {
+ camIds = manager.getCameraIdList();
+ } catch (CameraAccessException e) {
+ Log.d(TAG, "Cam access exception getting IDs");
+ }
+ if (camIds.length < 1) {
+ Log.d(TAG, "No cameras found");
+ }
+ String id = camIds[0];
+ Log.d(TAG, "Using camera id " + id);
+ try {
+ CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
+ StreamConfigurationMap configs = characteristics.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ for (int format : configs.getOutputFormats()) {
+ Log.d(TAG, "Getting sizes for format: " + format);
+ for (Size s : configs.getOutputSizes(format)) {
+ Log.d(TAG, "\t" + s.toString());
+ }
+ }
+ int[] effects = characteristics.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS);
+ for (int effect : effects) {
+ Log.d(TAG, "Effect available: " + effect);
+ }
+ } catch (CameraAccessException e) {
+ Log.d(TAG, "Cam access exception getting characteristics.");
+ }
+ }
+}
\ No newline at end of file